TP3.2.3 接入銀聯支付
項目接入銀聯支付的過程, 在此記錄下,希望能幫助開發盆友平坑。
銀聯SKD鏈接:https://open.unionpay.com/ajweb/product/newProDetail?proId=1&cataId=14
首先我們先下載官方提供的SDK ,下載好了解壓選擇版本 ,里面有PHP java .net 這里我們自然是選擇PHP版本的,接入前我們先配環境,它要我們PHP
的版本 在5.3以上,並且需開啟環境的curl、openssl功能。
然后就是它提供的測試證書了,默認在window系統是放在D:/certs ,意思是在你的電腦的D創建一個名為certs 的文件夾,然后將4個證書放進去,測試的名為
acp_test_enc.cer acp_test_middle.cer acp_test_root.cer acp_test_sign.pfx ,在創建一個名為logs文件夾D:/logs/ 放支付生成的日志文件,linux中請
修改成Linux中的路徑。
如圖:
你可以在assets文件夾中找到你要的證書,測試環境官方提供四個證書,生產環境官方提供三個,還個簽名證書就是后綴為.pfx 的需要你去你的銀聯那里申請
下載對應的你還需要可以需要解簽名證書的密碼 和商戶號 , 這些后面都會提到的。
在然后我們將名為SDK文件夾中的六個文件放到我們項目放到 ThinkPHP\Library\Vendor\Yunpay 的文件夾中
寫邏輯代碼前你還得前配置好你的acp_sdk.ini 文件 ,文件里面都寫的很清楚,這里就不一一解釋了,這里是我的支付代碼
//銀聯充值操作 public function pay() { header ( 'Content-type:text/html;charset=utf-8' ); Vendor('Yunpay.acp_service'); //前台通知地址 $frontUrl = "http://".I("server.HTTP_HOST")."/Assets/rechargedetail"; //后台通知地址 $backUrl = "http://".I("server.HTTP_HOST"); $params = array( //以下信息非特殊情況不需要改動 'version' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->version, //版本號 'encoding' => 'utf-8', //編碼方式 'txnType' => '01', //交易類型 'txnSubType' => '01', //交易子類 'bizType' => '000201', //業務類型 'frontUrl' => $frontUrl, //前台通知地址 'backUrl' => $backUrl, //后台通知地址 'signMethod' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->signMethod, //簽名方法 'channelType' => '08', //渠道類型,07-PC,08-手機 'accessType' => '0', //接入類型 'currencyCode' => '156', // 超過超時時間調查詢接口應答origRespCode不是A6或者00的就可以判斷為失敗。 'payTimeout' => date('YmdHis', strtotime('+15 minutes')) //訂單發送時間 ); $txnAmt = I('post.txnAmt'); //交易金額 $orderId = I('post.orderId'); //商戶訂單號 //加入商戶參數 $params['txnAmt'] = $txnAmt*100; $params['merId'] = C('Yunpay.merId'); //商戶號 $params['orderId'] = $orderId; $params['txnTime'] = date('YmdHis'); //商品描述,可空 $body = trim(I('post.WIDbody')); $ud = session('users.uid'); $data = array( 'uid'=>$ud, //用戶id 'win_code'=>$orderId, //商戶訂單號 'winsubject'=>I('post.WIDsubject'), //訂單名稱 'wintotal_amount'=>$txnAmt, //付款金額 'winbody'=>I('post.WIDbody'), //商品描述 'state'=>'yl', //支付方式 'status'=>'0', //是否支付 'ordertime'=>time() //交易時間 ); M("pay_record")->add($data); // 保存交易信息 \com\unionpay\acp\sdk\AcpService::sign ( $params ); $uri = \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->frontTransUrl; $html_form = \com\unionpay\acp\sdk\AcpService::createAutoFormHtml( $params, $uri ); echo $html_form; }
這里我曾試過刪除里面的命名空間用 new的方式去寫,這樣可以簡潔代碼,但是后面異步的時候報錯,客服說不能刪除命名空間,這樣會導致方法名重復,所以還是乖乖的用demo
中提供的方法。
異步方法--------
1 //銀聯充值異步 2 public function xxx() 3 { 4 Vendor('Yunpay.acp_service'); 5 $logger = \com\unionpay\acp\sdk\LogUtil::getLogger(); 6 $logger->LogInfo("receive back notify: " . \com\unionpay\acp\sdk\createLinkString ( $_POST, false, true )); 7 if (isset ( $_POST ['signature'] )) { 8 // echo \com\unionpay\acp\sdk\AcpService::validate ( $_POST ) ? '驗簽成功' : '驗簽失敗'; 9 $respCode = I('post.respCode'); 10 $orderId = I('post.orderId'); // 商戶訂單號 11 $total_amount = I('post.settleAmt'); //訂單金額 12 $trade_no = I('post.queryId'); // queryId 銀聯唯一標識一筆交易 13 14 //判斷respCode=00、A6后,對涉及資金類的交易,請再發起查詢接口查詢,確定交易成功后更新數據庫。 15 if( $respCode=='00' ){ 16 $this->unionpay($orderId,$total_amount,$trade_no); 17 }else{ 18 $res = $this->confirmpay($orderId,'1'); 19 if( $res == 'Successful' ){ 20 $this->unionpay($orderId,$total_amount,$trade_no); 21 } else { 22 echo '交易失敗'; 23 } 24 } 25 26 } else { 27 echo '簽名為空'; 28 } 29 30 31 } 32 33 34 //銀聯充值 35 public function unionpay($orderId,$total_amount,$trade_no) 36 { 37 $per = M("pay_record")->where('win_code='.$orderId)->find(); //查找該訂單 38 if( $per['status']=='1' ){ 39 echo '已充值'; 40 return; 41 } 42 $Pay = M("pay"); 43 // 在Pay模型中啟動事務 44 $Pay->startTrans(); 45 // 進行相關的業務邏輯操作 46 $res = $Pay->where('uid='.$per['uid'])->setInc('money',$total_amount/100); 47 //數據組合 48 $data = array( 49 'alipay_number'=>$trade_no, //銀聯唯一標識 50 'status'=>'1', //交易狀態 51 'paytime'=>time() //交易時間 52 ); 53 M("pay_record")->where('win_code='.$orderId)->save($data); // 修改交易信息 54 55 if (!empty($res)){ 56 // 提交事務 57 $Pay->commit(); 58 }else{ 59 // 事務回滾 60 $Pay->rollback(); 61 } 62 } 63 64 65 //確定是否充值操作 66 public function confirmpay($orderId,$L) 67 { 68 header ( 'Content-type:text/html;charset=utf-8' ); 69 Vendor('Yunpay.acp_service'); 70 $params = array( 71 //以下信息非特殊情況不需要改動 72 'version' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->version, //版本號 73 'encoding' => 'utf-8', //編碼方式 74 'signMethod' => \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->signMethod, //簽名方法 75 'txnType' => '00', //交易類型 76 'txnSubType' => '00', //交易子類 77 'bizType' => '000000', //業務類型 78 'accessType' => '0', //接入類型 79 'channelType' => '07', //渠道類型 80 ); 81 if($L == '0'){ 82 $time = M("order_pay")->where('win_code = "'.$orderId.'"')->find()['addtime']; 83 }else{ 84 $time = M("pay_record")->where('win_code = "'.$orderId.'"')->find()['ordertime']; 85 } 86 $params['merId'] = C('Yunpay.merId'); //商戶號 87 $params['orderId'] = $orderId; //交易的訂單號 88 $params['txnTime'] = date('YmdHis',$time); //訂單發送時間 89 90 \com\unionpay\acp\sdk\AcpService::sign ( $params ); // 簽名 91 $url = \com\unionpay\acp\sdk\SDKConfig::getSDKConfig()->singleQueryUrl; 92 93 $result_arr = \com\unionpay\acp\sdk\AcpService::post ( $params, $url); 94 if(count($result_arr)<=0) { //沒收到200應答的情況 95 return 'No200'; 96 } 97 if (!\com\unionpay\acp\sdk\AcpService::validate ($result_arr) ){ 98 return "應答報文驗簽失敗"; 99 } 100 if ($result_arr["respCode"] == "00"){ 101 if ($result_arr["origRespCode"] == "00"){ 102 //交易成功 103 //TODO 104 return "Successful"; 105 } else if ($result_arr["origRespCode"] == "03" 106 || $result_arr["origRespCode"] == "04" 107 || $result_arr["origRespCode"] == "05"){ 108 //后續需發起交易狀態查詢交易確定交易狀態 109 //TODO 110 return "交易處理中,請稍微查詢"; 111 } else { 112 //其他應答碼做以失敗處理 113 //TODO 114 return "交易失敗:" . $result_arr["origRespMsg"]; 115 } 116 } else if ($result_arr["respCode"] == "03" 117 || $result_arr["respCode"] == "04" 118 || $result_arr["respCode"] == "05" ){ 119 //后續需發起交易狀態查詢交易確定交易狀態 120 //TODO 121 return "處理超時,請稍微查詢"; 122 } else { 123 //其他應答碼做以失敗處理 124 //TODO 125 return "失敗:" . $result_arr["respMsg"]; 126 } 127 128 }
這里第一個方法xxx 中的respCode等於00 就是支付成功 ,如果沒有需要根據你生成的訂單號在次查詢在結果。這里客服說這種失敗不好模擬,就不說了,但是這操作方法還是
建議寫下,以防萬一 。
最后說明下幾個參數 queryId 銀聯唯一標識,需要保存, 還有銀聯支付是按 '分' 做單位的 所以支付跳轉前 假如是1元,你得乘以100,它才可以識別為1元,要不然就是0.01元
然后異步到你的時候,如果你是元的單位在除於100,如果是分就不用了。
搞定收工 , 祝大家早日成為大牛