抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

开发流程

  1. 创建预订单,同时传递回调地址参数
  2. 订单创建成功后返回一个 URL 地址
  3. 将 URL 地址生成二维码显示在用户界面
  4. 执行扫码支付
  5. 后台执行回调事件,更改订单状态,同时返回通知给微信服务端

接入准备

请参考:

开发平台

.Net 微信第三方 SDK 这里选用 SKIT.FlurlHttpClient.Wechat,其它类似的还有盛派微信 SDK(Senparc.Weixin),SKIT.FlurlHttpClient.Wechat 官方文档是这样介绍的:基于 Flurl.Http 的微信 API HTTP 客户端,支持公众平台、开放平台、商户平台、企业微信、广告平台、对话开放平台等模块。

特性:

  • 基于 Flurl.Http,可与 IHttpClientFactory 集成。
  • 支持 .NET Framework 4.6.1+、.NET Standard 2.0+、.NET Core 2.0+、.NET 5、.NET 6。
  • 支持 Windows / Linux / macOS 多平台部署。
  • 支持 System.Text.Json(默认)和 Newtonsoft.Json 两种序列化方式。
  • 异步式编程。
  • 强类型接口模型。
  • 提供拦截器功能。
  • 包含 SourceLink 符号文件,可在项目中无源代码调试。
  • 完整、完善、完全的微信 API 封装。

安装

如果使用 Visual Studio NuGet 管理器图形化界面,请在搜索结果中勾选“包括预发行版”。

1
2
3
4
5
# 通过 NuGet 安装
> Install-Package SKIT.FlurlHttpClient.Wechat.TenpayV3

# 通过 dotnet-tools 安装
> dotnet add package SKIT.FlurlHttpClient.Wechat.TenpayV3

配置

配置 Client 实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
internal class WechatPayClients
{
public static readonly WechatTenpayClient Instance;
static WechatPayClients()
{
var certificateManager = new SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings.InMemoryCertificateManager();
Instance = new WechatTenpayClient(new WechatTenpayClientOptions()
{
MerchantId = "",
MerchantV3Secret = "",
MerchantCertificateSerialNumber = "",
MerchantCertificatePrivateKey =System.IO.File.ReadAllText("apiclient_key.pem"),
PlatformCertificateManager = certificateManager,
AutoEncryptRequestSensitiveProperty = true,
AutoDecryptResponseSensitiveProperty = true,
});
}

internal static async Task InitializeCertificateManagerAsync()
{
var request = new SKIT.FlurlHttpClient.Wechat.TenpayV3.Models.QueryCertificatesRequest();
var response = await Instance.ExecuteQueryCertificatesAsync(request);

response = Instance.DecryptResponseSensitiveProperty(response);
foreach (var certificateModel in response.CertificateList)
{
Instance.PlatformCertificateManager.AddEntry(new SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings.CertificateEntry(certificateModel));
}
}
}

MerchantId、MerchantV3Secret、MerchantCertificateSerialNumber 和 MerchantCertificatePrivateKey 这几个参数均为第 1 步“接入准备”中获取的。

API接口

创建预订单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public async Task CreatePrePayOrder()
{
var request = new CreatePayTransactionNativeRequest()
{
OutTradeNumber = "or123456", //生成订单号
AppId = "", //配置AppId
Description = "Payment for or123456",//订单描述,显示在支付界面
ExpireTime = DateTime.Now.AddMinutes(15), //过期时间
NotifyUrl = "https://www.zerow.cn/notification", //回调地址,需外网可以访问
Amount = new CreatePayTransactionNativeRequest.Types.Amount()
{
Total = 100 //支付金额,单位为“分”
},

};
var response= await WechatPayClients.Instance.ExecuteCreatePayTransactionNativeAsync(request);
if (response.IsSuccessful())
{
//返回支付二维码链接,类似:weixin://wxpay/bizpayurl?pr=p4lpSuKzz,需将该链接生成二维码供用户扫码
Console.WriteLine($"QRCode URL:{response.QrcodeUrl}");
}
else
{
Console.WriteLine($"错误代码:{response.ErrorCode}");
Console.WriteLine($"错误描述:{response.ErrorMessage}");
}
}

