## 緣起

今年公司某個專案需要全面接入微信支付 V3 版 API。起初覺得,2014 年微信支付就已上線了 V3 版 API,這都 2021 年了,就算官方不給力,怎麼著社群也該有幾個造好的 .NET 的輪子了吧?於是興沖沖地到 NuGet 上開始搜尋“微信支付”四個大字,結果……

倒不是沒有現成的輪子,但基本都是隻包含一些簡單 API(如下單、查單、退款等等),與需求不符;偶爾有一些看似封裝全的,點進去一看卻是基於 V2 版 API 的。

沒辦法,自己動手,豐衣足食!

接入了微信支付後想著,既然微信支付都有了,為啥不把公眾號、小程式、企業微信之類的也接入了呢?

於是乎 `SKIT.FlurlHttpClient.Wechat` 這個專案就誕生了。

---
 
## 專案介紹

`SKIT.FlurlHttpClient.Wechat` 是一個基於 `Flurl.Http` 的微信 API HTTP 客戶端。

包含以下特性:

- 支援 .NET Framework 4.6.1+、.NET Standard 2.0+、.NET Core 2.0+、.NET 5。
- 支援 Windows / Linux / macOS 多平臺部署。
- 支援 System.Text.Json(預設)和 Newtonsoft.Json 兩種序列化方式。
- 非同步式程式設計。
- 強型別介面模型。
- 提供攔截器功能。
- 提供微信 API 所需的 MD5、SHA-1、SHA-256、AES、RSA 等演算法工具類。
- 完整、完善、完全的微信 API 封裝,同時可靈活自行擴充套件。

現有以下模組:

- 公眾平臺(公眾號、小程式、小遊戲、小商店) & 開放平臺模組:`SKIT.FlurlHttpClient.Wechat.Api`
- 商戶平臺(微信支付)模組:`SKIT.FlurlHttpClient.Wechat.TenpayV3`
- 企業微信(企業號)模組:`SKIT.FlurlHttpClient.Wechat.Work`
- 廣告平臺(廣點通)模組:`SKIT.FlurlHttpClient.Wechat.Ads`

---
 
## 快速開始

以接入微信支付為例,其他模組的開發流程與之十分類似。

### 安裝:

```shell
dotnet add package SKIT.FlurlHttpClient.Wechat.TenpayV3
```

### 初始化:

```csharp
using SKIT.FlurlHttpClient.Wechat;
using SKIT.FlurlHttpClient.Wechat.TenpayV3;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings;

/* 平臺證書管理器,具體用法請參見文件 */
var certManager = new InMemoryCertificateManager();
/* 僅列出必須配置項。也包含一些諸如超時時間、UserAgent 等的配置項 */
var options = new WechatTenpayClientOptions()
{
MerchantId = "微信商戶號",
MerchantV3Secret = "微信商戶 v3 API 金鑰",
MerchantCertSerialNumber = "微信商戶證書序列號",
MerchantCertPrivateKey = "-----BEGIN PRIVATE KEY-----微信商戶證書私鑰-----END PRIVATE KEY-----",
CertificateManager = certManager
};
var client = new WechatTenpayClient(options);
```

### 傳送請求:

```csharp
using SKIT.FlurlHttpClient.Wechat.TenpayV3;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models;

/* 以 JSAPI 統一下單介面為例 */
var request = new CreatePayTransactionJsapiRequest()
{
OutTradeNumber = "商戶訂單號",
AppId = "微信 AppId",
Description = "訂單描述",
ExpireTime = DateTimeOffset.Now.AddMinutes(15),
NotifyUrl = "https://example.com",
Amount = new CreatePayTransactionJsapiRequest.Types.Amount()
{
Total = 100
},
Payer = new CreatePayTransactionJsapiRequest.Types.Payer()
{
OpenId = "微信 OpenId"
}
};
var response = await client.ExecuteCreatePayTransactionJsapiAsync(request);
if (response.IsSuccessful())
{
Console.WriteLine("PrepayId:" + response.PrepayId);
}
else
{
Console.WriteLine("HTTP 狀態:" + response.RawStatus);
Console.WriteLine("錯誤程式碼:" + response.ErrorCode);
Console.WriteLine("錯誤描述:" + response.ErrorMessage);
}
```

### 驗證響應簽名:

