### 一、后臺添加接口
1、登錄管理員后臺 → 接口通道 → 添加接口

2、接口英文別名就是接口實現類的文件名(必須是唯一的,不要和其他接口重名)

### 二、編寫接口代碼
1、pay/application/common/目錄里創建接口類,extends繼承自Pay.php,例如元寶支付Yuanbaopay.php,參考代碼如下:

`<?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目錄下,新建接口實現類(類名和后臺添加的接口通道名稱一樣),如下:

`<?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),參考代碼如下:

`<?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, '', '&');
$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("'","'",$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']是傳遞過去的通道賬戶
?>`