优化微信支付功能,BUG:1、前端创建定单界面错误 2、退款没有使用回调接口 3、退款记录的关键字段传值有误 Fix:增加事件接截器

This commit is contained in:
yzp 2025-02-25 10:19:01 +08:00
parent 1358cd397d
commit 1073066309
8 changed files with 494 additions and 93 deletions

View File

@ -1,4 +1,4 @@
{
{
"$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
"Wechat": {
@ -16,16 +16,16 @@
},
//
"WechatPay": {
"AppId": "", // AppIdAppIdAppIdCorpId
"MerchantId": "", //
"MerchantV3Secret": "", // APIv3
"MerchantCertificateSerialNumber": "", //
"AppId": "wxaaaaaaaaaaaaaa85", // AppIdAppIdAppIdCorpId
"MerchantId": "1500000001", //
"MerchantV3Secret": "3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaoo", // APIv3
"MerchantCertificateSerialNumber": "66aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0", //
"MerchantCertificatePrivateKey": "\\WxPayCert\\apiclient_key.pem" // API(apiclient_key.pem)
},
//
"PayCallBack": {
"WechatPayUrl": "https://xxx/api/sysWechatPay/payCallBack", //
"WechatRefundUrl": "", // 退
"WechatPayUrl": "https://ip/sysWechatPay/payCallBack", // : https://ip/sysWechatPay/payCallBack
"WechatRefundUrl": "https://ip/api/sysWechatPay/refundCallBack", // 退https://ip/api/sysWechatPay/refundCallBack
"AlipayUrl": "", //
"AlipayRefundUrl": "" // 退
}

View File

@ -33,7 +33,12 @@ public class SysWechatRefund : EntityBase
/// </summary>
[SugarColumn(ColumnDescription = "商户退款单号", Length = 64)]
[Required]
public string OutRefundNo { get; set; }
public string OutRefundNumber { get; set; }
/// <summary>
/// 微信接口退款ID
/// </summary>
public string RefundId { get; set; }
/// <summary>
/// 退款原因,示例:商品已售完
@ -75,19 +80,25 @@ public class SysWechatRefund : EntityBase
/// 关联的商户订单状态(或者为第几次支付,有些订单涉及多次支付,比如先付预付款,后补尾款)
/// </summary>
[SugarColumn(ColumnDescription = "关联的商户订单状态", Length = 32)]
public string? OrderStatus { get; set; }
public string? RefundStatus { get; set; }
/// <summary>
/// 支完成时间
/// </summary>
[SugarColumn(ColumnDescription = "完成时间")]
public DateTime? SuccessTime { get; set; }
/// <summary>
/// 关联的商户商品编码
/// </summary>
[SugarColumn(ColumnDescription = "关联的商户商品编码", Length = 32)]
public string MerchantGoodsId { get; set; }
public string? MerchantGoodsId { get; set; }
/// <summary>
/// 关联的商户商品名称
/// </summary>
[SugarColumn(ColumnDescription = "关联的商户商品名称", Length = 256)]
public string GoodsName { get; set; }
public string? GoodsName { get; set; }
/// <summary>
/// 关联的商户商品单价

View File

@ -140,13 +140,13 @@ public class PageSysWechatPayInput : BasePageInput
/// order_id
/// </summary>
/// <example></example>
public string? OrderId { get; set; } = "-1";
public string? OrderId { get; set; }
/// <summary>
/// order_status
/// </summary>
/// <example></example>
public string? OrderStatus { get; set; } = "-1";
public string? OrderStatus { get; set; }
/// <summary>
/// out_trade_number

View File

@ -15,6 +15,20 @@ namespace Admin.NET.Core.Service;
[ApiDescriptionSettings(Order = 210, Description = "微信支付")]
public class SysWechatPayService : IDynamicApiController, ITransient
{
private static List<WechatPayEventInterceptor> wechatPayEventHandlers = new List<WechatPayEventInterceptor>();
/// <summary>
/// 注册支付记录变化事件处理器
/// </summary>
/// <param name="eh">处理器</param>
/// <param name="order">排序,数据越大越先执行</param>
public static void AddPayEventInterceptor(WechatPayEventInterceptor eh, int order)
{
eh.Order = order;
wechatPayEventHandlers.Add(eh);
wechatPayEventHandlers.Sort((a, b) => b.Order - a.Order);
}
private readonly SqlSugarRepository<SysWechatPay> _sysWechatPayRep;
private readonly SqlSugarRepository<SysWechatRefund> _sysWechatRefundRep;
private readonly WechatPayOptions _wechatPayOptions;
@ -250,6 +264,82 @@ public class SysWechatPayService : IDynamicApiController, ITransient
return await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == tradeId);
}
/// <summary>
/// 获取支付订单详情(微信接口) 🔖
/// </summary>
/// <param name="tradeId"></param>
/// <returns></returns>
[DisplayName("获取支付订单详情(微信接口)")]
public async Task<SysWechatPay> GetPayInfoFromWechat(string tradeId)
{
var request = new GetPayTransactionByOutTradeNumberRequest();
request.OutTradeNumber = tradeId;
var response = await _wechatTenpayClient.ExecuteGetPayTransactionByOutTradeNumberAsync(request);
// 修改订单支付状态
var wechatPayOld = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == response.OutTradeNumber
&& u.MerchantId == response.MerchantId);
SysWechatPay wechatPayNew = null;
if (wechatPayOld != null)
wechatPayNew = wechatPayOld.DeepCopy();
// 如果状态不一致就更新数据库中的记录
if (wechatPayNew != null && wechatPayNew.TradeState != response.TradeState)
{
wechatPayNew.OpenId = response.Payer?.OpenId;
wechatPayNew.TransactionId = response.TransactionId; // 支付订单号
wechatPayNew.TradeType = response.TradeType; // 交易类型
wechatPayNew.TradeState = response.TradeState; // 交易状态
wechatPayNew.TradeStateDescription = response.TradeStateDescription; // 交易状态描述
wechatPayNew.BankType = response.BankType; // 付款银行类型
if (response.Amount != null)
{
wechatPayNew.Total = response.Amount.Total; // 订单总金额
wechatPayNew.PayerTotal = response.Amount.PayerTotal; // 用户支付金额
}
if (response.SuccessTime.HasValue)
wechatPayNew.SuccessTime = response.SuccessTime.Value.DateTime; // 支付完成时间
else
wechatPayNew.SuccessTime = DateTime.Now;
await _sysWechatPayRep.AsUpdateable(wechatPayNew).IgnoreColumns(true).ExecuteCommandAsync();
}
if (wechatPayOld == null || wechatPayOld.TradeState!=wechatPayNew.TradeState)
{
//没必要等所有回调事件处理完再返回给微信开一个Task执行
Task.Run(async () =>
{
foreach (var eh in wechatPayEventHandlers)
{
try
{
if (!await eh.PayInforChanged(wechatPayOld, wechatPayNew))
break;
}
catch (Exception ex)
{
$"GetPayInfoFromWechat 中执行微信支付回调{eh.GetType().Name}出错".LogError(ex);
}
}
}).Start();
}
// 下面这里创建一个新的对象,是因为不想把全部字段都返回
wechatPayNew = new SysWechatPay()
{
AppId = _wechatPayOptions.AppId,
MerchantId = _wechatPayOptions.MerchantId,
SubAppId = _wechatPayOptions.AppId,
SubMerchantId = _wechatPayOptions.MerchantId,
OutTradeNumber = request.OutTradeNumber,
Attachment = response.Attachment,
TransactionId = response.TransactionId,
TradeType = response.TradeType, // 交易类型
TradeState = response.TradeState, // 交易状态
TradeStateDescription = response.TradeStateDescription, // 交易状态描述
BankType = response.BankType, // 付款银行类型
SuccessTime = response.SuccessTime.HasValue ? response.SuccessTime.Value.DateTime : DateTime.Now // 支付完成时间
};
return wechatPayNew;
}
/// <summary>
/// 微信支付成功回调(商户直连) 🔖
/// </summary>
@ -269,35 +359,97 @@ public class SysWechatPayService : IDynamicApiController, ITransient
var callbackResource = _wechatTenpayClient.DecryptEventResource<TransactionResource>(callbackModel);
// 修改订单支付状态
var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == callbackResource.OutTradeNumber
var wechatPayOld = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == callbackResource.OutTradeNumber
&& u.MerchantId == callbackResource.MerchantId);
if (wechatPay == null) return null;
//wechatPay.OpenId = callbackResource.Payer.OpenId; // 支付者标识
//wechatPay.MerchantId = callbackResource.MerchantId; // 微信商户号
//wechatPay.OutTradeNumber = callbackResource.OutTradeNumber; // 商户订单号
wechatPay.TransactionId = callbackResource.TransactionId; // 支付订单号
wechatPay.TradeType = callbackResource.TradeType; // 交易类型
wechatPay.TradeState = callbackResource.TradeState; // 交易状态
wechatPay.TradeStateDescription = callbackResource.TradeStateDescription; // 交易状态描述
wechatPay.BankType = callbackResource.BankType; // 付款银行类型
wechatPay.Total = callbackResource.Amount.Total; // 订单总金额
wechatPay.PayerTotal = callbackResource.Amount.PayerTotal; // 用户支付金额
wechatPay.SuccessTime = callbackResource.SuccessTime; // 支付完成时间
await _sysWechatPayRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync();
if (wechatPayOld == null) return null;
var wechatPayNew = wechatPayOld.DeepCopy();
if (callbackResource.Payer!=null)
wechatPayNew.OpenId = callbackResource.Payer.OpenId; // 支付者标识
wechatPayNew.TransactionId = callbackResource.TransactionId; // 支付订单号
wechatPayNew.TradeType = callbackResource.TradeType; // 交易类型
wechatPayNew.TradeState = callbackResource.TradeState; // 交易状态
wechatPayNew.TradeStateDescription = callbackResource.TradeStateDescription; // 交易状态描述
wechatPayNew.BankType = callbackResource.BankType; // 付款银行类型
wechatPayNew.Total = callbackResource.Amount.Total; // 订单总金额
wechatPayNew.PayerTotal = callbackResource.Amount.PayerTotal; // 用户支付金额
wechatPayNew.SuccessTime = callbackResource.SuccessTime; // 支付完成时间
await _sysWechatPayRep.AsUpdateable(wechatPayNew).IgnoreColumns(true).ExecuteCommandAsync();
//因为这个是回调给微信的所以这里没必要等所有回调事件处理完再返回给微信开一个Task执行
if (wechatPayOld.TradeState != wechatPayNew.TradeState)
{
Task.Run(async () =>
{
foreach (var eh in wechatPayEventHandlers)
{
try
{
if (!await eh.PayInforChanged(wechatPayOld, wechatPayNew))
break;
}
catch (Exception ex)
{
$"PayCallBack 中执行微信支付回调{eh.GetType().Name}出错".LogError(ex);
}
}
}).Start();
}
return new WechatPayOutput()
{
Total = wechatPay.Total,
Attachment = wechatPay.Attachment,
GoodsTag = wechatPay.GoodsTag,
OrderId = long.Parse(wechatPay.OrderId)
Total = wechatPayNew.Total,
Attachment = wechatPayNew.Attachment,
GoodsTag = wechatPayNew.GoodsTag,
OrderId = long.Parse(wechatPayNew.OrderId)
};
}
return null;
}
/// <summary>
/// 微信退款回调(商户直连) 🔖
/// </summary>
/// <returns></returns>
[AllowAnonymous]
[DisplayName("微信退款回调(商户直连)")]
public async Task<WechatPayOutput> RefundCallBack()
{
using var ms = new MemoryStream();
await App.HttpContext.Request.Body.CopyToAsync(ms);
var b = ms.ToArray();
var callbackJson = Encoding.UTF8.GetString(b);
var callbackModel = _wechatTenpayClient.DeserializeEvent(callbackJson);
if ("REFUND.SUCCESS".Equals(callbackModel.EventType))
{
//参考https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/refund-result-notice.html
try
{
var callbackRefundResource = _wechatTenpayClient.DecryptEventResource<RefundResource>(callbackModel);
// 修改订单支付状态
var wechatRefund = await _sysWechatRefundRep.GetFirstAsync(u => u.OutRefundNumber == callbackRefundResource.OutRefundNumber);
if (wechatRefund == null) return null;
wechatRefund.RefundStatus = callbackRefundResource.RefundStatus; // 交易状态
wechatRefund.SuccessTime = callbackRefundResource.SuccessTime.Value.DateTime; // 支付完成时间
await _sysWechatRefundRep.AsUpdateable(wechatRefund).IgnoreColumns(true).ExecuteCommandAsync();
// 有退款,刷新一下订单状态(通过主动查询Wechat接口获取)
// 通过 GetPayInfoFromWechat 也会触发 WechatPayEventHandler所以这个回调中不用主动调用
await GetPayInfoFromWechat(callbackRefundResource.OutTradeNumber);
}
catch (Exception ex)
{
"微信退款回调时出错:".LogError(ex);
}
}
else
{
callbackModel.EventType.LogInformation();
}
return null;
}
/// <summary>
/// 微信支付成功回调(服务商模式) 🔖
/// </summary>
@ -317,22 +469,43 @@ public class SysWechatPayService : IDynamicApiController, ITransient
var callbackResource = _wechatTenpayClient.DecryptEventResource<PartnerTransactionResource>(callbackModel);
// 修改订单支付状态
var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == callbackResource.OutTradeNumber
var wechatPayOld = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == callbackResource.OutTradeNumber
&& u.MerchantId == callbackResource.MerchantId);
if (wechatPay == null) return;
if (wechatPayOld == null) return;
var wechatPayNew = wechatPayOld.DeepCopy();
//wechatPay.OpenId = callbackResource.Payer.OpenId; // 支付者标识
//wechatPay.MerchantId = callbackResource.MerchantId; // 微信商户号
//wechatPay.OutTradeNumber = callbackResource.OutTradeNumber; // 商户订单号
wechatPay.TransactionId = callbackResource.TransactionId; // 支付订单号
wechatPay.TradeType = callbackResource.TradeType; // 交易类型
wechatPay.TradeState = callbackResource.TradeState; // 交易状态
wechatPay.TradeStateDescription = callbackResource.TradeStateDescription; // 交易状态描述
wechatPay.BankType = callbackResource.BankType; // 付款银行类型
wechatPay.Total = callbackResource.Amount.Total; // 订单总金额
wechatPay.PayerTotal = callbackResource.Amount.PayerTotal; // 用户支付金额
wechatPay.SuccessTime = callbackResource.SuccessTime; // 支付完成时间
wechatPayNew.TransactionId = callbackResource.TransactionId; // 支付订单号
wechatPayNew.TradeType = callbackResource.TradeType; // 交易类型
wechatPayNew.TradeState = callbackResource.TradeState; // 交易状态
wechatPayNew.TradeStateDescription = callbackResource.TradeStateDescription; // 交易状态描述
wechatPayNew.BankType = callbackResource.BankType; // 付款银行类型
wechatPayNew.Total = callbackResource.Amount.Total; // 订单总金额
wechatPayNew.PayerTotal = callbackResource.Amount.PayerTotal; // 用户支付金额
wechatPayNew.SuccessTime = callbackResource.SuccessTime; // 支付完成时间
await _sysWechatPayRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync();
await _sysWechatPayRep.AsUpdateable(wechatPayNew).IgnoreColumns(true).ExecuteCommandAsync();
//因为这个是回调给微信的所以这里没必要等所有回调事件处理完再返回给微信开一个Task执行
if (wechatPayOld.TradeState != wechatPayNew.TradeState)
{
Task.Run(async () =>
{
foreach (var eh in wechatPayEventHandlers)
{
try
{
if (!await eh.PayInforChanged(wechatPayOld, wechatPayNew))
break;
}
catch (Exception ex)
{
$"PayPartnerCallBack 中执行微信支付回调{eh.GetType().Name}出错".LogError(ex);
}
}
}).Start();
}
}
}
@ -345,10 +518,16 @@ public class SysWechatPayService : IDynamicApiController, ITransient
[DisplayName("微信退款申请)")]
public async Task Refund(RefundRequestInput input)
{
var vechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == input.OutTradeNumber);
if (vechatPay==null)
{
throw Oops.Bah("没有相应支付记录");
}
var request = new CreateRefundDomesticRefundRequest()
{
OutTradeNumber = input.OutTradeNumber,
OutRefundNumber = "REFUND_" + DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"),
NotifyUrl = _payCallBackOptions.WechatRefundUrl,
Amount = new CreateRefundDomesticRefundRequest.Types.Amount()
{
Total = input.Total,
@ -359,7 +538,15 @@ public class SysWechatPayService : IDynamicApiController, ITransient
var response = await _wechatTenpayClient.ExecuteCreateRefundDomesticRefundAsync(request);
if (!response.IsSuccessful())
{
// 退款失败,该单可能已经退款了,所以主动查询微信接口更新状态
try
{
await this.GetPayInfoFromWechat(input.OutTradeNumber);
}
catch (Exception ex) { }
throw Oops.Oh<Exception>($"JSAPI 退款申请失败(状态码:{response.GetRawStatus()},错误代码:{response.ErrorCode},错误描述:{response.ErrorMessage}");
}
var wechatRefund = await _sysWechatRefundRep.GetFirstAsync(u => u.OutTradeNumber == input.OutTradeNumber);
if (wechatRefund == null)
@ -367,15 +554,16 @@ public class SysWechatPayService : IDynamicApiController, ITransient
// 保存退款申请信息
wechatRefund = new SysWechatRefund()
{
TransactionId = input.OutTradeNumber,
OutTradeNumber = request.OutTradeNumber,
OutRefundNo = request.OutTradeNumber, // 每笔付款只退一次,所以这里直接用付款单号
TransactionId = vechatPay.TransactionId,
OutTradeNumber = input.OutTradeNumber,
OutRefundNumber = request.OutRefundNumber, // 每笔付款只退一次,所以这里直接用付款单号
Reason = request.Reason,
Refund = input.Refund,
RefundId = response.RefundId,
Total = input.Total,
//NotifyUrl = "",
NotifyUrl = _payCallBackOptions.WechatRefundUrl,
OrderId = input.OrderId,
OrderStatus = input.OrderStatus,
RefundStatus = input.OrderStatus,
MerchantGoodsId = input.MerchantGoodsId,
GoodsName = input.GoodsName,
UnitPrice = input.UnitPrice,
@ -385,6 +573,8 @@ public class SysWechatPayService : IDynamicApiController, ITransient
Remark = input.Remark
};
await _sysWechatRefundRep.InsertAsync(wechatRefund);
// 发送了退款请求也要更新原定单的状态(从微信查询)
await this.GetPayInfoFromWechat(input.OutTradeNumber);
}
}
@ -530,10 +720,14 @@ public class SysWechatPayService : IDynamicApiController, ITransient
/// 根据支付Id获取退款信息列表 🔖
/// </summary>
/// <param name="transactionId"></param>
/// <param name="outTradeNumber"><</param>
/// <returns></returns>
[DisplayName("根据支付Id获取退款信息列表")]
public async Task<List<SysWechatRefund>> GetRefundList([FromQuery] string transactionId)
public async Task<List<SysWechatRefund>> GetRefundList([FromQuery] string transactionId, [FromQuery] string outTradeNumber )
{
return await _sysWechatRefundRep.AsQueryable().Where(u => u.TransactionId == transactionId).ToListAsync();
return await _sysWechatRefundRep.AsQueryable()
.WhereIF(!string.IsNullOrEmpty(transactionId), u => u.TransactionId == transactionId)
.WhereIF(!string.IsNullOrEmpty(outTradeNumber), u=> u.OutTradeNumber == outTradeNumber)
.ToListAsync();
}
}