```csharp
/* 一般情況下可以跳過驗證響應的簽名 */
bool valid = client.VerifyResponseSignature(response);
```

### 生成客戶端 JS-SDK 調起支付所需引數:

```csharp
/* 字典結構,包含客戶端 JS-SDK 調起支付所需的完整引數 */
var paramMap = client.GenerateParametersForJsapiPayRequest(request.AppId, response.PrepayId);
```

### 驗籤、解析並解密微信回撥通知中的敏感資訊:

```csharp
string callbackJson = "{ 微信商戶平臺發來的 JSON 格式的通知內容 }";
string callbackTimestamp = "微信回撥通知中的 Wechatpay-Timestamp 標頭";
string callbackNonce = "微信回撥通知中的 Wechatpay-Nonce 標頭";
string callbackSignature = "微信回撥通知中的 Wechatpay-Signature 標頭";
string callbackSerialNumber = "微信回撥通知中的 Wechatpay-Serial 標頭";

bool valid = client.VerifyEventSignature(callbackTimestamp, callbackNonce, callbackJson, callbackSignature, callbackSerialNumber);
if (valid)
{
/* 將 JSON 反序列化得到通知物件 */
/* 你也可以將 WechatTenpayEvent 型別直接繫結到 MVC 模型上,這樣就不再需要手動反序列化 */
var callbackModel = client.DeserializeEvent(callbackJson);
if ("TRANSACTION.SUCCESS".Equals(callbackModel.EventType))
{
/* 根據事件型別,解密得到支付通知敏感資料 */
var callbackResource = client.DecryptEventResource<Events.TransactionResource>(callbackModel);
string outTradeNumber = callbackResource.OutTradeNumber;
string transactionId = callbackResource.TransactionId;
Console.WriteLine("訂單 {0} 已完成支付,交易單號為 {1}", outTradeNumber, transactionId);
}
}
```

更多使用說明請閱讀專案倉庫中的開發文件。

專案倉庫中還包含了一個示例專案,以供開發者快速掌握本庫的使用方法。

---
 
## FAQ

### 1. Flurl.Http 是什麼?

[`Flurl.Http`](https://flurl.dev/) 是一個輕量級 HTTP 庫,是 .NET 中最受歡迎擴充套件庫之一,在 NuGet 上的累計下載量超過 1700 萬、日均下載量超過 6 千、GitHub 2.6k Stars(資料統計截至 2021-06-01)。

與另一個流行的 HTTP 庫 [`RestSharp`](https://restsharp.dev/) 相比,`Flurl.Http` 底層基於 `System.Net.Http.HttpClient`,而 `RestSharp` 底層則基於 `System.Net.HttpWebRequest`,前者在多核多執行緒環境下的效能基準測試中表現要遠優於後者,同時也是微軟官方目前推薦的 HTTP 客戶端方案。

### 2. 本庫與盛派微信 SDK(Senparc.Weixin)有什麼區別?

- 本庫**專注於 API 本身的封裝**,捎帶提供了一些用於加解密、序列化的工具類,**使用更靈活**;盛派微信 SDK 提供了大而全的功能,可與 MVC / WebAPI 深度整合。
- 本庫**遵循微軟官方推薦的 C# 屬性命名方式(大駝峰命名法)**對介面模型進行定義;盛派微信 SDK 提供的是微信介面本身的命名方式(蛇形命名法和小駝峰命名法混雜)。
- 本庫**封裝了目前微信官方提供的所有 API**;盛派微信 SDK 只提供了常用的 API。

### 3. 為什麼不支援 .NET Framework 4.0 / .NET Framework 4.5?

直接原因是本庫的依賴庫最低支援到 .NET Framework 4.6.1。

間接原因是為了支援跨平臺的 .NET Standard 2.0,只能相容到 .NET Framework 4.6.1。

根本原因是微軟官方已於 2016 年 1 月 12 日終止了對 .NET Framework 4.6.1 以下版本的技術支援。也就是說,微軟已經不再為此提供安全更新,在大部分技術合規要求中這一點都是扣分項,所以建議各位開發者目標框架能升級就升級。

---
 
## 倉庫

- GitHub:https://github.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat
- Gitee:https://gitee.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat

以上倉庫地址同步更新,均可接受 Issue 或 Pull Request。