Senparc.Weixin.TenPayV3 微信支付(APIv3)模块
Senparc.Weixin.TenPayV3 模块用于提供微信支付(ApiV3)的支持能力。
注意:此模块是针对最新的微信支付 APIv3,而不是 V3 文档。

微信全平台支持
微信公众号、小程序、企业微信、微信支付 V2 / V3、开放平台……

强大技术社区支持
线上讨论社区、图书、视频课程、各类文档

2012 开源至今
十年如一日持续更新 盛派出品值得信赖
源码
Senparc.Weixin SDK 源码
GitHub:https://github.com/JeffreySu/WeiXinMPSDK(更新更快)
Gitee:https://gitee.com/JeffreySu/WeiXinMPSDK (从 GitHub 同步)
打包代码
Nuget 包地址:https://www.nuget.org/packages/Senparc.Weixin.TenpayV3
本地源码文件
Senparc.Weixin.TenpayV3 源码位置:/src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3
如何安装?
您可以直接引用 Senparc.Weixin 的源码进行开发,也可以引用已经打包完成的 dll(通过 Nuget 包,推荐),以方便随时获取官方的更新。注意:直接引用源码和引用 Nuget 包只能二选一。
引用程序集(推荐)
您可以通过 Visual Studio
、Visual Studio Code
、dotnet 命令行
等多种方式自动安装 Nuget 包。
在开发项目【解决方案资源管理器】中,对需要添加 Senparc.Weixin.TenpayV3 的模块点击右键,点击【管理 Nuget 程序包】,在【浏览】标签中输入 Senparc.Weixin.TenpayV3,点击右侧【安装】按钮。如下图所示:

首先,确认已经安装好 VS Code 以及 dotnet 命令行(安装 .NET SDK 后会自动安装)。
然后,打开解决方案或项目所在目录,按 Ctrl+~,打开终端面板:

进入需要添加 Senparc.Weixin.Tenpay 的模块的项目的目录,输入:
dotnet add package Senparc.Weixin.TenpayV3

安装完成后,可查看对应 .csproj 文件,被添加引用,如:
<ItemGroup>
<PackageReference Include="Senparc.Weixin.TenpayV3" Version="0.6.4-beta1" />
</ItemGroup>
首先,确认已经安装好 dotnet 命令行(安装 .NET SDK 后会自动安装)。
进入需要添加 Senparc.Weixin.TenpayV3 的模块的项目的目录,输入:
dotnet add package Senparc.Weixin.TenpayV3

安装完成后,可查看对应 .csproj 文件,被添加引用,如:
<ItemGroup>
<PackageReference Include="Senparc.Weixin.TenPayV3" Version="0.6.4-beta1" />
</ItemGroup>
如何使用?
全局注册
所有的 Senparc.Weixin SDK 注册过程都是类似的。
首先,完成所有 Senparc.Weixin SDK 的整体注册代码。在 Program.cs 中加入以下代码:

说明:
builder.Services.AddMemoryCache()
Senparc.Weixin 支持本机缓存、Redis、Memcached 等多种缓存策略,默认使用本机缓存,此时需要激活本地缓存。builder.Services.AddSenparcWeixin(builder.Configuration)
用于完成 Senparc.Weixin 的注册。app.UseSenparcWeixin()
方法用于配置和启用 Senparc.Weixin。
以上代码对于所有的 Senparc.Weixin 下级模块都是相同的,只需要 3 句代码。
本项目参考文件:
公众号注册
在上述代码中的第 27 行委托方法中插入代码,即可完成默认公众号以及微信支付的注册:
//注册公众号信息(可以执行多次,注册多个公众号)
register.RegisterMpAccount(weixinSetting, "【盛派网络小助手】公众号");
//注册微信支付(可以执行多次,注册多个微信支付)
register.RegisterTenpayApiV3(weixinSetting, "【盛派网络小助手】微信支付(ApiV3)");

