🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 一、后台添加接口 1、登录管理员后台 → 接口通道 → 添加接口 ![](https://img.kancloud.cn/99/a4/99a441e777708f83951964c0910851d4_1667x802.png) 2、接口英文别名就是接口实现类的文件名(必须是唯一的,不要和其他接口重名) ![](https://img.kancloud.cn/66/c2/66c249fa95caedb888d32d2621ba3b3e_1411x1329.png) ### 二、编写接口代码 1、pay/application/common/目录里创建接口类,extends继承自Pay.php,例如元宝支付Yuanbaopay.php,参考代码如下: ![](https://img.kancloud.cn/40/9e/409ef8f55ef5e3c666257cf4772836b8_1104x1370.png) `<?php namespace app\common; use think\Request; use app\common\model\Order as OrderModel; use app\common\model\WayAccount; abstract class Yuanbaopay 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["notify_url"] = $this->notify_url($alias);//异步通知地址 $params["return_url"] = $this->return_url($alias);//前台地址 $params['paytype']= $this->get_paytype();//接口支付方式 //创建支付对象 $payobj= new \pay\Yuanbaopay($this->account['params']); //冻结接口额度(子类调用父类方法) parent::freezeApiAmount($params,$this->account); echo $payobj->submit($params); } public function query($order_no){ $falg=false; //查询支付订单状态 try{ //创建支付对象 $payobj= new \pay\Yuanbaopay($this->account['params']); $result=$payobj->orderquery($order_no); if($result){ $falg=true; }else { $falg=false; } } catch (\Exception $e) { } return $falg; } /** * 页面回调 */ public function page_callback($params){ //查询订单 $order= OrderModel::where('number',$params["pay_id"])->find(); if(!$order){ exit('不存在编号['.$params["pay_id"].']的订单!'); } //渠道账户 $accountid = $order["way_account_id"]; $account = CacheData("account-".$accountid,function() use($accountid){ return WayAccount::where('id',$accountid)->find(); },300); //创建支付对象 $payobj= new \pay\Yuanbaopay(json_decode($account['params'],true)); //调用异步通知验证 $verifyresult=$payobj->notify($params); if ($verifyresult) { //整理需要的数据(统一格式) $postdata=array( "pay_id" => empty($order['pay_id'])?$order['number']:$order['pay_id'],//需要充值的ID 或订单号 或用户名 "pay_no" => $order['number'], //付款后生成的唯一流水号 "pay_money" => $order['money'],//实际付款金额 "pay_time" => (int)$order['pay_time'], //付款的时间戳 "pay_type" => (int)$order['pay_type'], //支付方式 1:支付宝 2:微信支付 3:QQ钱包 ); }else{ $postdata=array( "pay_id" => '', "pay_no" => '', "pay_money" => '0', "pay_time" => (int)time(), "pay_type" => 0 ); } //跳转到页面回调地址 $this->jumpPageCallback($order,$postdata); } /** * 异步通知回调 */ public function notify_callback($params){ //查询数据库订单状态 $order = OrderModel::where('number', $params['pay_id'])->find(); if(!$order){ exit('不存在编号['.$params['pay_id'].']的订单!'); } //防止恶意刷新加钱 if ($order['status'] == 1) { //直接返回给上游OK exit('success'); } //渠道账户 $account = WayAccount::where('id',$order["way_account_id"])->find(); //创建支付对象 $payobj= new \pay\Yuanbaopay(json_decode($account['params'],true)); //调用异步通知验证 $verifyresult=$payobj->notify($params); if ($verifyresult) { //实际付款金额 $money = $params['pay_money']; //完成订单 $falg=parent::completeOrder($order,$money); exit($falg ? 'success' : 'fail'); }else{ exit('fail'); //返回失败 继续补单 } } //获取支付方式 abstract public function get_paytype(); //异步通知地址 abstract public function notify_url($alias); //同步通知地址 abstract public function return_url($alias); } ?>` 3、pay/application/common/payment目录下,新建接口实现类(类名和后台添加的接口通道名称一样),如下: ![](https://img.kancloud.cn/90/b1/90b17931ad838c0b00b760b9f1ca1e16_1146x455.png) `<?php namespace app\common\payment; use think\Request; use app\common\Yuanbaopay; class YuanbaopayAlipay extends Yuanbaopay{ public function get_paytype() { return "1"; } 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),参考代码如下: ![](https://img.kancloud.cn/ad/6d/ad6d18f1e6e0d7b15b9aacd30ed24b9d_1039x483.png) `<?php namespace pay; use \think\Db; use think\Request; class Yuanbaopay { //网关地址 private $gateway; //查询订单地址 private $order_url; //商户号 private $appid; //请求单号 private $sn; //支付秘钥 private $appkeyappkey; //字符集 private $charset = 'UTF-8'; //超时时间 private $timeOut = 120; //http状态码 private $responseCode = 0; //异常信息 private $error; //构造方法 public function __construct($account){ $this->appid = $account['appid']; $this->appkey = $account['appkey']; 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']; } } /** * * 提交订单 * $params 传输的数据 */ public function submit($params){ //商品名称 $subject=isset($params['subject']) && !empty($params['subject']) ? $params['subject'] : "购买商品"; //组合生成签名参数 $postdata=array( "appid" => $this->appid,//商户ID "type" => $params['paytype'],//支付方式,支付宝1 微信支付2 "payid" => $params['pay_id'],//商户订单号 "notify_url" => $params['notify_url'],//异步通知地址 "return_url" => $params['return_url'],//同步通知地址 "subject" => $subject,//商品名称 "money" => $params['money'], //订单金额(元) ); //生成签名 $postdata["sign"] = $this->generateSign($postdata,$this->appkey); //发起请求 if(isset($params['native']) && $params['native'] ){ //返回原生支付结果 $pay_url = $this->gateway."?".http_build_query($postdata); returnNativeJson($params['pay_id'],$pay_url); }else{ // 跳转到页面 return $this->buildRequestForm($this->gateway,$postdata); } } /** * 支付通知 * @param $params 通知的数据 */ public function notify($params){ $falg=false; //验证签名 $rst = $this->rsaCheck($params); if(!$rst){ //file_put_contents(LOG_PATH .'yuanbaopay.log', '验签失败\r\n' , FILE_APPEND ); return false; } //查询支付订单状态 try{ $rst = $this->orderquery($params['pay_no'],true); } catch (\Exception $e) { } if($rst){ return true; }else { //file_put_contents(LOG_PATH .'yuanbaopay.log', '查询订单状态错误\r\n', FILE_APPEND); return false; } } /** * * 支付查询接口 * @param order_no 订单编号 */ public function orderquery($order_no,$pay_no=false){ $param = $pay_no ? "pay_no" : "pay_id"; $data=array( "appid" => $this->appid, $param => $order_no//商户订单号 ); //生成签名 $data["sign"] = $this->generateSign($data,$this->appkey); $responseData = $this->curl_post($this->order_url,$data); if(empty($responseData)){ return false; } if($responseData['code']==1){ return true; }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'|| $key == '__token__'||$key=='encrypt'||$key=='charset') continue; //跳过这些不签名 if ($sign) $sign.= '&'; //第一个字符串签名不加& 其他加&连接起来参数 $sign.= "$key=$val"; //拼接为url参数形式 } //拼接商户密钥,然后md5加密 $stringTemp = md5($sign.$appkey); //转为小写 return strtolower($stringTemp); } /** * 转换字符集编码 * @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 ($sign1 != $sign2) { return false; }else{ return true; } } /** * curl发送post请求 * @param url 请求地址 * @param postData 要传递的post数据 */ private function curl_post($url, $xmldata) { //启动一个CURL会话 $ch = curl_init(); // 设置curl允许执行的最长秒数 curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeOut); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false); // 获取的信息以文件流的形式返回,而不是直接输出。 curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); //发送一个常规的POST请求。 curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_URL, $url); //要传送的所有数据 curl_setopt($ch, CURLOPT_POSTFIELDS, $xmldata); // 执行操作 $res = curl_exec($ch); $this->responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if ($res == NULL) { $this->error = "call http err :" . curl_errno($ch) . " - " . curl_error($ch) ; curl_close($ch); return null; } else if($this->responseCode != "200") { $this->error = "call http err httpcode=" . $this->responseCode ; curl_close($ch); return null; } curl_close($ch); return $res; } /** * 建立请求,以表单HTML形式构造(默认) * @param $url 请求地址 * @param $params 请求参数数组 * @return 提交表单HTML文本 */ protected function buildRequestForm($url,$params) { $sHtml = "正在跳转至支付页面...<form id='baofupaysubmit' name='baofupaysubmit' action='".$url."?charset=".$this->charset."' method='GET'>"; foreach($params as $key=>$val){ if (false === $this->checkEmpty($val)) { $val = str_replace("'","&apos;",$val); $sHtml.= "<input type='hidden' name='".$key."' value='".$val."'/>"; } } //submit按钮控件请不要含有name属性 $sHtml = $sHtml."<input type='submit' value='ok' style='display:none;''></form>"; $sHtml = $sHtml."<script>document.forms['baofupaysubmit'].submit();</script>"; return $sHtml; } } ?>` 在接口类或者接口实现类里调用,调用代码如下: `<?php //创建支付对象 $payobj= new \pay\Yuanbaopay($this->account['params']); //补充说明:$this->account['params']是传递过去的通道账户 ?>`