View File

@ -253,6 +253,9 @@ public class Startup : AppStartup
// 注册启动执行任务
services.AddHostedService<StartHostedService>();
services.AddHostedService<MqttHostedService>();
// 下面代码演示,引入自己的微信息支付信息变化事件处理器
Admin.NET.Core.Service.SysWechatPayService.AddPayEventInterceptor(new WechatPayEventInterceptor(), int.MaxValue);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

View File

@ -174,6 +174,55 @@ export const SysWechatPayApiAxiosParamCreator = function (configuration?: Config
options: localVarRequestOptions,
};
},
/**
*
* @summary () 🔖
* @param {string} tradeId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
apiSysWechatPayPayInfoFromWechatTradeIdGet: async (tradeId: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'tradeId' is not null or undefined
if (tradeId === null || tradeId === undefined) {
throw new RequiredError('tradeId','Required parameter tradeId was null or undefined when calling apiSysWechatPayPayInfoFromWechatTradeIdGet.');
}
const localVarPath = `/api/sysWechatPay/payInfoFromWechat/{tradeId}`
.replace(`{${"tradeId"}}`, encodeURIComponent(String(tradeId)));
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication Bearer required
// http bearer authentication required
if (configuration && configuration.accessToken) {
const accessToken = typeof configuration.accessToken === 'function'
? await configuration.accessToken()
: await configuration.accessToken;
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
}
const query = new URLSearchParams(localVarUrlObj.search);
for (const key in localVarQueryParameter) {
query.set(key, localVarQueryParameter[key]);
}
for (const key in options.params) {
query.set(key, options.params[key]);
}
localVarUrlObj.search = (new URLSearchParams(query)).toString();
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
options: localVarRequestOptions,
};
},
/**
*
* @summary 🔖
@ -559,12 +608,56 @@ export const SysWechatPayApiAxiosParamCreator = function (configuration?: Config
},
/**
*
* @summary Id获取退款信息列表 🔖
* @param {string} [transactionId]
* @summary 退() 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
apiSysWechatPayRefundListGet: async (transactionId?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
apiSysWechatPayRefundCallBackPost: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/api/sysWechatPay/refundCallBack`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication Bearer required
// http bearer authentication required
if (configuration && configuration.accessToken) {
const accessToken = typeof configuration.accessToken === 'function'
? await configuration.accessToken()
: await configuration.accessToken;
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
}
const query = new URLSearchParams(localVarUrlObj.search);
for (const key in localVarQueryParameter) {
query.set(key, localVarQueryParameter[key]);
}
for (const key in options.params) {
query.set(key, options.params[key]);
}
localVarUrlObj.search = (new URLSearchParams(query)).toString();
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
options: localVarRequestOptions,
};
},
/**
*
* @summary Id获取退款信息列表
* @param {string} [transactionId]
* @param {string} [outTradeNumber]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
apiSysWechatPayRefundListGet: async (transactionId?: string, outTradeNumber?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/api/sysWechatPay/refundList`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
@ -589,6 +682,10 @@ export const SysWechatPayApiAxiosParamCreator = function (configuration?: Config
localVarQueryParameter['transactionId'] = transactionId;
}
if (outTradeNumber !== undefined) {
localVarQueryParameter['outTradeNumber'] = outTradeNumber;
}
const query = new URLSearchParams(localVarUrlObj.search);
for (const key in localVarQueryParameter) {
query.set(key, localVarQueryParameter[key]);
@ -703,6 +800,20 @@ export const SysWechatPayApiFp = function(configuration?: Configuration) {
return axios.request(axiosRequestArgs);
};
},
/**
*
* @summary () 🔖
* @param {string} tradeId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysWechatPayPayInfoFromWechatTradeIdGet(tradeId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultSysWechatPay>>> {
const localVarAxiosArgs = await SysWechatPayApiAxiosParamCreator(configuration).apiSysWechatPayPayInfoFromWechatTradeIdGet(tradeId, options);
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
return axios.request(axiosRequestArgs);
};
},
/**
*
* @summary 🔖
@ -816,13 +927,27 @@ export const SysWechatPayApiFp = function(configuration?: Configuration) {
},
/**
*
* @summary Id获取退款信息列表 🔖
* @param {string} [transactionId]
* @summary 退() 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysWechatPayRefundListGet(transactionId?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultListSysWechatRefund>>> {
const localVarAxiosArgs = await SysWechatPayApiAxiosParamCreator(configuration).apiSysWechatPayRefundListGet(transactionId, options);
async apiSysWechatPayRefundCallBackPost(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultWechatPayOutput>>> {
const localVarAxiosArgs = await SysWechatPayApiAxiosParamCreator(configuration).apiSysWechatPayRefundCallBackPost(options);
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
return axios.request(axiosRequestArgs);
};
},
/**
*
* @summary Id获取退款信息列表
* @param {string} [transactionId]
* @param {string} [outTradeNumber]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysWechatPayRefundListGet(transactionId?: string, outTradeNumber?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultListSysWechatRefund>>> {
const localVarAxiosArgs = await SysWechatPayApiAxiosParamCreator(configuration).apiSysWechatPayRefundListGet(transactionId, outTradeNumber, options);
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
return axios.request(axiosRequestArgs);
@ -880,6 +1005,16 @@ export const SysWechatPayApiFactory = function (configuration?: Configuration, b
async apiSysWechatPayPayCallBackPost(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultWechatPayOutput>> {
return SysWechatPayApiFp(configuration).apiSysWechatPayPayCallBackPost(options).then((request) => request(axios, basePath));
},
/**
*
* @summary () 🔖
* @param {string} tradeId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysWechatPayPayInfoFromWechatTradeIdGet(tradeId: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultSysWechatPay>> {
return SysWechatPayApiFp(configuration).apiSysWechatPayPayInfoFromWechatTradeIdGet(tradeId, options).then((request) => request(axios, basePath));
},
/**
*
* @summary 🔖
@ -961,13 +1096,23 @@ export const SysWechatPayApiFactory = function (configuration?: Configuration, b
},
/**
*
* @summary Id获取退款信息列表 🔖
* @param {string} [transactionId]
* @summary 退() 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysWechatPayRefundListGet(transactionId?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultListSysWechatRefund>> {
return SysWechatPayApiFp(configuration).apiSysWechatPayRefundListGet(transactionId, options).then((request) => request(axios, basePath));
async apiSysWechatPayRefundCallBackPost(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultWechatPayOutput>> {
return SysWechatPayApiFp(configuration).apiSysWechatPayRefundCallBackPost(options).then((request) => request(axios, basePath));
},
/**
*
* @summary Id获取退款信息列表
* @param {string} [transactionId]
* @param {string} [outTradeNumber]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysWechatPayRefundListGet(transactionId?: string, outTradeNumber?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultListSysWechatRefund>> {
return SysWechatPayApiFp(configuration).apiSysWechatPayRefundListGet(transactionId, outTradeNumber, options).then((request) => request(axios, basePath));
},
/**
*
@ -1021,6 +1166,17 @@ export class SysWechatPayApi extends BaseAPI {
public async apiSysWechatPayPayCallBackPost(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultWechatPayOutput>> {
return SysWechatPayApiFp(this.configuration).apiSysWechatPayPayCallBackPost(options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary () 🔖
* @param {string} tradeId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof SysWechatPayApi
*/
public async apiSysWechatPayPayInfoFromWechatTradeIdGet(tradeId: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultSysWechatPay>> {
return SysWechatPayApiFp(this.configuration).apiSysWechatPayPayInfoFromWechatTradeIdGet(tradeId, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary 🔖
@ -1110,14 +1266,25 @@ export class SysWechatPayApi extends BaseAPI {
}
/**
*
* @summary Id获取退款信息列表 🔖
* @param {string} [transactionId]
* @summary 退() 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof SysWechatPayApi
*/
public async apiSysWechatPayRefundListGet(transactionId?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultListSysWechatRefund>> {
return SysWechatPayApiFp(this.configuration).apiSysWechatPayRefundListGet(transactionId, options).then((request) => request(this.axios, this.basePath));
public async apiSysWechatPayRefundCallBackPost(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultWechatPayOutput>> {
return SysWechatPayApiFp(this.configuration).apiSysWechatPayRefundCallBackPost(options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary Id获取退款信息列表
* @param {string} [transactionId]
* @param {string} [outTradeNumber]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof SysWechatPayApi
*/
public async apiSysWechatPayRefundListGet(transactionId?: string, outTradeNumber?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultListSysWechatRefund>> {
return SysWechatPayApiFp(this.configuration).apiSysWechatPayRefundListGet(transactionId, outTradeNumber, options).then((request) => request(this.axios, this.basePath));
}
/**
*

View File

@ -106,7 +106,7 @@ export interface SysWechatRefund {
* @type {string}
* @memberof SysWechatRefund
*/
outRefundNo: string;
outRefundNumber: string;
/**
* 退
@ -162,7 +162,15 @@ export interface SysWechatRefund {
* @type {string}
* @memberof SysWechatRefund
*/
orderStatus?: string | null;
refundStatus?: string | null;
/**
*
*
* @type {Date}
* @memberof SysWechatRefund
*/
successTime?: Date | null;
/**
*

View File

@ -10,7 +10,7 @@
</el-form-item>
<el-form-item>
<el-button-group>
<el-button type="primary" icon="ele-Search" @click="handleQuery(true)"> 查询 </el-button>
<el-button type="primary" icon="ele-Search" @click="handleQuery()"> 查询 </el-button>
<el-button icon="ele-Refresh" @click="resetQuery"> 重置 </el-button>
</el-button-group>
</el-form-item>
@ -31,10 +31,12 @@
<template #default="scope">
<el-tag v-if="scope.row.tradeState == 'SUCCESS'" type="success"> 完成 </el-tag>
<el-tag v-else-if="scope.row.tradeState == 'REFUND'" type="danger"> 退款 </el-tag>
<el-tag v-else-if="scope.row.tradeState == 'NOTPAY'" type="warning"> 未支付 </el-tag>
<el-tag v-else type="info"> 未完成 </el-tag>
</template>
</el-table-column>
<el-table-column prop="attachment" label="附加信息" width="180"></el-table-column>
<el-table-column prop="goodsTag" label="GoodsTag" width="90"></el-table-column>
<el-table-column prop="tags" label="业务类型" width="90"></el-table-column>
<el-table-column prop="createTime" label="创建时间" width="150"></el-table-column>
<el-table-column prop="successTime" label="完成时间" width="150"></el-table-column>
@ -45,13 +47,14 @@
size="small"
text
type="primary"
v-if="scope.row.qrcodeContent != null && scope.row.qrcodeContent != '' && (scope.row.tradeState === '' || !scope.row.tradeState)"
v-if="scope.row.qrcodeContent != null && scope.row.qrcodeContent != '' && (scope.row.tradeState === '' || !scope.row.tradeState || scope.row.tradeState=='NOTPAY')"
@click="openQrDialog(scope.row.qrcodeContent)"
>
付款二维码
</el-button>
<el-button size="small" text type="primary" v-if="scope.row.tradeState === 'REFUND'" @click="openRefundDialog(scope.row.transactionId)">查看退款</el-button>
<el-button size="small" text type="primary" v-if="scope.row.tradeState === 'SUCCESS'" @click="doRefund(scope.row)">全额退款</el-button>
<el-button size="small" text type="primary" @click="doRefreshRecord(scope.row)">刷新</el-button>
</template>
</el-table-column>
</el-table>
@ -75,16 +78,19 @@
<span> 新增模拟数据 </span>
</div>
</template>
<el-form ref="ruleFormRef" label-width="auto">
<el-form ref="ruleFormRef" :model="addData" label-width="auto">
<el-form-item label="商品名称" prop="description" :rules="[{ required: true, message: '商品名称不能为空', trigger: 'blur' }]">
<el-input v-model="addData.description" placeholder="商品名称" clearable />
</el-form-item>
<el-form-item label="金额(分)" prop="total" :rules="[{ required: true, message: '商品名称不能为空', trigger: 'blur' }]">
<el-form-item label="金额(分)" prop="total" :rules="[{ required: true, message: '金额(分)不能为空', trigger: 'blur' }]">
<el-input v-model="addData.total" placeholder="填数字,单位是分" clearable />
</el-form-item>
<el-form-item label="附加信息">
<el-input v-model="addData.attachment" clearable />
</el-form-item>
<el-form-item label="GoodsTag">
<el-input v-model="addData.goodsTag" clearable />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
@ -113,14 +119,14 @@
</template>
<el-table :data="subTableData" style="width: 100%" tooltip-effect="light" row-key="id" border>
<el-table-column type="index" label="序号" width="55" align="center" />
<el-table-column prop="outRefundNumber" label="商户退款号" width="180"></el-table-column>
<el-table-column prop="outRefundNumber" label="商户退款号" width="210"></el-table-column>
<el-table-column prop="transactionId" label="支付订单号" width="220"></el-table-column>
<el-table-column prop="refund" label="金额(分)" width="70"></el-table-column>
<el-table-column prop="reason" label="退款原因" width="180"></el-table-column>
<el-table-column prop="tradeState" label="状态" width="70">
<el-table-column prop="refundStatus" label="状态" width="70">
<template #default="scope">
<el-tag v-if="scope.row.tradeState == 'SUCCESS'" type="success"> 完成 </el-tag>
<el-tag v-else-if="scope.row.tradeState == 'REFUND'" type="danger"> 退款 </el-tag>
<el-tag v-if="scope.row.refundStatus == 'SUCCESS'" type="success"> 完成 </el-tag>
<el-tag v-else-if="scope.row.refundStatus == 'REFUND'" type="danger"> 退款 </el-tag>
<el-tag v-else type="info"> 未完成 </el-tag>
</template>
</el-table-column>
@ -134,7 +140,7 @@
<script setup lang="ts" name="weChatPay">
import { ref, nextTick, onMounted, reactive } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import { ElMessageBox, ElMessage, ElForm } from 'element-plus';
import QRCode from 'qrcodejs2-fixes';
import { getAPI } from '/@/utils/axios-utils';
@ -148,24 +154,25 @@ const showRefundDialog = ref(false);
const subTableData = ref<any>([]);
const addData = ref<any>({});
const ruleFormRef = ref<InstanceType<typeof ElForm>>();
const state = reactive({
loading: false,
tableData: [] as Array<SysWechatPay>,
queryParams: {
keyword: undefined,
createTimeRange: undefined,
},
tableParams: {
page: 1,
pageSize: 50,
total: 0 as any,
},
loading: false,
tableData: [] as Array<SysWechatPay>,
queryParams: {
keyword: undefined,
createTimeRange: undefined,
},
tableParams: {
page: 1,
pageSize: 50,
total: 0 as any,
},
});
//
onMounted(async () => {
await handleQuery();
await handleQuery();
});
//
@ -192,6 +199,7 @@ const openAddDialog = () => {
description: null,
total: null,
attachment: null,
goodsTag: null,
};
showAddDialog.value = true;
};
@ -226,11 +234,15 @@ const openRefundDialog = async (transactionId: string) => {
//
const saveData = async () => {
var res = await getAPI(SysWechatPayApi).apiSysWechatPayPayTransactionNativePost(addData.value);
closeAddDialog();
let code = res.data.result?.qrcodeUrl;
openQrDialog(code == undefined ? '' : code);
await handleQuery();
ruleFormRef.value?.validate(async (valid) => {
if (valid) {
var res = await getAPI(SysWechatPayApi).apiSysWechatPayPayTransactionNativePost(addData.value);
closeAddDialog();
let code = res.data.result?.qrcodeUrl;
openQrDialog(code == undefined ? '' : code);
await handleQuery();
}
});
};
// 退
@ -253,6 +265,12 @@ const doRefund = async (orderInfo: any) => {
});
};
//
const doRefreshRecord = async (orderInfo: any) => {
await getAPI(SysWechatPayApi).apiSysWechatPayPayInfoFromWechatTradeIdGet(orderInfo.outTradeNumber);
await handleQuery();
}
//
const amountFormatter = (row: any, column: any, cellValue: number) => {
return (cellValue / 100).toFixed(2);