銀聯(lián)支付
商戶入駐
1. 填寫商戶基礎(chǔ)信息
2. 提交信息后,跳轉(zhuǎn)銀聯(lián)簽約頁面完成簽約
3. 簽約完成后,如果是對公賬戶,需要等待銀聯(lián)打款(交易附言可能會有交易碼),在完成對公賬戶打款驗證
4. 等待銀聯(lián)審核完成,下發(fā)商戶號
支付
主從商戶模式
-
支付寶
-
APP支付
-
通過APP支付接口下單,成功后得到支付要素(js串),通過在APP內(nèi)集成銀聯(lián)提供的SDK,吊起APP支付
- 支付成功后回調(diào)下單時上送的回調(diào)地址,進(jìn)行對應(yīng)回調(diào)處理
-
-
生活窗支付
-
下單后,得到支付要素,通過支付要素、單號,在瀏覽器中跳轉(zhuǎn)到自己的收銀頁面,調(diào)起支付寶收銀臺
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <title>跳轉(zhuǎn)支付中...</title> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0"> <script src="<?php echo env('WEB_SERVER');?>/web/js/jquery.min.js" type="text/javascript"></script> <script> var tradeNo = <?php echo json_encode($tradeNo);?>; var return_url = '<?php echo $return_url;?>'; // 調(diào)試時可以通過在頁面定義一個元素,打印信息,使用alert方法不夠優(yōu)雅 function log(obj) { $("#result").append(obj).append(" ").append("<br />"); } $(document).ready(function(){ // 頁面載入完成后即喚起收銀臺 // 此處${tradeNO}為模板語言語法,實際調(diào)用樣例類似為tradePpay("2016072621001004200000000752") tradePay(tradeNo); // 點擊payButton按鈕后喚起收銀臺 $("#payButton").click(function() { tradePay(tradeNo); }); // 通過jsapi關(guān)閉當(dāng)前窗口,僅供參考,更多jsapi請訪問 // /aod/54/104510 $("#closeButton").click(function() { AlipayJSBridge.call('closeWebview'); }); }); // 由于js的載入是異步的,所以可以通過該方法,當(dāng)AlipayJSBridgeReady事件發(fā)生后,再執(zhí)行callback方法 function ready(callback) { if (window.AlipayJSBridge) { callback && callback(); } else { document.addEventListener('AlipayJSBridgeReady', callback, false); } } function tradePay(tradeNO) { ready(function(){ // 通過傳入交易號喚起快捷調(diào)用方式(注意tradeNO大小寫嚴(yán)格) AlipayJSBridge.call("tradePay", { tradeNO: tradeNO }, function (data) { if ("9000" == data.resultCode) { log("支付成功"); window.location.href=return_url; } }); }); } </script> </head> <body> <p id="result">result: </p> </body> </html>- 支付成功后回調(diào)下單時上送的回調(diào)地址,進(jìn)行對應(yīng)回調(diào)處理
-
-
H5支付
-
下單后,得到一個url,在瀏覽器中跳轉(zhuǎn)后,會調(diào)起手機支付寶APP
- 支付成功后回調(diào)下單時上送的回調(diào)地址,進(jìn)行對應(yīng)回調(diào)處理
-
-
-
微信
-
APP支付
-
通過APP支付接口下單,成功后得到支付要素(js串),通過在APP內(nèi)集成銀聯(lián)提供的SDK,吊起APP支付
- 支付成功后回調(diào)下單時上送的回調(diào)地址,進(jìn)行對應(yīng)回調(diào)處理
-
-
公眾號支付
- 下單后,得到支付要素,通過支付要素、單號,在微信客戶端內(nèi)跳轉(zhuǎn)到自己的收銀頁面,調(diào)起微信收銀臺
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <title>跳轉(zhuǎn)支付中...</title> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0"> <script src="<?php echo env('WEB_SERVER');?>/web/js/jquery.min.js" type="text/javascript"></script> <script> var json = <?php echo json_encode($data);?>; var return_url = '<?php echo $return_url;?>'; $(function(){ onBridgeReady(json); }); function onBridgeReady(content){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', content, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) { window.location.href=return_url; } // 使用以上方式判斷前端返回,微信團(tuán)隊鄭重提示:res.err_msg將在用戶支付成功后返回 ok,但并不保證它絕對可靠。 } ); } if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', function(){ onBridgeReady(json) }, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', function(){ onBridgeReady(json) }); document.attachEvent('onWeixinJSBridgeReady', function(){ onBridgeReady(json) }); } }else{ onBridgeReady(json); } </script> </head> <body> </body> </html> ``` - 支付成功后回調(diào)下單時上送的回調(diào)地址,進(jìn)行對應(yīng)回調(diào)處理 小程序支付
-
-
銀聯(lián)
- B掃C支付(條碼支付)
- C掃B支付(主動掃碼支付)
獨立商戶模式
-
支付寶
- APP支付
- 生活窗支付
- H5支付
-
微信
- APP支付
- 公眾號支付
- 小程序支付
-
銀聯(lián)
- B掃C支付(條碼支付)
- C掃B支付(主動掃碼支付)
結(jié)算
自動清分
- 在商戶進(jìn)件時,設(shè)置好清分比例,每筆訂單支付成功后,銀聯(lián)按設(shè)置比例自動清分
指令清分
- 每日通過上送清分指令文件到銀聯(lián)SFTP服務(wù)器,告訴銀聯(lián)每筆訂單如何進(jìn)行分賬
對賬
每天14點以后,銀聯(lián)將前一日的訂單對賬文件推送至服務(wù)器,用戶通過SFTP方式,到銀聯(lián)服務(wù)器下載每日的對賬文件
部分代碼
<?php
/**
* Created by PhpStorm.
* User: isesame
* Date: 2019/11/05
* Time: 19:08
* 銀聯(lián)支付服務(wù)
*/
namespace App\Http\Services\PayClass;
use App\Http\Services\BaseService;
use App\Http\Services\Params\ChinaUMSPay\Exceptions\CurlException;
use App\Http\Services\Params\Params;
class ChinaUMSPayService extends BaseService
{
public $notifyUrl; //支付回調(diào)地址
public function __construct()
{
$this->notifyUrl = env('WEB_SERVER').'/chinaums/pay/getNotify';
}
public function getAuthorization($content)
{
$arr['appId'] = config('chinaumspay.AppId');
$arr['timestamp'] = date('YmdHis');//yyyyMMddHHmmss
$arr['nonce'] = md5(uniqid(microtime(true),true));
$arr['content'] = json_encode($content);
$strA=bin2hex(hash('sha256', $arr['content'], true));
$appKey = config('chinaumspay.AppKey');
$signature = base64_encode(hash_hmac('sha256',$arr['appId'].$arr['timestamp'].$arr['nonce'].$strA, $appKey, true));
$Authorization="OPEN-BODY-SIG AppId=\"".$arr['appId']."\", Timestamp=\"".$arr['timestamp']."\", Nonce=\"".$arr['nonce']."\", Signature=\"".$signature."\"";
return $Authorization;
}
public function getH5Authorization($content)
{
$arr['appId'] = config('chinaumspay.AppId');
$arr['authorization'] = 'OPEN-FORM-PARAM';
$arr['timestamp'] = date('YmdHis');//yyyyMMddHHmmss
$arr['nonce'] = md5(uniqid(microtime(true),true));
$arr['content'] = json_encode($content);
$strA=bin2hex(hash('sha256', $arr['content'], true));
$appKey = config('chinaumspay.AppKey');
$signature = base64_encode(hash_hmac('sha256',$arr['appId'].$arr['timestamp'].$arr['nonce'].$strA, $appKey, true));
$arr['signature'] = $signature;
return $arr;
}
/**
* @param Params $params
* @return mixed
* @throws \Exception
*/
public function ChinaUMSRequest(Params $params)
{
$param = array_filter($params->toArray(), function ($item) {
return $item !== "" && !is_null($item);
}); //object to array
$request_url = array_pull($param,'request_url');
$Authorization = $this->getAuthorization($param);
$header[]='AUTHORIZATION:'.$Authorization;
app('myLog')->lumenLog('銀聯(lián)轉(zhuǎn)發(fā)接口請求參數(shù),trade:' . json_encode($param), 'chinaums_trade');
// 請求銀聯(lián)
try {
$result = $this->postUmsCurl($request_url, $param, $header);
} catch (\Exception $e) {
throw new \Exception('請求轉(zhuǎn)發(fā)銀聯(lián)接口失敗請稍后重試!', 602);
}
app('myLog')->lumenLog('銀聯(lián)接口請求成功,return:' . json_encode($result), 'chinaums_trade_success');
return json_decode(json_encode($result),true);
}
/**
* @param Params $params
* @return mixed
* @throws \Exception
*/
public function ChinaUMSH5Request(Params $params)
{
$param = array_filter($params->toArray(), function ($item) {
return $item !== "" && !is_null($item);
}); //object to array
$request_url = array_pull($param,'request_url');
app('myLog')->lumenLog('銀聯(lián)轉(zhuǎn)發(fā)接口請求參數(shù),trade:' . json_encode($param), 'chinaums_trade');
$param = $this->getH5Authorization($param);
$request_url = $request_url .'?'. http_build_query($param);
return $request_url;
}
/**
* 請求商戶接口
* @param Params $params
* @return mixed
* @throws \Exception
*/
public function ChinaUMSBusinessRequest(Params $params)
{
if (env('APP_ENV') == 'dev') {
$key = 'udik876ehjde32dU61edsxsf';
$business_accesser_id = '100004';
} else {
$key = config('chinaumspay.business_key');
$business_accesser_id = config('chinaumspay.business_accesser_id');
}
$param = array_filter($params->toArray(), function ($item) {
return $item !== "" && !is_null($item);
}); //object to array
$request_url = array_pull($param,'request_url');
$json_str = json_encode($param);
$sign_data = hash('sha256', $json_str);
$json_data = self::encrypt($json_str, $key);
$arr = [
'json_data' => $json_data,
'sign_data' => $sign_data,
'accesser_id' => $business_accesser_id
];
app('myLog')->lumenLog('銀聯(lián)轉(zhuǎn)發(fā)接口請求參數(shù),trade:' . json_encode($param), 'chinaums_trade');
try {
$result = postCurl($request_url, $arr);
} catch (\Exception $e) {
throw new \Exception('請求轉(zhuǎn)發(fā)銀聯(lián)接口失敗請稍后重試!', 602);
}
app('myLog')->lumenLog('銀聯(lián)接口請求成功,return:' . json_encode($result), 'chinaums_trade_success');
return json_decode($result,true);
}
/**
* 請求UMS
* @param $url
* @param array $body
* @param array $header
* @param string $method
* @return bool|mixed
* @throws CurlException
*/
public function postUmsCurl($url, $body = array(), $header = array(), $method = 'POST')
{
$curl = curl_init(); //初始化
$body = json_encode($body);
curl_setopt($curl, CURLOPT_URL,$url); //設(shè)置url
array_push($header, 'Accept:application/json');
array_push($header, 'Content-Type:application/json;charset=utf-8');
array_push($header, 'Content-Length:'.strlen($body));
curl_setopt($curl, CURLOPT_HTTPHEADER,$header);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 對認(rèn)證證書來源的檢查
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); // 從證書中檢查SSL加密算法是否存在 設(shè)為0表示不檢查證書 設(shè)為1表示檢查證書中是否有CN(common name)字段 設(shè)為2表示在1的基礎(chǔ)上校驗當(dāng)前的域名是否與CN匹配
// curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模擬用戶使用的瀏覽器
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自動跳轉(zhuǎn)
curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自動設(shè)置Referer
curl_setopt($curl, CURLOPT_POST, 1); // 發(fā)送一個常規(guī)的Post請求
curl_setopt($curl, CURLOPT_POSTFIELDS,$body);
curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 設(shè)置超時限制防止死循環(huán)
curl_setopt($curl, CURLOPT_HEADER, 0); // 顯示返回的Header區(qū)域內(nèi)容
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 獲取的信息以文件流的形式返回
$return = curl_exec($curl);
$err = curl_error($curl);
$errno = curl_errno($curl);
curl_close($curl);
if ($errno) {
app('log')->error(sprintf(
'postUmsCurl %s %s error: %s[%s] body: %s%s',
strtoupper($method),
$url,
$err,
$errno,
json_encode(
$body,
JSON_UNESCAPED_SLASHES
| JSON_UNESCAPED_UNICODE
| JSON_PRETTY_PRINT
| JSON_FORCE_OBJECT
),
PHP_EOL
));
return false;
} else {
app('log')->error(sprintf(
'postUmsCurl %s %s body: %s%s response: %s%s',
strtoupper($method),
$url,
json_encode(
$body,
JSON_UNESCAPED_SLASHES
| JSON_UNESCAPED_UNICODE
| JSON_PRETTY_PRINT
| JSON_FORCE_OBJECT
),
PHP_EOL,
$return,
PHP_EOL
));
}
if($return === false){
throw new CurlException('ChinaUms請求失敗', 501);
}
$return=json_decode($return,true);
return $return;
}
// 加密算法
public static function encrypt($str, $key)
{
$desJsonStr = self::pkcs5Pad($str, 8);
if (strlen($desJsonStr) % 8) {
$desJsonStr = str_pad($desJsonStr, strlen($desJsonStr) + 8 - strlen($desJsonStr) % 8, "\0");
}
$method = 'DES-EDE3';
return bin2hex(openssl_encrypt($desJsonStr, $method, $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING));
}
public static function pkcs5Pad($text, $byteLen)
{
$pad = $byteLen - (strlen($text) % $byteLen);
return $text . str_repeat(chr($pad), $pad);
}
}