官方支付成功回调,SDK 已实现解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"id": "xxxx-xxxx-xxxx-xxxx",
"create_time": "2022-04-25T17:04:47+08:00",
"resource_type": "encrypt-resource",
"event_type": "TRANSACTION.SUCCESS",
"summary": "支付成功",
"resource": {
"original_type": "transaction",
"algorithm": "AEAD_AES_256_GCM",
"ciphertext": "xxxxxx",
"associated_data": "transaction",
"nonce": "xxxxxx"
}
}

支付回调

创建订单的请求参数中提供了 NotifyUrl 回调地址参数,所以需要有一个回调函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public async Task PrePayCallback()
{
//回调签名验证
var sr= new StreamReader(Request.InputStream, Encoding.UTF8);
bool valid = WechatPayClients.Instance.VerifyEventSignature(
callbackTimestamp: Request.Headers.GetValues("Wechatpay-Timestamp").First(),
callbackNonce: Request.Headers.GetValues("Wechatpay-Nonce").First(),
callbackBody: sr.ReadToEnd(),
callbackSignature: Request.Headers.GetValues("Wechatpay-Signature").First(),
callbackSerialNumber: Request.Headers.GetValues("Wechatpay-Serial").First(), out Exception e
);
var callbackModel = WechatPayClients.Instance.DeserializeEvent(sr.ReadToEnd());
var callbackResource = WechatPayClients.Instance.DecryptEventResource<SKIT.FlurlHttpClient.Wechat.TenpayV3.Events.TransactionResource>(callbackModel);
//验证成功
if (valid)
{
//交易成功
if ("TRANSACTION.SUCCESS".Equals(callbackModel.EventType))
{
//获取交易数据
Console.WriteLine(callbackResource.TransactionId);
Console.WriteLine(callbackResource.OutTradeNumber);
}
}
}

查询交易状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public async Task QueryTransactionStatus(string outTradeNumber)
{
var request = new GetPayTransactionByOutTradeNumberRequest()
{
OutTradeNumber = outTradeNumber
};
var response = await WechatPayClients.Instance.ExecuteGetPayTransactionByOutTradeNumberAsync(request);
if (response.IsSuccessful())
{
//交易状态
Console.WriteLine(response.TradeState);
Console.WriteLine(response.TransactionId);
Console.WriteLine(response.OutTradeNumber);
}
else
{
Console.WriteLine(response.ErrorMessage);
}
}

查询交易状态 API:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_2.shtml

交易状态枚举值:
SUCCESS:支付成功
REFUND:转入退款
NOTPAY:未支付
CLOSED:已关闭
REVOKED:已撤销(仅付款码支付会返回)
USERPAYING:用户支付中(仅付款码支付会返回)
PAYERROR:支付失败(仅付款码支付会返回)

申请退款

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public async Task CreateRefund(string outTradeNumber)
{
var request = new CreateRefundDomesticRefundRequest()
{
OutTradeNumber = outTradeNumber, //订单号
OutRefundNumber = "", //生成退款单号
Amount = new CreateRefundDomesticRefundRequest.Types.Amount()
{
Total = 100, //原订单金额
Refund = 100 //退款金额
},
Reason = “” //退款原因
};
var response = await WechatPayClients.Instance.ExecuteCreateRefundDomesticRefundAsync(request);
if(response.IsSuccessful)
{
//退款状态
Console.WriteLine(response.Status);
}
else
{
Console.WriteLine(response.ErrorMessage);
}
}

申请退款 API:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_9.shtml

退款状态枚举值:
SUCCESS:退款成功
CLOSED:退款关闭
PROCESSING:退款处理中
ABNORMAL:退款异常

关闭订单

1
2
3
4
5
6
7
8
9
10
11
12
public async Task CloseTransaction(string outTradeNumber)
{
var request = new ClosePayTransactionRequest()
{
OutTradeNumber = outTradeNumber
};
var response = await WechatPayClients.Instance.ExecuteClosePayTransactionAsync(request);
if (!response.IsSuccessful())
{
Console.WriteLine(response.ErrorMessage);
}
}

关闭订单 API:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_3.shtml

参考

评论