🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[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, '', '&amp'); $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)