一,申请支付
商户在微信公众平台或开放平台提交微信支付申请,微信支付工作人员审核资料无误后开通相应的微信支付权限。微信支付申请审核通过后,商户在申请资料填写的邮箱中收取到由微信支付小助手发送的邮件,此邮件包含开发时需要使用的支付账户信息
二,实现流程
- 调用登陆接口先获取用户的openid
- 生成prepay_id, 开发者服务器调用如下接口
统一下单接口URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder 这里需要传入大量参数:
参数(以下为必要参数,不是所有参数)
appid: 此小程序的唯一标识
mch_id: 商户id 需在 商户平台 查看(申请商户平台成功就会得到)
body: 商品描述
nonce_str: 随机32位内字符串
notify_url 成功后的通知地址
out_trade_no 商户订单号 需要无重复
trade_type 交易类型 小程序用 ‘JSAPI’
openid JSAPI类型支付时,openid也是必要参数
total_fee 此单的交易额度(钱 money) 例如:888 单位是分钱,类型为int。即8.88元钱
spbill_create_ip 终端ip(服务器的ip)
sign需要通过算法得到:(这是第一次签名)
【签名规范】
◆ 参数名ASCII码从小到大排序(字典序);常见ASCII码的大小规则,0-9<A-Z<a-z:
◆ 如果参数的值为空不参与签名;
◆ 参数名区分大小写;
stringA=="appid=* &body=* &device_info=* &mch_id=* &nonce_str=* “;
拼接完后, 再把key拼接在最后, 再进行加密, 得到参数sign
stringASignTemp=stringA+”&key=* " //注:key为商户平台设置的密钥key
sign=MD5(stringASignTemp).toUpperCase() //注:MD5签名方式 toUpperCase()转大写
最终发送数据时, 再把sign加上去
*
* * * * * * * JSAPI *
3.统一下单接口返回正常的prepay_id,再按【签名规范】重新生成签名后(第二次签名),将数据传输给前端。
将参数 appid、noncestr、package(注意:此处的值为 Sign=WXPay)、partnerid、prepayid、timestamp 签名
stringB="appid=* &nonce_str=* &package=Sign=WXPay&partnerid=* &prepayid=* ×tamp=* “;
stringBSignTemp=stringB+”&key=* " //注:key为商户平台设置的密钥key
paySign=MD5(stringBSignTemp).toUpperCase() //注:MD5签名方式 toUpperCase()转大写
4.返回前端发起支付需要的5个参数
timeStamp – 时间戳10位
nonceStr – 随机字符串
package – 此处的值为从统一下单接口返回的prepay_id参数值:prepay_id=*
signType – 签名类型
paySign – 2次签名
附:
微信验签工具:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=20_1
其他参数和错误代码说明: https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1
小程序支付官方文档: https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5
三,前端代码
小程序的js
/**确认购买 */
formSubmit: function(e) {
var that = this;
if (e.detail.value.read.length == 0) {
wx.showToast({
title: '请同意相关协议',
icon: 'loading',
duration: 1500
})
setTimeout(function() {
wx.hideToast()
}, 2000)
} else if (e.detail.value.vip_class.length == 0) {
wx.showToast({
title: '请选择开通服务',
icon: 'loading',
duration: 1500
})
setTimeout(function() {
wx.hideToast()
}, 2000)
} else {
util.requestUrl({
url: "pay.php",
data: {
'guide_money': e.detail.value.vip_class
},
method: "post",
success: function(res) {
console.log(res)
let out_trade_no = res.data.out_trade_no;
let form_id = e.detail.formId;
console.log('获取formid:' + form_id);
//获取所需的返回值后,调起微信支付窗口
wx.requestPayment({
timeStamp: res.data.msg.timeStamp, //时间戳
nonceStr: res.data.msg.nonceStr, //随机字符串,32位以内
package: res.data.msg.package, //统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=***
signType: res.data.msg.signType, //签名算法
paySign: res.data.msg.paySign, //签名
success(res) {
console.log('支付成功'); //成功后,主动回调结果给后台
util.requestUrl({
url: "guide_pay_notify_url.php",
data: {
'out_trade_no': out_trade_no,
'pay_result': 'success',
'form_id': form_id
},
method: "post",
success: function(res) {
console.log('回调成功后,后台返回的指示:' + res.data.msg) //按此时后台返回的结果展示给用户
wx.navigateTo({
url: '/pages/msg/msg?type=' + res.data.msg.type + '&title=' + res.data.msg.title + '&content=' + res.data.msg.content
})
}
})
},
fail(res) {
return;
},
complete(res) {
}
})
}
})
}
},
新建msg回馈提示页面
wxml
<!--pages/msg/msg.wxml-->
<view class="page">
<view class="weui-msg">
<view class="weui-msg__icon-area">
<icon type="{{type}}" size="93"></icon>
</view>
<view class="weui-msg__text-area">
<view class="weui-msg__title">{{title}}</view>
<view class="weui-msg__desc">{{content}}</view>
</view>
<view class="weui-msg__opr-area">
<view class="weui-btn-area">
<button bindtap="btn1" class="weui-btn" type="primary">{{btn1_text}}</button>
<button bindtap="btn2" class="weui-btn" type="default">{{btn2_text}}</button>
</view>
</view>
<view class="weui-msg__extra-area">
<view class="weui-footer">
<view class="weui-footer__links">
<navigator url="" class="weui-footer__link"></navigator>
</view>
<view class="weui-footer__text">Copyright©2019,sheliant.com.All rights reserved.</view>
</view>
</view>
</view>
</view>
css
/* pages/msg/msg.wxss */
@import '../../weui.wxss';
js
// pages/msg/msg.js
const app = getApp()
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
var that=this;
//默认值
/*let type= 'success';//success成功图标,warn失败图标
let title= '支付成功';//大提示(标题)
let content= '尊贵的会员,您已成功开通高级vip套餐!';//小提示(内容)*/
let btn1_text= '确认';//主按钮文字,默认返回上一页
let btn2_text='返回主页';//副按钮文字,默认返回主页
//设置值
let s_type = decodeURIComponent(options.type);
let s_title = decodeURIComponent(options.title);
let s_content = decodeURIComponent(options.content);
let s_btn1_text = decodeURIComponent(options.btn1_text);
let s_btn2_text = decodeURIComponent(options.btn1_text);
that.setData({
type: s_type,
title: s_title,
content: s_content,
btn1_text: btn1_text,
btn2_text: btn2_text,
})
},
btn1:function(){
wx.navigateBack({
delta: 2
})
},
btn2: function () {
wx.reLaunch({
url: '../index/index',
});
},
})
四,后台处理
SDK下载接口 pay.php
if($_POST['comp_id'])//扫码支付
{
require_once '../../Class/DBBEN.php';
$SESSION_COMP_ID=intval(deben($_POST['comp_id']));
if($SESSION_COMP_ID<10000)
exit('请登录!!');
}
require_once ROOT."WxpayAPI_php/lib/WxPay.Api.php";//微信支付接口
require_once ROOT."WxpayAPI_php/lib/WxPay.NativePay.php";//微信支付接口
$msg["success"]=3;
//取回用户openId
$sqlopid="SELECT comp_openId FROM company WHERE comp_id='$SESSION_COMP_ID' LIMIT 1";
$opid2=$db->query($sqlopid);
$opid=$opid2->fetch();
$syac_total_amount=intval($_POST['money']);//年续金额 单位元
if(empty($opid['comp_openId']))
{
$msg["success"]=2;
$msg["error"]="请登录";
}else if(empty($arrGuideVipFee[$syac_total_amount]))
{
$msg["success"]=3;
$msg["error"]="缺少金额";
}else
{
//发起支付统一下单
$out_trade_no=Id();//生成新订单号
$input = new WxPayUnifiedOrder();
$input->SetBody(''.$SESSION_COMP_ID);//商品简述128字
$input->SetAttach(3);//附加数据 可自巳参数 原值返回 这里附加的是 项目类别$arrSypiItemClass 的 Key
$input->SetOut_trade_no($out_trade_no);//商户订单号
$input->SetTotal_fee($syac_total_amount*100);//标价金额 单位为分
$input->SetTime_start(date("YmdHis"));//交易起始时间
$input->SetTime_expire(date("YmdHis", time() + 86400));//交易结束时间
//$input->SetGoods_tag("test");//订单优惠标记
$input->SetNotify_url("https://wx.sheliant.com/api/guide/guide_pay_notify_url.php");//通知回调地址
if($_POST['comp_id'])//扫码支付
{
$input->SetTrade_type("NATIVE");//交易类型
$input->SetProduct_id($out_trade_no);
$notify = new NativePay();
$result = $notify->GetPayUrl($input);
$url2 = $result["code_url"];
if($result["return_code"]!="SUCCESS")
exit('请刷新再试');
//var_dump($result);
}
else
{
$tools = new JsApiPay();
$input->SetTrade_type("JSAPI");//交易类型
$input->SetOpenid($opid['comp_openId']);
$config = new WxPayConfig();
$order = WxPayApi::unifiedOrder($config, $input);
//echo '<font color="#f00"><b>统一下单支付单信息</b></font><br/>';
//print_info($order);
}
if($order["return_code"]=="SUCCESS" || !empty($url2))//或者扫码支付
{
$sqlpin="INSERT INTO sys_pay_info (out_trade_no,sypi_pay_class,sypi_item_class,syac_total_amount,coin_id,code_id,send_pay_date,comp_id,sypi_type) VALUE ('$out_trade_no',2,3,$syac_total_amount,0,0,'".date("Y-m-d G:i:s")."','$SESSION_COMP_ID',0)";
$inpay=$db->query($sqlpin);
if($inpay>0)
{
if(!empty($url2))//扫码支付
{
$msg["code_url"]=$url2;
$msg["out_trade_no"]=$out_trade_no;
}
else
{
$msg["success"]=1;
$msg["msg"]= json_decode($tools->GetJsApiParameters($order));
$msg["out_trade_no"]=$out_trade_no;
}
}else
{
$msg["success"]=3;
$msg["error"]="订单录入失败,请重新操作!";
}
}else
{
$msg["success"]=3;
$msg["error"]="创建支付失败,请重新操作!";
}
}
echo json_encode($msg);
?>
pay_notify_url.php
<?
/*
微信支付下回调页
*/
//定义系统常用变量
define('ROOT',substr(dirname(__FILE__), 0, -9));//根路径
header("Content-type: text/html; charset=utf-8");
//包含系统常用类
require_once ROOT."WxpayAPI_php/lib/WxPay.Api.php";//微信支付接口
/*$_POST['out_trade_no']='28052360018305';
$_POST['pay_result']="success";*/
$out_trade_no=(!empty($_POST['out_trade_no']))?htmlspecialchars($_POST['out_trade_no']):htmlspecialchars($_GET['out_trade_no']);//商户订单号
$pay_result=(!empty($_POST['out_trade_no']))?htmlspecialchars($_POST['pay_result']):htmlspecialchars($_GET['pay_result']); //$pay_result=="success" 支付成功的标记
$transaction_id=htmlspecialchars($_POST['transaction_id']);//微信支付订单号
//$msg["success"]=3;
$input = new WxPayOrderQuery();
$config = new WxPayConfig();
if(!empty($_POST['transaction_id']))//小程序服务器发起的回调
{
$input->SetTransaction_id($transaction_id);
}
if(!empty($out_trade_no) && $pay_result=="success")//用户小程序发起的回调
{
$input->SetOut_trade_no($out_trade_no);
}
$pay_info=WxPayApi::orderQuery($config, $input);
if($pay_info['trade_state']=="SUCCESS" && !empty($pay_info['out_trade_no']) && !empty($pay_info['transaction_id']) && !empty($pay_info['appid']))
{
//查询是否巳经回调过了
$sqlcc="SELECT sypi_type,comp_id FROM sys_pay_info WHERE out_trade_no='".$pay_info['out_trade_no']."' LIMIT 1";// AND buyer_logon_id='".$pay_info['appid']."'
$cc2=$db->query($sqlcc);
$cc=$cc2->fetch();
if($cc['sypi_type']==0)//未回调过
{
if($cc['comp_id']>0)
{
$sqlup="UPDATE sys_pay_info SET trade_no='".$pay_info['transaction_id']."',buyer_logon_id='".$pay_info['openid']."',sypi_type='1' WHERE out_trade_no='".$pay_info['out_trade_no']."' AND sypi_type=0 LIMIT 1";
$payup=$db->query($sqlup);
if($payup!=1)
{
$msg["error"]="支付成功但更新失败,请截图联系本站客服!";
}else
{
$guideEX="SELECT a1.usgu_class_deadline,a1.comp_id FROM user_guide AS a1 WHERE a1.comp_id='".$cc['comp_id']."' LIMIT 1";
$EX2=$db->query($guideEX);
$EX=$EX2->fetch();
if($EX['comp_id']<1)
{
$msg["error"]="支付成功但续期用户不存在哦,请截图联系本站客服!";
}else
{
if($EX['usgu_class_deadline']<time())$EX['usgu_class_deadline']=time();
$usgu_class_deadline=strtotime("+1year",$EX['usgu_class_deadline']);
$sqlupdl="UPDATE user_guide SET usgu_class_deadline='$usgu_class_deadline' WHERE comp_id='".$EX['comp_id']."' LIMIT 1";
$updl=$db->query($sqlupdl);
if($updl<1)
{
$msg["error"]="支付成功但续期失败,请截图联系本站客服!";
}
if($pay_info["trade_type"]=="NATIVE")//扫码支付成功
{
exit('1');
}
}
}
}else
$msg["error"]="用户不存在哦,请截图联系本站客服!";
}
}else
$msg["error"]="订单不存在哦,请截图联系本站客服!";
if($_POST['out_trade_no'])///用户小程序发起的回调
{
$msg["success"]=1;
if($msg["error"]=='')//如果没错
{
$msg["msg"]["type"]='success';//图标
$msg["msg"]["title"]='续费成功';//标题
$msg["msg"]["content"]='尊贵的会员,您已成功续费高级vip!';//内容
}
else
{
$msg["msg"]["type"]='warn';
$msg["msg"]["title"]='续费失败';
$msg["msg"]["content"]=$msg["error"];
}
echo json_encode($msg,JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
}
if($_POST['transaction_id'] && $msg["error"]=='')///用户小程序发起的回调
{
echo '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
}
?>