银联支付相关笔记
银联开放平台:
https://open.unionpay.com
商家中心:
https://merchant.unionpay.com
ping++ 聚合支付(看着不错):
https://www.pingxx.com/
参考文章:
// 该文件非常不错,注册、测试整个流程都有,一步一步都有图文介绍
https://blog.csdn.net/zyw_java/article/details/78167481
关于银联支付要求的前、后台通知链接,要求是外网地址,我们自己可以搭建 ngrok,或使用网上免费或付费产品,搭建 ngork 也很简单,查看笔记
总结下:
整个支付测试还是很简单的。
官网下载下来 PHP-SDK,然后部署到本地,访问各个页面即可测试接口。
需要注意的几点:
1.sdk/acp_sdk.ini 配置文件里的:前、后台地址,证书文件,以及日志目录,需要替换成我们自己的本地路径
2.接口的商户id号,需要替换成我们测试参数中的 "测试商户号"
3.我测试过程中,一直报错:
10报文格式错误[5500030]
网上也找不到错误问题,有一篇文章提到了,好像是时区的问题。
查看了下,本地因为新安装的 php,时区还真不对,修改 php 配置文件:
php --ini // 找到 php 配置文件
vim /usr/local/etc/php/7.1/php.ini
date.timezone = "Asia/Shanghai" // 设置时区
测试完成了,项目代码中得实现整个银联支付,自己可能也能实现,但是不太优雅,在 composer 找有没有银联支付的包,找到一个:
lokielse/omnipay-unionpay
github 上查找 payment 相关包:
https://github.com/topics/payment?o=desc&s=stars
发现 laravel 学院,也有该包的一个教程:
https://laravelacademy.org/post/1492.html
https://laravelacademy.org/post/1475.html
https://packagist.org/packages/ignited/laravel-omnipay
这里顺便过一下 omnipay-unionpay 笔记:
github 地址:
https://github.com/lokielse/omnipay-unionpay
Omnipay 是一个独立于框架、多网关的支付类库,支持 PHP 7.1+。Omnipay-unionpay 为 Omnipay 实现了对银联支付的支持。
Ominipay 地址:
https://github.com/omnipay/omnipay
安装:
composer require lokielse/omnipay-unionpay dev-master
基本用法:
Ominipay-unionpay 提供了以下支付网关接口:
Union_Wtz (Union No Redirect Payment) 银联无跳转支付(alpha)
Union_Express (Union Express Payment) 银联全产品网关(PC,APP,WAP支付)
Union_LegacyMobile (Union Legacy Mobile Payment) 银联老网关(APP)
Union_LegacyQuickPay (Union Legacy QuickPay Payment) 银联老网关(PC)
使用:
测试参数可以在银联商户平台查看:
https://open.unionpay.com/ajweb/account/testPara
准备:
如何获取 PrivateKey, PublicKey, Cert ID:
1.准备好 cert.pfx、密码、verify_sign_acp.cer
2.获取私钥
openssl pkcs12 -in cert.pfx -nocerts -nodes | openssl rsa -out private_key.pem
3.公钥就是 verify_sign_acp.cer
4.获取 Cert ID
openssl pkcs12 -in cert.pfx -clcerts -nokeys | openssl x509 -serial -noout // 得到 16 进制数
visit https://lokielse.github.io/hex2dec // 将 16 进制数转换为 10 进制数
接口:
Consume
$gateway = Omnipay::create('UnionPay_Express');
$gateway->setMerId($config['merId']);
$gateway->setCertId($config['certId']);
$gateway->setPrivateKey($config['privateKey']); // path or content
$gateway->setReturnUrl($config['returnUrl']);
$gateway->setNotifyUrl($config['notifyUrl']);
$order = [
'orderId' => date('YmdHis'), //Your order ID
'txnTime' => date('YmdHis'), //Should be format 'YmdHis'
'orderDesc' => 'My order title', //Order Title
'txnAmt' => '100', //Order Total Fee
];
//For PC/Wap
$response = $gateway->purchase($order)->send();
$response->getRedirectHtml();
//For APP
$response = $gateway->createOrder($order)->send();
$response->getTradeNo();
Return/Notify
$gateway = Omnipay::create('UnionPay_Express');
$gateway->setMerId($config['merId']);
$gateway->setPublicKey($config['publicKey']); // path or content
$response = $gateway->completePurchase(['request_params'=>$_REQUEST])->send();
if ($response->isPaid()) {
//pay success
}else{
//pay fail
}
Query Order Status
$response = $gateway->query([
'orderId' => '20150815121214', //Your site trade no, not union tn.
'txnTime' => '20150815121214', //Order trade time
'txnAmt' => '200', //Order total fee
])->send();
var_dump($response->isSuccessful());
var_dump($response->getData());
Consume Undo
$response = $gateway->consumeUndo([
'orderId' => '20150815121214', //Your site trade no, not union tn.
'txnTime' => date('YmdHis'), //Regenerate a new time
'txnAmt' => '200', //Order total fee
'queryId' => 'xxxxxxxxx', //Order total fee
])->send();
var_dump($response->isSuccessful());
var_dump($response->getData());
Refund
// 注意:
1. 银联退款时,必须加上 queryId,
2. 作为商户生成的订单号orderId与退款时的订单号是不一样的。也就意味着退款时的订单号必须重新生成。
3. txnAmt 这个参数银联是精确到分的。直接返回元为单位的值,将会出现报错信息。
// get the queryId first
$response = $gateway->query([
'orderId' => '20150815121214', //Your site trade no, not union tn.
'txnTime' => '20150815121214', //Order trade time
'txnAmt' => 200 * 100, //Order total fee; notice that: you should multiply the txnAmt by 100 with the Unionpay gateway. Such as 200 * 100;
])->send();
$queryId = ($response->getData())['queryId'];
$response = $gateway->refund([
'orderId' => '20150815121215', //Your site trade no, not union tn. notice: this orderId must not be the same with the order's created orderId.
'txnTime' => date('YmdHis'), //Order trade time
'txnAmt' => 200 * 100, //Order total fee; notice that: you should multiply the txnAmt by 100 with the Unionpay gateway. Such as 200 * 100;
'queryId' => $queryId
])->send();
var_dump($response->isSuccessful());
var_dump($response->getData());
File Transfer
$response = $gateway->fileTransfer([
'txnTime' => '20150815121214', //Order trade time
'settleDate' => '0119', //Settle Date
'fileType' => '00', //File Type
])->send();
var_dump($response->isSuccessful());
var_dump($response->getData());
laravel 还有一个 Ominipay 的封装包,是将 Ominipay 集成到 Laravel,并提供了一个简单的配置。
ignited/laravel-omnipay
github地址:
https://github.com/ignited/laravel-omnipay
安装:
composer require ignited/laravel-omnipay
config/app.php 添加 provider 和 facade
'providers' => [
Ignited\LaravelOmnipay\LaravelOmnipayServiceProvider::class
]
'Omnipay' => Ignited\LaravelOmnipay\Facades\OmnipayFacade::class
发布配置文件:
php artisan vendor:publish --provider="Ignited\LaravelOmnipay\LaravelOmnipayServiceProvider" --tag=config
配置:
config/laravel-ominipay.php
return [
// 支付使用的默认网关
'default' => 'unionpay',
// 各个支付网关
'gateways' => [
// PayPal支付
'paypal' => [
'driver' => 'PayPal_Express',
'options' => [
'solutionType' => '',
'landingPage' => '',
'headerImageUrl' => ''
]
],
]
];
使用:
1.支付
// 订单
$order = array(
'orderId' => '订单号',
'txnTime' => date('YmdHis'),
'title' => '测试订单',
'txnAmt' => '订单价格',
);
// 调用支付(purchase - 查看 omnipay-unionpay 用法)
$response = Omnipay::purchase($order)->send();
// 触发银联支付跳转
$response->redirect();
2.同步/异步通知
// 调用支付完成
$response = Omnipay::completePurchase(['request_params' => $_REQUEST])->send();
// 支付成功
if($response->isPaid()){
// 支付失败
}else{
}
/*
注意,这里有个小坑:
我在测试支付回调时,异步签名认证成功!但是同步签名认证失败!
排查了半天,觉得程序没啥问题,后来在 github 上提问了作者:
https://github.com/lokielse/omnipay-unionpay/issues/27
将 $_REQUEST 改为 $_POST 即可($_REQUEST 可能包含了的内容)
*/
3.切换其他网关
Omnipay::setGateway('unionpay');
4.实例化网关
$gateway = Omnipay::gateway('paypal');
$gateway = Omnipay::gateway('unionpay');
总结下项目步骤:
1.安装 laravel-omnipay
composer require ignited/laravel-omnipay
config/app.php 添加 provider 和 facade
'providers' => [
Ignited\LaravelOmnipay\LaravelOmnipayServiceProvider::class
]
'Omnipay' => Ignited\LaravelOmnipay\Facades\OmnipayFacade::class
发布配置文件:
php artisan vendor:publish --provider="Ignited\LaravelOmnipay\LaravelOmnipayServiceProvider" --tag=config
2.安装 ominipay-unionpay
composer require lokielse/omnipay-unionpay dev-master
3.配置银联支付
1>获取私钥、公钥、证书ID 配置
1)准备好 证书(cert.pfx)、证书密码(000000)、verify_sign_acp.cer
2)获取私钥
openssl pkcs12 -in cert.pfx -nocerts -nodes | openssl rsa -out private_key.pem
3)获取公钥
公钥就是 verify_sign_acp.cer
4)获取证书ID
openssl pkcs12 -in cert.pfx -clcerts -nokeys | openssl x509 -serial -noout // 得到16进制
访问:https://lokielse.github.io/hex2dec,将16进制转换为10进制
2>创建银联支付证书目录:
mkdir -p config/unionpay-cart/test/ // 测试环境证书配置目录
private_key.pem // 私钥
verify_sign_acp.cer // 公钥
cert.pfx // 证书
mkdir -p config/unionpay-cart/production/ // 生产环境证书配置目录
3>配置 config/laravel-ominipay.php,银联支付:
return [
// 支付使用的默认网关
'default' => 'unionpay',
// 各个支付网关
'gateways' => [
// PayPal支付
'paypal' => [
'driver' => 'PayPal_Express',
'options' => [
'solutionType' => '',
'landingPage' => '',
'headerImageUrl' => ''
]
],
// 银联支付
'unionpay' => [
'driver' => 'UnionPay_Express',
'options' => [
'merId' => 'xxx',
'certId' => 'xxx',
'privateKey' => 'private_key.pem',
'publicKey' => 'verify_sign_acp.cer',
'certPath' => '700000000000001_acp.pfx',
'certPassword' => '000000',
'certDir' => 'xxx',
'returnUrl' => url('payment/unionpay/return'),
'notifyUrl' => url('payment/unionpay/notify'),
]
],
]
];
4.调用
路由:
web/wap端支付
Route::get('payment/unionpay/web-pay', 'PaymentUnionpayController@webPay');
Route::get('payment/unionpay/wap-pay', 'PaymentUnionpayController@wapPay');
同步/异步通知
// 注意:银联的同步通知也是 POST 提交
Route::post('payment/unionpay/return', 'PaymentUnionpayController@return');
Route::post('payment/unionpay/notify', 'PaymentUnionpayController@notify');
/*
这里注意下 "同步通知":
不同于一般的同步通知,是 'get' 请求,银联的是 'post' 请求
*/
中间件:
VerifyCsrfToken 也得配置 '同步/异步通知' 排除 csrf token 认证
protected $except = [
...
'payment/unionpay/return', // 同步通知(银联的同步通知,也是 post 提交)
'payment/unionpay/notify', // 异步通知
];
控制器:
public function webPay()
&&
public function wapPay()
{
$order = array(
'orderId' => '订单号',
'txnTime' => date('YmdHis'),
'title' => '测试订单',
'txnAmt' => '100',
);
$response = Omnipay::purchase($order)->send();
$response->redirect();
}
public function return()
&&
public function notify()
{
// 调用支付完成
$response = Omnipay::completePurchase(['request_params' => $_REQUEST])->send();
// 支付成功
if($response->isPaid()){
// 支付失败
}else{
}
}
5.其他
1>目前 ominipay-unionpay 只支持 5.0.0 版本,想要使用 5.0.1 版本,需要我们自己来扩展
2>看的几篇教程、还有官方文档,以及银联自身的 5.0.1 SDK,看的相当混乱,包括银联支付里该设置哪些配置,都很乱。
需要我们来查看 ominipay-unionpay 源码,才能大概了解到底是怎么使用!