微信支付退款和退款结果查询接口简单实现(.Net 7.0)

〇、前言

相较于支付宝,微信支付对 .Net 的支持就没那么充分,官方没有提供 SDK。

但值得庆幸的是,在社区有大佬封装了 v3 版 .Net SDK。

原文链接:https://developers.weixin.qq.com/community/develop/article/doc/00020aadc384a0a5f01c3526b56813

SDK 名称:SKIT.FlurlHttpClient.Wechat.TenpayV3,支持 .NET Core / Framework,完整封装全部 v3 API。

本文也将以此 SDK 来应用,主要介绍退款和退款状态查询两个接口。

一、接入准备

1.1 引入 SDK

名称:SKIT.FlurlHttpClient.Wechat.TenpayV3,支持 .Net Standard 2.0、.Net Framework 4.6.2-4.7.1、.Net 6.0 等版本。

NuGet 简介地址:https://www.nuget.org/packages/SKIT.FlurlHttpClient.Wechat.TenpayV3

简介:基于 Flurl.Http 的微信支付 API v3 版客户端,支持商户(直连)、合作伙伴(服务商、渠道商、机构、银行)模式,支持基础支付、代金券、商家券、委托营销、消费卡、支付有礼、银行定向促活、微信支付分、微信先享卡、支付即服务、点金计划、智慧商圈、电商收付通、平台收付通、二级商户进件、小微商户进件、消费者投诉、商户风控管理、商户违规通知、批量转账到零钱、银行组件、海关报关、融合钱包、微工卡、电子发票、电子小票、车主平台、教育续费通、校园轻松付等功能。

1.2 必要的账户信息

|--------------|------------------------------------------------|--------------------------------|
| 名称 | 示例 | 简介 |
| 商户号 | 110 ... ... | 10 位,数字 |
| 商户 API 证书序列号 | D566D2 ... ... | 40 位,大写字母、数字,组合 |
| 商户 API v3 密钥 | gSk0EA ... ... | 32 位,数字、大写字母、小写字母,组合 |
| 商户 API 证书私钥 | -----BEGIN PRIVATE KEY-----\r\nMIIEv ... ... | 通常为 `apiclient_key.pem` 文件内容 |

关于微信的服务商和普通商户:

普通商户

资金由微信直接与商户结算,不涉及资金冻结和分账;

可独立申请,可自行通过接口进行支付、退款等操作;

适合大型企业或品牌商户,有自己技术团队和支付系统。

服务商

资金会先被微信冻结,服务商发起分账后微信才进行分账;

可免费申请不限个数的子商户,但服务商的商户号需要每年支付认证费用;

自身不能直接收款,需通过申请的"特约商户"才能收款;

"特约商户"可以像普通商户一样收款,但退款接口必须由服务商发起;

适合于提供支付解决方案给多个特约商户的第三方机构,特别是那些需要处理多商户分账或收取服务费的复杂场景。

特别注意:特约商户,也就是服务商下边的子商户,是没有权限直接调用退款接口的,只能根据服务商的账户信息加上子商户号来。

二、接口调用

2.1 根据账户信息创建共用客户端对象 client

复制代码
var manager = new InMemoryCertificateManager();
var options = new WechatTenpayClientOptions()
{
    MerchantId = "商户号", // 商户号
    MerchantV3Secret = "商户 API v3 密钥", // 商户 API v3 密钥
    MerchantCertificateSerialNumber = "商户 API 证书序列号", // 商户 API 证书序列号
    MerchantCertificatePrivateKey = "-----BEGIN PRIVATE KEY-----\r\nMIIEv ... ... Q71AG\r\n-----END PRIVATE KEY-----", // 商户 API 证书私钥
    PlatformCertificateManager = manager
};
var client = new WechatTenpayClient(options);

2.2 退款接口

以下是退款接口的代码,其中入参仅示例了必要的字段,其他详情见官方文档。

官方文档:https://pay.weixin.qq.com/docs/merchant/apis/refund/refunds/create.html

