vue+thinkphp5實現微信掃碼支付(NATIVE支付)
前言
統一支付是JSAPI/NATIVE/APP各種支付場景下生成支付訂單,返回預支付訂單號的接口,目前微信支付所有場景均使用這一接口。下面介紹的是其中NATIVE的支付實現流程與PC端實現掃碼支付流程
流程實現(后端)(PHP)
- 創建Wechatpay.php文件,放到指定文件目錄下(我是放到了extend目錄)
<?php
class Wechatpay{
/**
* 模擬提交參數,支持https提交 可用于各類api請求
* @param string $url : 提交的地址
* @param array $data :POST數組
* @param string $method : POST/GET,默認GET方式
* @return mixed
*/
function curl_https($url, $xml='', $useCert=false){
$ch = curl_init();
//設置超時
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch,CURLOPT_URL, $url);
//設置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求結果為字符串且輸出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
if(stripos($url,"https://")!==FALSE){
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
}else{
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//嚴格校驗
}
if($useCert == true){
//設置證書
//使用證書:cert 與 key 分別屬于兩個.pem文件
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT,"");
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY,"");
}
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//運行curl
$data = curl_exec($ch);
//返回結果
if($data){
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
curl_close($ch);
//echo $error;
}
}
/**
*
* 拼接簽名字符串
* @param array $urlObj
*
* @return 返回已經拼接好的字符串
*/
function ToUrlParams($urlObj)
{
$buff = "";
foreach ($urlObj as $k => $v)
{
if($k != "sign"){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
//數組轉XML
function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key=>$val)
{ if (is_numeric($val)){
$xml.="<".$key.">".$val."</".$key.">";
}else{
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml.="</xml>";
return $xml;
}
//將XML轉為array
function xmlToArray($xml)
{
//禁止引用外部xml實體
libxml_disable_entity_loader(true);
$values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $values;
}
/**
* 獲取隨機字符串
* @return mixed
*/
function getRandString($len=12,$str='ABCDEFGHIJKLMNOPQRSTUVWYXZabcdefghijklmnopqrstuvwyxz1234567890'){
$strlen=strlen($str)-1;
$string='';
for ($i=0; $i < $len; $i++) {
$r=rand(1,$strlen);
$string=$string.$str[$r];
}
return $string;
}
}
2.定義公共變量
private $config = array(
'appid' => "wxa******", /*微信小程序的appid*/
'appid_app' => "wx******", /*微信開放平臺上的應用id*/
'mch_id' => "*******", /*微信申請成功之后郵件中的商戶id*/
'api_key' => "*************", /*在微信商戶平臺上自己設定的api密鑰 32位*/
'notify_url' => 'https://***', /*支付回調地址,確保可以訪問*/
);
3.支付接口
public function NativePay($id,$price,$body,$type,$attach){
Loader::import('wechatpay.Wechatpay', EXTEND_PATH,".php");
$wechatpay = new \Wechatpay();
$url='https://api.mch.weixin.qq.com/pay/unifiedorder';
$orderID='XXX'.time().rand(0,9).rand(0,9).rand(0,9);
$parameters=array(
'appid'=>$this->config["appid"],//appID
'mch_id'=>$this->config['mch_id'],//商戶號
'nonce_str'=>$wechatpay->getRandString(30),//隨機字符串
'body'=>$body,//商品描述
'out_trade_no'=>$orderID,//商戶訂單號
'total_fee'=>$price*100,//總金額 單位 分
'spbill_create_ip'=>$this->get_client_ip(),//終端IP
'notify_url'=>$this->config["notify_url"],//通知地址
'trade_type'=>'NATIVE',//交易類型
'attach'=>$attach
);
//參數名ASCII碼從小到大排序
ksort($parameters);
//統一下單簽名
$String = $wechatpay->ToUrlParams($parameters);
//簽名步驟二:在string后加入KEY
$String = $String."&key=".$this->config['api_key']; // key設置路徑:微信商戶平臺(pay.weixin.qq.com)-->賬戶設置-->API安全-->密鑰設置
//簽名步驟三:MD5加密
$parameters['sign']=strtoupper(md5($String));
$xmlData=$wechatpay->arrayToXml($parameters);
$return=$wechatpay->xmlToArray($wechatpay->curl_https($url,$xmlData));
if($return["return_code"]=="SUCCESS" && $return["result_code"]=="SUCCESS"){
//生成訂單或其他邏輯
//返回二維碼鏈接給前端
$returnData=[
'code_url'=>$return["code_url"],
'orderID'=>$orderID
];
// echo $this->creatqrcode($return["code_url"]);
$this->result($returnData,'1','支付二維碼生成成功!','json');
}else{
echo json_encode(array("status"=>false,"msg"=>$return));
}
}
/*
獲取當前服務器的IP
*/
public function get_client_ip(){
if ($_SERVER['REMOTE_ADDR']) {
$cip = $_SERVER['REMOTE_ADDR'];
} elseif (getenv("REMOTE_ADDR")) {
$cip = getenv("REMOTE_ADDR");
} elseif (getenv("HTTP_CLIENT_IP")) {
$cip = getenv("HTTP_CLIENT_IP");
} else {
$cip = "unknown";
}
return $cip;
}
- 支付回調
//微信支付回調接口
public function wxpaynotify(){
$xml = file_get_contents('php://input');
Loader::import('wechatpay.Wechatpay', EXTEND_PATH,".php");
$wechatpay = new \Wechatpay();
//將服務器返回的XML數據轉化為數組
$data = $wechatpay->xmlToArray($xml);
// 保存微信服務器返回的簽名sign
$data_sign = $data['sign'];
// sign不參與簽名算法
unset($data['sign']);
$sign = $wechatpay->ToUrlParams($data);
$payData=$sign;
$sign=strtoupper(md5($sign."&key=".$this->config["api_key"]));
// 判斷簽名是否正確 判斷支付狀態
if ( ($sign===$data_sign) && ($data['result_code']=='SUCCESS') ) {
// 更新消費訂單狀態等操作
$result = true;
}else{
file_put_contents('payResult.tex','驗簽失敗!');
$result = false;
}
// 返回狀態給微信服務器
if ($result) {
$str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
}else{
$str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[簽名失敗]]></return_msg></xml>';
}
echo $str;
}
流程實現(前端)(Vue)
<!-- HTML -->
<!-- 支付二維碼 (切記別在canvas使用v-if/v-show等做條件判斷,會導致渲染不出來的)-->
<canvas id="qrcode" ref="qrcode"></canvas>
<!-- JS -->
//導入二維碼組件QRCode
import QRCode from 'qrcode';
let qrcodeUrl = res.data.code_url
const canvas = this.$refs.qrcode
QRCode.toCanvas(canvas, qrcodeUrl, function (error) {
if (error) console.error(error)
console.log('QR code generated successfully.')
})

浙公網安備 33010602011771號