5.微信支付 - ILazyBasicPayV2
概念
在接入微信支付过程中,会出现APPID、MCH_ID、公众平台、开放平台、商户平台等概念,下面仅从微信支付的角度来做简单分析:

平台
● 公众平台(mp.weixin.qq.com):注册、配置服务号、订阅号、小程序的入口,注册成功后系统就会下发一个与之一一对应的APPID(其中订阅号的APPID不支持申请和使用微信支付)。
● 商户平台( pay.weixin.qq.com):微信支付业务管理中心,商户可以在商户平台进行所有支付业务相关操作,例如退款、下载对账单、查询订单、提现、账号绑定、API证书下载、API密钥设置、查看证书序列号等操作。
● 开放平台(open.weixin.qq.com):注册、配置APP移动应用、网站应用的入口,注册成功后系统就会下发一个与之一一对应的APPID。
参数
APPID:在公众平台或开放平台申请注册之后由平台下发,在支付接口中通常作为配置参数,必须上传。
MCH_ID: 微信支付商户号,在公众平台、开放平台申请微信支付成功后由微信支付下发,或者直接在商户平台注册也可获得MCH_ID,在支付接口中通常作为配置参数,必须上传。
支付接口要求APPID与MCH_ID必须有绑定关系,在商户平台注册获得的MCH_ID需要在【商户平台—>产品中心—>APPID授权管理】菜单下与APPID进行绑定后方可使用。
KEY:支付秘钥,设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
配置员工账号
可参考如下链接配置员工账号
配置员工账号 :https://kf.qq.com/faq/140225MveaUz150107EvMfyq.html
开通支付产品
在产品中心, 根据需求开通需要的支付产品

