由此图可以看出,后端在此卖力1、平台订单天生;2、银联全渠道平台订单推送;3、返回tn码给前端进行支付;4、处理前台关照以及全渠道平台的异步关照。

此间难点有三,订单推送、异步关照处理、订单状态查询。

通过官方的邮件解释下载干系的包并放入后端php代码中,(支付控件去下载你看到的估计只有IOS,安卓版的SDK,对付后端来说,随便下载一个即可,PHP的代码在里面都有放置);然后仔细阅读SDK中的readme.txt文件,此后进行以下步骤:

phpacp文件下载PHP后端实现银联付出及退款实例代码干货 GraphQL

干系参数配置

对接过程中利用在sdk的assets文件夹中测试环境配置文件及证书,放置到sdk文件夹中,并配置/sdk/SDKconfig.php文件已精确的读取acp_sdk.ini配置文件。

在acp_sdk.ini文件中配置好acpsdk.signCert.path、acpsdk.encryptCert.path、acpsdk.rootCert.path、acpsdk.middleCert.path四个文件的绝对地址(自定义文件路径即可)。

因项目开拓过程中会涌现系统不同或项目地址不同导致的证书绝对地址等缺点,尤其在实际生产环境中,极易涌现项目支配文件地址不同,不可能在开拓后每次更新都要改换证书地址,在此修正了一下SDK中的SDKconfig.php已兼容不同文件地址较长,这里还请点击展开查看

