PHP to implement back-end UnionPay payment and refund case detailed

Source: Internet
Author: User
Tags ack response code
This article mainly introduces the PHP implementation of the back-end UnionPay payment and refund examples, interested in the reference of friends, I hope to be helpful to everyone.

Statement: This article with the current official UnionPay latest SDK (2016-08-09 5.1.0 version) to explain, if the package is not the same situation, please check whether this version

Recently encountered UnionPay payment and related refunds (this article is only based on mobile phone control payment as a precondition) operation, the following will write down the problems encountered during and the basic process, before this through an official picture of a payment, for the back-end personnel we need to do something

As can be seen from this figure, the backend is responsible for 1, platform order generation, 2, UnionPay omni-channel platform order push, 3, return TN code to the front end to pay, 4, processing the front desk notification and the Omni-channel platform asynchronous notification.

Here are three difficulties, order push, asynchronous notification processing, order status query.

Download the relevant package through the official email instructions and put it into the back-end PHP code, (the payment control to download the estimate you see only iOS, the Android version of the SDK, for the backend, a random download, PHP code in the inside are placed) And then carefully read the Readme.txt file in the SDK, followed by the following steps:

First, the relevant parameter configuration

During docking, use the Assets folder in the SDK to test the environment profile and certificate, place it in the SDK folder, and configure the/sdk/sdkconfig.php file to read the Acp_sdk.ini configuration file correctly.

Configure Acpsdk.signCert.path, Acpsdk.encryptCert.path, Acpsdk.rootCert.path, Acp_sdk.ini files in the. Acpsdk.middleCert.path the absolute address of four files (custom file path).

Because the project development process will appear different system or the project address of the certificate absolute address and other errors, especially in the actual production environment, very easy to appear project deployment file address is different, it is not possible to change the certificate address every time after development In this modified the SDK sdkconfig.php is compatible with the different file address longer, here also click Expand View