JSAPI支付 : GetJsApiScriptAsync
商户平台与公众平台配置
-
登录到微信支付商户平台-> 产品中心-> 开发配置中, 配置JSAPI支付授权目录, 支付目录必须完全匹配(包括http和https)
-
支付授权目录校验规则说明:
1、如果支付授权目录设置为顶级域名(例如:https://www.weixin.com/ ),那么只校验顶级域名,不校验后缀;
2、如果支付授权目录设置为多级目录,就会进行全匹配,例如设置支付授权目录为https://www.weixin.com/abc/123/,则实际请求页面目录不能为https://www.weixin.com/abc/,也不能为https://www.weixin.com/abc/123/pay/,必须为https://www.weixin.com/abc/123/
具体配置参考 : https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3

- 登录到微信公众平台-> 设置-> 公众号设置, 配置网页安全域名
- 业务域名 : 设置业务域名后,在微信内访问该域名下页面时,不会被重新排版。用户在该域名上进行输入时,不出现下图所示的安全提示。
- JS接口安全域名 : 设置JS接口安全域名后,公众号开发者可在该域名下调用微信开放的JS接口
- 网页安全域名 : 用户在网页授权页同意授权给公众号后,微信会将授权数据传给一个回调页面,回调页面需在此域名下,以确保安全可靠。

使用LazyWeChat调用JSAPI支付
方法说明
-
参数
- out_trade_no : 商户订单号, 商户系统内部订单号(商户自定义单号规则即可), 要求32个字符内, 只能是数字、大小写字母_-|* 且在同一个商户号下唯一
- body : 商品描述
- total_fee : 总金额(单位 : 分)
- openid : 购买者openid
- notify_url : 通知地址, 异步接收微信支付结果通知的回调地址, 通知url必须为外网可访问的url, 不能携带参数, 在LazyWeChat中请使用服务器地址 http://domain.com/LazyWechatListener
-
返回值 : 用于JSAPI支付的JS脚本
<script type="text/javascript"> function onBridgeReady() { WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId": "wxbb2xxxxxxxxb991d", "nonceStr": "2adce8d54016458da98a7876edf3d1c4", "package": "prepay_id=wx1421xxxxxxxx99a917b08facfb59330000", "paySign": "11B80E6F3Exxxxxxxx3B55CD1D30F5AA", "signType": "MD5", "timeStamp": "1597439963735" }, function (res) { WeixinJSBridge.log(res.err_msg); } ); } 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{ onBridgeReady(); } </script>
示例代码
- ConfigureServices中添加 services.AddLazyPay();
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddLazyPay();
}
- 在Controller中生成JsApi支付的JS代码
using LazyWeChat.Abstract.WeChatPay.V2;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System;
using System.Threading.Tasks;
namespace WebTest.OfficialAccount.Controllers
{
public class PayController : Controller
{
private readonly ILazyBasicPayV2 _lazyBasicPay;
private Random random = new Random();
public PayController(ILazyBasicPayV2 lazyBasicPay)
{
_lazyBasicPay = lazyBasicPay;
}
public async Task<IActionResult> JsApi()
{
var out_trade_no = $"JsApi-{random.Next(0, 100000)}";
var notify_url = "https://test.lazywechat.cn/LazyWechatListener";
var js = await _lazyBasicPay.GetJsApiScriptAsync(out_trade_no, "Body desc for jsapi", 1, "oNDiC0d-r7Su5mYCU-mXFSXuhmtQ", notify_url); //生成JsApi代码
ViewBag.js = js;
return View();
}
}
}
备注 : notify_url请指定为公众号的服务器URL地址, 设置回调地址,回调地址需返回如下xml格式数据(该xml已经在LazyWeChat服务器URL地址中集成),不返回的话,微信会向此地址不停的发送请求
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
- 在cshtml中注册JsApi, 其他语言客户端请自行脑补
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>JsApi</title>
<script type="text/javascript">
@Html.Raw(ViewBag.js)
</script>
</head>
<body>
<h2>This is JsApi test page</h2>
</body>
</html>
调用接口弹出如下页面

Native(扫码)支付
模式一 : GetPrePayQRCode
-
需要登录商户平台 -> 产品中心 -> 开发配置 -> 支付配置 , 配置Native支付回调链接
-
需在Startup.cs中配置中间件, 该中间件用于获取商品描述以及商品总金额
-
在Controller中传递out_trade_no, 生成支付二维码

在LazyWeChat中NativeNotifyListener是专门负责监听扫码支付模式一的URL, 在你的Native支付回调函数中便可配置为 http://xxxxxxx/NativeNotifyListener
在调用时仅传递out_trade_no(可认为是商品ID), 但是具体的商品信息以及商品金额,需要在后调函数中指定。
LazyWeChat中调用扫描支付模式一
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddLazyPay(); //添加微信支付相关服务
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(
IApplicationBuilder app,
IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
//为扫描支付模式一配置中间件
app.UseLazyNativePay((out_trade_no) =>
{
//此处仅模拟生成商品描述以及商品金额, 真实情况应该是通过out_trade_no去相关数据库查询获取body与total_fee
System.Random random = new System.Random();
var body = $"test product body:{out_trade_no}-{random.Next(100000)}";
var total_fee = 1;
return (body, total_fee);
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Pay}/{action=GetNativePay1QRCode}/{id?}");
});
}
- services.AddLazyPay(); 添加微信支付相关的服务
- UseLazyNativePay是专门针对扫码支付模式一的一个中间件, 参数为一个 Func<string, (string, int)>的委托, 这个委托的input参数为商品ID, output为一个元组, Item1为商品描述、Item2为商品金额,这个委托参数的作用就是通过商品ID查询该商品的描述信息与商品金额
在Controller中调用扫码支付模式一
public class PayController : Controller
{
private readonly ILazyBasicPayV2 _lazyBasicPay;
private Random random = new Random();
public PayController(ILazyBasicPayV2 lazyBasicPay)
{
_lazyBasicPay = lazyBasicPay;
}
public FileContentResult GetNativePay1QRCode()
{
//商品ID
var out_trade_no = $"NativePay1-{random.Next(0, 100000)}";
var code = _lazyBasicPay.GetPrePayQRCode(out_trade_no);
return new FileContentResult(code, "image/Jpeg");
}
}
_lazyBasicPay.GetPrePayQRCode(out_trade_no) 该方法接收参数out_trade_no, 生成二维码,该二维码会请求前面中间件中的Func来生成支付信息
代码运行后直接显示二维码

