feat(tenpayv3): 新增电商收付通跨境付款相关接口

This commit is contained in:
Fu Diwei
2022-06-09 18:05:06 +08:00
parent 38c595cfca
commit 7e6d619539
15 changed files with 587 additions and 0 deletions

View File

@@ -792,6 +792,16 @@
- 按日下载提现异常文件:`GetMerchantFundWithdrawBill`
- 电商收付通(跨境支付)
- 查询订单剩余可出境余额:`GetFundsToOverseaTransactionAvailableAbroadAmountByTransactionId`
- 申请资金出境:`CreateFundsToOverseaOrder`
- 查询出境结果:`GetFundsToOverseaOrderByOutOrderId`
- 获取购付汇账单文件下载链接:`GetFundsToOverseaBillDownloadUrl`
- 电商收付通(下载账单)
- 申请交易账单:`GetBillTradeBill`

View File

@@ -0,0 +1,96 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Flurl;
using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
{
/// <summary>
/// 为 <see cref="WechatTenpayClient"/> 提供电商收付通跨境付款相关的 API 扩展方法。
/// </summary>
public static class WechatTenpayClientExecuteFundsToOverseaExtensions
{
/// <summary>
/// <para>异步调用 [GET] /funds-to-oversea/transactions/{transaction_id}/available_abroad_amounts 接口。</para>
/// <para>REF: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_10_1.shtml </para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<Models.GetFundsToOverseaTransactionAvailableAbroadAmountByTransactionIdResponse> ExecuteGetFundsToOverseaTransactionAvailableAbroadAmountByTransactionIdAsync(this WechatTenpayClient client, Models.GetFundsToOverseaTransactionAvailableAbroadAmountByTransactionIdRequest request, CancellationToken cancellationToken = default)
{
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "funds-to-oversea", "transactions", request.TransactionId, "available_abroad_amounts")
.SetQueryParam("sub_mchid", request.SubMerchantId);
return await client.SendRequestWithJsonAsync<Models.GetFundsToOverseaTransactionAvailableAbroadAmountByTransactionIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
/// <para>异步调用 [POST] /funds-to-oversea/orders 接口。</para>
/// <para>REF: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_10_2.shtml </para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<Models.CreateFundsToOverseaOrderResponse> ExecuteCreateFundsToOverseaOrderAsync(this WechatTenpayClient client, Models.CreateFundsToOverseaOrderRequest request, CancellationToken cancellationToken = default)
{
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Post, "funds-to-oversea", "orders");
return await client.SendRequestWithJsonAsync<Models.CreateFundsToOverseaOrderResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
/// <para>异步调用 [GET] /funds-to-oversea/orders/{out_order_id} 接口。</para>
/// <para>REF: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_10_3.shtml </para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<Models.GetFundsToOverseaOrderByOutOrderIdResponse> ExecuteGetFundsToOverseaOrderByOutOrderIdAsync(this WechatTenpayClient client, Models.GetFundsToOverseaOrderByOutOrderIdRequest request, CancellationToken cancellationToken = default)
{
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "funds-to-oversea", "orders", request.OutOrderId)
.SetQueryParam("sub_mchid", request.SubMerchantId)
.SetQueryParam("transaction_id", request.TransactionId);
return await client.SendRequestWithJsonAsync<Models.GetFundsToOverseaOrderByOutOrderIdResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
/// <summary>
/// <para>异步调用 [GET] /funds-to-oversea/bill-download-url 接口。</para>
/// <para>REF: https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_10_4.shtml </para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<Models.GetFundsToOverseaBillDownloadUrlResponse> ExecuteGetFundsToOverseaBillDownloadUrlAsync(this WechatTenpayClient client, Models.GetFundsToOverseaBillDownloadUrlRequest request, CancellationToken cancellationToken = default)
{
if (client is null) throw new ArgumentNullException(nameof(client));
if (request is null) throw new ArgumentNullException(nameof(request));
IFlurlRequest flurlReq = client
.CreateRequest(request, HttpMethod.Get, "funds-to-oversea", "bill-download-url")
.SetQueryParam("sub_mchid", request.SubMerchantId)
.SetQueryParam("bill_date", request.BillDateString);
return await client.SendRequestWithJsonAsync<Models.GetFundsToOverseaBillDownloadUrlResponse>(flurlReq, data: request, cancellationToken: cancellationToken);
}
}
}

