php對接微信小程序支付
前言:這裏我就假裝你已經註冊了微信小程序,並且基本的配置都已經好了。註: 個人註冊小程序不支持微信支付,所以我還是假裝你是企業或者個體工商戶的微信小程序,其他的商戶號註冊,二者綁定,授權,支付開通,就閱讀文檔吧,這裏我先負責實戰。
微信小程序支付開發文檔:
https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1
基本流程:
1. 申請商戶平臺賬號 https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2F
2. 微信小程序綁定已有商戶號並開通微信支付 http://kf.qq.com/faq/140225MveaUz161230yqiIby.html
3. 登錄商戶平臺對小程序授權,下載支付證書,記錄商戶號,支付密鑰。
4. 閱讀微信支付官方文檔,完成接口的對接編碼。
開發支付流程:
1. 微信小程序的基本配置。(app_id[小程序唯一id],mch_id[商戶號],md5_key[支付密鑰],notify_url[異步回調通知] )。
2. 按微信要求的順序將參數組成鍵值對數組,並對其進行簽名(先將參數進行字段排序,參數可以處理中文字符,在請求參數字符串後拼上支付密鑰,最後md5,簽名完成)
3. 所有請求參數和簽名一起組成新數組,再轉為XML。
4. 以XML格式參數,POST請求方式對https://api.mch.weixin.qq.com/pay/unifiedorder發起統一下單請求。
5. 微信服務器接收下單請求,返回預支付ID(prepay_id)到自己服務端。
6. 自己服務端聯合預支付ID,小程序APPID,32位隨機串,時間戳,簽名方式一並返回到小程序。
7. 小程序根據微信提供的函數和返回的參數集調起微信支付。
8. 支付完成,微信通過異步通知到自己服務指定的控制器。
9. 接受微信返回的通知,將XML轉為數組,需要先判斷通知過來的是不是同一個訂單(根據訂單號),因為有時微信異步通知,自己服務器未接收處理,他會過一段時間重復發起通知。
10. 根據通知狀態,更新自己業務的數據表,最後返回一個成功標識的XML給微信服務器。
一、支付配置
‘wxxcx‘ =>[
‘app_id‘ => ‘wx4c0e*******664b4‘, // 微信小程序appid
‘mch_id‘ => ‘149***3342‘, // 微信商戶id
‘md5_key‘ => ‘3FN8WHO**********iPnNoJ576AxMmwQ‘, // 微信支付密鑰
‘app_cert_pem‘ => APP_PATH.‘v1/wechat_cert/apiclient_cert.pem‘, // 支付證書,統一下單不需,退款等其他接口需要
‘app_key_pem‘ => APP_PATH.‘v1/wechat_cert/apiclient_key.pem‘,
‘sign_type‘ => ‘MD5‘,// MD5 HMAC-SHA256
‘limit_pay‘ => [
],
‘fee_type‘ => ‘CNY‘,// 貨幣類型 當前僅支持該字段
‘notify_url‘ => ‘https://***********.com/v1/Pay/notifyUrlApi‘, // 異步通知地址
‘redirect_url‘ => ‘‘,
‘return_raw‘ => false,
]
二、前端傳來的參數或服務端生成
$this->openid = $openid; // 前端也可不傳
$this->out_trade_no = $out_trade_no; // 服務端生成
$this->body = $body;
$this->total_fee = $total_fee; // 最好服務端數據庫抓取,避免前端傳
$this->spbill_create_ip = $spbill_create_ip; // 請求的ip地址
三、封裝統一下單類
<?php
/**
* @author: fuchao
* @createTime: 2018-04-30 18:02
* @description: 小程序微信支付
* 公眾號:ZEROFC_DEV
*/
namespace app\v1\extend;
class WeixinPay {
protected $appid;
protected $mch_id;
protected $key;
protected $openid;
protected $out_trade_no;
protected $body;
protected $total_fee;
protected $notify_url;
protected $spbill_create_ip;
function __construct($appid, $openid, $mch_id, $key,$out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip) {
$this->appid = $appid;
$this->openid = $openid;
$this->mch_id = $mch_id;
$this->key = $key;
$this->out_trade_no = $out_trade_no;
$this->body = $body;
$this->total_fee = $total_fee;
$this->notify_url = $notify_url;
$this->spbill_create_ip = $spbill_create_ip;
}
/************測試方法可刪除*****************/
public function test() {
$ha = "hello world";
return $this->appid;
}
/************可刪除*****************/
public function pay() {
// var_dump($this->notify_url);
// die;
//統一下單接口
$return = $this->weixinapp();
return $return;
}
//統一下單接口
private function unifiedorder() {
$url = ‘https://api.mch.weixin.qq.com/pay/unifiedorder‘;
// 這裏的參數順序一定要按下面的,不然可能就一直報商戶號此功能未授權等錯誤
$parameters = array(
‘appid‘ => $this->appid, // 小程序ID
//‘body‘ => ‘test‘, // 商品描述
‘body‘ => $this->body,
‘mch_id‘ => $this->mch_id, // 商戶號
‘nonce_str‘ => $this->createNoncestr(), // 隨機字符串
‘notify_url‘ => $this->notify_url, //‘https://shop.gdpress.cn/syw_jingzhun/index.php/Api/xiaochengxu/notify_url_api‘, // 通知地址 確保外網能正常訪問
‘openid‘ => $this->openid, // 用戶id
// ‘out_trade_no‘ => ‘2015450806125348‘, // 商戶訂單號
‘out_trade_no‘=> $this->out_trade_no,
//‘spbill_create_ip‘ => $_SERVER[‘REMOTE_ADDR‘], // 終端IP
‘spbill_create_ip‘ => $this->spbill_create_ip, // 終端IP
‘total_fee‘ => floatval(($this->total_fee) * 100), // 單位 分
//‘total_fee‘ => $this->total_fee, // 單位 分
‘trade_type‘ => ‘JSAPI‘ // 交易類型
);
//統一下單簽名
$parameters[‘sign‘] = $this->getSign($parameters);
$xmlData = $this->arrayToXml($parameters);
$return = $this->xmlToArray($this->postXmlCurl($xmlData, $url, 60));
//$return = $this->postXmlCurl($xmlData, $url, 60);
// print_r($return);
// die;
return $return;
}
// curl請求方法封裝
private static function postXmlCurl($xml, $url, $second = 30)
{
$ch = curl_init();
//設置超時
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); //嚴格校驗
//設置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求結果為字符串且輸出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
curl_setopt($ch, CURLOPT_TIMEOUT, 40);
set_time_limit(0);
//運行curl
$data = curl_exec($ch);
//返回結果
if ($data) {
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
curl_close($ch);
throw new WxPayException("curl出錯,錯誤碼:$error");
}
}
//數組轉換成xml
private function arrayToXml($arr) {
$xml = "<xml>";
foreach ($arr as $key => $val) {
if (is_array($val)) {
$xml .= "<" . $key . ">" . arrayToXml($val) . "</" . $key . ">";
} else {
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
}
}
$xml .= "</xml>";
return $xml;
}
//xml轉換成數組
private function xmlToArray($xml) {
//禁止引用外部xml實體
libxml_disable_entity_loader(true);
$xmlstring = simplexml_load_string($xml, ‘SimpleXMLElement‘, LIBXML_NOCDATA);
$val = json_decode(json_encode($xmlstring), true);
return $val;
}