網站支付:一般接入微信支付、支付寶支付、銀聯支付,本文介紹的是如何接入中國銀聯在線網關支付
銀聯介紹
銀聯在線支付網關是中國銀聯聯合各商業銀行爲持卡人提供的集成化、綜合性互聯網支付工具,主要支持輸入卡號付款、用戶登錄支付、網銀支付、迷你付(IC卡支付)等多種支付方式,爲持卡人提供境內外網上購物、水電煤繳費、商旅預訂等支付服務。
具體流程如下圖:
SDK&Demo下載
開放平臺服務端SDK
下圖爲下載好的sdk
ps:官方給的demo都是php文件中寫html,沒有分離,網上找了各種資料,沒有找到分離過後的demo,所以自己總結了demo,希望我的文章可以幫助到大家快速接入銀聯
異常應答說明
- 不返回報文體的情況:
版本號,交易類型、子類,簽名方法,簽名值等關鍵域未上送,返回“Invalid request.”; 交易類型和請求地址校驗有誤,返回“Invalid request URI.” - 返回全部的請求報文,附加應答碼和應答描述(包括的應答碼有:01、02、03、10、11、32):
驗證簽名失敗;
報文格式錯(包含,必填域缺失,上送銀聯報文未定義的域,報文域的格式非法,交易時間超出範圍);
簽名失敗;
超時等其他系統異常
友情提示
中間如遇到問題:請登錄商家後臺進行人工諮詢,什麼qq客服已停用。
基本錯誤的話文檔都會有介紹,如果不知道debug到了哪一步,客服會告訴你調用接口時返回的錯誤
登錄步驟:開放平臺在線諮詢:https://open.unionpay.com/tjweb/index 用已有賬號登錄或者註冊新用戶登錄,選擇“普通用戶”身份登錄,登錄後頁面點擊“前往商戶測試中心”,界面上點擊客服圖標後,點擊“確定登錄”,再點擊“商戶諮詢”進入諮詢。
業務問題與生產問題請撥打4007795516電話諮詢。
銀聯在線網關支付接口地址
https://open.unionpay.com/tjweb/api/dictionary?apiSvcId=448
修改公共參數配置
製作簽名需要配置參數,由於sdk中用到的大部分是ini配置文件,所以我將部分配置信息放到了php的配置文件中,修改了原有的配置文件
下圖要特別注意:消費和查詢交易,三個值必須統一,則退款不需要
/sdk/SDKConfig.php文件
// __construct方法中的配置文件添加
$this->env = 'test';
$this->signCertPwd = '000000';
//如果想把acp_sdk.ini挪到其他路徑的話,請修改下面這行指定絕對路徑。
$configFilePath = dirname(__FILE__) . "/acp_sdk_test.ini";
if(env('APP_ENV') == 'production'){
$this->env = 'prod';
$this->signCertPwd = env('UNION_SIGN_CERT_PWD','000000');
$configFilePath = dirname(__FILE__) . "/acp_sdk.ini";
}
// __construct方法中的配置文件修改
/*
* 重新定義配置文件路徑 ---- 自定義
$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->signCertPath = array_key_exists("acpsdk.signCert.path", $sdk_array)?$sdk_array["acpsdk.signCert.path"]: null;
$this->encryptCertPath = array_key_exists("acpsdk.encryptCert.path", $sdk_array)? $sdk_array["acpsdk.encryptCert.path"]: null;
$this->rootCertPath = array_key_exists("acpsdk.rootCert.path", $sdk_array)? $sdk_array["acpsdk.rootCert.path"]: null;
$this->middleCertPath = array_key_exists("acpsdk.middleCert.path", $sdk_array)?$sdk_array["acpsdk.middleCert.path"]: 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;
$this->signCertPwd = array_key_exists("acpsdk.signCert.pwd", $sdk_array)?$sdk_array["acpsdk.signCert.pwd"]: null;
*/
// 由於是laravel框架,所以放到了env中,也可以放到config中,其它框架放到配置文件中即可
$this->backUrl = env('HOST_FAST').env('UNION_PAY_BACKURL','');
$this->frontUrl = env('HOST_EDU').env('UNION_PAY_FRONTURL','/');
$this->signCertPath = dirname(dirname(dirname(__FILE__))).'/union_pay/assets/'.$this->env.'/acp_sign.pfx';
$this->encryptCertPath = dirname(dirname(dirname(__FILE__))).'/union_pay/assets/'.$this->env.'/acp_enc.cer';
$this->middleCertPath = dirname(dirname(dirname(__FILE__))).'/union_pay/assets/'.$this->env.'/acp_middle.cer';
$this->rootCertPath = dirname(dirname(dirname(__FILE__))).'/union_pay/assets/'.$this->env.'/acp_root.cer';
$this->logFilePath = storage_path(env("UNION_PAY_LOGFILE_PATH"));
$this->logLevel = env("UNION_PAY_LOG_LEVEL",null);
/sdk/acp_service.php文件
createAutoFormHtml方法 直接返回參數
static function createAutoFormHtml($params, $reqUrl) {
return $params;
}
配置信息
.env文件
UNION_PAY_TEST_MERID=測試商戶號
UNION_PAY_MERID=線上商戶號
UNION_SIGN_CERT_PWD=線上商戶號密碼
UNION_PAY_BACKURL=
UNION_PAY_FRONTURL=
UNION_PAY_LOGFILE_PATH=logs/union_pay
UNION_PAY_LOG_LEVEL=DEBUG
控制器
- consume方法爲給前端返回的所有配置信息
- union_validate是去驗證支付參數是否正確(後臺通知地址)
- queryTrans交易狀態查詢
- handle_result處理銀聯的回調
- union_refund退款
實戰demo地址
https://github.com/WXiangQian/laravel-api
接入消費接口
<?php
namespace App\Http\Controllers\Sys;
use App\Http\Tools\Tools;
use com\unionpay\acp\sdk\AcpService;
use com\unionpay\acp\sdk\SDKConfig;
use Illuminate\Http\Request;
class UnionPayController extends BasicController
{
protected $merId;
public function __construct(Request $request)
{
header ( 'Content-type:text/html;charset=utf-8' );
ini_set('date.timezone','Asia/Shanghai');
include_once dirname(dirname(dirname(__FILE__))) . '/Tools/union_pay/sdk/acp_service.php';
parent::__construct($request);
$this->merId = env('UNION_PAY_TEST_MERID'); // 測試號
if(env('APP_ENV') == 'production'){
$this->merId = env('UNION_PAY_MERID');;
}
}
/**
* 銀聯消費的使用參數
* @return \Illuminate\Http\JsonResponse
* User: https://github.com/WXiangQian
*/
public function consume()
{
/**
* 重要:聯調測試時請仔細閱讀註釋!
*
* 產品:跳轉網關支付產品<br>
* 交易:消費:前臺跳轉,有前臺通知應答和後臺通知應答<br>
* 日期: 2015-09<br>
* 版權: 中國銀聯<br>
* 說明:以下代碼只是爲了方便商戶測試而提供的樣例代碼,商戶可以根據自己需要,按照技術文檔編寫。該代碼僅供參考,不提供編碼性能規範性等方面的保障<br>
* 提示:該接口參考文檔位置:open.unionpay.com幫助中心 下載 產品接口規範 《網關支付產品接口規範》,<br>
* 《平臺接入接口規範-第5部分-附錄》(內包含應答碼接口規範,全渠道平臺銀行名稱-簡碼對照表)<br>
* 《全渠道平臺接入接口規範 第3部分 文件接口》(對賬文件格式說明)<br>
* 測試過程中的如果遇到疑問或問題您可以:1)優先在open平臺中查找答案:
* 調試過程中的問題或其他問題請在 https://open.unionpay.com/ajweb/help/faq/list 幫助中心 FAQ 搜索解決方案
* 測試過程中產生的7位應答碼問題疑問請在https://open.unionpay.com/ajweb/help/respCode/respCodeList 輸入應答碼搜索解決方案
* 2) 諮詢在線人工支持: open.unionpay.com註冊一個用戶並登陸在右上角點擊“在線客服”,諮詢人工QQ測試支持。
* 交易說明:1)以後臺通知或交易狀態查詢交易確定交易成功,前臺通知不能作爲判斷成功的標準.
* 2)交易狀態查詢交易(Form_6_5_Query)建議調用機制:前臺類交易建議間隔(5分、10分、30分、60分、120分)發起交易查詢,如果查詢到結果成功,則不用再查詢。(失敗,處理中,查詢不到訂單均可能爲中間狀態)。也可以建議商戶使用payTimeout(支付超時時間),過了這個時間點查詢,得到的結果爲最終結果。
*/
$channelType = $this->request->input('channelType','07');
$merId = $this->merId;
$riskRateInfo = $this->request->input('riskRateInfo','');
$orderId = $this->request->input('orderId',0);
$txnAmt = $this->request->input('txnAmt',1);
$txnAmt = $txnAmt * 100;
$txnTime = $this->request->input('txnTime',0);
if ($txnTime == 0) {
$txnTime = date('YmdHis');
}
// 在生產環境測試的時候,交易金額請勿小於1角。
if(env('APP_ENV') == 'production' && $txnAmt <= 10){
return $this->response->tag('PARAM_ERROR')->response();
}
// 查詢數據庫中的實際付款金額
$order_txnAmt = AwsOrders::get_value_by_where(['oid'=>$orderId],'pay_amount');
// 沒有查到則定金異常
if (!isset($order_txnAmt)) {
return $this->response->tag('ORDER_NOT_EXIST')->response();
}
$order_txnAmt = $order_txnAmt * 100;
// 不一樣則認爲惡意修改金額 返回錯誤
if ($txnAmt != $order_txnAmt) {
return $this->response->tag('ORDER_EXCEPTION')->response();
}
$params = array(
//以下信息非特殊情況不需要改動
'version' => SDKConfig::getSDKConfig()->version, //版本號
'encoding' => 'utf-8', //編碼方式
'txnType' => '01', //交易類型
'txnSubType' => '01', //交易子類
'bizType' => '000201', //業務類型
'frontUrl' => SDKConfig::getSDKConfig()->frontUrl.'?order_id='.$orderId, //前臺通知地址
'backUrl' => SDKConfig::getSDKConfig()->backUrl.$txnTime, //後臺通知地址
'signMethod' => SDKConfig::getSDKConfig()->signMethod, //簽名方法
'channelType' => $channelType, //渠道類型,07-PC,08-手機
'accessType' => '0', //接入類型
'currencyCode' => '156', //交易幣種,境內商戶固定156
//TODO 以下信息需要填寫
'merId' => $merId, //商戶代碼,請改自己的測試商戶號,此處默認取demo演示頁面傳遞的參數
'orderId' => $orderId, //商戶訂單號,8-32位數字字母,不能含“-”或“_”,此處默認取demo演示頁面傳遞的參數,可以自行定製規則
'txnTime' => $txnTime, //訂單發送時間,格式爲YYYYMMDDhhmmss,取北京時間,此處默認取demo演示頁面傳遞的參數
'txnAmt' => $txnAmt, //交易金額,單位分,此處默認取demo演示頁面傳遞的參數
// 訂單超時時間。
// 超過此時間後,除網銀交易外,其他交易銀聯繫統會拒絕受理,提示超時。 跳轉銀行網銀交易如果超時後交易成功,會自動退款,大約5個工作日金額返還到持卡人賬戶。
// 此時間建議取支付時的北京時間加15分鐘。
// 超過超時時間調查詢接口應答origRespCode不是A6或者00的就可以判斷爲失敗。
'payTimeout' => date('YmdHis', strtotime('+15 minutes')),
'riskRateInfo' =>'{commodityName='.$riskRateInfo.'}',
// 請求方保留域,
// 透傳字段,查詢、通知、對賬文件中均會原樣出現,如有需要請啓用並修改自己希望透傳的數據。
// 出現部分特殊字符時可能影響解析,請按下面建議的方式填寫:
// 1. 如果能確定內容不會出現&={}[]"'等符號時,可以直接填寫數據,建議的方法如下。
// 'reqReserved' =>'透傳信息1|透傳信息2|透傳信息3',
// 2. 內容可能出現&={}[]"'符號時:
// 1) 如果需要對賬文件裏能顯示,可將字符替換成全角&={}【】“‘字符(自己寫代碼,此處不演示);
// 2) 如果對賬文件沒有顯示要求,可做一下base64(如下)。
// 注意控制數據長度,實際傳輸的數據長度不能超過1024位。
// 查詢、通知等接口解析時使用base64_decode解base64後再對數據做後續解析。
// 'reqReserved' => base64_encode('任意格式的信息都可以'),
//TODO 其他特殊用法請查看 special_use_purchase.php
);
AcpService::sign ( $params );
$uri = SDKConfig::getSDKConfig()->frontTransUrl;
$html_form = AcpService::createAutoFormHtml( $params, $uri );
$data['url'] = $uri;
foreach ($html_form as $key=>$value) {
$data['data'][] = ['name'=>$key,'value'=>$value];
}
return $this->response->data($data)->response();
}
/**
* 驗證支付參數是否正確
* @param $txnTime 訂單發送時間
* @return \Illuminate\Http\JsonResponse
* User: https://github.com/WXiangQian
*/
public function union_validate($txnTime)
{
if (isset ( $_POST ['signature'] )) {
// 驗籤失敗
if (!AcpService::validate ($_POST)) {
return $this->response->tag('PARAM_ERROR')->response();
}
$orderId = $_POST ['orderId']; //其他字段也可用類似方式獲取
$respCode = $_POST ['respCode'];
//判斷respCode=00、A6後,對涉及資金類的交易,請再發起查詢接口查詢,確定交易成功後更新數據庫。
if ($respCode == 00 || $respCode == 'A6') {
// todo 將下單時間存到redis key:order_id value:txnTime 查詢交易的時候需要使用
// todo 調用api項目的回調地址
return $this->response->response();
} else {
return $this->response->tag('OPERATION_FAILED')->response();
}
}
return $this->response->tag('PARAM_LACK')->response();
}
}
接入交易狀態查詢接口
/**
* 交易狀態查詢
* @param $channelType
* @param $merId
* @param $orderId
* @param $txnTime
* @return mixed
* @throws LogicException
* User: https://github.com/WXiangQian
*/
public function queryTrans($channelType,$merId,$orderId,$txnTime)
{
$params = array(
//以下信息非特殊情況不需要改動
'version' => SDKConfig::getSDKConfig()->version, //版本號
'encoding' => 'utf-8', //編碼方式
'signMethod' => SDKConfig::getSDKConfig()->signMethod, //簽名方法
'txnType' => '00', //交易類型
'txnSubType' => '00', //交易子類
'bizType' => '000000', //業務類型
'accessType' => '0', //接入類型
'channelType' => $channelType, //渠道類型
//TODO 以下信息需要填寫
'orderId' => $orderId, //請修改被查詢的交易的訂單號,8-32位數字字母,不能含“-”或“_”,此處默認取demo演示頁面傳遞的參數
'merId' => $merId, //商戶代碼,請改自己的測試商戶號,此處默認取demo演示頁面傳遞的參數
'txnTime' => $txnTime, //請修改被查詢的交易的訂單發送時間,格式爲YYYYMMDDhhmmss,此處默認取demo演示頁面傳遞的參數
);
AcpService::sign ( $params ); // 簽名
$url = SDKConfig::getSDKConfig()->singleQueryUrl;
$result_arr = $this->handle_result($params,$url);
return $result_arr;
}
/**
* 處理銀聯的回調
* @param $params
* @param $url
* @throws LogicException
* @return mixed
* User: https://github.com/WXiangQian
*/
public function handle_result($params,$url)
{
$result_arr = AcpService::post ( $params, $url);
if(count($result_arr)<=0) { //沒收到200應答的情況
throw new LogicException('REFUND_FAIL');
}
if (!AcpService::validate ($result_arr) ){
throw new LogicException('CHECK_SIGN_FAIL');
}
if ($result_arr["respCode"] == "00"){
//交易已受理,等待接收後臺通知更新訂單狀態,如果通知長時間未收到也可發起交易狀態查詢
//TODO
return $result_arr;
} else if ($result_arr["respCode"] == "03"
|| $result_arr["respCode"] == "04"
|| $result_arr["respCode"] == "05" ){
//後續需發起交易狀態查詢交易確定交易狀態
//TODO
throw new LogicException('HADNLE_OVERTIME');
} else {
//其他應答碼做以失敗處理
//TODO
throw new LogicException('失敗:'.$result_arr["respMsg"],14004);
}
}
接入退貨接口
/**
* 銀聯退款
* @throws LogicException
* @return \Illuminate\Http\JsonResponse
* User: https://github.com/WXiangQian
*/
public function union_refund()
{
$channelType = $this->request->input('channelType','07');
$merId = $this->merId;
$oid = $this->request->input('orderId',0);
$txnAmt = $this->request->input('txnAmt',1);
$txnAmt2 = $txnAmt * 100;
$txnTime = date('YmdHis');
// todo 查詢訂單信息 判斷訂單是否存在
$order_info = ['plat_oid'=>11111111];
$origQryId = $order_info['plat_oid'];
$orderId = time().rand(1111, 9999); // 生成退款訂單號
/**
* 重要:聯調測試時請仔細閱讀註釋!
*
* 產品:跳轉網關支付產品<br>
* 交易:退貨交易:後臺資金類交易,有同步應答和後臺通知應答<br>
* 日期: 2015-09<br>
* 版權: 中國銀聯<br>
* 說明:以下代碼只是爲了方便商戶測試而提供的樣例代碼,商戶可以根據自己需要,按照技術文檔編寫。該代碼僅供參考,不提供編碼性能規範性等方面的保障<br>
* 該接口參考文檔位置:open.unionpay.com幫助中心 下載 產品接口規範 《網關支付產品接口規範》<br>
* 《平臺接入接口規範-第5部分-附錄》(內包含應答碼接口規範,全渠道平臺銀行名稱-簡碼對照表)<br>
* 測試過程中的如果遇到疑問或問題您可以:1)優先在open平臺中查找答案:
* 調試過程中的問題或其他問題請在 https://open.unionpay.com/ajweb/help/faq/list 幫助中心 FAQ 搜索解決方案
* 測試過程中產生的7位應答碼問題疑問請在https://open.unionpay.com/ajweb/help/respCode/respCodeList 輸入應答碼搜索解決方案
* 2) 諮詢在線人工支持: open.unionpay.com註冊一個用戶並登陸在右上角點擊“在線客服”,諮詢人工QQ測試支持。
* 交易說明: 1)以後臺通知或交易狀態查詢交易(Form_6_5_Query)確定交易成功,建議發起查詢交易的機制:可查詢N次(不超過6次),每次時間間隔2N秒發起,即間隔1,2,4,8,16,32S查詢(查詢到03,04,05繼續查詢,否則終止查詢)
* 2)退貨金額不超過總金額,可以進行多次退貨
* 3)退貨能對11個月內的消費做(包括當清算日),支持部分退貨或全額退貨,到賬時間較長,一般1-10個清算日(多數髮卡行5天內,但工行可能會10天),所有銀行都支持
*/
$params = array(
//以下信息非特殊情況不需要改動
'version' => SDKConfig::getSDKConfig()->version, //版本號
'encoding' => 'utf-8', //編碼方式
'signMethod' => SDKConfig::getSDKConfig()->signMethod, //簽名方法
'txnType' => '04', //交易類型
'txnSubType' => '00', //交易子類
'bizType' => '000201', //業務類型
'accessType' => '0', //接入類型
'channelType' => $channelType, //渠道類型
'backUrl' => 'http://www.specialUrl.com', //後臺通知地址
//TODO 以下信息需要填寫
'orderId' => $orderId, //商戶訂單號,8-32位數字字母,不能含“-”或“_”,可以自行定製規則,重新產生,不同於原消費,此處默認取demo演示頁面傳遞的參數
'merId' => $merId, //商戶代碼,請改成自己的測試商戶號,此處默認取demo演示頁面傳遞的參數
'origQryId' => $origQryId, //原消費的queryId,可以從查詢接口或者通知接口中獲取,此處默認取demo演示頁面傳遞的參數
'txnTime' => $txnTime, //訂單發送時間,格式爲YYYYMMDDhhmmss,重新產生,不同於原消費,此處默認取demo演示頁面傳遞的參數
'txnAmt' => $txnAmt2, //交易金額,退貨總金額需要小於等於原消費
// 請求方保留域,
// 透傳字段,查詢、通知、對賬文件中均會原樣出現,如有需要請啓用並修改自己希望透傳的數據。
// 出現部分特殊字符時可能影響解析,請按下面建議的方式填寫:
// 1. 如果能確定內容不會出現&={}[]"'等符號時,可以直接填寫數據,建議的方法如下。
// 'reqReserved' =>'透傳信息1|透傳信息2|透傳信息3',
// 2. 內容可能出現&={}[]"'符號時:
// 1) 如果需要對賬文件裏能顯示,可將字符替換成全角&={}【】“‘字符(自己寫代碼,此處不演示);
// 2) 如果對賬文件沒有顯示要求,可做一下base64(如下)。
// 注意控制數據長度,實際傳輸的數據長度不能超過1024位。
// 查詢、通知等接口解析時使用base64_decode解base64後再對數據做後續解析。
// 'reqReserved' => base64_encode('任意格式的信息都可以'),
);
AcpService::sign ( $params ); // 簽名
$url = SDKConfig::getSDKConfig()->backTransUrl;
$this->handle_result($params,$url);
// todo 退款成功-需要將退款信息存到自己的業務表中
return $this->response->response();
}
測試環境測試銀聯支付
測試商戶號爲:777290058110048。測試環境只能使用銀聯給的銀行卡信息:測試環境的測試卡信息
測試流程截圖
注意事項
注:在生產環境測試的時候,交易金額請勿小於1角。
從測試配置切到線上配置時,線上必須修改UNION_SIGN_CERT_PWD線上商戶號密碼
遇到報錯信息:The each() function is deprecated. This message will be suppressed on further calls
php7.2+已將each函數廢除,則需要自己換爲foreach
解決方案:
// 在sdk/common.php中的createLinkString方法進行修改
// while ( list ( $key, $value ) = each ( $para ) ) {
// if ($encode) {
// $value = urlencode ( $value );
// }
// $linkString .= $key . "=" . $value . "&";
// }
// php7.2後廢棄each
foreach ($para as $key => $value) {
if ($encode) {
$value = urlencode ( $value );
}
$linkString .= $key . "=" . $value . "&";
}
結束語
真槍實戰php接入中國銀聯在線網關支付消費和交易狀態查詢以及銀聯退款接口。