微信支付有多种方式,本文章主要介绍JsApi支付,前端调用使用的是vue,后端使用的是c# webapi,后端使用了插件Senparc.Weixin.TenPayV3。
开发工具:
前端:hbuilder
后端:Visual Studio 2022
注意:
小程序和公众号区别:
1、后端配置global.json的时候,Appid用对应平台上申请的
2、后端代码没有什么区别
3、vue在生成预付订单后,调起手机支付输入密码页面有区别,由于开发工具用的是Hbuilder,小程序支付可以直接用官方的wx.requestPayment,公众号不支持这种写法,所以稍微有一点点区别,公众号直接用微信公众号官方方法就可以了,下文中有贴出代码。
微信商户平台配置
在微信商户平台开通JsApi接口
支付授权目录设置,注意路径要到你api的实际路径,不要只写到域名
API安全设置(后端配置需要TenPayV3_Key、TenPayV3_PrivateKey、TenPayV3_SerialNumber,都是在此页面设置的)
后端配置
我后端开发用的是Visual Studio 2022,要安装插件Senparc.Weixin.TenPayV3(盛派开发的)
配置global.json
//Senparc.Weixin SDK 设置
"SenparcWeixinSetting": {
//以下为 Senparc.Weixin 的 SenparcWeixinSetting 微信配置
//微信全局
"IsDebug": true,
//微信支付V3(新版)
"TenPayV3_AppId": "xxxxxxxxx", //
"TenPayV3_AppSecret": "xxxxxxxxx",
"TenPayV3_MchId": "xxxxxx",//商户号
"TenPayV3_TenpayNotify": "http://cd.xxxxxxxxx.com/PayNotifyUrl", //支付成功后的回调
"TenPayV3_PrivateKey": "xxxx....", //(新)证书私钥(在证书文件夹中,很长一串,自己去复制)
"TenPayV3_SerialNumber": "xxxx...", //证书序列号(在商户平台申请证后,复制过来)
"TenPayV3_ApiV3Key": "xxxxxxxxx", //(新)APIv3 密钥 跟上面的TenPayV3_Key填同一个
},
在program.cs要注册Senparc,代码如下
先添加引用
using Senparc.Weixin.AspNet;
using Senparc.Weixin.AspNet.RegisterServices;
using Senparc.Weixin.Entities;
using Senparc.Weixin.RegisterServices;
using Senparc.Weixin.TenPayV3;
继续在program.cs中添加注册代码
//Senparc.Weixin 注册(必须)
builder.Services.AddSenparcWeixinServices(builder.Configuration);
var app = builder.Build();
var senparcWeixinSetting = app.Services.GetService<IOptions<SenparcWeixinSetting>>()!.Value;
//启用微信配置(必须)
var registerService = app.UseSenparcWeixin(app.Environment,
null /* 不为 null 则覆盖 appsettings 中的 SenpacSetting 配置*/,
null /* 不为 null 则覆盖 appsettings 中的 SenpacWeixinSetting 配置*/,
register => { },
(register, weixinSetting) =>
{
//注册公众号信息(可以执行多次,注册多个小程序)
register.RegisterTenpayApiV3(weixinSetting, "【xxx项目名称】微信支付(ApiV3)");//自定义一个名字
});
支付流程
1、前端vue调用后端api,生成一条状态为待支付的数据
2、待支付订单生成结果返回到前端,前端发起支付,跳转输入密码界面
3、输入密码成功付款后,自动触发后端支付成功回调函数,修改支付状态
vue(公众号)
```html
//生成订单预支付信息
Recharge() {
let that = this
var recharge = {
virtual_card_number: '123',
phone: this.memberinfo.phone,
recharge_money: this.renew_money,
recharge_type: this.renew_type,
openid: uni.getStorageSync('open_id')
}//传输到后端api的参数,根据自己项目修改
get_prepay_id(recharge).then(res => {
this.prepay_result = res.data.data
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
this.onBridgeReady();
}
})
},
onBridgeReady() {
let that=this
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":that.prepay_result.appId , //公众号ID,由商户传入
"timeStamp":that.prepay_result.timeStamp, //时间戳,自1970年以来的秒数
"nonceStr": that.prepay_result.nonceStr, //随机串
"package": that.prepay_result.package,
"signType": that.prepay_result.signType, //微信签名方式:
"paySign": that.prepay_result.paySign//微信签名
},
function(res){
console.log('this.prepay_result',that.prepay_result)
if(res.err_msg == "get_brand_wcpay_request:ok" ){
// 使用者支付成功
alert('支付成功');
} else if(res.err_msg == "get_brand_wcpay_request:fail" ){
// 使用者支付失败
alert('支付失败');
}
}
);
}```
**
vue(小程序)
Recharge() {
let that = this
var recharge = {
virtual_card_number: '123',
phone: this.memberinfo.phone,
recharge_money: this.renew_money,
recharge_type: this.renew_type,
openid: uni.getStorageSync('open_id')
}//传输到后端api的参数,根据自己项目修改
get_prepay_id(recharge).then(res => {
var result = res.data.data
wx.requestPayment({
timeStamp: result.timeStamp,
nonceStr: result.nonceStr,
package: result.package,
signType: result.signType,
paySign: result.paySign,
success(res) {
console.log('支付成功', res)
},
fail(res) {
console.log('支付失败', res)
}
})
})
},
**后端 WebApi
生成预支付订单
/// <summary>
/// JsApi支付
/// </summary>
/// <param name="record">订单信息</param>
/// <returns></returns>
//发布到服务器iis时,选择应用池-鼠标右键–>高级设置–>进程模型–>加载用户配置文件–>设置为true
public async Task<ApiReturnValue> GetPrepayid(c_recharge_record record)
{
ApiReturnValue<wx_pay_param> apiresult = new ApiReturnValue<wx_pay_param>();
try
{
string HostIp = "xxxx.xx.xx.xxx";
//生成订单10位序列号,此处用时间和随机数生成,商户根据自己调整,保证唯一
record.sp_billno = "cz_"+SystemTime.Now.ToString("yyyyMMddHHmmss");
record.recharge_time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var body = "智慧停车充值";
int price = record.recharge_money*100;//单位:分
//int price =1;//单位:分
var basePayApis = new Senparc.Weixin.TenPayV3.Apis.BasePayApis(Config.SenparcWeixinSetting);
var requestData = new Senparc.Weixin.TenPayV3.Apis.BasePay.TransactionsRequestData(Config.SenparcWeixinSetting.TenPayV3_AppId, Config.SenparcWeixinSetting.TenPayV3_MchId, body, record.sp_billno, new Senparc.Weixin.TenPayV3.Entities.TenpayDateTime(SystemTime.Now.AddMinutes(120).DateTime, false), HostIp, Config.SenparcWeixinSetting.TenPayV3_TenpayNotify, null, new() { currency = "CNY", total = price }, new(record.openid), null, null, null);
var result = await basePayApis.JsApiAsync(requestData);
if (result.ResultCode.Success == true)
{
var packageStr = "prepay_id=" + result.prepay_id;
var jsApiUiPackage = TenPaySignHelper.GetJsApiUiPackage(Config.SenparcWeixinSetting.TenPayV3_AppId, result.prepay_id);
wx_pay_param param = new wx_pay_param
{
prepay_id = result.prepay_id,//预付_id
appId = Config.SenparcWeixinSetting.TenPayV3_AppId,//appId
timeStamp = jsApiUiPackage.Timestamp,//时间戳
nonceStr = jsApiUiPackage.NonceStr,//随机字符串,长度为32个字符以下
package = packageStr,// 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id = ***
signType = "RSA",// 签名算法,应与后台下单时的值一致
paySign = jsApiUiPackage.Signature//签名
};
//插入一条订单
DB.Insertable(record).ExecuteCommand();
apiresult.data = param;
}
else
{
apiresult.code = 500;
apiresult.message = result.ResultCode.ErrorCode.ToString() + result.ResultCode.ErrorMessage + result.ResultCode.Solution;
}
}
catch (Exception ex)
{
apiresult.code = 500;
apiresult.message = ex.Message;
}
return apiresult;
}
支付回调函数
//支付成功后的回调
[HttpPost("PayNotifyUrl")]
public async Task<ApiReturnValue> PayNotifyUrl()
{
ApiReturnValue api_result = new ApiReturnValue();
try
{
//获取微信服务器异步发送的支付通知信息
var resHandler = new TenPayNotifyHandler(HttpContext);
var orderReturnJson = await resHandler.AesGcmDecryptGetObjectAsync<OrderReturnJson>();
//记录日志
Senparc.Weixin.WeixinTrace.SendCustomLog("PayNotifyUrl 接收到消息", orderReturnJson.ToJson(true));
//演示记录 transaction_id,实际开发中需要记录到数据库,以便退款和后续跟踪
string out_trade_no = orderReturnJson.out_trade_no;
//获取支付状态
string trade_state = orderReturnJson.trade_state;
//验证请求是否从微信发过来(安全)
NotifyReturnData returnData = new();
//验证可靠的支付状态
if (orderReturnJson.VerifySignSuccess == true && trade_state == "SUCCESS")
{
returnData.code = "SUCCESS";//正确的订单处理
/* 提示:
* 1、直到这里,才能认为交易真正成功了,可以进行数据库操作,但是别忘了返回规定格式的消息!
* 2、上述判断已经具有比较高的安全性以外,还可以对访问 IP 进行判断进一步加强安全性。
* 3、下面演示的是发送支付成功的模板消息提示,非必须。
*/
recharge_service.recharge_callback(out_trade_no);//这个方法自定义,我这里是直接去改变订单的状态为已支付
}
else
{
returnData.code = "FAILD";//错误的订单处理
returnData.message = "验证失败";
//此处可以给用户发送支付失败提示等
}
#region 记录日志(也可以记录到数据库审计日志中)
var logDir = ServerUtility.ContentRootMapPath(string.Format("~/App_Data/TenPayNotify/{0}", SystemTime.Now.ToString("yyyyMMdd")));
if (!Directory.Exists(logDir))
{
Directory.CreateDirectory(logDir);
}
var logPath = Path.Combine(logDir, string.Format("{0}-{1}-{2}.txt", SystemTime.Now.ToString("yyyyMMdd"), SystemTime.Now.ToString("HHmmss"), Guid.NewGuid().ToString("n").Substring(0, 8)));
using (var fileStream = System.IO.File.OpenWrite(logPath))
{
var notifyJson = orderReturnJson.ToString();
await fileStream.WriteAsync(Encoding.Default.GetBytes(notifyJson), 0, Encoding.Default.GetByteCount(notifyJson));
fileStream.Close();
}
#endregion
}
catch (Exception ex)
{
WeixinTrace.WeixinExceptionLog(new WeixinException(ex.Message, ex));
throw;
}
return api_result;
}