[TOC]
### 一、平臺已集成的支付通道
已集成的第三方包括:支付寶官方、微信支付官方、易生支付、匯付、信匯支付、寶付;

### 二、 添加新的支付通道步驟
1、網站后臺 → 接口管理 → 接口通道 → 添加接口,英文別名不能和其他接口重名,創建接口文件要使用這個名稱。假設我要添加“**信匯支付**”的接口,在這里我定義盛大支付支付寶的英文別名為**XhepayAlipay**

2、在pay/application/common目錄創建一個XhepayAlipay.php,繼承自同目錄里的Pay.php,大概代碼如下:
```
<?php
namespace app\common;
use think\Request;
use app\common\model\Order as OrderModel;
use app\common\model\WayAccount;
/**
* 信匯支付
* https://www.xhepay.com/
*/
abstract class Xhepay extends Pay{
public function startPay($params){
//驗證訂單
if(!isset($params["debug"])){
$order = OrderModel::field("id")->where('number',$params["pay_id"])->find();
empty($order) && outputError('訂單不存在,請重新提交');
}
$alias = $this->way['alias'];
$params["bizCode"] = $this->getBizCode();//業務代碼 (微信掃碼:1001 支付寶掃碼:1002)
$params["notify_url"] = $this->notify_url($alias);//異步通知地址
//創建信匯支付對象
$payobj= new \pay\Xhepay($this->account['params']);
$result=$payobj->submit($params);
if($result['code']==0) {
exit('信匯支付交易取消,'.$result['msg']);
}
//接收返回的訂單編號和支付二維碼
$params['out_trade_no'] = $result['data']['out_trade_no'];
$params['qr_code'] = $result['data']['qr_code'];
$params['type']= $this->getPayType();//支付類型:1支付寶 2微信支付 3QQ錢包
//凍結接口額度(子類調用父類方法)
parent::freezeApiAmount($params,$this->account);
//返回原生支付結果
if(isset($params["native"])){
parent::returnNativeJson($params['pay_id'], $params["qr_code"]);
}
echo buildRequestForm(url('@h5/pay'),$params);
}
public function query($order_no){
$falg=false;
//查詢支付訂單狀態
try{
//創建支付對象
$payobj= new \pay\Xhepay($this->account['params']);
$result=$payobj->orderquery($order_no,'00');
if($result){
$falg=true;
}else {
$falg=false;
}
} catch (\Exception $e) {
}
return $falg;
}
/**
* 服務器回調
*/
public function notify_callback($params){
//查詢數據庫訂單狀態
$order = OrderModel::where('number', $params['orderId'])->find();
if(!$order){
exit('不存在編號['.$params['orderId'].']的訂單!');
}
//防止惡意刷新加錢
if ($order['status'] == 1) {
//直接返回給上游 success 或者 OK 之類的
exit('ok');
}
//渠道賬戶
$accountid = $order["way_account_id"];
$account = CacheData("account-".$accountid,function() use($accountid){
return WayAccount::where('id',$accountid)->find();
},300);
//創建支付對象
$payobj= new \pay\Xhepay(json_decode($account['params'],true));
//調用異步通知驗證
$verifyresult=$payobj->notify($params);
if ($verifyresult) {
//將微信或支付寶端的訂單號寫入數據庫(0.0.2以上版本增加payNo參數)
if(isset($params['payNo']) && !empty($params['payNo'])){
OrderModel::where('number', $params['orderId'])->setField('pay_no',$params['payNo']);
}
//實際付款金額(分轉元)
$money = sprintf("%.2f", $params['orderAmt'] / 100);
//完成訂單
$falg=parent::completeOrder($order,$money);
exit($falg ? 'ok' : 'fail');
}else{
exit('fail'); //返回失敗 繼續補單
}
}
//獲取支付類型
abstract public function getPayType();
//獲取業務碼
abstract public function getBizCode();
//異步通知地址
abstract public function notify_url($alias);
//同步通知地址
abstract public function return_url($alias);
}
?>
```
3、在pay/application/common/payment目錄下創建**XhepayAlipay.php**文件,這個文件名要求的接口通道的英文別名一樣,要繼承自Shengdapay.php,大概代碼如下:
```
<?php
namespace app\common\payment;
use think\Request;
use app\common\Xhepay;
class XhepayAlipay extends Xhepay{
public function getPayType()
{
return 1;
}
public function getBizCode()
{
return "1002";
}
public function return_url($alias)
{
return Request::instance()->domain().'/callback/page/'.$alias;
}
public function notify_url($alias)
{
return Request::instance()->domain().'/callback/notify/'.$alias;
}
public function page_callback($params)
{
return parent::page_callback($params);
}
public function notify_callback($params)
{
return parent::notify_callback($params);
}
}
?>
```
4、在pay/extend/pay目錄創建盛大支付的sdk文件**Xhepay.php**,這個文件主要用來和第三方接口進行數據交互,該文件的代碼要根據第三方提供的sdk示例或者接口開發文檔來編寫,信匯支付的sdk支付類文件代碼如下:
```
<?php
namespace pay;
use \think\Db;
use think\Request;
/**
* 信匯支付類
*/
class Xhepay {
//網關地址
private $gateway = 'http://www.hyuepay.com/order/pay.do';
//查詢地址
private $order_url = 'http://www.hyuepay.com/query/order.do';
//商戶號
private $appid;
//支付秘鑰
private $appkey;
//版本號
private $version="0.0.1";
//業務代碼
private $bizCode;
private $charset = 'utf-8';
public function setAppid($appid)
{
$this->appid = $appid;
}
public function setAppkey($appkey)
{
$this->appkey = $appkey;
}
public function setBizCode($bizCode)
{
$this->bizCode = $bizCode;
}
//構造方法
public function __construct($account){
$this->appid = $account['id'];
$this->appkey = $account['key'];
if(isset($account['url']) && !empty($account['url'])){
$this->gateway = $account['url'];
}
if(isset($account['order_url']) && !empty($account['order_url'])){
$this->order_url = $account['order_url'];
}
if(isset($account['version']) && !empty($account['version'])){
$this->version = $account['version'];
}
}
/**
*
* 提交訂單
* $params 傳輸的數據
*/
public function submit($params){
//商品名稱
$subject=isset($params['subject']) && !empty($params['subject']) ? $params['subject'] : "購買商品";
//組合生成簽名參數
$postdata=array(
"orderId" => $params['pay_id'], //流水號
"merchantId" => $this->appid, //商戶號
"version" => $this->version, //版本號(固定)
"orderAmt" => floatval($params['money'])* 100, //訂單金額(分)
"bizCode" => $params['bizCode'], //業務代碼 (微信掃碼:1001 支付寶掃碼:1002)
"bgUrl" => $params['notify_url'], //異步通知地址
"terminalIp" => Request::instance()->ip(), //用戶ip
"productName" => $subject, //商品名稱
"productDes" => "訂單編號:".$params['pay_id'], //商品描述
"timeExpire" => "5", //訂單超時時間,時間單位分鐘(最小1,最大不能超過60)
);
//echo "<pre>";
//var_dump($postdata);die;
//生成簽名
$postdata["sign"] = $this->generateSign($postdata,$this->appkey);
//echo "<pre>";
//var_dump($postdata);die;
//發起請求
$content = $this->file_post($this->gateway,$postdata);
$returnData = json_decode($content, true);
//公共響應參數
if($returnData['rspCode'] == '00'){
//生成成功,返回結果給前端
$data = [];
$data['out_trade_no'] = $returnData['orderId'];
$data['qr_code'] = $returnData['payUrl'];
return ['code' => 1 , 'msg' => '成功' , 'data' => $data];
}else {
//file_put_contents(LOG_PATH .'xhepay.log', 'err code:' . $returnData['rspCode'] . ', err msg:' . $returnData['rspMsg'] . '\r\n', FILE_APPEND);
return ['code' => 0 , 'msg' => '錯誤碼:' . $returnData['rspCode'] . ',錯誤信息:' . $returnData['rspMsg']];
}
}
/**
* 支付通知
* @param $params 通知的數據
*/
public function notify($params){
$falg=false;
//驗證簽名
$rst = $this->rsaCheck($params);
if(!$rst){
//file_put_contents(LOG_PATH .'xhepay.log', '驗簽失敗\r\n' , FILE_APPEND );
return false;
}
//查詢支付訂單狀態
try{
$rst = $this->orderquery($params['orderId'], '00');
} catch (\Exception $e) {
//printLog("查詢支付訂單狀態失敗:".$e);
}
if($rst){
$falg=true;
}else {
//file_put_contents(LOG_PATH .'xhepay.log', '查詢訂單狀態錯誤\r\n', FILE_APPEND);
$falg=false;
}
return $falg;
}
/**
*
* 支付查詢接口
* @param order_no 訂單編號
* @param status 交易實際狀態,00:成功,02:失敗,01:處理中
*/
public function orderquery($order_no , $status){
$postdata=array(
'orderId' => $order_no,
'merchantId' => $this->appid,
'bizCode' => "4001",//固定編碼
'version' => $this->version,
);
//生成簽名
$postdata['sign'] = $this->generateSign($postdata,$this->appkey);
$content = $this->file_post($this->order_url,$postdata);
//將JSON格式的字符串轉換成數組
$responseData = json_decode($content,true);
//公共響應參數
if(strcmp($responseData['rspCode'],$status)==0){
if(strcmp($responseData['status'],$status)==0){
return true;
}else {
return false;
}
}else {
return false;
}
}
protected function generateSign($params,$appkey) {
//ksort()對數組按照鍵名進行升序排序
ksort($params);
//reset()內部指針指向數組中的第一個元素
reset($params);
//$sign = http_build_query($params, '', '&');
$sign = ''; //初始化
foreach ($params AS $key => $val) { //遍歷POST參數
if ($val == '' || $key == 'sign' || $key == 'signArray') continue; //跳過這些不簽名
if ($sign) $sign.= '&'; //第一個字符串簽名不加& 其他加&連接起來參數
$sign.= "$key=$val"; //拼接為url參數形式
}
//拼接商戶密鑰,然后md5加密
$stringTemp = md5($sign."&key=". $appkey);
return strtoupper($stringTemp);
}
protected function getSignContent($params) {
ksort($params);
$stringToBeSigned = "";
$i = 0;
foreach ($params as $k => $v) {
if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
// 轉換成目標字符集
$v = $this->characet($v, $this->charset);
if ($i == 0) {
$stringToBeSigned .= "$k" . "=" . "$v";
} else {
$stringToBeSigned .= "&" . "$k" . "=" . "$v";
}
$i++;
}
}
unset ($k, $v);
return $stringToBeSigned;
}
/**
* 轉換字符集編碼
* @param $data
* @param $targetCharset
* @return string
*/
protected function characet($data, $targetCharset) {
if (!empty($data)) {
$fileType = $this->charset;
if (strcasecmp($fileType, $targetCharset) != 0) {
$data = mb_convert_encoding($data, $targetCharset, $fileType);
//$data = iconv($fileType, $targetCharset.'//IGNORE', $data);
}
}
return $data;
}
/**
*
* 校驗$value是否非空
*/
protected function checkEmpty($value) {
if (!isset($value))
return true;
if ($value === null)
return true;
if (trim($value) === "")
return true;
return false;
}
/**
*
* 驗簽函數
*/
protected function rsaCheck($params){
$sign1 = $params['sign'];
//不參與簽名
unset($params['sign']);
unset($params['alias']);
$sign2= $this->generateSign($params,$this->appkey);
if (!$params['upOrderId'] || $sign1 != $sign2) {
return false;
}else{
return true;
}
}
/**
* file_get_contents發送post請求
* @param url 請求地址
* @param postData 要傳遞的post數據
*/
protected function file_post($url, $post_data) {
try{
$postdata = http_build_query($post_data);
$options = array('http' => array('method' => 'POST', 'header' => 'Content-type:application/x-www-form-urlencoded', 'content' => $postdata, 'timeout' => 300
// 超時時間(單位:s)
));
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
//去空格
$result = trim($result);
//轉換字符編碼
$result = mb_convert_encoding($result, 'utf-8', 'UTF-8,GBK,GB2312,BIG5');
//解決返回的json字符串中返回了BOM頭的不可見字符(某些編輯器默認會加上BOM頭)
$result = trim($result,chr(239).chr(187).chr(191));
return $result;
} catch (\Exception $e) {
return null;
}
}
}
?>
```
5、使用Navicat for MySQL數據庫工具,打開pay_way數據表,fields字段添加通道賬戶的參數,pay/extend/pay/Xhepay.php文件里需要什么參數,就添加什么參數,如下:
> 商戶ID:id|支付秘鑰:key|網關地址:url|訂單查詢地址:order_url

### 三、全局修改支付通道費率
網站后臺 → 接口管理 → 接口通道,找到要修改的支付通道,然后點擊“編輯”圖標按鈕:

結算費率填寫97,就是3%的通道服務費率,商戶成功支付100元的訂單,會自動扣除3元,最終結算給商戶的金額是97元。

### 四、單獨調整某個商戶的支付通道費率
網站后臺 → 會員管理 → 會員列表,找到你要修改的商戶,點擊“通道”菜單,進行調整:

### 五、單獨給某個商戶指定支付通道賬戶
單獨指定支付通道子賬戶,可以有效預防風控。設置方法如下:
1、網站后臺 → 會員管理 → 會員列表,找到你要修改的商戶,點擊“通道”菜單;

2、找到你想設置的支付通道,然后點擊“添加指定通道賬戶”,勾選你要添加的賬戶,最后“保存”即可。