注意:
上述代码同时注册了公众号和微信支付(V2),因为微信支付需要提供微信用户的标识(OpenId),OpenId 必须来自微信公众号、小程序等模块。如果需要在小程序内使用微信支付,则需要对应注册小程序。
其中,weixinSetting
的值默认来自于 appsettings.json
:
"SenparcWeixinSetting": {
"IsDebug": true,
//公众号
"Token": "#{Token}#",
"EncodingAESKey": "#{EncodingAESKey}#",
"WeixinAppId": "#{WeixinAppId}#",
"WeixinAppSecret": "#{WeixinAppSecret}#",
//微信支付V3
"TenPayV3_AppId": "#{TenPayV3_AppId}#",
"TenPayV3_AppSecret": "#{TenPayV3_AppSecret}#",
"TenPayV3_SubAppId": "#{TenPayV3_SubAppId}#",
"TenPayV3_SubAppSecret": "#{TenPayV3_SubAppSecret}#",
"TenPayV3_MchId": "#{TenPayV3_MchId}#",
"TenPayV3_SubMchId": "#{TenPayV3_SubMchId}#", //子商户,没有可留空
"TenPayV3_Key": "#{TenPayV3_Key}#",
"TenPayV3_TenpayNotify": "#{TenPayV3_TenpayNotify}#", //http://YourDomainName/TenpayApiV3/PayNotifyUrl
/* 支付证书私钥
* 1、支持明文私钥(无换行字符)
* 2、私钥文件路径(如:~/App_Data/cert/apiclient_key.pem),注意:必须放在 App_Data 等受保护的目录下,避免泄露
*/
"TenPayV3_PrivateKey": "#{TenPayV3_PrivateKey}#", //(新)证书私钥
"TenPayV3_SerialNumber": "#{TenPayV3_SerialNumber}#", //(新)证书序列号
"TenPayV3_ApiV3Key": "#{TenPayV3_APIv3Key}#", //(新)APIv3 密钥
}

其中,Token
、EncodingAESKey
、WeixinAppId
和 WeixinAppSecret
对应了微信公众号后台的配置参数。
特别说明: TenPayV3_PrivateKey
可使用已经处理好的私钥,也可以直接提供从微信支付官网下载的私钥文件(下载的压缩包中解压文件 apiclient_key.pem,并复制到安全的路径,推荐 App_Data
目录下),虚拟路径从网站根目录开始,必须以 ~/
开头,如 ~/App_Data/cert/apiclient_key.pem
,SDK 将全自动处理。
本项目参考文件:
配置完成。
提示:自动注册的信息可通过 Senparc.Weixin.Config.SenparcWeixinSetting
获取。
JSAPI 支付
在微信公众号网页中使用微信支付,必须借助 JSAPI,完成支付流程。
首先,需要创建一个商品页面,并在页面上提供一个下订单的入口,例如:一键购买。
(具体实现此处略)
当前示例中,提供了 3 个关键的页面:ProductList(商品列表)、ProductItem(商品详情) 和 JsApi(JSAPI 订单支付)。
ProductList 商品列表
后端
参考代码:TenPayApiV3Controller 下的 ProductList() 方法。
public ActionResult ProductList()
{
var products = ProductModel.GetFakeProductList();
return View(products);
}
本项目参考文件:
前端
本项目参考文件:
效果

ProductItem 商品详情
后端
参考代码:TenPayApiV3Controller 下的 ProductItem() 方法。
public ActionResult ProductItem(int productId, int hc)
{
var products = ProductModel.GetFakeProductList();
var product = products.FirstOrDefault(z => z.Id == productId);
if (product == null || product.GetHashCode() != hc)
{
return Content("商品信息不存在,或非法进入!2003");
}
//判断是否正在微信端
if (Senparc.Weixin.BrowserUtility.BrowserUtility.SideInWeixinBrowser(HttpContext))
{
//正在微信端,直接跳转到微信支付页面
return RedirectToAction("JsApi", new { productId = productId, hc = hc });
}
else
{
//在PC端打开,提供二维码扫描进行支付
return View(product);
}
}
说明:上述代码使用
SideInWeixinBrowser()
方法对当前页面的运行环境做了判断,如果是在微信内打开,则直接进入 JsApi 支付页面(见下放 JsApi 的介绍),如果在非微信内部打开(如 PC),则展示商品详情,并提供多种支付方式的选择。
本项目参考文件:
前端
本项目参考文件:
效果
从 商品列表 中选择一个商品点击,如果在 PC 端,则可以看到详情页面:

