《PHP實戰:PHP后端銀聯支付及退款實例代碼》要點:
本文介紹了PHP實戰:PHP后端銀聯支付及退款實例代碼,希望對您有用。如果有疑問,可以聯系我們。
PHP實例聲明:此文以當前銀聯官方最新SDK(2016-08-09 5.1.0版)進行說明,若出現包不相同的情況請檢查是否是此版本
PHP實例近期遇到銀聯支付以及相關退款(此文僅以手機控件支付作為前提)操作,下面會依次寫出期間遇到的問題以及基本流程,在此之前通過官方的一張圖片了解一個支付中,對于后端人員的我們需要做到的一些事情
PHP實例
PHP實例由此圖可以看出,后端在此負責1、平臺訂單生成;2、銀聯全渠道平臺訂單推送;3、返回tn碼給前端進行支付;4、處理前臺通知以及全渠道平臺的異步通知.
PHP實例此間難點有三,訂單推送、異步通知處理、訂單狀態查詢.
PHP實例通過官方的郵件說明下載相關的包并放入后端php代碼中,(支付控件去下載你看到的估計只有IOS,安卓版的SDK,對于后端來說,隨便下載一個即可,PHP的代碼在里面都有放置);然后仔細閱讀SDK中的readme.txt文件,此后進行以下步驟:
PHP實例一、相關參數配置
PHP實例對接過程中使用在sdk的assets文件夾中測試環境配置文件及證書,放置到sdk文件夾中,并配置/sdk/SDKconfig.php文件已正確的讀取acp_sdk.ini配置文件.
PHP實例在acp_sdk.ini文件中配置好acpsdk.signCert.path、acpsdk.encryptCert.path、acpsdk.rootCert.path、acpsdk.middleCert.path四個文件的絕對地址(自定義文件路徑即可).
PHP實例因項目開發過程中會出現系統不同或項目地址不同導致的證書絕對地址等錯誤,尤其在實際生產環境中,極易出現項目部署文件地址不同,不可能在開發后每次更新都要更換證書地址,在此修改了一下SDK中的SDKconfig.php已兼容不同文件地址較長,這里還請點擊展開查看
PHP實例
<?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);
}
}
}
PHP實例二、全渠道商品訂單推送
PHP實例相關代碼請點擊查看
PHP實例
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";
}
}
PHP實例在此注意txnTime格式不要傳錯,測試環境下應該不會出現什么問題,將得到的tn返回APP進行支付即可
PHP實例三、異步通知處理以及訂單交易狀態查詢
PHP實例這一步主要作用為處理銀聯交易成功信息,并盡可能避免出現回調未處理導致問題.
PHP實例先說異步通知處理,此步驟為訂單狀態修改的主要依據.無實際難點,保證相關參數無問題即可
PHP實例
/**
* 銀聯回調
*
* @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;
}
PHP實例訂單交易狀態查詢
PHP實例
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;
}
PHP實例到此為止,若是項目沒有訂單線上退款就完成了.
PHP實例訂單退款相關
PHP實例
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"] . ".");
}
}
PHP實例依據返回狀態值進行相關操作即可,實際邏輯代碼請自行實現
PHP實例切換生產環境
PHP實例項目關系暫無法進行-后續補充
PHP實例未完待續....
PHP實例以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持維易PHP.
轉載請注明本頁網址:
http://www.snjht.com/jiaocheng/576.html