准备
我们接下来要为网站中添加支付的功能,在添加这些功能之前,我们需要先做些准备的工作。
curl
curl 是一个比较常用的小软件,它可以用来在命令行中发送 GET 、POST 等请求,在开发时非常有用。
可以在命令行中输入 curl
来查看是否电脑已经安装了该软件,如果没有安装可以下载安装。
如果已经安装了会提示以下信息:(注意是cmd中执行,不是 powershell中~)
curl下载地址:https://curl.haxx.se/download.html,如下图所示:**
安装扩展包
专注 支付宝
和 微信支付
的支付扩展包。
composer require yansongda/pay
使用文档:https://packagist.org/packages/yansongda/pay
支付宝
获取沙箱参数
支付宝有一个沙箱环境,可以让我们不需要拥有真实的商家账号就可以进行支付的开发测试。
首先到支付定的开放平台中注册账号 https://open.alipay.com/platform/home.htm,用你的支付宝账号登录并找到沙箱环境:
进入沙箱之后,我们可以得到 APPID,然后点击设置应用公钥:
点击设置应用公钥:
点击设置公钥按钮:
点击 查看密钥生成方法
链接之后会跳转到一篇文档,里面可以下载 RSA 密钥生成工具,请根据自己的系统下载对应的版本并打开:
复制公钥内容到框中:
点击保存之后,查看支付宝公钥
:
复制这段支付宝公钥:
到此我们得到了以下三个参数,我们在代码里面要使用!!:
APPID:
2016091600527626
商户应用
私钥:
MIIEpAIBAAKCAQEAuoy1DMW1bDvNQc7CZOALVRERL+wcVV3UhbP59ICtpBx+zGiEsDuOmAFAcEPDwGNk4NhXdAkN9n/1jGiSZaKwfkBNU3tmyuYVLH0shgGzWL/S4XG51EwonUH2a6rVaI2OaiAjUXcYPfxbnpo/52PwiF8B/bfezUp+G6pjC0UbxruO2tE8nyfCq0cz1qYNGBulmECPyG5y69ECtiwVvw/+cK3rm+lxlSCUXp5cqB+HaGgd+trXLOJxZONd/fPkmKEts1pllCkTsDTxT77O0ZLtM5+D1e7VE68OMiIQvNRyl1p+HNQjsUNVX62cNfRb3cjtET5wqUT56AOeJgl9qVDDFwIDAQABAoIBAQCoy9C2sd6rBKGBPjifVipq2nqWxioNBE3cfTFaj2SO7km9Y4VMgVdRKzDHZEmnt0f8O0VGdTrxJG9mkOiGlmLkmgJd23bzeKUIEGtNBhTl5QxHecQP2KmXQaxbV8SqSgvm8xWCDSUeUU4FgMT59nAatPz0On+beiAJoG7mL64mbtuW8YsUB0wIFhTwe1T1+Q9qTO+YEh4ejteXZc1flGahjGDJbzCWwErmMILAAopiNaLNDcgx3AXeqf+ddP6jK32MikXhddYSDogX7/BXIMkOg7co5gXCG/aXMueMKsuR2wWC+pUZfxl7VBM8IEBfR8ptq9DIE8QcfMWYml4qpPNhAoGBAOZ0twlxxPGthlJ1q1EWdUy6qGD/yzHvVY0TEW4dXTJNqBv4GyF0sQktDRG0FCQtqk1lzbOHcbsEz737xwn9EpuJGWqFB4gy3Z1e77Dr5qM6bXScm5HSPX7MfC99H6cu4uxogrGLqlgE/S7OO0bNFEU4nFNcaASqZjcH9C82P1FTAoGBAM86If8Sk1tYm/TqT8p9IB2ww8+mNwZo7AVrTgYvmampRPfDX0UrmrlkdZLJVbdxYLNexmu+AwsvS3XXknWNyPf6UXqELPYNO9y+0VNusNRl03UabW4Czf5QWZPSlC0bu8xbBjVUA3O761XMfBbPtS7/X41wn3/4w1T2eGVyPjqtAoGAOMiBYR5bPIFZG3BK6gvykxla66ubUY57MeuE2/D4SbDAv0N+y9uI0436LmaEn/VwhOmUqaux5jblSRaEkH1+3DwHuytUE8cUu/XscVdu2MFIvvbnjiKTbG7OGpVl+zeeSknmCgEz08RG7gV6rZNSb0vnmNKn/p5N2TlofUmMiGkCgYApXKgWeoWxEOGoI/CjMRBs/LBIzRtkiyK4/i8Hqw6Xv7KFZZipfMeYQ4X4M3mJcPblNoCSVs3SuLDuJ4YTMqavYGZM9v7macPODsRHS+u9qUlosUqwT50AKteGWty6mDOG2ZBGqqs5uYOCj5shDnpSlCRlXdpoN6X9Wmizjvb+zQKBgQCJl5ppVH363lV6cY1dVY80oEfLHBaiMom3O/N5WcDe6G4rulBvJYtZeTF2A/EoYozsPz25MrtEFCwvluNFoH36U6hg5HL8HiXYdbp442KY/GMWLYJsSpWo737fmU5hlSqrqzOYy+ukGX/toKgBAMC72UY2S4Z5gaywlUA9GiNPHg==
支付宝公钥:(注意:这里是支付宝公钥!!!!!! 不是商户应用公钥)
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzcDiUmAFgiaBi2PzVpP+GdyPNLQuja3B26Z/3tcDDiuWbndLaA4o45J+Xj6KZ2GgQpZe5a9ZGoasas7YLGxPA5Z91eTooqmVwfPsaTUx4JHJlhiS+3R4738kA7BCXvmVKAHjG52ExwjwM4XWh+zN0jldPaS+4EtZnCrciDQtUk7IU7hbjDwqe6mUzwpI0zgyGQ0CPvug1VJBLdRgOlIaGNwVn1WbwqkrXYkHXMKHZhgT/WF4UTr+X4TpFIyA24ZVl2BtLchvFvf9C0+Ui8pWjfxjD6uE1VHtVQNEJ4MIaisfXpDMgQgkqClYDQN/udub7mq/CQUGsm9ZN5SUJEUXLwIDAQAB
支付流程
立即支付
创建支付控制器:
controllers/AlipayController.php
<?php
namespace controllers;
use Yansongda\Pay\Pay;
class AlipayController
{
public $config = [
'app_id' => '2016091600527626',
// 通知地址
'notify_url' => 'http://requestbin.fullcontact.com/r6s2a1r6',
// 跳回地址
'return_url' => 'http://localhost:9999/alipay/return',
// 支付宝公钥
'ali_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzcDiUmAFgiaBi2PzVpP+GdyPNLQuja3B26Z/3tcDDiuWbndLaA4o45J+Xj6KZ2GgQpZe5a9ZGoasas7YLGxPA5Z91eTooqmVwfPsaTUx4JHJlhiS+3R4738kA7BCXvmVKAHjG52ExwjwM4XWh+zN0jldPaS+4EtZnCrciDQtUk7IU7hbjDwqe6mUzwpI0zgyGQ0CPvug1VJBLdRgOlIaGNwVn1WbwqkrXYkHXMKHZhgT/WF4UTr+X4TpFIyA24ZVl2BtLchvFvf9C0+Ui8pWjfxjD6uE1VHtVQNEJ4MIaisfXpDMgQgkqClYDQN/udub7mq/CQUGsm9ZN5SUJEUXLwIDAQAB',
// 商户应用密钥
'private_key' => 'MIIEpAIBAAKCAQEAuoy1DMW1bDvNQc7CZOALVRERL+wcVV3UhbP59ICtpBx+zGiEsDuOmAFAcEPDwGNk4NhXdAkN9n/1jGiSZaKwfkBNU3tmyuYVLH0shgGzWL/S4XG51EwonUH2a6rVaI2OaiAjUXcYPfxbnpo/52PwiF8B/bfezUp+G6pjC0UbxruO2tE8nyfCq0cz1qYNGBulmECPyG5y69ECtiwVvw/+cK3rm+lxlSCUXp5cqB+HaGgd+trXLOJxZONd/fPkmKEts1pllCkTsDTxT77O0ZLtM5+D1e7VE68OMiIQvNRyl1p+HNQjsUNVX62cNfRb3cjtET5wqUT56AOeJgl9qVDDFwIDAQABAoIBAQCoy9C2sd6rBKGBPjifVipq2nqWxioNBE3cfTFaj2SO7km9Y4VMgVdRKzDHZEmnt0f8O0VGdTrxJG9mkOiGlmLkmgJd23bzeKUIEGtNBhTl5QxHecQP2KmXQaxbV8SqSgvm8xWCDSUeUU4FgMT59nAatPz0On+beiAJoG7mL64mbtuW8YsUB0wIFhTwe1T1+Q9qTO+YEh4ejteXZc1flGahjGDJbzCWwErmMILAAopiNaLNDcgx3AXeqf+ddP6jK32MikXhddYSDogX7/BXIMkOg7co5gXCG/aXMueMKsuR2wWC+pUZfxl7VBM8IEBfR8ptq9DIE8QcfMWYml4qpPNhAoGBAOZ0twlxxPGthlJ1q1EWdUy6qGD/yzHvVY0TEW4dXTJNqBv4GyF0sQktDRG0FCQtqk1lzbOHcbsEz737xwn9EpuJGWqFB4gy3Z1e77Dr5qM6bXScm5HSPX7MfC99H6cu4uxogrGLqlgE/S7OO0bNFEU4nFNcaASqZjcH9C82P1FTAoGBAM86If8Sk1tYm/TqT8p9IB2ww8+mNwZo7AVrTgYvmampRPfDX0UrmrlkdZLJVbdxYLNexmu+AwsvS3XXknWNyPf6UXqELPYNO9y+0VNusNRl03UabW4Czf5QWZPSlC0bu8xbBjVUA3O761XMfBbPtS7/X41wn3/4w1T2eGVyPjqtAoGAOMiBYR5bPIFZG3BK6gvykxla66ubUY57MeuE2/D4SbDAv0N+y9uI0436LmaEn/VwhOmUqaux5jblSRaEkH1+3DwHuytUE8cUu/XscVdu2MFIvvbnjiKTbG7OGpVl+zeeSknmCgEz08RG7gV6rZNSb0vnmNKn/p5N2TlofUmMiGkCgYApXKgWeoWxEOGoI/CjMRBs/LBIzRtkiyK4/i8Hqw6Xv7KFZZipfMeYQ4X4M3mJcPblNoCSVs3SuLDuJ4YTMqavYGZM9v7macPODsRHS+u9qUlosUqwT50AKteGWty6mDOG2ZBGqqs5uYOCj5shDnpSlCRlXdpoN6X9Wmizjvb+zQKBgQCJl5ppVH363lV6cY1dVY80oEfLHBaiMom3O/N5WcDe6G4rulBvJYtZeTF2A/EoYozsPz25MrtEFCwvluNFoH36U6hg5HL8HiXYdbp442KY/GMWLYJsSpWo737fmU5hlSqrqzOYy+ukGX/toKgBAMC72UY2S4Z5gaywlUA9GiNPHg==',
// 沙箱模式(可选)
'mode' => 'dev',
];
// 发起支付
public function pay()
{
$order = [
'out_trade_no' => time(), // 本地订单ID
'total_amount' => '0.01', // 支付金额
'subject' => 'test subject', // 支付标题
];
$alipay = Pay::alipay($this->config)->web($order);
$alipay->send();
}
// 支付完成跳回
public function return()
{
$data = Pay::alipay($this->config)->verify(); // 是的,验签就这么简单!
echo '<h1>支付成功!</h1> <hr>';
var_dump( $data->all() );
}
// 接收支付完成的通知
public function notify()
{
$alipay = Pay::alipay($this->config);
try{
$data = $alipay->verify(); // 是的,验签就这么简单!
// 这里需要对 trade_status 进行判断及其它逻辑进行判断,在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时,支付宝才会认定为买家付款成功。
// 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号;
// 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额);
echo '订单ID:'.$data->out_trade_no ."\r\n";
echo '支付总金额:'.$data->total_amount ."\r\n";
echo '支付状态:'.$data->trade_status ."\r\n";
echo '商户ID:'.$data->seller_id ."\r\n";
echo 'app_id:'.$data->app_id ."\r\n";
} catch (\Exception $e) {
echo '失败:';
var_dump($e->getMessage()) ;
}
// 返回响应
$alipay->success()->send();
}
}
浏览器中访问 http://localhost:9999/aliypay/pay 跳转到支付宝网站:
使用沙箱账号完成支付:
支付通知
在支付宝网站当中完成支付之后,支付宝网站会做两种事件:
- 跳转回我们的网站
- 在后台向我们的网站发消息通知我们已经支付成功,我们收到通知之后需要回复收到通知,否则支付宝会定期重复的向我们发消息
我们需要在设置在设置 支付成功跳转地址
和 支付成功的通知地址
。
$config = [
...
'notify_url' => 'http://localhost:9999/alipay/notify', // 支付成功后台通知地址
'return_url' => 'http://localhost:9999/alipay/return', // 支付成功后跳转地址
...
];
回调通知
因为我们的项目现在处在内网之中(http://localhost:9999/alipay/notify),支付宝向我们发的通知,我们现在根本就收不到,我们需要把网站放到外网上线之后才能收到通知。
我们便于开发,我们可以借助一些第三方的网站来帮我们接收通知。
RequestBin 网站就是一个接收各种通知的网站,我们可以借助它来接收通知。
1、创建新的接收地址
首先我们需要先创建网站创建一个用来接收通知的地址。
访问 https://requestbin.fullcontact.com/ ,点击中间那个绿按钮 Create a RequestBin
:
系统会分配一个 URL:
这个 URL 就是我们用来接收通知的地址,把这个地址放到支付时的通知地址上:
$config = [
...
'notify_url' => 'http://requestbin.fullcontact.com/r6s2a1r6', // 支付成功后台通知地址
...
];
设置好通知地址之后,重新完成一次支付流程,支付成功之后,到网站中查看接收到的通知。
点击以下小圆点查看接收到的消息:
然后点击查看已经收到了数据:
复制最下方 的数据:
最下方的 RAW BODY
,就是支付宝发给我们的通知。
处理通知
现在我们通过 ResustBin 网站接收到的支付宝的通知,现在我们就可以手动的把这个通知发给我们自己的网站,我们可以使用 curl 指令完成该操作。
使用 curl 把数据以 POST 方式发给我们的网站:
$ curl -XPOST http://localhost:9999/alipay/notify -d'复制 raw body 内容'
如:
发送之后会显示违法操作,这是因为我们之前项目中添加了 CSRF 防御功能,所以这里我们先注释掉 CSRF 防御的代码。
<?php
// 使用 redis 保存 SESSION
ini_set('session.save_handler', 'redis');
// 设置 redis 服务器的地址、端
ini_set('session.save_path', 'tcp://127.0.0.1:32768?database=3');
session_start();
// 如果用户以 POST 方式访问网站时,需要验证令牌
// if($_SERVER['REQUEST_METHOD'] == 'POST')
// {
// if(!isset($_POST['_token']))
// die('违法操作!');
// if($_POST['_token'] != $_SESSION['token'])
// die('违法操作!');
// }
去掉之后,就可以接收通知了。
通知数据
支付宝在给我们发送通知时,会传递以下数据:
array(23) {
["gmt_create"]=>
string(19) "2018-09-06 17:51:14"
["charset"]=>
string(3) "GBK"
["gmt_payment"]=>
string(19) "2018-09-06 17:51:19"
["notify_time"]=>
string(19) "2018-09-06 17:51:20"
["subject"]=>
string(12) "test subject"
["sign"]=>
string(344) "cL6kVn8q5UQdnzoM1RmKR8GR8Z3kg7RQ5ftpg9GatlLQLodnw1r2RLPgkKXTYgGwi6ZqNuKgQzGacYNSpdk7arWAzfeFEl266KR73y0WM+bvGVFaBejX9jLl4WTIyzn742WoJfFklA3hleYh9q8DlFeJQh6acFEwvx9GJiuxv5521xWWNN91mzLF2a4XHjkIvFqZBYU/hc1CFCx/9+bBTNebf9sOBsD0H3q9NiQrnSfI9EQ9n2UYF/NYPhsrZ0ZV/jej5CPbHyggic8OQUszmknm8EEga5VjtGRs+oJQZHI9K+HrpeFTdtmX2Ecyv/Ob7D+qLSsiGndlllOGoZ7lRQ=="
["buyer_id"]=>
string(16) "2088102176659083"
["invoice_amount"]=>
string(4) "0.01"
["version"]=>
string(3) "1.0"
["notify_id"]=>
string(34) "a835b845abf96ff1e5c23a20d9b6505gmd"
["fund_bill_list"]=>
string(49) "[{"amount":"0.01","fundChannel":"ALIPAYACCOUNT"}]"
["notify_type"]=>
string(17) "trade_status_sync"
["out_trade_no"]=>
string(10) "1536227456"
["total_amount"]=>
string(4) "0.01"
["trade_status"]=>
string(13) "TRADE_SUCCESS"
["trade_no"]=>
string(28) "2018090621001004080500252780"
["auth_app_id"]=>
string(16) "2016091600527626"
["receipt_amount"]=>
string(4) "0.01"
["point_amount"]=>
string(4) "0.00"
["app_id"]=>
string(16) "2016091600527626"
["buyer_pay_amount"]=>
string(4) "0.01"
["sign_type"]=>
string(4) "RSA2"
["seller_id"]=>
string(16) "2088102175946009"
}
退款
退款时也需要有一个订单号,之后通过这个订单号可以在支付宝中查看退款的进度。
// 退款
public function refund()
{
// 生成唯一退款订单号
$refundNo = md5( rand(1,99999) . microtime() );
try{
// 退款
$ret = Pay::alipay($this->config)->refund([
'out_trade_no' => '1536227456', // 之前的订单流水号
'refund_amount' => 0.01, // 退款金额,单位元
'out_request_no' => $refundNo, // 退款订单号
]);
if($ret->code == 10000)
{
echo '退款成功!';
}
}
catch(\Exception $e)
{
var_dump( $e->getMessage() );
}
}
退款结果
退款之后,如果服务器返回的 code 是 10000
则说明退款成功:
if($ret->code == 10000)
{
echo '退款成功!';
}
else
{
echo '退款失败,错误信息'.$ret->sub_msg;
echo '错误编号':$ret->sub_code;
}
作业:用户充值系统
- 显示一个充值的表单,用户输入充值金额 ,点击充值按钮
- 在本地生成一个充值的订单,跳转到订单列表页
- 在列表中显示支付按钮,
- 用户点击支付按钮跳转到支付宝支付
- 支付成功之后,订单状态变为已支付,并且为用户账号中添加相应的积分
- 30分钟之内 ,用户还可以退款