复制代码
var request = new CreateRefundDomesticRefundRequest()
{
    //OutTradeNumber = "商户订单号", // 【商户订单号】 原支付交易对应的商户订单号,与transaction_id二选一
    TransactionId = "微信订单号", // 【微信支付订单号】 原支付交易对应的微信订单号,与out_trade_no二选一
    // 商户自定义退款唯一标识,要做好记录,退款状态查询时入参必填
    OutRefundNumber = $"WX{DateTime.Now.ToString("yyyyMMddHHmmssffffff")}", 
    Reason = "测试退款080611",
    //NotifyUrl = "https://...", // 回调地址
    Amount = new CreateRefundDomesticRefundRequest.Types.Amount()
    {
        Total = 100, // 单位:分
        Refund = 99, // 单位:分
        Currency = "CNY" // 【退款币种】 目前只支持人民币:CNY。
    },
};
//string json = Json_Object.ObjectToJsonstr(request);
var response = await client.ExecuteCreateRefundDomesticRefundAsync(request);
if (response.IsSuccessful())
{
    Console.WriteLine("RefundId:", response.RefundId); // 【微信支付退款号】 微信支付退款号
    Console.WriteLine("OutRefundNumber:", response.OutRefundNumber); // 【商户退款单号】 商户系统内部的退款单号,商户系统内部唯一
    Console.WriteLine("TransactionId:", response.TransactionId); // 【微信支付订单号】
    Console.WriteLine("OutRefundNumber:", response.OutTradeNumber); // 【商户订单号】 原支付交易对应的商户订单号
}
else
{
    Console.WriteLine("HTTP 状态:" + response.GetRawStatus());
    Console.WriteLine("错误代码:" + response.ErrorCode);
    Console.WriteLine("错误描述:" + response.ErrorMessage);
}

2.3 退款状态查询接口

以下是退款状态查询接口的代码,入参实际上就只有一个:商户退款单号。文档中是要求直接跟在接口请求路径之后,SDK 中是封装到 Request 模型中。

官方文档:https://pay.weixin.qq.com/docs/merchant/apis/refund/refunds/query-by-out-refund-no.html

复制代码
var request = new GetRefundDomesticRefundByOutRefundNumberRequest()
{
    OutRefundNumber = "WX20240806104450972506",
};
//string json = Json_Object.ObjectToJsonstr(request);
var response = await client.ExecuteGetRefundDomesticRefundByOutRefundNumberAsync(request);
if (response.IsSuccessful())
{
    Console.WriteLine("RefundId:", response.RefundId); // 【微信支付退款号】 微信支付退款号
    Console.WriteLine("OutRefundNumber:", response.OutRefundNumber); // 【商户退款单号】 商户系统内部的退款单号,商户系统内部唯一
    Console.WriteLine("OutRefundNumber:", response.TransactionId); // 【微信支付订单号】
    Console.WriteLine("OutRefundNumber:", response.OutTradeNumber); // 【商户订单号】 原支付交易对应的商户订单号
}
else
{
    Console.WriteLine("HTTP 状态:" + response.GetRawStatus());
    Console.WriteLine("错误代码:" + response.ErrorCode);
    Console.WriteLine("错误描述:" + response.ErrorMessage);
}

2.4 接口的 Request、Response 模型的对应逻辑

下面列一下以上使用到的两个接口信息:

|----------|--------------------------------------------------|-------------------------------------------------|--------------------------------------------------|
| 接口名称 | 接口地址 | Request | Response |
| 退款 | 【POST】/v3/refund/domestic/refunds | CreateRefundDomesticRefundRequest | CreateRefundDomesticRefundResponse |
| 退款状态查询 | 【GET】/v3/refund/domestic/refunds/{out_refund_no} | GetRefundDomesticRefundByOutRefundNumberRequest | GetRefundDomesticRefundByOutRefundNumberResponse |

由上表对照可以看出,对应的模型实际上就是以接口地址为基础的,其他接口就可以按照这个逻辑去一一对应。

相关推荐
波波0077 小时前
每日一题:中间件是如何工作的?
中间件·.net·面试题
无风听海8 小时前
.NET 10之可空引用类型
数据结构·.net
码云数智-园园8 小时前
基于 JSON 配置的 .NET 桌面应用自动更新实现指南
.net
无风听海8 小时前
.NET 10 之dotnet run的功能
.net
岩屿8 小时前
Ubuntu下安装Docker并部署.NET API(二)
运维·docker·容器·.net
码云数智-大飞8 小时前
.NET 中高效实现 List 集合去重的多种方法详解
.net
easyboot8 小时前
使用tinyply.net保存ply格式点云
.net
张人玉9 小时前
WPF 多语言实现完整笔记(.NET 4.7.2)
笔记·.net·wpf·多语言实现·多语言适配
波波0071 天前
Native AOT 能改变什么?.NET 预编译技术深度剖析
开发语言·.net
Crazy Struggle2 天前
.NET 中如何快速实现 List 集合去重?
c#·.net