namespace com\unionpay\acp\sdk;;include_once 'log.class.php';include_once 'common.php';class SDKConfig { private static $_config = null; public static function getSDKConfig(){ if (SDKConfig::$_config == null ) { SDKConfig::$_config = new SDKConfig(); } return SDKConfig::$_config; } private $frontTransUrl; private $backTransUrl; private $singleQueryUrl; private $batchTransUrl; private $fileTransUrl; private $appTransUrl; private $cardTransUrl; private $jfFrontTransUrl; private $jfBackTransUrl; private $jfSingleQueryUrl; private $jfCardTransUrl; private $jfAppTransUrl; private $qrcBackTransUrl; private $qrcB2cIssBackTransUrl; private $qrcB2cMerBackTransUrl; private $signMethod; private $version; private $ifValidateCNName; private $ifValidateRemoteCert; private $signCertPath; private $signCertPwd; private $validateCertDir; private $encryptCertPath; private $rootCertPath; private $middleCertPath; private $frontUrl; private $backUrl; private $secureKey; private $logFilePath; private $logLevel; function __construct(){ //如果想把acp_sdk.ini挪到其他路径的话,请修正下面这行指定绝对路径。
$configFilePath = dirname(__FILE__) . \"大众/acp_sdk.ini\公众; $certsFilePath = dirname(dirname(__FILE__)) . \公众/certs/\"大众; if(!file_exists($configFilePath)){ $logger = LogUtil::getLogger(); $logger->LogError(\公众配置文件加载失落败,文件路径:[\公众 . $configFilePath . \公众].请检讨启动php的用户是否有读权限。
\公众); return; } $ini_array = parse_ini_file($configFilePath, true); $sdk_array = $ini_array[\"大众acpsdk\公众]; $this->frontTransUrl = array_key_exists(\公众acpsdk.frontTransUrl\"大众, $sdk_array)?$sdk_array[\"大众acpsdk.frontTransUrl\"大众] : null; $this->backTransUrl = array_key_exists(\公众acpsdk.backTransUrl\公众, $sdk_array)?$sdk_array[\"大众acpsdk.backTransUrl\"大众] : null; $this->singleQueryUrl = array_key_exists(\公众acpsdk.singleQueryUrl\"大众, $sdk_array)?$sdk_array[\公众acpsdk.singleQueryUrl\"大众] : null; $this->batchTransUrl = array_key_exists(\公众acpsdk.batchTransUrl\公众, $sdk_array)?$sdk_array[\公众acpsdk.batchTransUrl\"大众] : null; $this->fileTransUrl = array_key_exists(\"大众acpsdk.fileTransUrl\"大众, $sdk_array)?$sdk_array[\"大众acpsdk.fileTransUrl\公众] : null; $this->appTransUrl = array_key_exists(\公众acpsdk.appTransUrl\"大众, $sdk_array)?$sdk_array[\"大众acpsdk.appTransUrl\公众] : null; $this->cardTransUrl = array_key_exists(\"大众acpsdk.cardTransUrl\"大众, $sdk_array)?$sdk_array[\"大众acpsdk.cardTransUrl\"大众] : null; $this->jfFrontTransUrl = array_key_exists(\公众acpsdk.jfFrontTransUrl\"大众, $sdk_array)?$sdk_array[\"大众acpsdk.jfFrontTransUrl\公众] : null; $this->jfBackTransUrl = array_key_exists(\"大众acpsdk.jfBackTransUrl\公众, $sdk_array)?$sdk_array[\公众acpsdk.jfBackTransUrl\"大众] : null; $this->jfSingleQueryUrl = array_key_exists(\"大众acpsdk.jfSingleQueryUrl\公众, $sdk_array)?$sdk_array[\"大众acpsdk.jfSingleQueryUrl\"大众] : null; $this->jfCardTransUrl = array_key_exists(\"大众acpsdk.jfCardTransUrl\公众, $sdk_array)?$sdk_array[\公众acpsdk.jfCardTransUrl\公众] : null; $this->jfAppTransUrl = array_key_exists(\"大众acpsdk.jfAppTransUrl\"大众, $sdk_array)?$sdk_array[\"大众acpsdk.jfAppTransUrl\"大众] : null; $this->qrcBackTransUrl = array_key_exists(\"大众acpsdk.qrcBackTransUrl\公众, $sdk_array)?$sdk_array[\"大众acpsdk.qrcBackTransUrl\"大众] : null; $this->qrcB2cIssBackTransUrl = array_key_exists(\"大众acpsdk.qrcB2cIssBackTransUrl\"大众, $sdk_array)?$sdk_array[\公众acpsdk.qrcB2cIssBackTransUrl\公众] : null; $this->qrcB2cMerBackTransUrl = array_key_exists(\"大众acpsdk.qrcB2cMerBackTransUrl\"大众, $sdk_array)?$sdk_array[\公众acpsdk.qrcB2cMerBackTransUrl\公众] : null; $this->signMethod = array_key_exists(\"大众acpsdk.signMethod\公众, $sdk_array)?$sdk_array[\公众acpsdk.signMethod\公众] : null; $this->version = array_key_exists(\"大众acpsdk.version\公众, $sdk_array)?$sdk_array[\公众acpsdk.version\"大众] : null; $this->ifValidateCNName = array_key_exists(\"大众acpsdk.ifValidateCNName\公众, $sdk_array)?$sdk_array[\"大众acpsdk.ifValidateCNName\公众] : \"大众true\公众; $this->ifValidateRemoteCert = array_key_exists(\"大众acpsdk.ifValidateRemoteCert\"大众, $sdk_array)?$sdk_array[\公众acpsdk.ifValidateRemoteCert\公众] : \公众false\"大众; $this->signCertPath = $certsFilePath . (array_key_exists(\"大众acpsdk.signCert.path\"大众, $sdk_array)?$sdk_array[\公众acpsdk.signCert.path\公众]: null); $this->signCertPwd = array_key_exists(\公众acpsdk.signCert.pwd\公众, $sdk_array)?$sdk_array[\公众acpsdk.signCert.pwd\"大众]: null; $this->validateCertDir = array_key_exists(\"大众acpsdk.validateCert.dir\"大众, $sdk_array)? $sdk_array[\"大众acpsdk.validateCert.dir\"大众]: null; $this->encryptCertPath = $certsFilePath . (array_key_exists(\公众acpsdk.encryptCert.path\"大众, $sdk_array)? $sdk_array[\公众acpsdk.encryptCert.path\公众]: null); $this->rootCertPath = $certsFilePath . (array_key_exists(\"大众acpsdk.rootCert.path\公众, $sdk_array)? $sdk_array[\公众acpsdk.rootCert.path\"大众]: null); $this->middleCertPath = $certsFilePath . (array_key_exists(\公众acpsdk.middleCert.path\"大众, $sdk_array)?$sdk_array[\"大众acpsdk.middleCert.path\"大众]: null); $this->frontUrl = array_key_exists(\"大众acpsdk.frontUrl\"大众, $sdk_array)?$sdk_array[\"大众acpsdk.frontUrl\公众]: null; $this->backUrl = array_key_exists(\"大众acpsdk.backUrl\"大众, $sdk_array)?$sdk_array[\"大众acpsdk.backUrl\"大众]: null; $this->secureKey = array_key_exists(\"大众acpsdk.secureKey\公众, $sdk_array)?$sdk_array[\"大众acpsdk.secureKey\"大众]: null; $this->logFilePath = array_key_exists(\"大众acpsdk.log.file.path\"大众, $sdk_array)?$sdk_array[\公众acpsdk.log.file.path\公众]: null; $this->logLevel = array_key_exists(\公众acpsdk.log.level\"大众, $sdk_array)?$sdk_array[\"大众acpsdk.log.level\"大众]: null; } public function __get($property_name) { if(isset($this->$property_name)) { return($this->$property_name); } else { return(NULL); } }}

全渠道商品订单推送

use com\unionpay\acp\sdk\AcpService;use com\unionpay\acp\sdk\LogUtil;use com\unionpay\acp\sdk\SDKConfig; / 银联支付下单 @param $orders @param $orders_type @return array / public function unionPay($orders, $orders_type = 0) { include_once dirname(dirname(dirname(__FILE__))) . '/Model/unionpay-sdk/sdk/acp_service.php'; $config = new SDKConfig(); $AcpService = new AcpService(); $log = LogUtil::getLogger(); $time = date('YmdHis', time()); $params = array( //以下信息非分外情形不须要改动 'version' => $config->getSDKConfig()->version, //版本号 'encoding' => 'utf-8', //编码办法 'txnType' => '01', //交易类型 'txnSubType' => '01', //交易子类 'bizType' => '000201', //业务类型 'frontUrl' => $config->getSDKConfig()->frontUrl, //前台关照地址 'backUrl' => $this->getURL('api_pay_unionpay_call_back'), //后台关照地址 'signMethod' => $config->getSDKConfig()->signMethod, //署名方法 'channelType' => '08', //渠道类型,07-PC,08-手机 'accessType' => '0', //接入类型 'currencyCode' => '156', //交易币种,境内商户固定156 //TODO 以下信息须要填写 'merId' => $this->getParameter('mer_id'), //商户代码,请改自己的测试商户号 'orderId' => $orders[\公众order_no\"大众], //商户订单号,8-32位数字字母,不能含“-”或“_” 'txnTime' => $time, //订单发送韶光,格式为YYYYMMDDhhmmss,取北京韶光 'txnAmt' => $orders['total_price'] 100, //交易金额,单位分 ); $AcpService->sign ( $params ); // 署名 $url = $config->getSDKConfig()->appTransUrl; $result_arr = $AcpService->post ($params, $url); if(count($result_arr)<=0) { //没收到200应答的情形 $log->LogInfo('没收到200应答的情形'); }// $this->printResult ($url, $params, $result_arr ); //页面打印要求应答数据 if (!$AcpService->validate ($result_arr) ){ $log->LogInfo('应答报文验签失落败'); } if ($result_arr[\"大众respCode\"大众] == \"大众00\"大众){ //成功 return array('txn_time'=>$time, 'tn'=>$result_arr[\公众tn\"大众]);// echo \"大众后续请将此tn传给手机开拓,由他们用此tn调起控件后完成支付。
\n\公众;// echo \公众手机端demo默认从仿真获取tn,仿真只返回一个tn,如不想修正手机和后台间的通讯办法,【此页面请修正代码为只输出tn】。
\n\"大众; } else { //其他应答码做以失落败处理 return array('txn_time'=>$time, 'tn'=>0); //echo \公众失落败:\"大众 . $result_arr[\"大众respMsg\公众] . \"大众。
\n\公众; } }

在此把稳txnTime格式不要传错,测试环境下该当不会涌现什么问题,将得到的tn返回APP进行支付即可

异步关照处理以及订单交易状态查询

这一步紧张浸染为处理银联交易成功信息,并尽可能避免涌现回调未处理导致问题。

先说异步关照处理,此步骤为订单状态修正的紧张依据。
无实际难点,担保干系参数无问题即可

/ 银联回调 @param Request $request @return array|Response / public function unionPayCallBackAction(Request $request) { if ($request->get('type') == 1){//前台关照-进行订单状态查询 $query = $this->unionPayQuery($request, array(), 1); return new JsonResponse($query); } require_once dirname(dirname(dirname(__FILE__))) . \公众/Model/unionpay-sdk/sdk/acp_service.php\公众; $log = LogUtil::getLogger(); $AcpService = new AcpService(); if ($request->request->has('signature') && $AcpService->validate($_POST)) { $order_no = $request->request->get('orderId'); $respCode = $request->request->get('respCode'); $total = $request->request->get('txnAmt'); // 交易金额 if ($respCode === '00' || $respCode === 'A6') { $trade_no = $request->request->get('origQryId')?:'UN' . date('YmdHis', time()) . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8); $this->dispose($order_no, $trade_no, 4);//订单交易处理-请根据实际情形自行编写 } } else { if (!$request->request->has('signature')) { $log->LogInfo('署名为空'); } else { $log->LogInfo('验签失落败'); } } exit; }

订单交易状态查询

do{//循环查询,直到获取到退款订单的queryID sleep($number 2); $query = $this->unionPayQuery('', $orders); $number += 1; }while($query['errorCode'] != 0 || empty($query['result_arr'][\公众queryId\"大众]));public function unionPayQuery($request, $orders) { require_once dirname(dirname(dirname(__FILE__))) . \公众/Model/unionpay-sdk/sdk/acp_service.php\"大众; $config = new SDKConfig(); $AcpService = new AcpService(); $log = LogUtil::getLogger(); $params = array( //以下信息非分外情形不须要改动 'version' => $config->getSDKConfig()->version, //版本号 'encoding' => 'utf-8', //编码办法 'signMethod' => $config->getSDKConfig()->signMethod, //署名方法 'txnType' => '00', //交易类型 'txnSubType' => '00', //交易子类 'bizType' => '000000', //业务类型 'accessType' => '0', //接入类型 'channelType' => '07', //渠道类型 //TODO 以下信息须要填写 'orderId' => $orders['order_no'], //请修正被查询的交易的订单号,8-32位数字字母,不能含“-”或“_” 'merId' => $this->getParameter('mer_id'), //商户代码,请改自己的测试商户号 'txnTime' => date('YmdHis', time()), //请修正被查询的交易的订单发送韶光,格式为YYYYMMDDhhmmss ); $AcpService->sign ( $params ); // 署名 $url = $config->getSDKConfig()->singleQueryUrl; $result_arr = $AcpService->post ( $params, $url); if(count($result_arr)<=0) { //没收到200应答的情形 $log->LogInfo('没收到200应答的情形'); } if (!$AcpService->validate ($result_arr) ){ $log->LogInfo('应答报文验签失落败'); } if ($result_arr[\公众respCode\公众] == \"大众00\"大众){ if ($result_arr[\"大众origRespCode\"大众] == \"大众00\"大众){ //交易成功 $trade_no = 'UN' . date('YmdHis', time()) . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8); $this->dispose($orders['order_no'], $trade_no, 4); $result = array('errorCode'=>0, 'message'=>'交易成功', 'result_arr'=>$result_arr); } else if ($result_arr[\"大众origRespCode\"大众] == \"大众03\"大众 || $result_arr[\"大众origRespCode\公众] == \"大众04\"大众 || $result_arr[\公众origRespCode\"大众] == \"大众05\公众){ //后续需发起交易状态查询交易确定交易状态 $result = array('errorCode'=>2, 'message'=>'交易处理中', 'result_arr'=>$result_arr); } else { //其他应答码做以失落败处理 echo \"大众交易失落败:\"大众 . $result_arr[\"大众origRespMsg\"大众] . \公众。
\n\"大众; $result = array('errorCode'=>1, 'message'=>\"大众交易失落败:\公众 . $result_arr[\"大众origRespMsg\"大众] . \"大众.\公众, 'result_arr'=>$result_arr); } } else if ($result_arr[\"大众respCode\公众] == \"大众03\公众 || $result_arr[\"大众respCode\"大众] == \"大众04\"大众 || $result_arr[\"大众respCode\公众] == \"大众05\"大众 ){ //后续需发起交易状态查询交易确定交易状态 $result = array('errorCode'=>2, 'message'=>\"大众处理超时,请稍后查询.\"大众, 'result_arr'=>$result_arr); } else { //其他应答码做以失落败处理 $result = array('errorCode'=>1, 'message'=>\"大众失落败:\"大众 . $result_arr[\"大众respMsg\公众] . \公众.\"大众, 'result_arr'=>$result_arr); } return $result; }

到此为止,若是项目没有订单线上退款就完成了。

订单退款干系

public function refundUnionPay($orders) { require_once(dirname(dirname(__FILE__)) . \公众/Model/unionpay-sdk/sdk/acp_service.php\公众); set_time_limit(100); $config = new SDKConfig(); $AcpService = new AcpService(); $log = LogUtil::getLogger(); $number = 0; do{//循环查询,直到获取到退款订单的queryID sleep($number 2); $query = $this->unionPayQuery('', $orders); $number += 1; }while($query['errorCode'] != 0 || empty($query['result_arr'][\公众queryId\公众])); if ($query['errorCode'] != 0) { return array('errorCode'=>1, 'message'=>'订单未成交,无法退款'); } $params = array( //以下信息非分外情形不须要改动 'version' => $config->getSDKConfig()->version, //版本号 'encoding' => 'utf-8', //编码办法 'signMethod' => $config->getSDKConfig()->signMethod, //署名方法 'txnType' => '04', //交易类型 'txnSubType' => '00', //交易子类 'bizType' => '000201', //业务类型 'accessType' => '0', //接入类型 'channelType' => '07', //渠道类型 'backUrl' => $config->getSDKConfig()->backUrl, //后台关照地址 //TODO 以下信息须要填写 'orderId' => \公众T\"大众 . $orders['order_no'], //商户订单号,8-32位数字字母,不能含“-”或“_”,可以自行定制规则,重新产生-此处为在退款订单前拼接 T 'merId' => $this->getParameter('mer_id'), //商户代码,请改本钱身的商户号 'origQryId' => $query['result_arr'][\公众queryId\"大众], //原消费的queryId,可以从查询接口或者关照接口中获取 'txnTime' => date('YmdHis', time()), //订单发送韶光,格式为YYYYMMDDhhmmss,重新产生,不同于原消费 'txnAmt' => $orders['total_price'] 100, //交易金额,退货总金额须要小于即是原消费 ); $AcpService->sign ( $params ); // 署名 $url = $config->getSDKConfig()->backTransUrl; $result_arr = $AcpService->post ( $params, $url); if(count($result_arr)<=0) { //没收到200应答的情形 return array('errorCode'=>1, 'message'=>\"大众没收到应答.\公众); } if (!$AcpService->validate ($result_arr) ){ return array('errorCode'=>1, 'message'=>\"大众应答报文验签失落败.\公众); } if ($result_arr[\公众respCode\"大众] == \公众00\"大众){ //交易已受理,等待吸收后台关照更新订单状态,如果关照永劫光未收到也可发起交易状态查询 return array('errorCode'=>0, 'message'=>\"大众受理成功.\公众); } else if ($result_arr[\公众respCode\"大众] == \"大众03\公众 || $result_arr[\公众respCode\"大众] == \公众04\"大众 || $result_arr[\"大众respCode\"大众] == \公众05\公众 ){ //后续需发起交易状态查询交易确定交易状态 return array('errorCode'=>1, 'message'=>\"大众处理超时,请轻微查询.\"大众); } else { //其他应答码做以失落败处理 return array('errorCode'=>1, 'message'=>\"大众失落败:\公众 . $result_arr[\公众respMsg\"大众] . \"大众.\"大众); } }

依据返回状态值进行干系操作即可,实际逻辑代码请自行实现!

资料领取:4个G的PHP微信二次开拓教程分享,第三方接口和小程序支付功能。

领取办法:关注然后私信回答PHP即可得到!

再次把稳:如果您以为获取办法跟步骤很麻烦,麻烦您高抬贵手不要找我,毕竟这些都是赠予!