不败君

前端萌新&初级后端攻城狮

ThinkPHP框架使用支付宝SDK并发起支付

发表:2020-01-09 18:12:13

围观(129)

之前写过一篇引入支付宝 SDK 并发起支付的文章:Laravel引入支付宝支付SDK


但那篇文章引入的 SDK 并不完整的,而是根据官方给的支付 Demo 二开的,而且用的是字串的密钥.

最近项目用到了证书形式的密钥,用那个 Demo 就不能支付了,会一直报 "错误代码 invalid-signature 错误原因: 验签出错,建议检查签名字符串或签名私钥与应用公钥是否匹配"


发起支付请求

想要发起支付宝支付请求,首先需要到支付宝开放平台登录,进入到沙箱配置页面 open.alipay.com

沙箱环境配置地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info

1.png

密钥工具:https://docs.open.alipay.com/58/103242

这里说一下密钥的配置方法

先打开上方密钥工具链接,下载密钥工具

2.png


下载后安装该密钥工具,打开后生成密钥,生成的应用私钥和公钥自己保管好

3.png

如果不用证书方式,就复制公钥到沙箱配置,然后拿到支付宝公钥.

如果用证书方式,需要生成密钥后点一下"获取 CSR 文件",然后填一些域名/公司名之类的信息,将生成到的 CSR 文件配置到沙箱,然后可以获取到三个证书文件.

将这三个证书文件放到 extend 里面的 alipay/certs.

没有 alipay/certs 这两个目录就创建然后丢证书进去.

5.png

密钥配置很简单,就不细说了.


接下来就是下载 SDK : https://docs.open.alipay.com/54/103419

以 ThinkPHP 框架为例,将下载的 SDK 丢进根目录的 extend 比如这样:

4.png

然后就是写请求了,新建一个业务逻辑类,或者直接在控制器写:

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

版权声明 : 未经允许禁止转载!

评论:我要评论
发布评论:
Copyright © 不败君 粤ICP备18102917号-1

不败君

首 页 作 品 微 语