这个是我,2021年写的代码了,最近参加比赛,涉及到支付,于是又拿来用,幸好代码健全,但是去微信平台看,已经出v3支付了,再次,这个教程以及代码部分,仅仅用于V2版本,请勿踩空!
前言
文章用于记录我的开发经历,以及记录代码,亲测可用,时间:2022/03/07 原生PHP开发,简单的说一下微信的支付流程,以及回调方法,大家可以先看完正文,再找代码
提示:以下是本篇文章正文内容,下面案例可供参考
一、开始
这里对微信支付每个方法做一下说明,
wx.requestPluginPayment
基础库 2.22.1 开始支持,低版本需做兼容处理。 以 Promise 风格 调用:不支持 小程序插件:支持,需要小程序基础库版本不低于 2.22.1 插件中发起支付。
wx.requestPayment
以 Promise 风格 调用:支持 小程序插件:不支持 微信 Windows 版:支持 微信 Mac 版:支持 发起微信支付。调用前需在小程序微信公众平台 -功能-微信支付入口申请接入微信支付。了解更多信息,可以参考 微信支付开发文档:
开发指引 下单接口 支付接口 旧版本 (v2) 开发指引 支付接口
wx.requestOrderPayment
基础库 2.16.0 开始支持,低版本需做兼容处理。 以 Promise 风格 调用:支持 小程序插件:不支持 创建自定义版交易组件订单,并发起支付。 仅接入了自定义版交易组件的小程序需要使用,普通小程序可直接使用 wx.requestPayment。
这里我们使用第二个,wx.requestPayment方法,看一下该方法具体使用需要些什么参数:
wx.requestPayment({
nonceStr: 'nonceStr',
package: 'package',
paySign: 'paySign',
signType: '',
timeStamp: 'timeStamp',
})
完整拉起小程序支付需要5个参数,看一下官方给的说明:
signType中官方给出了:
这里本教程用的是MD5
wx.requestPayment({
timeStamp: '',
nonceStr: '',
package: '',
signType: 'MD5',
paySign: '',
success (res) { },
fail (res) { }
})
接下来我们就要完成统一下单操作,小程序绑定微信商户,拿到商户号,在微信支付网页找到:
我的账户->API审核->V2密钥->生成随机32位密钥输入即可(自己做个备份,不需要用什么工具,自己打随意乱输入包含大写即可!)
接下来拿到商户号(mch_id)、V2密钥(key)
那!!wx.requestPayment中的各个参数怎么获得?
别急,我已经为各位踩坑了,一定讲解清楚!
首先把整个流程说明白,我们把获得的商户号、v2密钥、小程序appid及其他参数 通过统一下单 获得prepay_id 详细可以看下统一下单文档,已经写明了:
会返回 预支付交易会话标识,那这个是干嘛的呢?
它是用于获得签名paySign的
到此我们已经获取到了,商户号、密钥、签名、package接下来开始本篇的详细教程!
二、详细教程
1.后端Payfee.php代码
<?php include 'WeixinPay.php'; $appid='appid'; $openid= $_GET['openid']; $mch_id='商户号'; $key='商户密钥32位'; // $out_trade_no = $_POST['out_trade_no']; $out_trade_no = $mch_id. time();//商户号 //业务构造 由前端提交 这里注释 $total_fee = $_GET['money']; //money不用多说了吧 $attach = $_GET['attach']; //自定义参数 用于回调
//处理金额
if(empty($total_fee)) //押金
{
$body = "支付";
$total_fee = floatval(99100);
}
else {
$body = "支付";
total_fee = floatval(total_fee100);
}
weixinpay = new WeixinPay(appid,openid,mch_id,key,out_trade_no,body,attach,$total_fee);
return=weixinpay->pay();
echo json_encode($return);
2.WeixinPay.php封装代码
记得改一下里面的回调通知
<?php
/*
- 小程序微信支付
*/class WeixinPay {
protected $appid; protected $mch_id; protected $key; protected $openid; protected $out_trade_no; protected $body; protected $attach; protected $total_fee; function __construct($appid, $openid, $mch_id, $key,$out_trade_no,$body,$attach,$total_fee) { $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->attach = $attach; $this->total_fee = $total_fee; } public function pay() { //统一下单接口 $return = $this->weixinapp(); return $return; } //统一下单接口 private function unifiedorder() { $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; $parameters = array( 'appid' => $this->appid, //小程序ID 'mch_id' => $this->mch_id, //商户号 'nonce_str' => $this->createNoncestr(), //随机字符串
// 'body' => 'test', //商品描述
'body' => $this->body,
'attach' =>$this->attach,
// 'out_trade_no' => '2015450806125348', //商户订单号
'out_trade_no'=> $this->out_trade_no,
// 'total_fee' => floatval(0.01 * 100), //总金额 单位 分
'total_fee' => $this->total_fee,
// 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'], //终端IP
'spbill_create_ip' => '192.168.0.161', //终端IP
'notify_url' => '回调地址', //通知地址 确保外网能正常访问
'openid' => $this->openid, //用户id
'trade_type' => 'JSAPI'//交易类型
);
//统一下单签名
parameters['sign'] = this->getSign($parameters);
xmlData = this->arrayToXml($parameters);
return = this->xmlToArray(this->postXmlCurl(xmlData, $url, 60));
return $return;
}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 = "<root>"; foreach ($arr as $key => $val) { if (is_array($val)) { $xml .= "<" . $key . ">" . arrayToXml($val) . "</" . $key . ">"; } else { $xml .= "<" . $key . ">" . $val . "</" . $key . ">"; } } $xml .= "</root>"; 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; } //微信小程序接口 private function weixinapp() { //统一下单接口 $unifiedorder = $this->unifiedorder();
// print_r($unifiedorder);
$parameters = array(
'appId' => $this->appid, //小程序ID
'timeStamp' => '' . time() . '', //时间戳
'nonceStr' => $this->createNoncestr(), //随机串
'package' => 'prepay_id=' . $unifiedorder['prepay_id'], //数据包
'signType' => 'MD5'//签名方式
);
//签名
parameters['paySign'] = this->getSign($parameters);
return $parameters;
}//作用:产生随机字符串,不长于32位 private function createNoncestr($length = 32) { $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; $str = ""; for ($i = 0; $i < $length; $i++) { $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); } return $str; } //作用:生成签名 private function getSign($Obj) { foreach ($Obj as $k => $v) { $Parameters[$k] = $v; } //签名步骤一:按字典序排序参数 ksort($Parameters); $String = $this->formatBizQueryParaMap($Parameters, false); //签名步骤二:在string后加入KEY $String = $String . "&key=" . $this->key; //签名步骤三:MD5加密 $String = md5($String); //签名步骤四:所有字符转为大写 $result_ = strtoupper($String); return $result_; } ///作用:格式化参数,签名过程需要使用 private function formatBizQueryParaMap($paraMap, $urlencode) { $buff = ""; ksort($paraMap); foreach ($paraMap as $k => $v) { if ($urlencode) { $v = urlencode($v); } $buff .= $k . "=" . $v . "&"; } $reqPar; if (strlen($buff) > 0) { $reqPar = substr($buff, 0, strlen($buff) - 1); } return $reqPar; }
}
3.后端支付成功后微信发送的通知接收
自己改一下,对接自己的业务即可,至于最底部的
echo ‘<return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg>’;
上面是处理通知的,简单的来说,就是微信告诉你,对方支付结果,你要告诉对方,我已经正确的处理了这件事情,否则微信将会按一定的周期通知你,接收,随着时间的流逝,通知的频率也会越来越低,直到不通知为止,我建议做一下处理,避免业务多重写,对于微信返回的参数我们做业务处理即可,这里不必多说,自己看代码,不懂就评论问我
<?php $postXml = file_get_contents("php://input"); //接收微信参数 if (empty($postXml)) { return false; }
//将xml格式转换成数组
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;
}
attr = xmlToArray(postXml);
total_fee = attr['total_fee'];//金额
open_id = attr['openid'];//用户openid
out_trade_no = attr['out_trade_no'];//商户单号
time = attr['time_end'];//支付时间
attach= attr['attach'];//自定义参数//下单成功通知商家和骑手用户
include '../conn.php';
include 'access_token.php';
touser=open_id;//需要接收的用户
ACCESS_TOKEN=access_token;
sq="UPDATE `order_pay` SET `is_success` = '1',`order_number` = 'out_trade_no',pay_time
= 'time' WHERE openid='open_id' and md_order='$attach'";
r=conn->query($sq);
mysqli_free_result($r);//释放
//查询订单数据进行推送
cx_tuis_data="select * from order_pay WHERE openid='open_id' and md_order='$attach'";
jieguo_data=conn->query($cx_tuis_data);
new_data=jieguo_data->fetch_assoc();
nnew_shopname=new_data['shop_name'];
nnew_shopmoney=new_data['money'];
nnew_shoptime=new_data['time'];
mysqli_free_result($jieguo_data);//释放
//消息推送
include 'payment_success_notify.php';
// $page="pages/myqj/myqj";//点击小程序订阅消息跳转的页
moban(touser,ACCESS_TOKEN,"7IG9zV_nPkwrJTiTDI7fUkVOSwgl8h60axbdXoE6h0Q",out_trade_no,nnew_shopname,nnew_shopmoney,nnew_shoptime);
//调用方法 格式(openid,ACCESS_TOKEN,模板id,数据1,数据2,数据3,数据4,数据5);
echo '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
4.小程序前端wxml
<button bindtap="pay" type="primary">支付</button>
5.小程序主要js
pay:function(){
wx.request({
url: '你的域名/payfee.php', //仅为示例,并非真实的接口地址
data: {
money: '12.00',//模拟的支付金额
openid: 'o7J2i5YNkHn0nELz87HFS6zKL9oQ',//发起人openid
attach:'12'//自定义参数
},
header: {
'content-type': 'application/json' // 默认值
},
success (res) {
console.log(res.data)
wx.requestPayment({'timeStamp': res.data.timeStamp, 'nonceStr': res.data.nonceStr, 'package': res.data.package, 'signType': 'MD5', 'paySign': res.data.paySign, 'success':function(res){
//支付成功
},'fail':function(res){
//支付失败
}})
}
})
},
6.小程序支付拉起截图
总结
提示:本文简单的讲了,关于小程序如何拉起支付踩坑经历,给大家附上了健全的代码,关于如何处理支付上述已经讲解完毕了,有问题或者能指教一二的,欢迎留言讨论