模式二 : GetPayQRCodeAsync
与模式一不同的是, 在传递out_trade_no(可认为是商品ID)的同时又传递了具体的商品信息(body)以及商品金额(total_fee)
参数
- out_trade_no : 商户订单号, 商户系统内部订单号(商户自定义单号规则即可), 要求32个字符内, 只能是数字、大小写字母_-|* 且在同一个商户号下唯一
- body : 商品描述
- total_fee : 总金额(单位 : 分)
- notify_url : 通知地址, 异步接收微信支付结果通知的回调地址, 通知url必须为外网可访问的url, 不能携带参数, 在LazyWeChat中请使用服务器地址 http://domain.com/LazyWechatListener
在startup中仅需要注册微信支付相关服务,无需添加中间件
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddLazyPay(); //添加微信支付相关服务
}
在Controller中调用扫描支付模式二
public class PayController : Controller
{
private readonly ILazyBasicPayV2 _lazyBasicPay;
private Random random = new Random();
public PayController(ILazyBasicPayV2 lazyBasicPay)
{
_lazyBasicPay = lazyBasicPay;
}
public FileContentResult GetNativePay2QRCode()
{
//商品ID
var out_trade_no = $"NativePay2-{random.Next(0, 100000)}";
//商品描述
var body = "Body desc for native pay2";
//商品金额
var total_fee = 1;
//支付完毕后通知的URL地址, 请配置为服务器URL地址
var notify_url = "https://test.lazywechat.cn/LazyWechatListener";
var code = _lazyBasicPay.GetPayQRCodeAsync(out_trade_no, body, total_fee, notify_url).Result;
return new FileContentResult(code, "image/Jpeg");
}
}
Micro(付款码支付)支付
打开微信中的付款码, 收款人员通过扫描设备收款, 但是支付结果可能不会立即返回(例如, 用户设置了免密支付, 该接口可立即得到支付结果, 否则需要调用支付结果查看接口来查看支付结果)
付款码支付 : RunMicroPayAsync
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddLazyPay(); //添加微信支付相关服务
}
-
参数
- out_trade_no : 商户订单号, 商户系统内部订单号(商户自定义单号规则即可), 要求32个字符内, 只能是数字、大小写字母_-|* 且在同一个商户号下唯一
- body : 商品描述
- total_fee : 总金额(单位 : 分)
- auth_code : 打开付款码后可以看到二维码上面有"点击可查看付款码数字",该参数定时刷新,且仅可被使用一次
-
返回值 : XML , 参考 官方文档 https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1
例如
<xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> <appid><![CDATA[wx2421b1c4370ec43b]]></appid> <mch_id><![CDATA[10000100]]></mch_id> <device_info><![CDATA[1000]]></device_info> <nonce_str><![CDATA[GOp3TRyMXzbMlkun]]></nonce_str> <sign><![CDATA[D6C76CB785F07992CDE05494BB7DF7FD]]></sign> <result_code><![CDATA[SUCCESS]]></result_code> <openid><![CDATA[oUpF8uN95-Ptaags6E_roPHg7AG0]]></openid> <is_subscribe><![CDATA[Y]]></is_subscribe> <trade_type><![CDATA[MICROPAY]]></trade_type> <bank_type><![CDATA[CCB_DEBIT]]></bank_type> <total_fee>1</total_fee> <coupon_fee>0</coupon_fee> <fee_type><![CDATA[CNY]]></fee_type> <transaction_id><![CDATA[1008450740201411110005820873]]></transaction_id> <out_trade_no><![CDATA[1415757673]]></out_trade_no> <attach><![CDATA[订单额外描述]]></attach> <time_end><![CDATA[20141111170043]]></time_end> </xml>
public class PayController : Controller
{
private readonly ILazyBasicPayV2 _lazyBasicPay;
private Random random = new Random();
public PayController(ILazyBasicPayV2 lazyBasicPay)
{
_lazyBasicPay = lazyBasicPay;
}
public async Task<IActionResult> MicroPay()
{
//商品ID
var out_trade_no = $"Micropay-{random.Next(0, 100000)}";
//商品描述
var body = "Body desc for Micropay";
//金额
var total_fee = 1;
//付款码数字, 打开付款码后可以看到二维码上面有"点击可查看付款码数字",该参数定时刷新,且仅可被使用一次
var auth_code = "134927305201477020";
var returnObject = await _lazyBasicPay.RunMicroPayAsync(out_trade_no, body, total_fee, auth_code);
ViewBag.returnObject = JsonConvert.SerializeObject(returnObject);
return View();
}
}
上面的Action会返回如下结果, 可以看到由于用户未开启免密支付, 所以result_code 返回FAIL

