In diesem Artikel wird hauptsächlich der PHP-Backend-Beispielcode für UnionPay-Zahlungen und -Rückerstattungen vorgestellt. Der Herausgeber findet ihn recht gut, daher werde ich ihn jetzt mit Ihnen teilen und als Referenz verwenden. Folgen wir dem Herausgeber und werfen wir einen Blick darauf
Aussage: Dieser Artikel basiert auf dem neuesten offiziellen UnionPay SDK (2016-08-09 Version 5.1.0). Wenn die Pakete unterschiedlich sind, überprüfen Sie bitte, ob dies der Fall ist Fall. Version
Kürzlich aufgetretene UnionPay-Zahlungen und damit verbundene Rückerstattungen (dieser Artikel basiert nur auf der Zahlung per Mobiltelefon). Im Folgenden werden die während des Zeitraums aufgetretenen Probleme und der grundlegende Vorgang beschrieben theofficial Dieses Bild zeigt einige Dinge, die wir für Back-End-Personal in einer Zahlung tun müssen
Wie aus diesem Bild ersichtlich ist, ist das Back-End für 1 verantwortlich . Plattformbestellungen generieren; 2. Bestellungen an die UnionPay-Omni-Channel-Plattform senden; 3. TN-Code zur Zahlung an das Front-End zurücksenden;
Hier gibt es drei Schwierigkeiten: Bestell-Push, asynchrone Benachrichtigungsverarbeitung und Bestellstatusabfrage.
Laden Sie das entsprechende Paket über die offiziellen E-Mail-Anweisungen herunter und fügen Sie es in den Back-End-PHP-Code ein (Wenn Sie die Zahlungskontrolle herunterladen, werden Sie wahrscheinlich nur die IOS- und Android-Versionen des SDK sehen . Für das Back-End laden Sie einfach einen beliebigen PHP-Code herunter. Lesen Sie dann die Datei readme.txt im SDK sorgfältig durch und führen Sie dann die folgenden Schritte aus:
1 . Zugehörige Parameterkonfiguration
Verwenden Sie während des Andockvorgangs die Testumgebung Konfigurationsdatei und das Zertifikat im Assets-Ordner von SDK, legen Sie sie im SDK-Ordner ab und konfigurieren Sie die / Die Datei sdk/SDKconfig.php muss korrekt gelesen werden. Rufen Sie die Konfigurationsdatei acp_sdk.ini ab.
Konfigurieren Sie die absoluten Adressen der vier Dateien acpsdk.signCert.path, acpsdk.encryptCert.path, acpsdk.rootCert.path und acpsdk.middleCert.path in der Datei acp_sdk.ini (passen Sie einfach den Dateipfad an). .
Aufgrund unterschiedlicher Systeme oder unterschiedlicher Projektadressen können während der Projektentwicklung Fehler wie absolute Zertifikatsadressen auftreten. Insbesondere in tatsächlichen Produktionsumgebungen ist es sehr einfach, unterschiedliche Projektbereitstellungsdateiadressen zu haben Nach jedem Update muss die Zertifikatsadresse geändert werden, um mit verschiedenen Dateiadressen kompatibel zu sein. Bitte klicken Sie zum Erweitern und Anzeigen
<?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); } } }
2 . Vollständiger Produkt-Bestell-Push
Bitte klicken Sie, um den entsprechenden Code anzuzeigen
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"; } }
Bitte beachten Sie, dass das txnTime-Format nicht falsch übertragen werden sollte Probleme in der Testumgebung. Der erhaltene TN kann zur Zahlung an die APP zurückgegeben werden.
3. Asynchrone Benachrichtigungsverarbeitung und Abfrage des Bestelltransaktionsstatus Die Funktion dieses Schritts besteht darin, Informationen zum Erfolg der UnionPay-Transaktion zu verarbeiten und zu versuchen, Probleme durch nicht bearbeitete Rückrufe zu vermeiden.
/** * 银联回调 * * @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"] . "."); } }
Die Projektbeziehung ist vorübergehend nicht verfügbar - Folgeergänzungen
Das obige ist der detaillierte Inhalt vonPHP-Backend UnionPay-Zahlungs- und Rückerstattungsbeispiele. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!