<?phpnamespace 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 $qrcB 2cIssBackTransUrl;     Private $qrcB 2cMerBackTransUrl;  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 () {///If you want to move Acp_sdk.ini to a different path, modify the following line to specify an absolute path. $configFilePath = DirName (__file__).    "/acp_sdk.ini"; $certsFilePath = DirName (dirname (__file__)).         "/certs/";      if (!file_exists ($configFilePath)) {$logger = Logutil::getlogger (); $logger->logerror ("configuration file failed to load, file path: [". $configFilePath. "]. Check to see if the user who started PHP has Read permissions.      ");    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 _n    AME);    } else {return (NULL); }  } }

Second, omni-channel merchandise order push

Please click here for the relevant code.

Use Com\unionpay\acp\sdk\acpservice;use com\unionpay\acp\sdk\logutil;use com\unionpay\acp\sdk\sdkconfig; /** * UnionPay Payment order * * @param $orders * @param $orders _type * @return Array */Public function UnionPay ($orders, $o Rders_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 (//The following information does not need to be changed ' version ' = $config->getsdkconfig ()->version,//version number ' E Ncoding ' + ' utf-8 ',//coded by ' txntype ' and ' 01 ',//transaction type ' txnsubtype ' + ' 01 ',/ /Trade sub-category ' BizType ' = ' 000201 ',//Business type ' fronturl ' + $config->getsdkconfig ()->fronturl,//front desk Notice Site ' backurl ' + $this->geturl (' Api_pay_unionpay_call_back '),//Background notification address ' signmethod ' + = $config-&GT;GETSD       KConfig ()->signmethod,  Signature method ' Channeltype ' = ' 08 ',//channel type, 07-pc,08-phone ' accessType ' = ' 0 ',//access type ' Curren Cycode ' + ' 156 ',//trading currency, domestic merchant fixed 156//todo The following information needs to be filled in ' merid ' + $this->getparameter (' mer_id '),// Merchant Code, please change your own test merchant number ' orderId ' + $orders ["Order_no"],//Merchant order number, 8-32-digit letter, cannot contain "-" or "_" ' txntime ' + $time,//order Sent     Time, format for YYYYMMDDHHMMSS, take the Beijing Times ' Txnamt ' + $orders [' total_price '] * 100,//transaction amount, Unit points); $AcpService->sign ($params);     Signature $url = $config->getsdkconfig ()->apptransurl;     $result _arr = $AcpService->post ($params, $url);    if (count ($result _arr) <=0) {//forfeiture to 200 of the response $log->loginfo (' forfeiture to 200 of the response); }//$this->printresult ($url, $params, $result _arr);    Page Print request answer data if (! $AcpService->validate ($result _arr)) {$log->loginfo (' failed to verify the response message '); } if ($result _arr["respcode"] = = "00") {//Successful return array (' txn_time ' = $time, ' tn ' = + $result _arr["TN"]); /echo "Follow upPlease transfer this TN to mobile phone development, and they will pay after the control is set up with this TN. \ n "///echo" mobile demo get TN from simulation by default, the simulation returns only one TN, if you don't want to modify the communication between the phone and the background, "This page please modify the code to output TN only".    \ n ";      } else {//Other response code to fail to process return array (' txn_time ' = $time, ' tn ' =>0); echo "failed:". $result _arr["Respmsg"]. "。     \ n "; }  }

Note that the txntime format should not pass the wrong, the test environment should not have any problems, will get TN back to the app to pay

Third, asynchronous notification processing and order transaction status query

This step is mainly to deal with the success of the UnionPay transaction information, and to avoid the occurrence of callback unhandled caused problems.

First, the asynchronous notification processing, this step is the main basis for order status modification. No practical difficulties, to ensure that the relevant parameters without problems can

/** * UnionPay Callback * * @param Request $request * @return array| Response */Public Function unionpaycallbackaction (Request $request) {if ($request->get (' type ') = = 1) {//foreground notification-for       Order Status Query $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-&gt      ; Request->get (' orderId ');      $respCode = $request->request->get (' Respcode '); $total = $request->request->get (' Txnamt '); Transaction amount if ($respCode = = = ' xx ' | | $respCode = = = ' A6 ') {$trade _no = $request->request->get (' Origqryid ')? : ' UN '. Date (' Ymdhis ', Time ()).        substr (Implode (NULL, Array_map (' Ord ', Str_split (substr (), 7, 13), 1)), 0, 8); $this->dispose ($orDer_no, $trade _no, 4);//order transaction processing-please write according to the actual situation}} else {if (! $request->request->has (' signature ')) {      $log->loginfo (' signature is empty ');      } else {$log->loginfo (' failed verification ');  }} exit; }

Order transaction Status Query

   do{//loops the query until the Queryid sleep ($number * 2) is obtained to the refund order;      $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)).    "/model/unionpay-sdk/sdk/acp_service.php";    $config = new Sdkconfig ();    $AcpService = new Acpservice ();    $log = Logutil::getlogger (); $params = Array (//The following information does not need to be changed ' version ' = $config->getsdkconfig ()->version,//version number ' Encodin G ' = ' utf-8 ',//encoded by ' Signmethod ' + $config->getsdkconfig ()->signmethod,//Signature method ' Txntype ' = ' 00 ',//transaction type ' txnsubtype ' = ' 00 ',//trading sub-class ' BizType ' = ' 000000 ',//business type ' accessty PE ' = ' 0 ',//access type ' channeltype ' + ' 07 ',//channel type//todo The following information needs to be filled in ' orderId ' and $orders [' Ord Er_no '],//Modify the order number of the transaction being queried, 8-32-digit letter, cannot contain "-" or "_" ' MerId ' + $this->getparameter (' mer_id '),//merchant code, please change your own test merchant number ' txntime ' = ' + Date ' (' Ymdhis ', Time ()),//Please modify the order of the transaction being queried     Send time, format is YYYYMMDDHHMMSS); $AcpService->sign ($params);     Signature $url = $config->getsdkconfig ()->singlequeryurl;    $result _arr = $AcpService->post ($params, $url);    if (count ($result _arr) <=0) {//forfeiture to 200 of the response $log->loginfo (' forfeiture to 200 of the response);    } if (! $AcpService->validate ($result _arr)) {$log->loginfo (' failed to verify the response message ');  if ($result _arr["respcode"] = = "XX") {if ($result _arr["origrespcode"] = = "00") {//successfully traded $trade _no = ' UN '. Date (' Ymdhis ', Time ()).        substr (Implode (NULL, Array_map (' Ord ', Str_split (substr (), 7, 13), 1)), 0, 8);        $this->dispose ($orders [' order_no '], $trade _no, 4);       $result = Array (' ErrorCode ' =>0, ' message ' = ' trading success ', ' result_arr ' = ' = $result _arr); } else if ($result _arr["origrespcode"] = = "_ar" | | $result _arr["origrespcode"] = = "$result" | |r["Origrespcode"] = = "05") {//subsequent need to initiate trading status inquiry trade to determine the trading status $result = Array (' ErrorCode ' =>2, ' message ' = ' transaction processing ',       ' Result_arr ' = $result _arr); } else {//Other ACK code to do with failed processing echo "Transaction failed:". $result _arr["Origrespmsg"]. "。         \ n "; $result = Array (' ErrorCode ' =>1, ' message ' = ' = ' Trade failed: '. $result _arr["Origrespmsg"].      ".", ' result_arr ' = $result _arr);  }} else if ($result _arr["respcode"] = = "_arr[" | | $result _arr["respcode"] = = "$result" | | "05") {//The subsequent need to initiate a transaction status query transaction to determine the transaction status $result = Array (' ErrorCode ' =>2, ' message ' = + ' processing timeout, please inquire later. ", ' Result_arr ' =&gt    ; $result _arr); } else {//Other ACK code to do with failed processing $result = Array (' ErrorCode ' =>1, ' message ' = ' = ' "failed:". $result _arr["Respmsg"].    ".", ' result_arr ' = $result _arr);  } return $result; }

So far, if the item does not have a refund on the order line is complete.

Order Refund Related

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{//loops the query until the Queryid sleep ($number * 2) is obtained to the refund order;      $query = $this->unionpayquery (", $orders);    $number + = 1;        }while ($query [' errorCode ']! = 0 | | empty ($query [' Result_arr '] ["Queryid"]));    if ($query [' errorCode ']! = 0) {return array (' ErrorCode ' =>1, ' message ' = ' order not traded, no refunds ');      } $params = Array (//The following information is not required to change ' version ' = $config->getsdkconfig ()->version,//version number      ' Encoding ' = ' utf-8 ',//coded by ' Signmethod ' + $config->getsdkconfig ()->signmethod,//Signature method ' Txntype ' = ' 04 ',//trading type ' txnsubtype ' + ' 00 ',//trading sub-class ' BizType ' = ' 000201 ',//industry     Service type ' accessType ' = ' 0 ',//access type ' Channeltype ' = ' 07 ',//channel type ' Backurl ' + $config->getsdkconfig ()->backurl,//Background notification address//todo The following information is required to fill out ' orderId ' and ' T '. $orders [' order_no '],//Merchant order number, 8-32-digit letter, cannot contain "-" or "_", can customize the rules, re-generated-here for the refund order before splicing T ' Merid ' + $this->getparameter (      ' mer_id '),//merchant code, please change to your merchant number ' Origqryid ' and $query [' Result_arr '] ["Queryid"],//The original consumption of Queryid, can be obtained from the query interface or the notification interface ' Txntime ' + date (' Ymdhis ', Time ()),//order sent, format YYYYMMDDHHMMSS, re-generated, different from the original consumption ' Txnamt ' and $orders [' Total_pri     Ce '] * 100,//Transaction amount, the total amount of return must be less than or equal to the original consumption; $AcpService->sign ($params);     Signature $url = $config->getsdkconfig ()->backtransurl;    $result _arr = $AcpService->post ($params, $url);     if (count ($result _arr) <=0) {//forfeited to 200 of the reply, the return array (' ErrorCode ' =>1, ' message ' = ') ' is forfeited to the answer.}     if (! $AcpService->validate ($result _arr)) {return array (' ErrorCode ' =>1, ' message ' = ' = ' response message verification failed. ");     if ($result _arr["respcode"] = = "00") { Transaction has been accepted, waiting to receive background notification update order status, if the notification is not received for a long time can also initiate a transaction status query return array (' ErrorCode ' =>0, ' message ' = ' = ' Accept success.} else if ($result _arr["respcode"] = = "_arr[" | | $result _arr["respcode"] = = ["] | | $result respcode" "" "[] = =" 05 ") {//Follow up the transaction status inquiry transaction to determine the transaction status return array (' ErrorCode ' =>1, ' message ' = + ' processing timeout, please query slightly. ");} else {//Other ACK code to do with failed to process return array (' ErrorCode ' =>1, ' message ' = ' = ' "failed:". $result _arr["Respmsg"].    "."); }  }

Depending on the return status value for the relevant operation, the actual logic code should be implemented by itself

Switching production environments

Project relationship is temporarily unavailable-follow-up supplement

Not to be continued ....

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.