查看支付结果 : OrderQueryAsync
因为付款码支付可能并不会立即返回支付结果, 所以需要调用查看支付结果接口进行查询(此接口也可以查看其它支付方式的支付结果)
-
参数
- transaction_id : 微信订单号
- out_trade_no : 商品ID
上面两个参数为二选一, 均可以用来查询支付结果, 两者均不为空时, transaction_id 有限选择
-
返回值
-
dynamic
{ "appid": "wxbb23a029883b991d", "attach": "", "bank_type": "CMB_CREDIT", "cash_fee": "1", "cash_fee_type": "CNY", "fee_type": "CNY", "is_subscribe": "Y", "mch_id": "1501396621", "nonce_str": "2yQBB4lemW1vJR1h", "openid": "oNDiC0d-r7Su5mYCU-mXFSXuhmtQ", "out_trade_no": "Micropay-40151", "result_code": "SUCCESS", "return_code": "SUCCESS", "return_msg": "OK", "sign": "A26AC03640B9D961D7CC1D215F37A6DF", "time_end": "20200815102701", "total_fee": "1", "trade_state": "SUCCESS", "trade_state_desc": "支付成功", "trade_type": "MICROPAY", "transaction_id": "4200000596202008150763626659" }
示例代码 :
public async Task<IActionResult> OrderQuery() { var transaction_id = "1009660380201506130728806387"; var out_trade_no = "20150806125346"; var returnObject = await _lazyBasicPay.OrderQueryAsync(transaction_id, out_trade_no); return View(); } -
H5支付 : RunH5PayAsync
H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付。 主要用于触屏版的手机浏览器请求微信支付的场景。可以方便的从外部浏览器唤起微信支付。
官方文档 : https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_1
H5支付说明
例如, 调用H5支付, 在手机端的非微信浏览器中获取mweb_url, 并跳转到mweb_url, 出现微信支付页面, 点击立即支付来完成h5支付


需要登录商户平台 -> 产品中心 -> 开发配置 -> 支付配置 , 配置H5支付域名

