PHP实现微信支付(微信小程序版)
先解答上一篇博客中form_id的获取方法:
js代码
Page({
formSubmit: function (e) {
console.log("formid:"+e.detail.formId)
},
})
wxml代码
<form bindsubmit="formSubmit" report-submit="ture">
<button formType="submit">submit</button>
</form>
结果:
注意两点
- 表单wxml中需要加入,普通表单是不携带formId。
- formId只在真器中才能获取,开发者工具无法获取。
下面开始讲解PHP实现微信小程序支付
准备工作:
1.你的小程序做过微信认证并且开通微信支付(相信能学编程的都不是愚笨之人,跟着微信指引填写资料就行,剩下就等他客服打电话给你)
2.微信小程序appid
3.微信商户号mch_id(开户邮件里面有)
4.微信API**key(登录商户平台->账户中心->API安全自行设置)
开发步骤:
1.获取prepay_id
2.获取paySign
封装方法代码:
<?php
namespace App\libs\Pay;
use App\libs\HttpUtils\HttpUtils;
class WxPay
{
const url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
const key = "";
const appid = "";
const mch_id = "";
const total_fee = "";
const notify_url = "";
const body = "";
/**
* 微信统一下单 所有参数为必填项 数据传输格式为xml 提交方式POST 其他可选项参照微信官方文档
* @param url 统一下单地址
* @param key API** 微信支付平台自定义
* @param appid 小程序appid 微信公众平台获取
* @param mch_id 商户号 微信支付平台获取
* @param total_fee 金额 单位:分 作者写的东西金额是固定的,可作为参数传入函数
* @param notify_url 异步接收微信支付结果通知的回调地址
* @param nonce_str 随机字符串,长度要求在32位以内 下方已给出方法str_rand()
* @param sign 通过签名算法计算得出的签名值 签名算法getSignTemp():1.参数名ASCII码从小到大排序(字典序)2.如果参数的值为空不参与签名;3.参数名区分大小写 4.验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
* @param out_trade_no 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一
* @param openid 用户对应小程序的openid
* @param body 商品描述 如:腾讯充值中心-QQ会员充值
* @param trade_type 小程序取值如下:JSAPI 其他支付参照官方文档
*/
public static function PrePay($out_trade_no,$openid){
$nonce_str=self::str_rand(); //生成随机32位字符串
$SignTemp=self::getSignTemp($nonce_str,$openid,$out_trade_no); // 生成签名
$data=[
"appid"=>self::appid, //appid
"mch_id"=>self::mch_id, //商户号
"nonce_str"=>$nonce_str, //随机字符串
"sign"=>$SignTemp, //签名
"body"=>self::body, //商品描述
"out_trade_no"=>$out_trade_no, //订单号
"total_fee"=>self::total_fee, //金额
"trade_type"=>"JSAPI", //支付类型 小程序取值如下:JSAPI 其他支付参照官方文档
"notify_url"=>self::notify_url, //异步接收微信支付结果通知的回调地址
"openid"=>$openid //用户对应小程序的openid
];
$result=HttpUtils::curl(self::url,$params=self::XmlAndArray($data,"array"),$ispost=1,$https=1);//请求接口得到预支付信息,curl方法在前一篇文章已经贴出,参数必须为xml格式,方法已给出XmlAndArray()
return self::XmlAndArray($result,"xml"); //转化为数组方便取值,返回结果
}
public static function getPaySign($prepay_id,$nonce_str){
$timeStamp=time();//时间戳
$PaySign=md5("appId=".self::appid."&nonceStr=".$nonce_str."&package=prepay_id=".$prepay_id."&signType=MD5&timeStamp=".(string)$timeStamp."&key=".self::key);//生成支付签名,严格按照官方要求填写参数
return ["PaySign"=>$PaySign,"timeStamp"=>$timeStamp];//返回签名以及时间戳
}
private static function getSignTemp($nonce_str,$openid,$out_trade_no){
$SignTemp="appid=".self::appid."&body=".self::body."&mch_id=".self::mch_id."&nonce_str=".$nonce_str."¬ify_url=".self::notify_url."&openid=".$openid."&out_trade_no=".$out_trade_no."&total_fee=".self::total_fee."&trade_type=JSAPI";//生成下单签名,严格按照官方要求填写参数
$SignTemp=md5($SignTemp."&key=".self::key);
return $SignTemp; //返回签名,统一下单需要调用
}
/**
* 数组与XML互转 方便取数据
* @param $data
* @param $type 转换对象
* return 数组或XML
*/
private static function XmlAndArray($data,$type)
{
if(!$data){
return false;
}
else if($type=="xml"){
libxml_disable_entity_loader(true);
$values = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $values;
}
else if($type=="array"){
$xml="<xml>";
foreach ($data as $key=>$val){
$xml.="<".$key.">".$val."</".$key.">";
}
$xml.="</xml>";
return $xml;
}
}
/**
* 随机字符串生成
* @param $length 字符串长度
* @param $char 字符集
* return $string 随机字符串
*/
private static function str_rand($length = 32, $char = 'abcdefghijklmnopqrstuvwxyz0123456789') {
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str .= substr($char, mt_rand(0, strlen($char)-1), 1);
}
return $str;
}
}
调用代码,我用的laravel框架,可以自行重构
<?php
namespace App\Http\Controllers\Pay;
use App\Http\Controllers\Controller;
use App\libs\Pay\WxPay;
use Illuminate\Http\Request;
class PayController extends Controller
{
public function getPrePay(Request $request){
$openid=$request->get("openid"); //接收openid
$out_trade_no=""; //订单号
$PrePay=WxPay::PrePay($out_trade_no,$openid); //统一下单
if($PrePay["return_code"]=="FAIL"){
/*返回错误*/
}
else{
$PrePayId=$PrePay["prepay_id"]; // 获取预支付id
$PaySign=WxPay::getPaySign($PrePayId,$PrePay["nonce_str"]); 生成支付签名
echo json_encode(["prepay_id"=>$PrePayId,"paySign"=>$PaySign["PaySign"],"nonce_str"=>$PrePay["nonce_str"],"timeStamp"=>$PaySign["timeStamp"],"order_no"=>$out_trade_no]); //返回小程序中拉起支付所需要的信息
}
}
}
小程序调用代码:
wx.request({
url: "你的服务器端接口地址",
data: {
openid: "用户的openid",
},
header: {
'content-type': 'application/json'
},
success: function (res) {
let prepay_id = res.data.prepay_id //预支付id
let order_no = res.data.order_no //订单号
let timeStamp = String(res.data.timeStamp) //服务器端返回的时间戳,必须强转字符串,微信要求的
let nonceStr = res.data.nonce_str //服务器返回的随机字符串
let package = 'prepay_id=' + res.data.prepay_id //package需要这么填写,自己注意
let paySign = res.data.paySign //支付签名
if (res.data.code == "") {
//服务器端预支付处理失败提示
wx.showToast({
title: '下单失败',
icon: 'none',
duration: 1000
})
}
else {
// 拉起微信支付
wx.requestPayment({
'timeStamp': timeStamp,
'nonceStr': nonceStr,
'package': package,
'signType': 'MD5',
'paySign': paySign,
success: function (res) {
//接口调用成功的回调函数
},
fail:function(res){
//接口调用失败的回调函数
},
complete:function(res){
//接口调用结束的回调函数(调用成功、失败都会执行)
}
})
}
}
})
总结
- 最重要的就是生成签名一定要按照官方给出的文档,参数顺序不能搞错!!!
- 所有接口返回的都是xml,为了方便我都转化成数组,方法也在代码中给出。
- 随机字符串生成后,会在统一下单接口返回值中给出,生成PaySign签名时延用就行,第一次开发就卡在这,能拉起支付,然后闪退说PaySign错误
- 统一下单接口返回的prepay_id就是模板消息需要的参数,支付成功后就可以用此参数调用模板消息提示用户
- 作者自己写了个小程序BadBoy,有兴趣的可以搜索来玩玩,其中的代码可找作者索取
- 微信小程序交流群:895964328 php交流群:165728481