View File

@@ -0,0 +1,158 @@
using System.Collections.Generic;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
{
/// <summary>
/// <para>表示 [POST] /funds-to-oversea/orders 接口的请求。</para>
/// </summary>
public class CreateFundsToOverseaOrderRequest : WechatTenpayRequest
{
public static class Types
{
public class Goods
{
/// <summary>
/// 获取或设置商品名称。
/// </summary>
[Newtonsoft.Json.JsonProperty("goods_name")]
[System.Text.Json.Serialization.JsonPropertyName("goods_name")]
public string GoodsName { get; set; } = string.Empty;
/// <summary>
/// 获取或设置商品类目。
/// </summary>
[Newtonsoft.Json.JsonProperty("goods_category")]
[System.Text.Json.Serialization.JsonPropertyName("goods_category")]
public string? GoodsCategory { get; set; }
/// <summary>
/// 获取或设置商品数量。
/// </summary>
[Newtonsoft.Json.JsonProperty("goods_quantity")]
[System.Text.Json.Serialization.JsonPropertyName("goods_quantity")]
public int Quantity { get; set; }
/// <summary>
/// 获取或设置商品单价(单位:分)。
/// </summary>
[Newtonsoft.Json.JsonProperty("goods_unit_price")]
[System.Text.Json.Serialization.JsonPropertyName("goods_unit_price")]
public int UnitPrice { get; set; }
}
public class Seller
{
/// <summary>
/// 获取或设置境外卖家经营主体名称。
/// </summary>
[Newtonsoft.Json.JsonProperty("oversea_business_name")]
[System.Text.Json.Serialization.JsonPropertyName("oversea_business_name")]
public string OverseaBusinessName { get; set; } = string.Empty;
/// <summary>
/// 获取或设置境外卖家店铺名称。
/// </summary>
[Newtonsoft.Json.JsonProperty("oversea_shop_name")]
[System.Text.Json.Serialization.JsonPropertyName("oversea_shop_name")]
public string OverseaShopName { get; set; } = string.Empty;
/// <summary>
/// 获取或设置卖家 ID。
/// </summary>
[Newtonsoft.Json.JsonProperty("seller_id")]
[System.Text.Json.Serialization.JsonPropertyName("seller_id")]
public string SellerId { get; set; } = string.Empty;
}
public class Express
{
/// <summary>
/// 获取或设置物流单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("courier_number")]
[System.Text.Json.Serialization.JsonPropertyName("courier_number")]
public string CourierNumber { get; set; } = string.Empty;
/// <summary>
/// 获取或设置物流商名称。
/// </summary>
[Newtonsoft.Json.JsonProperty("express_company_name")]
[System.Text.Json.Serialization.JsonPropertyName("express_company_name")]
public string ExpressCompanyName { get; set; } = string.Empty;
}
public class Payee
{
/// <summary>
/// 获取或设置付款人 ID。
/// </summary>
[Newtonsoft.Json.JsonProperty("payee_id")]
[System.Text.Json.Serialization.JsonPropertyName("payee_id")]
public string PayeeId { get; set; } = string.Empty;
}
}
/// <summary>
/// 获取或设置微信二级商户号。
/// </summary>
[Newtonsoft.Json.JsonProperty("sub_mchid")]
[System.Text.Json.Serialization.JsonPropertyName("sub_mchid")]
public string SubMerchantId { get; set; } = string.Empty;
/// <summary>
/// 获取或设置商户出境单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("out_order_id")]
[System.Text.Json.Serialization.JsonPropertyName("out_order_id")]
public string OutOrderId { get; set; } = string.Empty;
/// <summary>
/// 获取或设置微信订单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("transaction_id")]
[System.Text.Json.Serialization.JsonPropertyName("transaction_id")]
public string TransactionId { get; set; } = string.Empty;
/// <summary>
/// 获取或设置出境金额(单位:分)。
/// </summary>
[Newtonsoft.Json.JsonProperty("amount")]
[System.Text.Json.Serialization.JsonPropertyName("amount")]
public int Amount { get; set; }
/// <summary>
/// 获取或设置境外收款币种。
/// </summary>
[Newtonsoft.Json.JsonProperty("foreign_currency")]
[System.Text.Json.Serialization.JsonPropertyName("foreign_currency")]
public string ForeignCurrency { get; set; } = string.Empty;
/// <summary>
/// 获取或设置商品列表。
/// </summary>
[Newtonsoft.Json.JsonProperty("goods_info")]
[System.Text.Json.Serialization.JsonPropertyName("goods_info")]
public IList<Types.Goods>? GoodsList { get; set; }
/// <summary>
/// 获取或设置卖家信息。
/// </summary>
[Newtonsoft.Json.JsonProperty("seller_info")]
[System.Text.Json.Serialization.JsonPropertyName("seller_info")]
public Types.Seller Seller { get; set; } = new Types.Seller();
/// <summary>
/// 获取或设置物流信息。
/// </summary>
[Newtonsoft.Json.JsonProperty("express_info")]
[System.Text.Json.Serialization.JsonPropertyName("express_info")]
public Types.Express Express { get; set; } = new Types.Express();
/// <summary>
/// 获取或设置付款人信息。
/// </summary>
[Newtonsoft.Json.JsonProperty("payee_info")]
[System.Text.Json.Serialization.JsonPropertyName("payee_info")]
public Types.Payee Payee { get; set; } = new Types.Payee();
}
}

