[TOC]
### 一、平台已集成的支付通道
已集成的第三方包括:支付宝官方、微信支付官方、易生支付、汇付、信汇支付、宝付;
![](https://img.kancloud.cn/bc/9c/bc9c8971540620040b02e665d3b59dd7_3482x1663.png)
### 二、 添加新的支付通道步骤
1、网站后台 → 接口管理 → 接口通道 → 添加接口,英文别名不能和其他接口重名,创建接口文件要使用这个名称。假设我要添加“**信汇支付**”的接口,在这里我定义盛大支付支付宝的英文别名为**XhepayAlipay**
![](https://img.kancloud.cn/32/bc/32bcc5aaf3b965dbd038666bbaac5d93_1563x1444.png)
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
![](https://img.kancloud.cn/1c/dc/1cdcfb9dc5af152067807d0cb44c19e0_1822x978.png)
### 三、全局修改支付通道费率
网站后台 → 接口管理 → 接口通道,找到要修改的支付通道,然后点击“编辑”图标按钮:
![](https://img.kancloud.cn/ff/bf/ffbf2661f4021936c77cb4f5c09cf8e0_1972x1297.png)
结算费率填写97,就是3%的通道服务费率,商户成功支付100元的订单,会自动扣除3元,最终结算给商户的金额是97元。
![](https://img.kancloud.cn/15/71/157143e8a08ea56b43967e35bb842d8a_1628x1478.png)
### 四、单独调整某个商户的支付通道费率
网站后台 → 会员管理 → 会员列表,找到你要修改的商户,点击“通道”菜单,进行调整:
![](https://img.kancloud.cn/f9/59/f959d3bfa9998587b6b670171ef9c764_3046x1588.png)
### 五、单独给某个商户指定支付通道账户
单独指定支付通道子账户,可以有效预防风控。设置方法如下:
1、网站后台 → 会员管理 → 会员列表,找到你要修改的商户,点击“通道”菜单;
![](https://img.kancloud.cn/8c/1d/8c1dc6bdf661173d438bbc8755e53e4d_3052x1579.png)
2、找到你想设置的支付通道,然后点击“添加指定通道账户”,勾选你要添加的账户,最后“保存”即可。
![](https://img.kancloud.cn/0f/ea/0fea315f83928942a57fb2cd73b31f44_2140x1268.png)