2020-01-09 18:12:13
围观(10921)
之前写过一篇引入支付宝 SDK 并发起支付的文章:Laravel引入支付宝支付SDK
但那篇文章引入的 SDK 并不完整的,而是根据官方给的支付 Demo 二开的,而且用的是字串的密钥.
最近项目用到了证书形式的密钥,用那个 Demo 就不能支付了,会一直报 "错误代码 invalid-signature 错误原因: 验签出错,建议检查签名字符串或签名私钥与应用公钥是否匹配"
发起支付请求
想要发起支付宝支付请求,首先需要到支付宝开放平台登录,进入到沙箱配置页面 open.alipay.com
沙箱环境配置地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info
密钥工具:https://docs.open.alipay.com/58/103242
这里说一下密钥的配置方法
先打开上方密钥工具链接,下载密钥工具
下载后安装该密钥工具,打开后生成密钥,生成的应用私钥和公钥自己保管好
如果不用证书方式,就复制公钥到沙箱配置,然后拿到支付宝公钥.
如果用证书方式,需要生成密钥后点一下"获取 CSR 文件",然后填一些域名/公司名之类的信息,将生成到的 CSR 文件配置到沙箱,然后可以获取到三个证书文件.
将这三个证书文件放到 extend 里面的 alipay/certs.
没有 alipay/certs 这两个目录就创建然后丢证书进去.
密钥配置很简单,就不细说了.
接下来就是下载 SDK : https://docs.open.alipay.com/54/103419
以 ThinkPHP 框架为例,将下载的 SDK 丢进根目录的 extend 比如这样:
然后就是写请求了,新建一个业务逻辑类,或者直接在控制器写:
Loader::import('alipay.AopCertClient'); Loader::import('alipay.request.AlipayTradePagePayRequest');
作用是引入 SDK 的某些类.
然后将支付信息存入一个数组:
$bizContentarr = [ 'body' => $param['body'], 'subject' => $param['subject'], 'total_amount' => $param['total_amount'], // 这个单位不是分 'out_trade_no' => $param['out_trade_no'], 'timeout_express' => '90m', 'product_code' => 'FAST_INSTANT_TRADE_PAY', ];
然后引入支付宝配置,因为一般情况下配置都会写到配置文件 config.php 中,比如 config.php 加入这段:
'alipay' => [ 'app_id' => '沙箱拿到的appid', 'charset' => 'UTF-8', 'sign_type' => 'RSA2', 'app_pay_url' => 'https://openapi.alipaydev.com/gateway.do', 'refund_url' => 'https://openapi.alipaydev.com/gateway.do', 'private_key' => '生成的应用私钥', 'app_cert_path' => 'extend/alipay/certs/appCertPublicKey.crt', //应用证书路径 'alipay_cert_path' => 'extend/alipay/certs/alipayCertPublicKey_RSA2.crt', //支付宝公钥证书路径 'root_cert_path' => 'extend/alipay/certs/alipayRootCert.crt', //支付宝根证书路径 'return_url' => '/', // 同步回调地址 仅填路径 ],
然后继续写请求方法,引入配置:
$config = config('alipay'); // 继续写支付请求 $aop = new \AopCertClient(); $aop->gatewayUrl = $config['app_pay_url']; $aop->appId = $config['app_id']; $alipayCertPath = ROOT_PATH . $config['alipay_cert_path']; // 支付宝RSA2公钥证书 $rootCertPath = ROOT_PATH . $config['root_cert_path']; // 支付宝根证书, $appCertPath = ROOT_PATH . $config['app_cert_path']; $aop->rsaPrivateKey = $config['private_key']; $aop->alipayrsaPublicKey = $aop->getPublicKey($alipayCertPath); $aop->apiVersion ="1.0"; $aop->signType = $config['sign_type']; $aop->postCharset = $config['charset']; $aop->format='json'; // 是否校验自动下载的支付宝公钥证书,如果开启校验要保证支付宝根证书在有效期内 $aop->isCheckAlipayPublicCert = true; // 调用getCertSN获取证书序列号 $aop->appCertSN = $aop->getCertSN($appCertPath); // 调用getRootCertSN获取支付宝根证书序列号 $aop->alipayRootCertSN = $aop->getRootCertSN($rootCertPath); $request = new \AlipayTradePagePayRequest(); $request->setBizContent(json_encode($bizContentarr)); $request->setReturnUrl(request()->domain() . $config['return_url']); $request->setNotifyUrl(request()->domain() . '/alipay_callback.php'); $result = $aop->pageExecute($request); // 直接输出 html 跳转到支付 echo $result;
这样,一个支付宝的支付请求就完成了, 下面继续支付宝回调.
支付结果回调
同样,新建一个逻辑类,或者在控制器,或者在上面发请求支付的逻辑类里面接着写.
先引入类:
Loader::import('alipay.AopCertClient');
如果和请求的逻辑放在同一个类,需要创建一个回调方法和一个构造方法,构造方法把所有需要引入的类,使用 Loader 的静态方法 import 引入.
接下来还是一样的,支付宝配置,如果是同一个类,可以设置一个类属性,存放支付宝配置,就不需要再次获取配置了.
// 支付宝配置 $config = config('alipay'); $alipay = new \AopCertClient(); $appCertPath = ROOT_PATH .$config['app_cert_path']; $alipayCertPath = ROOT_PATH . $config['alipay_cert_path']; $rootCertPath = ROOT_PATH . $config['root_cert_path']; $alipay->gatewayUrl = $config['app_pay_url']; $alipay->appId = $config['app_id']; $alipay->rsaPrivateKey = $config['private_key']; $alipay->alipayrsaPublicKey = $alipay->getPublicKey($alipayCertPath); $alipay->apiVersion = '1.0'; $alipay->signType = $config['sign_type']; $alipay->postCharset = $config['charset']; $alipay->format='json'; // 是否校验自动下载的支付宝公钥证书,如果开启校验要保证支付宝根证书在有效期内 $alipay->isCheckAlipayPublicCert = true; // 调用getCertSN获取证书序列号 $alipay->appCertSN = $alipay->getCertSN($appCertPath); // 调用getRootCertSN获取支付宝根证书序列号 $alipay->alipayRootCertSN = $alipay->getRootCertSN($rootCertPath);
接下来到了回调关键的一步,也可以说是最后一步.
// 验签 $check_sign = $alipay->rsaCheckV1($param = $_POST, NULL, $config['sign_type']); if (!$check_sign) { Log::info('验签失败'); return 'failure'; } // ... 可以接下去写业务逻辑了,已经通过了验签 // ... 业务逻辑成功执行后需要返回 "成功" 标识告诉支付宝 // ... 成功返回 success return 'success';
这样回调也就完成了.
可能会有人说为啥不用一些第三方的包,直接集成了多个支付,或者封装了支付接口.
但其实细想一下,支付都是敏感操作,要是以后某个支付接口改了,又不熟悉安装的第三方包,就很坑了,需要你回过头来看第三方的代码,而且第三方的代码质量还不一定好,看起来更要命.
总结一句话就是 用第三方包开发支付,前期舒服了,后期维护可能要了命.
本文地址 : bubaijun.com/page.php?id=162
版权声明 : 未经允许禁止转载!
上一篇文章: PHP实现微信红包金额拆分及原理解析
下一篇文章: PHP使用地址获取附近小区信息