View File

@@ -0,0 +1,9 @@
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
{
/// <summary>
/// <para>表示 [POST] /funds-to-oversea/orders 接口的响应。</para>
/// </summary>
public class CreateFundsToOverseaOrderResponse : GetFundsToOverseaOrderByOutOrderIdResponse
{
}
}

View File

@@ -0,0 +1,22 @@
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
{
/// <summary>
/// <para>表示 [GET] /funds-to-oversea/bill-download-url 接口的请求。</para>
/// </summary>
public class GetFundsToOverseaBillDownloadUrlRequest : WechatTenpayRequest
{
/// <summary>
/// 获取或设置微信二级商户号。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string SubMerchantId { get; set; } = string.Empty;
/// <summary>
/// 获取或设置账单日期格式yyyy-MM-dd
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string BillDateString { get; set; } = string.Empty;
}
}

View File

@@ -0,0 +1,29 @@
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
{
/// <summary>
/// <para>表示 [GET] /funds-to-oversea/bill-download-url 接口的响应。</para>
/// </summary>
public class GetFundsToOverseaBillDownloadUrlResponse : WechatTenpayResponse
{
/// <summary>
/// 获取或设置哈希类型。
/// </summary>
[Newtonsoft.Json.JsonProperty("hash_type")]
[System.Text.Json.Serialization.JsonPropertyName("hash_type")]
public string HashType { get; set; } = default!;
/// <summary>
/// 获取或设置哈希值。
/// </summary>
[Newtonsoft.Json.JsonProperty("hash_value")]
[System.Text.Json.Serialization.JsonPropertyName("hash_value")]
public string HashValue { get; set; } = default!;
/// <summary>
/// 获取或设置账单下载地址。
/// </summary>
[Newtonsoft.Json.JsonProperty("download_url")]
[System.Text.Json.Serialization.JsonPropertyName("download_url")]
public string DownloadUrl { get; set; } = default!;
}
}

View File

@@ -0,0 +1,29 @@
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
{
/// <summary>
/// <para>表示 [GET] /funds-to-oversea/orders/{out_order_id} 接口的请求。</para>
/// </summary>
public class GetFundsToOverseaOrderByOutOrderIdRequest : WechatTenpayRequest
{
/// <summary>
/// 获取或设置微信二级商户号。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string SubMerchantId { get; set; } = string.Empty;
/// <summary>
/// 获取或设置商户出境单号。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string OutOrderId { get; set; } = string.Empty;
/// <summary>
/// 获取或设置微信订单号。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string TransactionId { get; set; } = string.Empty;
}
}