上述的支付方式二:“扫一扫”支付下方有一个根据所选商品自动生成的二维码,使用手机微信扫一扫,即可进入对应的商品订单页面(即 JsApi 订单页面)。
JSAPI 订单支付页面
后端
用户点击下单按钮后,需要在后台生成一个预支付订单并在页面上登记,代码请参考 TenPayApiV3Controller.JsApi()
[CustomOAuth(null, "/TenpayApiV3/OAuthCallback")]
public ActionResult JsApi(int productId, int hc)
{
try
{
//获取产品信息
//...
//调用 JsApi 获取预支付信息
//请求信息
TransactionsRequestData jsApiRequestData = new(TenPayV3Info.AppId, TenPayV3Info.MchId, name,
sp_billno, new TenpayDateTime(DateTime.Now.AddHours(1), false), null, notifyUrl, null,
new() { currency = "CNY", total = price }, new(openId), null, null, null);
//请求接口
var basePayApis2 = new Senparc.Weixin.TenPayV3.TenPayHttpClient.BasePayApis2(_httpClient,
_tenpayV3Setting);
var result = await basePayApis2.JsApiAsync(jsApiRequestData);
if (result.VerifySignSuccess != true)
{
throw new WeixinException("获取 prepay_id 结果校验出错!");
}
//获取 UI 信息包
var jsApiUiPackage = TenPaySignHelper.GetJsApiUiPackage(TenPayV3Info.AppId, result.prepay_id);
ViewData["jsApiUiPackage"] = jsApiUiPackage;
//其他逻辑
//...
return View();
}
catch (Exception ex)
{
//...
}
}
本项目参考文件:
上述代码中,传入的 productId
参数是商品的编号,此处作为 Sample 演示,是从内存中模拟列表并查询,实际项目中,商品信息一般存储在数据库中,根据 productId
从数据中查找商品数据;hc
函数是为了确保当前内存信息的有效性而设置的对应商品信息的 HashCode,实际开发项目中无需使用,可忽略。
此过程中,最关键的代码是:var result = TenPayOldV3.Unifiedorder(xmlDataInfo)
,result.prepay_id
即“预支付ID”,前端页面必须凭借 prepay_id 才能让手机端唤起微信支付。此时,当前订单编号已经在微信支付后台进行了注册。
注意:该方法使用了 [CustomOAuth] 特性,用于自动使用微信公众号的 OAuth 功能识别用户身份,此功能属于公众号范畴,不在这里展开。
前端
前端的关键操作是当用户点击“支付”按钮后,执行 JS 代码:
// 当微信内置浏览器完成内部初始化后会触发WeixinJSBridgeReady事件。
document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() {
//公众号支付
jQuery('a#getBrandWCPayRequest').click(function (e) {
WeixinJSBridge.invoke('getBrandWCPayRequest', {
"appId": "@jsApiUiPackage.AppId", //公众号名称,由商户传入
"timeStamp": "@jsApiUiPackage.Timestamp", //时间戳
"nonceStr": "@jsApiUiPackage.NonceStr", //随机串
"package": "@Html.Raw(jsApiUiPackage.PrepayIdPackage)",//扩展包
"signType": "RSA", //微信V3签名方式:RSA
"paySign": "@Html.Raw(jsApiUiPackage.Signature)" //微信签名
}, function (res) {
//alert(JSON.stringify(res));
if (res.err_msg == "get_brand_wcpay_request:ok") {
if (confirm('支付成功!点击“确定”进入退款流程测试。')) {
location.href = '/Docs/TenPayV3/TenpayApiV3/Refund';
}
//console.log(JSON.stringify(res));
}else{
alert(JSON.stringify(res));
}
// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
//因此微信团队建议,当收到ok返回时,向商户后台询问是否收到交易成功的通知,若收到通知,前端展示交易成功的界面;若此时未收到通知,商户后台主动调用查询订单接口,查询订单的当前状态,并反馈给前端展示相应的界面。
});
});
注意:微信支付 ApiV2 和 ApiV3 在订单接口有完全不同的区别,如果升级请留意!
本项目参考文件:
效果



支付回调
当用户在微信端完成支付后,微信服务器会主动推送一条通知到应用服务器。这条信息只会在微信官方服务器和应用服务器之间发生,不会有用户的参与,并且附加签名校验,因此才是可信的。
注意:千万不能信任手机客户端完成支付的状态,并以此通知应用服务器用户已完成支付!
以 JsApi 支付为例,当发起统一支付时,会要求填写回调地址(TenPayV3UnifiedorderRequestData
中的 notifyUrl
参数,见 JSAPI 支付 相关说明)。
注意:不同的支付方式提供回调地址的设置可能不同,如“Native 支付”,则是在微信支付的管理后台设置。
定义回调入口
/// <summary>
/// JS-SDK支付回调地址(在下单接口中设置的 notify_url)
/// </summary>
/// <returns></returns>
public async Task<IActionResult> PayNotifyUrl()
{
try
{
//获取微信服务器异步发送的支付通知信息
var resHandler = new TenPayNotifyHandler(HttpContext);
var orderReturnJson = await resHandler.DecryptGetObjectAsync<OrderReturnJson>();
//获取支付状态
string trade_state = orderReturnJson.trade_state;
//验证请求是否从微信发过来(安全)
NotifyReturnData returnData = new();
//验证可靠的支付状态
if (orderReturnJson.VerifySignSuccess == true && trade_state == "SUCCESS")
{
returnData.code = "SUCCESS";//正确的订单处理
/* 提示:
* 1、直到这里,才能认为交易真正成功了,可以进行数据库操作,但是别忘了返回规定格式的消息!
* 2、上述判断已经具有比较高的安全性以外,还可以对访问 IP 进行判断进一步加强安全性。
* 3、下面演示的是发送支付成功的模板消息提示,非必须。
*/
}
else
{
returnData.code = "FAILD";//错误的订单处理
returnData.message = "验证失败";
//此处可以给用户发送支付失败提示等
}
return Json(returnData);
}
catch (Exception ex)
{
WeixinTrace.WeixinExceptionLog(new WeixinException(ex.Message, ex));
throw;
}
}
本项目参考文件:
Native 支付
Native 支付用于线下(或微信环境以外)的支付,通过微信扫描二维码,唤起个人微信支付完成支付过程。
生成二维码的控件很多,以 ZXing.Net 为例,在 TenPayApiV3Controller
中创建方法:
/// <summary>
/// 使用 Native 支付
/// </summary>
/// <param name="productId"></param>
/// <param name="hc"></param>
/// <returns></returns>
public async Task<IActionResult> NativePayCode(int productId, int hc)
{
var products = ProductModel.GetFakeProductList();
var product = products.FirstOrDefault(z => z.Id == productId);
if (product == null || product.GetHashCode() != hc)
{
return Content("商品信息不存在,或非法进入!2004");
}
//使用 Native 支付,输出二维码并展示
MemoryStream fileStream = null;//输出图片的URL
var price = (int)(product.Price * 100);
var name = product.Name + " - 微信支付 V3 - Native 支付";
var sp_billno = string.Format("{0}{1}{2}", TenPayV3Info.MchId/*10位*/, SystemTime.Now.ToString("yyyyMMddHHmmss"),
TenPayV3Util.BuildRandomStr(6));
var notifyUrl = TenPayV3Info.TenPayV3Notify.Replace("/TenpayApiV3/", "/TenpayApiV3/");
TransactionsRequestData requestData = new(TenPayV3Info.AppId, TenPayV3Info.MchId, name, sp_billno, new TenpayDateTime(DateTime.Now.AddHours(1)), null, notifyUrl, null, new() { currency = "CNY", total = price }, null, null, null, null);
BasePayApis basePayApis = new BasePayApis();
var result = await basePayApis.NativeAsync(requestData);
//进行安全签名验证
if (result.VerifySignSuccess == true)
{
fileStream = QrCodeHelper.GerQrCodeStream(result.code_url);
}
else
{
fileStream = QrCodeHelper.GetTextImageStream("Native Pay 未能通过签名验证,无法显示二维码");
}
return File(fileStream, "image/png");
}
本项目参考文件:
上述过程将自动生成对应于指定商户、指定商品(productId)的付款二维码,前端 HTML 调用方式如下:
<img src="/TenpayApiV3/NativePayCode" alt="扫码付款" />
用户扫码完成支付后,微信服务器会自动请求回调地址,如 /TenpayApiV3/NativeNotifyUrl,代码如下:
//待补充
本项目参考文件:
提示:
Native 支付的回调地址设置位置位于:微信支付后台 > 产品中心 > 开发配置 > Native支付回调链接。
![]()
Native 支付回调链接设置
退款
退款方法核心代码如下:
/// <summary>
/// 退款申请接口
/// </summary>
/// <returns></returns>
public async Task<IActionResult> Refund()
{
try
{
string nonceStr = TenPayV3Util.GetNoncestr();
string outTradeNo = HttpContext.Session.GetString("BillNo");
if (!TradeNumberToTransactionId.TryGetValue(outTradeNo, out string transactionId))
{
return Content("transactionId 不正确,可能是服务器还没有收到微信回调确认通知,退款失败。请稍后刷新再试。");
}
string outRefundNo = "OutRefunNo-" + SystemTime.Now.Ticks;
int totalFee = int.Parse(HttpContext.Session.GetString("BillFee"));
int refundFee = totalFee;
string opUserId = TenPayV3Info.MchId;
var notifyUrl = "https://sdk.weixin.senparc.com/TenpayApiV3/RefundNotifyUrl";
var dataInfo = new RefundRequsetData(transactionId, null, outRefundNo, "Senparc TenPayV3 demo退款测试", notifyUrl, null, new RefundRequsetData.Amount(refundFee, null, refundFee, "CNY"), null);
var result = await _basePayApis.RefundAsync(dataInfo);
return View();
}
catch (Exception ex)
{
WeixinTrace.WeixinExceptionLog(new WeixinException(ex.Message, ex));
throw;
}
}
本项目参考文件:
说明:上述代码为了方便演示,并限定在没有登录功能的情况下只能退款本人自己支付过的订单,因此将 BillNo(订单号)存在 Session 中,实际开发过程中可放入 URL 或 Post 参数中进行请求,并注意做好权限验证!
退款回调
在退款接口调用过程中,有一个 notifyUrl
的参数,此地址用于接收微信服务器发送的退款信息回调信息。代码如下:
/// <summary>
/// 退款通知地址
/// </summary>
/// <returns></returns>
public async Task<IActionResult> RefundNotifyUrl()
{
WeixinTrace.SendCustomLog("RefundNotifyUrl被访问", "IP" + HttpContext.UserHostAddress()?.ToString());
NotifyReturnData returnData = new();
try
{
var resHandler = new TenPayNotifyHandler(HttpContext);
var refundNotifyJson = await resHandler.DecryptGetObjectAsync<RefundNotifyJson>();
WeixinTrace.SendCustomLog("跟踪RefundNotifyUrl信息", refundNotifyJson.ToJson());
string refund_status = refundNotifyJson.refund_status;
if (/*refundNotifyJson.VerifySignSuccess == true &*/ refund_status == "SUCCESS")
{
returnData.code = "SUCCESS";
returnData.message = "OK";
//获取接口中需要用到的信息 例
string transaction_id = refundNotifyJson.transaction_id;
string out_trade_no = refundNotifyJson.out_trade_no;
string refund_id = refundNotifyJson.refund_id;
string out_refund_no = refundNotifyJson.out_refund_no;
int total_fee = refundNotifyJson.amount.payer_total;
int refund_fee = refundNotifyJson.amount.refund;
//填写逻辑
WeixinTrace.SendCustomLog("RefundNotifyUrl被访问", "验证通过");
}
else
{
returnData.code = "FAILD";
returnData.message = "验证失败";
WeixinTrace.SendCustomLog("RefundNotifyUrl被访问", "验证失败");
}
//进行后续业务处理
}
catch (Exception ex)
{
returnData.code = "FAILD";
returnData.message = ex.Message;
WeixinTrace.WeixinExceptionLog(new WeixinException(ex.Message, ex));
}
//https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_3.shtml
return Json(returnData);
}
本项目参考文件:
进阶
当前示例展示了最基本和常用的功能,如果您需要了解更多高级玩法,并且有扎实的编程功底,请参考完整示例。
完整示例解决方案文件:
务必阅读 readme 文件:
关于
使用 Senparc.Weixin,您可以方便快速地开发微信全平台的应用(包括微信公众号、小程序、小游戏、企业号、开放平台、微信支付、JS-SDK、微信硬件/蓝牙,等等)。本项目的 Demo 同样适合初学者进行 .NET 编程学习。
目前 Senparc.Weixin 已经支持几乎所有微信平台模块和接口,并同时支持 .NET 3.5 / 4.0 / 4.5 / .NET Standard 2.x / .NET Core 2.x / .NET Core 3.x / .NET 6.0 / .NET 8.0 多种框架。
Senparc.Weixin SDK 是目前使用率最高的微信 .NET SDK,也是国内最受欢迎的 .NET 开源项目之一,是唯一入选 2021“科创中国”开源创新榜[1] [2] 的 .NET 项目。
项目自 2012 年开源,2013 年 1 月起正式发布到 GitHub。十几年来,我们一直保持着项目的持续更新,并将完整的源代码以及设计思想毫无保留地分享给大家,希望有更多的人可以从中受益,理解并传播开源的精神,一同助力中国开源事业!感恩一路上给我们提供帮助的朋友们!
团队
Senparc.Weixin 由盛派网络及盛派开发者社区核心团队负责维护,同时正在得到大量来自社区成员和社会各界的支持,欢迎加入我们!
支持
Senparc.Weixin 提供 100% 源码、线上 Sample、文档、图书、视频课程、线上开发者平台、问答平台、QQ / 微信群,以及不定期的线上/线下分享会等各种形式的支持服务,并坚持不间断维护源码,发布新版本。
联系邮箱:zsu@senparc.com
开源协议
Senaprc.Weixin 使用 APACHE LICENSE V2.0 开源协议,支持商用。