最近项目中要用到银联支付,所以记录一下开发过程:
1.先去银联申请接口https://open.unionpay.com/tjweb/api/dictionary?apiSvcId=453,我用的是手机网页支付(WAP支付)
2.去个人中心查看产品和测试参数
这些测试数据写到config.php里,稍后会用到。
//银联支付部分配置信息(具体配置查看extend/unionpay/sdk/acp_sdk.ini)
'unionpay_config' => [
'merId' => '',//商户代码
'configFile' => Env::get('root_path') . 'config/acp_sdk.ini',//银联配置
],
3.下载银联官方实例:https://open.unionpay.com/tjweb/acproduct/list?apiSvcId=453
4.我的是放到extend里了:
重命名一下文件,在这些文件头部加上命名空间:namespace unionpay\sdk;
5.在application/common.php里面引入
use unionpay\sdk\SDKConfig;
use unionpay\sdk\AcpService;
然后就可以写方法调用了:
/**
* 银联支付
* @param array $data付款参数
* @return string
*/
function unionpay($order){
$SDKConfig = new SDKConfig();
$params = array(
//以下信息非特殊情况不需要改动
'version' => $SDKConfig->getSDKConfig()->version, //版本号
'encoding' => 'utf-8', //编码方式
'txnType' => '01', //交易类型
'txnSubType' => '01', //交易子类
'bizType' => '000201', //业务类型
'frontUrl' => $SDKConfig->getSDKConfig()->frontUrl, //前台通知地址
'backUrl' => $SDKConfig->getSDKConfig()->backUrl, //后台通知地址
'signMethod' => $SDKConfig->getSDKConfig()->signMethod, //签名方法
'channelType' => '08', //渠道类型,07-PC,08-手机
'accessType' => '0', //接入类型
'currencyCode' => '156', //交易币种,境内商户固定156
//TODO 以下信息需要填写
'merId' => $order['merId'], //商户代码,请改自己的测试商户号,此处默认取demo演示页面传递的参数
'orderId' => $order["orderId"], //商户订单号,8-32位数字字母,不能含“-”或“_”,此处默认取demo演示页面传递的参数,可以自行定制规则
'txnTime' => date("YmdHis",$order["txnTime"]), //订单发送时间,格式为YYYYMMDDhhmmss,取北京时间,此处默认取demo演示页面传递的参数
'txnAmt' => $order["txnAmt"], //交易金额,单位分,此处默认取demo演示页面传递的参数
// 订单超时时间。
// 超过此时间后,除网银交易外,其他交易银联系统会拒绝受理,提示超时。 跳转银行网银交易如果超时后交易成功,会自动退款,大约5个工作日金额返还到持卡人账户。
// 此时间建议取支付时的北京时间加15分钟。
// 超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败。
'payTimeout' => date('YmdHis', strtotime('+15 minutes')),
'riskRateInfo' => $order["riskRateInfo"],
// 请求方保留域,
// 透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据。
// 出现部分特殊字符时可能影响解析,请按下面建议的方式填写:
// 1. 如果能确定内容不会出现&={}[]"'等符号时,可以直接填写数据,建议的方法如下。
// 'reqReserved' =>'透传信息1|透传信息2|透传信息3',
// 2. 内容可能出现&={}[]"'符号时:
// 1) 如果需要对账文件里能显示,可将字符替换成全角&={}【】“‘字符(自己写代码,此处不演示);
// 2) 如果对账文件没有显示要求,可做一下base64(如下)。
// 注意控制数据长度,实际传输的数据长度不能超过1024位。
// 查询、通知等接口解析时使用base64_decode解base64后再对数据做后续解析。
// 'reqReserved' => base64_encode('任意格式的信息都可以'),
//TODO 其他特殊用法请查看 special_use_purchase.php
);
$AcpService = new AcpService();
$AcpService->sign($params);
$uri = $SDKConfig->getSDKConfig()->frontTransUrl;
$html_form = $AcpService->createAutoFormHtml($params, $uri);
echo $html_form;
}
/**
* 银联支付验证签名
* @param array $data付款参数
* @return bool
*/
function checkUnionpaySign($data){
$AcpService = new AcpService();
return $AcpService->validate($data);
}
/**
* 银联支付查询订单
* @param array $data付款参数
* @return bool
*/
function checkUnionpayOrder($data){
$SDKConfig = new SDKConfig();
$params = array(
//以下信息非特殊情况不需要改动
'version' => $SDKConfig->getSDKConfig()->version, //版本号
'encoding' => 'utf-8', //编码方式
'signMethod' => $SDKConfig->getSDKConfig()->signMethod, //签名方法
'txnType' => '00', //交易类型
'txnSubType' => '00', //交易子类
'bizType' => '000000', //业务类型
'accessType' => '0', //接入类型
'channelType' => '07', //渠道类型
//TODO 以下信息需要填写
'orderId' => $data["orderId"], //请修改被查询的交易的订单号,8-32位数字字母,不能含“-”或“_”,此处默认取demo演示页面传递的参数
'merId' => $data["merId"], //商户代码,请改自己的测试商户号,此处默认取demo演示页面传递的参数
'txnTime' => date("YmdHis",$data["txnTime"]), //请修改被查询的交易的订单发送时间,格式为YYYYMMDDhhmmss,此处默认取demo演示页面传递的参数
);
$AcpService = new AcpService();
$AcpService->sign($params);// 签名
$url = $SDKConfig->getSDKConfig()->singleQueryUrl;
$result_arr = $AcpService->post($params, $url);
//没收到200应答的情况
if(count($result_arr) <= 0) {
return ['status'=>0,'msg'=>'无应答'];
}
//应答报文验签失败
if (!$AcpService->validate ($result_arr) ){
return ['status'=>2,'msg'=>'应答报文验签失败'];
}
if ($result_arr["respCode"] == "00"){
if ($result_arr["origRespCode"] == "00"){
//交易成功
//TODO
return ['status'=>1,'msg'=>'交易成功'];
} else if ($result_arr["origRespCode"] == "03"
|| $result_arr["origRespCode"] == "04"
|| $result_arr["origRespCode"] == "05"){
//后续需发起交易状态查询交易确定交易状态
//TODO
return ['status'=>3,'msg'=>'后续需发起交易状态查询交易确定交易状态'];
} else {
//其他应答码做以失败处理
//TODO
return ['status'=>4,'msg'=>'交易失败:'. $result_arr["origRespMsg"]];
}
} else if ($result_arr["respCode"] == "03"
|| $result_arr["respCode"] == "04"
|| $result_arr["respCode"] == "05" ){
//后续需发起交易状态查询交易确定交易状态
//TODO
return ['status'=>5,'msg'=>'处理超时,请稍微查询。'];
} else {
//其他应答码做以失败处理
//TODO
return ['status'=>6,'msg'=>'失败:'. $result_arr["respMsg"]];
}
}
6.然后在需要的地方就可以直接调用了:
/**
* 调用银联支付
* @param int $id 订单ID
* @return html 跳转到银联支付页面或调起云闪付app
*/
public function pay()
{
$id = input('id');
$row = Db::name('order')->where('id',$id)->find();
$uniopayData = [];
$uniopayData['merId'] = config('app.unionpay_config.merId');
$uniopayData['orderId'] = $row['order_id'];
$uniopayData['txnAmt'] = $row['money'];
$uniopayData['txnTime'] = time();
$uniopayData['riskRateInfo'] = '{commodityName=test}';
$tempRow = Db::name('unionpay')->where(['orderId'=>$row['order_id']])->find();
if(!$tempRow){
$id = Db::name('unionpay')->insertGetId($uniopayData);
if($id){
unionpay($uniopayData);
}else{
exit('请求失败,请联系客服');
}
}else{
unionpay($uniopayData);
}
}
/**
* 银联支付异步回调
*/
public function unionpayNotify(){
$notifyData = $_POST;
logStr("支付回调POST返回值:".json_encode($notifyData));
//判断订单是否存在
if(isset($notifyData['orderId'])){
$notifyRow = Db::name('unionpay_notify')->where(['orderId'=>$notifyData['orderId']])->find();
if(!$notifyRow){
$notifyData['time'] = time();
Db::name('unionpay_notify')->insertGetId($notifyData);
//验证签名
if (isset($_POST['signature'])){
$signCheck = checkUnionpaySign($_POST);
logStr("支付回调POST验证签名:".$signCheck);
//验证签名成功的话,查询一下订单,查询成功更新数据状态
if($signCheck){
$orderId = $_POST ['orderId']; //其他字段也可用类似方式获取
$respCode = $_POST ['respCode'];
logStr("支付回调POST状态码:".$respCode.($respCode == "00"));
//状态代码
if($respCode == "00"){
$unionpayRow = Db::name('unionpay')->where('orderId',$orderId)->find();
//数据存在且状态为未支付
if(!empty($unionpayRow) && $unionpayRow['status'] == 0){
//查询订单信息
$res = checkUnionpayOrder($unionpayRow);
logStr("支付回调POST查询订单:".json_encode($res));
if($res['status'] == 1){
//更新支付状态
Db::name('unionpay')->where('orderId',$orderId)->update(['status'=>1]);
}
}
}
}
}
}else{
if($notifyData['respCode'] != $notifyData['respCode']){
$notifyData['time'] = time();
Db::name('unionpay_notify')->where(['orderId'=>$notifyData['orderId']])->update($notifyData);
}
}
}
}