View File

@@ -0,0 +1,120 @@
using System;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
{
/// <summary>
/// <para>表示 [GET] /funds-to-oversea/orders/{out_order_id} 接口的响应。</para>
/// </summary>
public class GetFundsToOverseaOrderByOutOrderIdResponse : WechatTenpayResponse
{
/// <summary>
/// 获取或设置微信二级商户号。
/// </summary>
[Newtonsoft.Json.JsonProperty("sub_mchid")]
[System.Text.Json.Serialization.JsonPropertyName("sub_mchid")]
public string SubMerchantId { get; set; } = default!;
/// <summary>
/// 获取或设置商户出境单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("out_order_id")]
[System.Text.Json.Serialization.JsonPropertyName("out_order_id")]
public string OutOrderId { get; set; } = default!;
/// <summary>
/// 获取或设置微信出境单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("order_id")]
[System.Text.Json.Serialization.JsonPropertyName("order_id")]
public string OrderId { get; set; } = default!;
/// <summary>
/// 获取或设置出境结果。
/// </summary>
[Newtonsoft.Json.JsonProperty("result")]
[System.Text.Json.Serialization.JsonPropertyName("result")]
public string Result { get; set; } = default!;
/// <summary>
/// 获取或设置出境失败原因。
/// </summary>
[Newtonsoft.Json.JsonProperty("fail_reason")]
[System.Text.Json.Serialization.JsonPropertyName("fail_reason")]
public string? FailReason { get; set; }
/// <summary>
/// 获取或设置出境金额(单位:分)。
/// </summary>
[Newtonsoft.Json.JsonProperty("amount")]
[System.Text.Json.Serialization.JsonPropertyName("amount")]
public int Amount { get; set; }
/// <summary>
/// 获取或设置境外收款金额(单位:该币种最小计价单位)。
/// </summary>
[Newtonsoft.Json.JsonProperty("foreign_amount")]
[System.Text.Json.Serialization.JsonPropertyName("foreign_amount")]
public int? ForeignAmount { get; set; }
/// <summary>
/// 获取或设置境外收款币种。
/// </summary>
[Newtonsoft.Json.JsonProperty("foreign_currency")]
[System.Text.Json.Serialization.JsonPropertyName("foreign_currency")]
public string ForeignCurrency { get; set; } = default!;
/// <summary>
/// 获取或设置汇率(格式:外币兑换人民币的比例乘以 10 的 8 次方)。
/// </summary>
[Newtonsoft.Json.JsonProperty("rate")]
[System.Text.Json.Serialization.JsonPropertyName("rate")]
[System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString)]
public long? ExchangeRate { get; set; }
/// <summary>
/// 获取或设置购汇时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("exchange_rate_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("exchange_rate_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
public DateTimeOffset? ExchangeRateTime { get; set; }
/// <summary>
/// 获取或设置预计购汇时间。
/// </summary>
[Newtonsoft.Json.JsonProperty("estimate_exchange_rate_time")]
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
[System.Text.Json.Serialization.JsonPropertyName("estimate_exchange_rate_time")]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Converters.RFC3339NullableDateTimeOffsetConverter))]
public DateTimeOffset? EstimateExchangeRateTime { get; set; }
/// <summary>
/// 获取或设置真实出境人民币金额(单位:分)。
/// </summary>
[Newtonsoft.Json.JsonProperty("departure_amount")]
[System.Text.Json.Serialization.JsonPropertyName("departure_amount")]
public int? DepartureAmount { get; set; }
/// <summary>
/// 获取或设置手续费金额(单位:分)。
/// </summary>
[Newtonsoft.Json.JsonProperty("fee")]
[System.Text.Json.Serialization.JsonPropertyName("fee")]
public int? Fee { get; set; }
/// <summary>
/// 获取或设置手续费承担商户号。
/// </summary>
[Newtonsoft.Json.JsonProperty("charge_mchid")]
[System.Text.Json.Serialization.JsonPropertyName("charge_mchid")]
public string? ChargeMerchantId { get; set; }
/// <summary>
/// 获取或设置手续费承担账户。
/// </summary>
[Newtonsoft.Json.JsonProperty("charge_account_type")]
[System.Text.Json.Serialization.JsonPropertyName("charge_account_type")]
public string? ChargeAccountType { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
{
/// <summary>
/// <para>表示 [GET] /funds-to-oversea/transactions/{transaction_id}/available_abroad_amounts 接口的请求。</para>
/// </summary>
public class GetFundsToOverseaTransactionAvailableAbroadAmountByTransactionIdRequest : WechatTenpayRequest
{
/// <summary>
/// 获取或设置微信二级商户号。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string SubMerchantId { get; set; } = string.Empty;
/// <summary>
/// 获取或设置微信订单号。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string TransactionId { get; set; } = string.Empty;
}
}

View File

@@ -0,0 +1,22 @@
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
{
/// <summary>
/// <para>表示 [GET] /funds-to-oversea/transactions/{transaction_id}/available_abroad_amounts 接口的响应。</para>
/// </summary>
public class GetFundsToOverseaTransactionAvailableAbroadAmountByTransactionIdResponse : WechatTenpayResponse
{
/// <summary>
/// 获取或设置微信订单号。
/// </summary>
[Newtonsoft.Json.JsonProperty("transaction_id")]
[System.Text.Json.Serialization.JsonPropertyName("transaction_id")]
public string TransactionId { get; set; } = default!;
/// <summary>
/// 获取或设置订单剩余可出境金额(单位:分)。
/// </summary>
[Newtonsoft.Json.JsonProperty("available_abroad_amount")]
[System.Text.Json.Serialization.JsonPropertyName("available_abroad_amount")]
public int AvailableAbroadAmount { get; set; }
}
}

View File

@@ -0,0 +1,27 @@
{
"amount": 10,
"express_info": {
"courier_number": "curier_number_1231",
"express_company_name": "国际xxx物流"
},
"foreign_currency": "USD",
"goods_info": [
{
"goods_category": "家用电器",
"goods_name": "橘子",
"goods_quantity": 1,
"goods_unit_price": 1
}
],
"out_order_id": "merchant_1123123",
"payee_info": {
"payee_id": "ID123112312"
},
"seller_info": {
"oversea_business_name": "香港xxxx公司",
"oversea_shop_name": "香港xxx公司xxx店铺",
"seller_id": "id2123123123"
},
"sub_mchid": "123456",
"transaction_id": "420000000000000010"
}

View File

@@ -0,0 +1,17 @@
{
"amount": 21,
"charge_account_type": "BASIC",
"charge_mchid": "1231231",
"departure_amount": 20,
"estimate_exchange_rate_time": "2015-05-20T13:29:35+08:00",
"exchange_rate_time": "2015-05-20T13:29:35+08:00",
"fail_reason": "DEPARTURE_AMOUNT_NO_ENOUGH",
"fee": 1,
"foreign_amount": 20,
"foreign_currency": "USD",
"order_id": "42000000000_123123",
"out_order_id": "merchant123123",
"rate": 650000000,
"result": "ACCEPT",
"sub_mchid": "1231231"
}

View File

@@ -0,0 +1,5 @@
{
"download_url": "https://api.mch.weixin.qq.com/v3/bill/downloadurl?token=xxx",
"hash_type": "SHA1",
"hash_value": "79bb0f45fc4c42234a918000b2668d689e2bde04"
}

View File

@@ -0,0 +1,17 @@
{
"amount": 21,
"charge_account_type": "BASIC",
"charge_mchid": "1231231",
"departure_amount": 20,
"estimate_exchange_rate_time": "2015-05-20T13:29:35+08:00",
"exchange_rate_time": "2015-05-20T13:29:35+08:00",
"fail_reason": "DEPARTURE_AMOUNT_NO_ENOUGH",
"fee": 1,
"foreign_amount": 20,
"foreign_currency": "USD",
"order_id": "42000000000_123123",
"out_order_id": "merchant123123",
"rate": 650000000,
"result": "ACCEPT",
"sub_mchid": "1231231"
}

View File

@@ -0,0 +1,4 @@
{
"available_abroad_amount": 21,
"transaction_id": "4208450740201411110007820472"
}