在LazyWeChat中使用H5支付
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddLazyPay(); //添加微信支付相关服务
}
参数说明
public class H5PayModel
{
/// <summary>
/// 商户订单号
/// </summary>
public string out_trade_no { get; set; }
/// <summary>
/// 商品描述
/// </summary>
public string body { get; set; }
/// <summary>
/// 商品价格,单位:分
/// </summary>
public int total_fee { get; set; }
/// <summary>
/// 通知地址
/// </summary>
public string notify_url { get; set; }
/// <summary>
/// 设备IP地址
/// </summary>
public string spbill_create_ip { get; set; }
/// <summary>
/// 场景信息 : 具体请参考后面的统一支付scene_info说明
/// </summary>
public dynamic h5_info { get; set; }
}
public async Task<IActionResult> H5Pay()
{
//下面的field均为H5支付的必填字段
H5PayModel h5PayModel = new H5PayModel();
//商品ID
h5PayModel.out_trade_no = $"H5pay-{random.Next(0, 100000)}";
//商品描述
h5PayModel.body = "Body desc for H5Pay";
//金额
h5PayModel.total_fee = 1;
//IP地址
h5PayModel.spbill_create_ip = "192.168.0.1";
//支付完毕后通知的URL地址, 请配置为服务器URL地址
h5PayModel.notify_url = "https://test.lazywechat.cn/LazyWechatListener";
//场景信息
h5PayModel.h5_info = new ExpandoObject();
h5PayModel.h5_info.type = "IOS";
h5PayModel.h5_info.app_name = "王者荣耀";
h5PayModel.h5_info.bundle_id = "com.tencent.wzryIOS";
var returnObject = await _lazyBasicPay.RunH5PayAsync(h5PayModel);
ViewBag.returnObject = JsonConvert.SerializeObject(returnObject);
ViewBag.mweb_url = returnObject.mweb_url;
return View();
}
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>H5Pay</title>
</head>
<body>
<h3>This is H5pay result checking page</h3>
<div>
@ViewBag.returnObject
</div>
<div>
<a href="@ViewBag.mweb_url">mweb_url</a>
</div>
</body>
</html>
统一支付scene_info说明
JSAPI
{
"store_info": {
"id": "SZTX001",
"name": "腾大餐厅",
"area_code": "440305",
"address": "科技园中一路腾讯大厦"
}
}
Native
{
"store_info": {
"id": "SZTX001",
"name": "腾大餐厅",
"area_code": "440305",
"address": "科技园中一路腾讯大厦"
}
}
H5
IOS
{
"h5_info": {
"type": "IOS",
"app_name": "王者荣耀",
"bundle_id": "com.tencent.wzryIOS"
}
}
Android
{
"h5_info": {
"type": "Android",
"app_name": "王者荣耀",
"package_name": "com.tencent.tmgp.sgame"
}
}
WAP
{
"h5_info": {
"type": "Wap",
"wap_url": "https://pay.qq.com",
"wap_name": "腾讯充值"
}
}
小程序支付 :RunMiniPayAsync
需要在小程序中申请开通微信支付, 具体步骤请参考如下链接
备注 : 如果小程序有关联的公众号且公众号已经申请了微信支付, 只需关联一下, 否则需要到微信商户平台为小程序申请微信支付
https://pay.weixin.qq.com/static/pay_setting/appid_protocol.shtml
LazyWeChat中使用小程序支付
[HttpGet("MiniPay", Name = "MiniPay")]
public async Task<string> MiniPay()
{
var out_trade_no = $"MiniPay-{random.Next(0, 100000)}";
var openid = "ogaiR4iP8a0JHaoCIXMZyFmCAZy4";
var body = "Body desc for mini";
var total_fee = 1;
var spbill_create_ip = "192.168.0.1";
var notify_url = "https://test.lazywechat.cn/LazyWechatListener";
var res = await _lazyBasicPay.RunMiniPayAsync(out_trade_no, openid, body, total_fee, spbill_create_ip, notify_url);
var json = JsonConvert.SerializeObject(res);
return json;
}
.wxml
<view>
<button bindtap="bindMiniPay">小程序支付</button>
</view>
.js Onload函数中发送请求, 获取小程序支付参数,并存放在payParam参数中
-
url : LazyWeChat中部署的小程序支付的后台请求地址
-
payParam
![image-20200815103851599]()
onLoad: function () {
var that = this
wx.request({
url: 'https://test.lazywechat.cn/MiniPay',
data: {},
header: { 'content-type': 'application/json' },
method: 'GET',
dataType: 'json',
responseType: 'text',
success: (result) => {
that.setData({
payParam: result.data
})
},
fail: () => { },
complete: () => { }
});
}
.js 调用wx.requestPayment函数激发小程序支付
bindMiniPay: function () {
var that = this.data
wx.requestPayment({
timeStamp: that.payParam.timeStamp,
nonceStr: that.payParam.nonceStr,
package: that.payParam.package,
signType: that.payParam.signType,
paySign: that.payParam.paySign,
success: (result) => {
console.log(result)
},
fail: () => {},
complete: () => {}
});
}


浙公网安备 33010602011771号