微信小程序支付封装-复制即用
主要三个页面,一个微信小程序页面,一个请求接口文件,一个回调页面
过程:小程序请求后端接口文件,付款成功后触发回调页面
微信小程序页面
zhifu() {
this.pay();
},
pay(total_fee) {
var total_fee = total_fee;
wx.login({
success: res => {
//code 用于获取openID的条件之一
var code = res.code;
wx.request({
url: 后台接口文件,
method: "POST",
data: {
total_fee: total_fee,
code: code,
},
header: {
'content-type': 'application/x-www-form-urlencoded' // 默认值
},
success: function (res) { //后端返回的数据
var data = res.data;
console.log(data);
console.log(data["timeStamp"]);
wx.requestPayment({
timeStamp: data['timeStamp'],
nonceStr: data['nonceStr'],
package: data['package'],
signType: data['signType'],
paySign: data['paySign'],
success: function (res) {
wx.showModal({
title: '支付成功',
content: '',
})
},
fail: function (res) {
console.log(res);
}
})
}
});
}
})
},
后端 接口文件
<?php
$aaa = new PayController;
$aaa->pay($_POST['code']);
class PayController
{
public function __construct()
{
// 支付
// $this->appid = 'w 8c3a'; //微信支付申请对应的公众号的APPID
// $this->appKey = '5895b ef6e327489f'; //微信支付申请对应的公众号的APP Key,绑定公众号做的小程序,所以虽然商户号这些是属于公众号的,但是这里必须使用小程序加公众号的信息
// 操作方法 添加特约商户号关联起来,在商户号后台关联小程序即可,调用时候使用小程序的appid,即 需要使用的 小程序appid+小程序AppSecret + 公众号mchid + 公众号apiKey
$this->mchid = '148 022'; //https://pay.weixin.qq.com 产品中心-开发配置-商户号
$this->apiKey = 'lnh rvjw'; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
$this->xiaoAppid = "wxa4 e31"; /* 小程序appid */
$this->xiaoAppSecret = "d5 55c";/* 小程序AppSecret */
// //回调的url【自己填写】
$this->notify_url = '回调/notify.php';
}
//微信支付
public function pay($code)
{
//获取openid
if ($code) { //用code获取openid
$code = $code;
$WX_APPID = $this->xiaoAppid; //appid
$WX_SECRET = $this->xiaoAppSecret; //AppSecret
$url = "https://api.weixin.qq.com/sns/jscode2session?appid=" . $WX_APPID . "&secret=" . $WX_SECRET . "&js_code=" . $code . "&grant_type=authorization_code";
$infos = json_decode(file_get_contents($url));
$openid = $infos->openid;
}
$fee = 0.01; //举例支付0.01
$appid = $this->xiaoAppid; //这里填写公众号appiid的话会显示 appid和openid不匹配 ,解决方法 添加特约商户号关联起来,在商户号后台关联小程序即可,调用时候使用小程序的appid
$body = '标题';
$mch_id = $this->mchid; //商户号
$nonce_str = $this->nonce_str(); //随机字符串
$notify_url = $this->notify_url;
$openid = $openid;
$out_trade_no = $this->order_number($openid); //商户订单号
$spbill_create_ip = '127.0.0.1'; //服务器的ip【自己填写】;
$total_fee = $fee * 100; // 微信支付单位是分,所以这里需要*100
$trade_type = 'JSAPI'; //交易类型 默认
//这里是按照顺序的 因为下面的签名是按照顺序 排序错误 肯定出错
$post['appid'] = $appid;
$post['body'] = $body;
$post['mch_id'] = $mch_id;
$post['nonce_str'] = $nonce_str; //随机字符串
$post['notify_url'] = $notify_url;
$post['openid'] = $openid;
$post['out_trade_no'] = $out_trade_no;
$post['spbill_create_ip'] = $spbill_create_ip; //终端的ip
$post['total_fee'] = $total_fee; //总金额
$post['trade_type'] = $trade_type;
// $sign1 = $this->sign($post); //签名
$sign = self::getSign($post, $this->apiKey);
$post_xml = '<xml>
<appid>' . $appid . '</appid>
<body>' . $body . '</body>
<mch_id>' . $mch_id . '</mch_id>
<nonce_str>' . $nonce_str . '</nonce_str>
<notify_url>' . $notify_url . '</notify_url>
<openid>' . $openid . '</openid>
<out_trade_no>' . $out_trade_no . '</out_trade_no>
<spbill_create_ip>' . $spbill_create_ip . '</spbill_create_ip>
<total_fee>' . $total_fee . '</total_fee>
<trade_type>' . $trade_type . '</trade_type>
<sign>' . $sign . '</sign>
</xml> ';
//统一接口prepay_id
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$xml = $this->http_request($url, $post_xml); //发送请求
$array = $this->xml($xml); //全要大写
// print_r($array); // print_r($xml); die();
/* */
if ($array['RETURN_CODE'] == 'SUCCESS' && $array['RESULT_CODE'] == 'SUCCESS') {
$time = time();
$tmp = []; //临时数组用于签名
$tmp['appId'] = $appid;
$tmp['nonceStr'] = $nonce_str;
$tmp['package'] = 'prepay_id=' . $array['PREPAY_ID'];
$tmp['signType'] = 'MD5';
$tmp['timeStamp'] = "$time";
$data['state'] = 200;
$data['timeStamp'] = "$time"; //时间戳
$data['nonceStr'] = $nonce_str; //随机字符串
$data['signType'] = 'MD5'; //签名算法,暂支持 MD5
$data['package'] = 'prepay_id=' . $array['PREPAY_ID']; //统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=*
$data['paySign'] = self::getSign($tmp, $this->apiKey); //签名,具体签名方案参见微信公众号支付帮助文档;
$data['out_trade_no'] = $out_trade_no;
} else {
$data['state'] = 0;
$data['text'] = "错误";
$data['RETURN_CODE'] = $array['RETURN_CODE'];
$data['RETURN_MSG'] = $array['RETURN_MSG'];
}
echo json_encode($data);
}
//随机32位字符串
private function nonce_str()
{
$result = '';
$str = 'QWERTYUIOPASDFGHJKLZXVBNMqwertyuioplkjhgfdsamnbvcxz';
for ($i = 0; $i < 32; $i++) {
$result .= $str[rand(0, 48)];
}
return $result;
}
//生成订单号
private function order_number($openid)
{
//date('Ymd',time()).time().rand(10,99);//18位
return md5($openid . time() . rand(10, 99)); //32位
}
//签名 $data要先排好顺序
private function sign($data)
{
$stringA = '';
foreach ($data as $key => $value) {
if (!$value) continue;
if ($stringA) $stringA .= '&' . $key . "=" . $value;
else $stringA = $key . "=" . $value;
}
$wx_key = ''; //申请支付后有给予一个商户账号和密码,登陆后自己设置的key
$stringSignTemp = $stringA . '&key=' . $wx_key;
return strtoupper(md5($stringSignTemp));
}
// 获取签名
public static function getSign($params, $key)
{
ksort($params, SORT_STRING);
$unSignParaString = self::formatQueryParaMap($params, false);
$signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
return $signStr;
}
protected static function formatQueryParaMap($paraMap, $urlEncode = false)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v) {
if (null != $v && "null" != $v) {
if ($urlEncode) {
$v = urlencode($v);
}
$buff .= $k . "=" . $v . "&";
}
}
$reqPar = '';
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff) - 1);
}
return $reqPar;
}
//curl请求
public function http_request($url, $data = null, $headers = array())
{
$curl = curl_init();
if (count($headers) >= 1) {
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
}
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
if (!empty($data)) {
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($curl);
curl_close($curl);
return $output;
}
//获取xml
private function xml($xml)
{
$p = xml_parser_create();
xml_parse_into_struct($p, $xml, $vals, $index);
xml_parser_free($p);
$data = [];
foreach ($index as $key => $value) {
if ($key == 'xml' || $key == 'XML') continue;
$tag = $vals[$value[0]]['tag'];
$value = $vals[$value[0]]['value'];
$data[$tag] = $value;
}
return $data;
}
}
后台回调页面,在付款成功后就会触发这个页面,我在里面写了一个函数,付款成功后就会把付款信息生成日志文件
<?php
$info = file_get_contents("php://input");
// $order = xmlToArray($info);
// $trade['order'] = $order['out_trade_no']; /* 微信分配的小程序ID,即订单号 */
// $trade["total_fee"] = $order["total_fee"]; /* 金额 */
// $trade["sign"] = $order["sign"]; /* 签名 */
// https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8
// // 3,针对信息做处理,
// // 3.1根据返回的信息在生成签名防止数据泄漏导致出现“假通知”,造成资金损失。
// $newSign = verifySign($order);
// // // 3.2根据订单id去数据库或者换成查找订单消息
// $data = $pdo->query("SELECT payAmount,status from a_buy WHERE outTradeNo='{$trade['order']}'")->fetch(PDO::FETCH_ASSOC); //需要注意单位为分
// if (($data['payAmount'] * 100) == $trade['total_fee'] && $newSign == $trade["sign"]) {
// if ($data['status'] == '待付款') {
// // 更改订单状态
// $pdo->exec("UPDATE a_buy set status='待发货' WHERE outTradeNo='{$trade['order']}'");
var_dump($info);
logInfo($info); //写入日志
// }
// return
// '<xml>
// <return_code><![CDATA[SUCCESS]]></return_code>
// <return_msg><![CDATA[OK]]></return_msg>
// </xml>';
// var_dump($data);
// } else {
// // 失败的请求
// }
// 将xml装换为数组
function xmlToArray($data)
{
return (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
}
// 将付款成功后的数据写入log 日志文件
function logInfo($info, $fileName = 'pay')
{
$debugInfo = debug_backtrace();
$message = date("Y-m-d H-i-s") . PHP_EOL . $info . PHP_EOL;;
$message .= '[' . $debugInfo[0]['file'] . ']' . 'line' . $debugInfo[0]["line"] . PHP_EOL;
file_put_contents($fileName . '-' . date("Y-m-d") . '.log', $message, FILE_APPEND);
}
// 判断返回的签名和根据数据生成的数据判断是否相同,防止数据泄漏导致出现“假通知”,造成资金损失。
function verifySign($params, $apikey = "c17FZND q71Tt9")
{
ksort($params);
$string = "";
foreach ($params as $k => $v) {
if ($k != "sign" && $v != "" && !is_array($v)) {
$string .= $k . "=" . $v . "&";
}
}
$string = $string . "key=" . $apikey;
$string = md5($string);
$result = strtoupper($string);
return $result;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)