本文解说如何开发微信小程序领取,蕴含小程序发动领取,后端对立下单,查问订单,订单回调,订单入库等操作。
流程
微信小程序获取订单参数->向后端发动批准下单申请->获取订单参数->小程序调用Api进行发动领取->领取实现->发送回调->领取后果入库->查问订单领取状态。
后端代码
getOpenid.php
须要配置的参数有 $appid、$secret、orderPrice
,其中 $appid、$secret、orderPrice
是你小程序的两个参数,orderPrice
是订单金额,以元为单位。
<?php// 页面编码header("content-type:application/json");// 取得小程序传过来的CODE$code = trim($_GET['code']);// 小程序appid$appid = "这里填写你的";// 小程序appscret$secret = "这里填写你的";// 受权登录api接口$api = "https://api.weixin.qq.com/sns/jscode2session?appid=$appid&secret=$secret&js_code=$code&grant_type=authorization_code";// 发动申请$result = file_get_contents($api);// 获取openid$arr_result = json_decode($result, true);$openid = $arr_result["openid"];// 返回信息$result = array( 'code' => 200, 'msg' => '获取openid胜利', 'openid' => $openid, 'orderPrice' => 0.01 // 单位:元);// 输入JSONecho json_encode($result,JSON_UNESCAPED_UNICODE);?>
creatOrder.php
须要配置的参数都在代码中有阐明。$appid、$mchid、$xlid、$data['notify_url']、$set_body、$price
<?php// 页面编码header('Content-type:text/html; Charset=utf-8');ini_set('date.timezone','Asia/Shanghai');// 对立下单function wechartAddOrder($name,$ordernumber,$money,$openid,$timeStamp,$noncestr){ $url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; $urlarr = parse_url($url); // 须要配置以下参数 // 须要配置以下参数 // 须要配置以下参数 // 须要配置以下参数 // 须要配置以下参数 $appid = '填写你的小程序appid'; // appID $mchid = '填写你的微信领取商户号'; // 商户ID $xlid = '填写你的秘钥序列号'; // 秘钥序列号 可在这个网址中查问 https://myssl.com/cert_decode.html $data = array(); $time = $timeStamp; $data['appid'] = $appid; $data['mchid'] = $mchid; $data['description'] = $name; // 商品形容 $data['out_trade_no'] = $ordernumber; // 订单编号 // 异步订单回调线上地址 // 就是notify.php这个文件的线上URL // 例如你的notify.php所在服务器的地位是wwwroot/xcxpay/notify.php // 你的域名是https://www.qq.com // 那么应该依照这个格局填写URL:https://www.qq.com/xcxpay/notify.php $data['notify_url'] = "填写notify.php这个文件的线上URL"; $data['amount']['total'] = intval($money * 1); // 金额(单位:分) $data['payer']['openid'] = $openid; // 用户openID $data = json_encode($data); $key = getSign($data,$urlarr['path'],$noncestr,$time); // 签名 $token = sprintf('mchid="%s",serial_no="%s",nonce_str="%s",timestamp="%d",signature="%s"',$mchid,$xlid,$noncestr,$time,$key); // 头部信息 $header = array( 'Content-Type:'.'application/json; charset=UTF-8', 'Accept:application/json', 'User-Agent:*/*', 'Authorization: WECHATPAY2-SHA256-RSA2048 '.$token ); $ret = curl_post_https($url,$data,$header); $ret = ltrim($ret,'{"prepay_id":"'); $ret = rtrim($ret,'}"'); // 微信领取(小程序)签名 $str = getWechartSign($appid,$timeStamp,$noncestr,'prepay_id='.$ret); // 需返回的一些预领取参数 $arr = array( 'appid' => $appid, 'timestamp' => $timeStamp, 'package' => 'prepay_id='.$ret, 'paySign' => $str, 'noncestr' => $noncestr, 'orderNum' => $ordernumber, 'orderPrice' => intval($money * 1) // 可用number_format转换金额 ); exit(json_encode($arr));}// 微信领取签名function getSign($data=array(),$url,$randstr,$time){ $str = "POST"."\n".$url."\n".$time."\n".$randstr."\n".$data."\n"; $key = file_get_contents('apiclient_key.pem');// 在商户平台下载的秘钥 $str = getSha256WithRSA($str,$key); return $str;} // 调起领取的签名function getWechartSign($appid,$timeStamp,$noncestr,$prepay_id){ $str = $appid."\n".$timeStamp."\n".$noncestr."\n".$prepay_id."\n"; $key = file_get_contents('apiclient_key.pem'); $str = getSha256WithRSA($str,$key); return $str;} function getSha256WithRSA($content, $privateKey){ $binary_signature = ""; $algo = "SHA256"; openssl_sign($content, $binary_signature, $privateKey, $algo); $sign = base64_encode($binary_signature); return $sign;} /* PHP CURL HTTPS POST */function curl_post_https($url,$data,$header){ // 模仿提交数据函数 $curl = curl_init(); // 启动一个CURL会话 curl_setopt($curl, CURLOPT_URL, $url); // 要拜访的地址 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书起源的查看 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); // 从证书中查看SSL加密算法是否存在 curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模仿用户应用的浏览器 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 应用主动跳转 curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 主动设置Referer curl_setopt($curl, CURLOPT_POST, 1); // 发送一个惯例的Post申请 curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包 curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限度避免死循环 curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的模式返回 curl_setopt($curl, CURLOPT_HTTPHEADER, $header); $tmpInfo = curl_exec($curl); // 执行操作 if (curl_errno($curl)) { echo 'Errno'.curl_error($curl);//捕抓异样 } curl_close($curl); // 敞开CURL会话 return $tmpInfo; // 返回数据,json格局}// 生成noncestrfunction creatnoncestr($length){ $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'; $randStr = str_shuffle($str); $rands= substr($randStr,0,$length); return $rands;}// 商品名称$set_body = '小程序领取';// 领取金额(单位:分)// 还须要去getOpenid.php外面批改这个价格才会在小程序显示精确的价格$price = 1;// 商户自定义订单号$out_trade_no = date('Ymd').time();// 工夫戳$timeStamp = time();// 领取用户$openid = trim($_GET['openid']);// 随机字符串$noncestr = creatnoncestr(8); // 创立订单wechartAddOrder($set_body,$out_trade_no,$price,$openid,$timeStamp,$noncestr);?>
notify.php
这个文件是领取回调。
<?php// 调用解密类require_once('./v3OrderCallBack.php');// 数据库配置require_once('./Db.php');// 接管领取后果回调告诉$getCallBackData = file_get_contents('php://input');$getData = new AesUtil;$getReturnData = $getCallBackData;// 将变量由json类型数据转换为数组$disposeReturnData = json_decode($getReturnData, true);// 获取associated_data数据,附加数据$associatedData = $disposeReturnData['resource']['associated_data'];// 获取nonce数据,加密应用的随机串$nonceStr = $disposeReturnData['resource']['nonce'];// 获取ciphertext数据,base64编码后的数据密文$ciphertext = $disposeReturnData['resource']['ciphertext'];// 调用微信官网给出的办法将解密后的数据赋值给变量$result = $getData -> decryptToString($associatedData,$nonceStr,$ciphertext);// 解析JSON$out_trade_no = json_decode($result)->out_trade_no; // 订单号$trade_state = json_decode($result)->trade_state; // 领取后果$openid = json_decode($result)->payer->openid; // 领取用户$payer_total = json_decode($result)->amount->payer_total; // 领取金额// 如果领取领取后果是已领取的if($trade_state == 'SUCCESS'){ // 将后果存入数据库 $conn = new mysqli($dbhost, $dbuser, $dbpwd, $dbname); // 去重 $sql_checkNotify = "SELECT * FROM xcxpay_order WHERE order_num='$out_trade_no'"; $result_checkNotify = $conn->query($sql_checkNotify); if ($result_checkNotify->num_rows > 0) { // 如果曾经存在这个订单 // 代表服务器屡次下发回调 // 就不须要再次存进来了 // 返回接管后果 $ret = array( 'code' => 200, 'message' => '接管胜利' ); echo json_encode($ret); }else{ // 否则就须要把这个订单存进来 $sql_notify = "INSERT INTO xcxpay_order (order_num, openid, order_total) VALUES ('$out_trade_no', '$openid', '$payer_total')"; if ($conn->query($sql_notify) === TRUE) { // 返回接管后果 $ret = array( 'code' => 200, 'message' => '接管胜利' ); echo json_encode($ret); }else{ // 返回接管后果 $ret = array( 'code' => 'FAIL', 'message' => '接管失败' ); echo json_encode($ret); } }}else{ // 返回接管后果 $ret = array( 'code' => 200, 'message' => '接管胜利' ); echo json_encode($ret);}?>
v3OrderCallBack.php
这个文件是领取回调的验签,须要配置的参数是 ApiV3Key
<?phpclass AesUtil{ public $aesKey = '填写你的ApiV3Key,在商户平台设置并获取'; const KEY_LENGTH_BYTE = 32; const AUTH_TAG_LENGTH_BYTE = 16; public function __construct() { $aesKey = '填写你的ApiV3Key,在商户平台设置并获取'; if (strlen($aesKey) != self::KEY_LENGTH_BYTE) { throw new InvalidArgumentException('有效的ApiV3Key,长度应为32个字节'); } $this->aesKey = $aesKey; } public function decryptToString($associatedData, $nonceStr, $ciphertext) { $ciphertext = \base64_decode($ciphertext); if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) { return false; } if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) { return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey); } if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) { return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey); } if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) { $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE); $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE); return \openssl_decrypt($ctext, 'aes-256-gcm', $this->aesKey, \OPENSSL_RAW_DATA, $nonceStr, $authTag, $associatedData); } throw new \RuntimeException('AEAD_AES_256_GCM须要PHP 7.1以上或者装置libsodium-php'); }}
Db.php
数据库配置。
<?php/** * 数据库配置文件 * BY TANKING * 2023-1-2 **/ $dbhost = "数据库地址";$dbuser = "数据库账号";$dbpwd = "数据库明码";$dbname = "数据库名称";?>
数据库表构造如下:
可通过以下SQL语句执行建表。
SQL语句
CREATE TABLE `xcxpay_order` ( `id` int(10) NOT NULL COMMENT '自增ID', `order_num` varchar(32) DEFAULT NULL COMMENT '订单号', `openid` varchar(32) DEFAULT NULL COMMENT 'openid', `order_total` varchar(10) DEFAULT NULL COMMENT '订单金额(分)', `order_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '订单工夫') ENGINE=InnoDB DEFAULT CHARSET=utf8;
证书文件
还须要返回商户平台下载证书文件。
获取形式:
小程序代码
pay.wxml
<view class="orderInfo">你须要领取{{orderPrice}}元</view><view class="paybtn" bindtap="Reqpay">立刻领取</view><view class="paySuccess" wx:if="{{paySuccess == true}}">领取胜利</view>
pay.js
须要配置域名和后端目录名。
Page({ data: { paySuccess:false }, // 进入页面加载 onLoad(e) { var that = this; // 获取用户 wx.login({ success: function(res) { if (res.code) { wx.request({ url: "https://你的域名/目录/getOpenid.php?code="+res.code, header: {"content-type": "application/json"}, success: function(res) { console.log(res.data) // 获取到openid和订单金额 that.setData({ openid:res.data.openid, orderPrice:res.data.orderPrice }) } }); } } }) }, // 发动领取 Reqpay: function(){ var that = this; wx.request({ url: "https://你的域名/目录/creatOrder.php?openid="+that.data.openid, header: {'content-type': 'application/json'}, success (res) { console.log(res.data) // 申请领取参数 var timestamp_ = res.data.timestamp; var noncestr_ = res.data.noncestr; var package_ = res.data.package; var paySign_ = res.data.paySign; // 订单参数 var orderNum = res.data.orderNum; // 订单号 wx.requestPayment({ timeStamp: timestamp_.toString(), nonceStr: noncestr_, package: package_, signType: 'MD5', paySign: paySign_, success (res) { console.log(res) if(res.errMsg == 'requestPayment:ok'){ // 领取胜利 console.log('领取胜利') that.setData({ paySuccess:true }) // 请勿应用requestPayment:ok的后果作为判断领取胜利的根据 // 如需确定订单领取的实在后果请往下持续增加你的查问订单领取后果的逻辑 } } }) } }) }})
pay.wxss
.orderInfo{ width: 88%; height: 100px; background: #eee; border-radius: 20px; margin: 30px auto 0; line-height: 100px; text-align: center; font-size: 25px;}.paybtn{ width: 88%; height: 55px; line-height: 55px; background: #f7c58a; border-radius: 10px; margin: 10px auto 0; text-align: center; font-size: 18px;}.paySuccess{ text-align: center; margin-top: 30px; font-size: 16px; color: #f7c58a; font-weight: bold;}
pay.json
{ "usingComponents": {}, "navigationBarTextStyle": "black", "backgroundColor": "#eee", "navigationBarBackgroundColor": "#f7c58a", "navigationBarTitleText": "微信小程序领取demo"}
图例
作者
TANKING
有问题请分割Wechat:sansure2016