微信小程序接口对接
This commit is contained in:
parent
efabdebbd2
commit
d4d5c13a13
@ -392,18 +392,16 @@ public class SubmitRegistrationInput
|
|||||||
public string? TeamName { get; set; }
|
public string? TeamName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 联系人姓名
|
/// 联系人姓名 (个人报名时可自动从用户信息获取)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required(ErrorMessage = "联系人姓名不能为空")]
|
|
||||||
[MaxLength(50, ErrorMessage = "联系人姓名不能超过50个字符")]
|
[MaxLength(50, ErrorMessage = "联系人姓名不能超过50个字符")]
|
||||||
public string ContactName { get; set; }
|
public string? ContactName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 联系人电话
|
/// 联系人电话 (个人报名时可自动从用户信息获取)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required(ErrorMessage = "联系人电话不能为空")]
|
|
||||||
[RegularExpression(@"^1[3-9]\d{9}$", ErrorMessage = "请输入正确的手机号格式")]
|
[RegularExpression(@"^1[3-9]\d{9}$", ErrorMessage = "请输入正确的手机号格式")]
|
||||||
public string ContactPhone { get; set; }
|
public string? ContactPhone { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 报名项目列表
|
/// 报名项目列表
|
||||||
@ -436,11 +434,9 @@ public class SubmitRegistrationItemInput
|
|||||||
public long? GroupId { get; set; }
|
public long? GroupId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 参赛人员列表
|
/// 参赛人员列表 (个人报名时可自动从用户信息获取)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required(ErrorMessage = "参赛人员列表不能为空")]
|
public List<PersonInput>? Persons { get; set; }
|
||||||
[MinLength(1, ErrorMessage = "请至少添加一名参赛人员")]
|
|
||||||
public List<PersonInput> Persons { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
37
YD_Event.Application/Client/Dtos/WeChatDtos.cs
Normal file
37
YD_Event.Application/Client/Dtos/WeChatDtos.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using YD_Event.Core.Infrastructure.WeChat;
|
||||||
|
|
||||||
|
namespace YD_Event.Application.Client.Dtos;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 微信小程序 code2session 输入参数
|
||||||
|
/// </summary>
|
||||||
|
public class WeChatCode2SessionInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 登录时获取的 code
|
||||||
|
/// </summary>
|
||||||
|
[Required(ErrorMessage = "code 不能为空")]
|
||||||
|
public string Code { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 微信小程序 code2session 输出参数
|
||||||
|
/// </summary>
|
||||||
|
public class WeChatCode2SessionOutput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 用户唯一标识
|
||||||
|
/// </summary>
|
||||||
|
public string OpenId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 会话密钥
|
||||||
|
/// </summary>
|
||||||
|
public string SessionKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户在开放平台的唯一标识符(在满足 UnionID 下发条件的情况下会返回)
|
||||||
|
/// </summary>
|
||||||
|
public string? UnionId { get; set; }
|
||||||
|
}
|
||||||
@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
using Furion.FriendlyException;
|
using Furion.FriendlyException;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace YD_Event.Application.Client;
|
namespace YD_Event.Application.Client;
|
||||||
|
|
||||||
@ -27,6 +28,7 @@ public class EventClientController : IDynamicApiController
|
|||||||
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Project> _projectRepository;
|
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Project> _projectRepository;
|
||||||
private readonly ISqlSugarRepository<EventProject> _eventProjectRepository;
|
private readonly ISqlSugarRepository<EventProject> _eventProjectRepository;
|
||||||
private readonly ISqlSugarRepository<ProjectGroup> _groupRepository;
|
private readonly ISqlSugarRepository<ProjectGroup> _groupRepository;
|
||||||
|
private readonly ISqlSugarRepository<EventProjectGroup> _eventProjectGroupRepository;
|
||||||
private readonly ISqlSugarRepository<EventRegistration> _registrationRepository;
|
private readonly ISqlSugarRepository<EventRegistration> _registrationRepository;
|
||||||
private readonly ISqlSugarRepository<EventRegistrationItem> _registrationItemRepository;
|
private readonly ISqlSugarRepository<EventRegistrationItem> _registrationItemRepository;
|
||||||
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Banner> _bannerRepository;
|
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Banner> _bannerRepository;
|
||||||
@ -51,6 +53,7 @@ public class EventClientController : IDynamicApiController
|
|||||||
ISqlSugarRepository<YD_Event.Core.Entities.Project> projectRepository,
|
ISqlSugarRepository<YD_Event.Core.Entities.Project> projectRepository,
|
||||||
ISqlSugarRepository<EventProject> eventProjectRepository,
|
ISqlSugarRepository<EventProject> eventProjectRepository,
|
||||||
ISqlSugarRepository<ProjectGroup> groupRepository,
|
ISqlSugarRepository<ProjectGroup> groupRepository,
|
||||||
|
ISqlSugarRepository<EventProjectGroup> eventProjectGroupRepository,
|
||||||
ISqlSugarRepository<EventRegistration> registrationRepository,
|
ISqlSugarRepository<EventRegistration> registrationRepository,
|
||||||
ISqlSugarRepository<EventRegistrationItem> registrationItemRepository,
|
ISqlSugarRepository<EventRegistrationItem> registrationItemRepository,
|
||||||
ISqlSugarRepository<YD_Event.Core.Entities.Banner> bannerRepository,
|
ISqlSugarRepository<YD_Event.Core.Entities.Banner> bannerRepository,
|
||||||
@ -74,6 +77,7 @@ public class EventClientController : IDynamicApiController
|
|||||||
_projectRepository = projectRepository;
|
_projectRepository = projectRepository;
|
||||||
_eventProjectRepository = eventProjectRepository;
|
_eventProjectRepository = eventProjectRepository;
|
||||||
_groupRepository = groupRepository;
|
_groupRepository = groupRepository;
|
||||||
|
_eventProjectGroupRepository = eventProjectGroupRepository;
|
||||||
_registrationRepository = registrationRepository;
|
_registrationRepository = registrationRepository;
|
||||||
_registrationItemRepository = registrationItemRepository;
|
_registrationItemRepository = registrationItemRepository;
|
||||||
_bannerRepository = bannerRepository;
|
_bannerRepository = bannerRepository;
|
||||||
@ -444,12 +448,39 @@ public class EventClientController : IDynamicApiController
|
|||||||
if (DateTime.Now < evt.RegistrationStartTime || DateTime.Now > evt.RegistrationEndTime)
|
if (DateTime.Now < evt.RegistrationStartTime || DateTime.Now > evt.RegistrationEndTime)
|
||||||
throw Oops.Bah("不在报名时间内");
|
throw Oops.Bah("不在报名时间内");
|
||||||
|
|
||||||
// 2. 校验重复报名 (同一赛事同一人只能报一次?还是可以多次?通常一次报名包含多个项目)
|
// 2. 校验重复报名 - 如果已有未取消的报名记录,直接返回现有记录ID
|
||||||
// 假设同一用户对同一赛事只能有一个“未取消”的报名记录
|
var existingRegistration = await _registrationRepository.GetFirstAsync(
|
||||||
var exist = await _registrationRepository.IsAnyAsync(r => r.EventId == input.EventId && r.CreatedUserId == userId && r.RegistrationStatus != 4); // 4=已取消
|
r => r.EventId == input.EventId && r.CreatedUserId == userId && r.RegistrationStatus != 4); // 4=已取消
|
||||||
if (exist) throw Oops.Bah("您已报名该赛事,请勿重复提交");
|
|
||||||
|
if (existingRegistration != null)
|
||||||
|
{
|
||||||
|
// 已有未取消的报名记录,直接返回现有记录ID,让前端跳转到支付页面
|
||||||
|
return existingRegistration.Id;
|
||||||
|
}
|
||||||
|
|
||||||
// 3. 校验项目有效性 & 限制
|
// 3. 获取当前用户信息(用于个人报名时自动填充)
|
||||||
|
var appUser = await _appUserRepository.GetFirstAsync(u => u.CreatedUserId == userId && !u.DeleteMark);
|
||||||
|
if (appUser == null && input.RegistrationMode == 1)
|
||||||
|
throw Oops.Bah("请先完成身份认证");
|
||||||
|
|
||||||
|
// 3.5 个人报名模式下自动填充联系人信息
|
||||||
|
if (input.RegistrationMode == 1)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input.ContactName))
|
||||||
|
input.ContactName = appUser?.Name;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(input.ContactPhone))
|
||||||
|
input.ContactPhone = appUser?.Mobile;
|
||||||
|
|
||||||
|
// 验证必填联系人信息
|
||||||
|
if (string.IsNullOrWhiteSpace(input.ContactName))
|
||||||
|
throw Oops.Bah("联系人姓名不能为空,请先完善身份认证信息");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(input.ContactPhone))
|
||||||
|
throw Oops.Bah("联系人电话不能为空,请先完善身份认证信息");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 校验项目有效性 & 限制
|
||||||
if (input.Items == null || !input.Items.Any()) throw Oops.Bah("请至少选择一个项目");
|
if (input.Items == null || !input.Items.Any()) throw Oops.Bah("请至少选择一个项目");
|
||||||
|
|
||||||
var limit = await _limitRepository.GetFirstAsync(l => l.EventId == input.EventId && !l.DeleteMark);
|
var limit = await _limitRepository.GetFirstAsync(l => l.EventId == input.EventId && !l.DeleteMark);
|
||||||
@ -480,9 +511,32 @@ public class EventClientController : IDynamicApiController
|
|||||||
throw Oops.Bah($"项目[{project?.Name}]已达到报名上限,无法继续报名");
|
throw Oops.Bah($"项目[{project?.Name}]已达到报名上限,无法继续报名");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 个人报名模式下自动填充参赛人员信息
|
||||||
|
if (input.RegistrationMode == 1 && (item.Persons == null || !item.Persons.Any()))
|
||||||
|
{
|
||||||
|
if (appUser == null)
|
||||||
|
throw Oops.Bah("请先完成身份认证");
|
||||||
|
|
||||||
|
item.Persons = new List<PersonInput>
|
||||||
|
{
|
||||||
|
new PersonInput
|
||||||
|
{
|
||||||
|
Name = appUser.Name ?? "",
|
||||||
|
Gender = appUser.Gender,
|
||||||
|
IdCardNumber = appUser.IdCardNumber ?? "",
|
||||||
|
BirthDate = appUser.BirthDate ?? DateTime.Now,
|
||||||
|
Mobile = appUser.Mobile
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最终验证参赛人员信息
|
||||||
|
if (item.Persons == null || !item.Persons.Any())
|
||||||
|
throw Oops.Bah($"项目未填写参赛人信息");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 创建报名主表
|
// 5. 创建报名主表
|
||||||
var registrationId = _idGenerator.NewLong();
|
var registrationId = _idGenerator.NewLong();
|
||||||
var registration = new EventRegistration
|
var registration = new EventRegistration
|
||||||
{
|
{
|
||||||
@ -507,6 +561,11 @@ public class EventClientController : IDynamicApiController
|
|||||||
var regItems = new List<EventRegistrationItem>();
|
var regItems = new List<EventRegistrationItem>();
|
||||||
var persons = new List<EventRegistrationPerson>();
|
var persons = new List<EventRegistrationPerson>();
|
||||||
|
|
||||||
|
// 先获取所有需要的 eventProjectGroup 信息
|
||||||
|
var eventProjectIds = input.Items.Select(i => i.ProjectId).Distinct().ToList();
|
||||||
|
var allEventProjectGroups = await _eventProjectGroupRepository.GetListAsync(
|
||||||
|
eg => eventProjectIds.Contains(eg.EventProjectId) && !eg.DeleteMark);
|
||||||
|
|
||||||
foreach (var item in input.Items)
|
foreach (var item in input.Items)
|
||||||
{
|
{
|
||||||
var eventProject = await _eventProjectRepository.GetFirstAsync(p => p.Id == item.ProjectId);
|
var eventProject = await _eventProjectRepository.GetFirstAsync(p => p.Id == item.ProjectId);
|
||||||
@ -515,11 +574,37 @@ public class EventClientController : IDynamicApiController
|
|||||||
var project = await _projectRepository.GetFirstAsync(p => p.Id == eventProject.ProjectId);
|
var project = await _projectRepository.GetFirstAsync(p => p.Id == eventProject.ProjectId);
|
||||||
if (project == null) throw Oops.Bah($"关联基础项目不存在: {item.ProjectId}");
|
if (project == null) throw Oops.Bah($"关联基础项目不存在: {item.ProjectId}");
|
||||||
|
|
||||||
// 校验组别
|
// 获取项目类型名称
|
||||||
// 略,假设前端传参正确,或需根据 GroupId 查
|
var projectType = await _projectTypeRepository.GetFirstAsync(t => t.Id == project.ProjectTypeId);
|
||||||
|
var projectTypeName = projectType?.Name ?? "";
|
||||||
|
|
||||||
if (item.Persons == null || !item.Persons.Any())
|
// 获取组别名称 - 优先从前端传的获取,否则从数据库获取默认组别
|
||||||
throw Oops.Bah($"项目[{project.Name}]未填写参赛人信息");
|
var groupName = item.GroupName;
|
||||||
|
if (string.IsNullOrWhiteSpace(groupName))
|
||||||
|
{
|
||||||
|
var eventProjectGroups = allEventProjectGroups.Where(eg => eg.EventProjectId == item.ProjectId).ToList();
|
||||||
|
if (item.GroupId.HasValue)
|
||||||
|
{
|
||||||
|
// 如果传了 groupId,优先使用
|
||||||
|
var selectedGroup = eventProjectGroups.FirstOrDefault(eg => eg.Id == item.GroupId.Value);
|
||||||
|
groupName = selectedGroup?.GroupName ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果还是没有,使用第一个组别
|
||||||
|
if (string.IsNullOrWhiteSpace(groupName))
|
||||||
|
{
|
||||||
|
var firstGroup = eventProjectGroups.FirstOrDefault();
|
||||||
|
groupName = firstGroup?.GroupName ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果还是没有,设置默认值
|
||||||
|
if (string.IsNullOrWhiteSpace(groupName))
|
||||||
|
{
|
||||||
|
groupName = "默认组别";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 参赛人员已经在前面确保了存在,这里不再重复验证
|
||||||
|
|
||||||
var regItemId = _idGenerator.NewLong();
|
var regItemId = _idGenerator.NewLong();
|
||||||
var regItem = new EventRegistrationItem
|
var regItem = new EventRegistrationItem
|
||||||
@ -528,9 +613,10 @@ public class EventClientController : IDynamicApiController
|
|||||||
RegistrationId = registrationId,
|
RegistrationId = registrationId,
|
||||||
ProjectId = item.ProjectId,
|
ProjectId = item.ProjectId,
|
||||||
ProjectName = project.Name,
|
ProjectName = project.Name,
|
||||||
GroupName = item.GroupName,
|
ProjectTypeName = projectTypeName,
|
||||||
|
GroupName = groupName,
|
||||||
UnitPrice = eventProject.Price / 100m,
|
UnitPrice = eventProject.Price / 100m,
|
||||||
PersonCount = item.Persons.Count,
|
PersonCount = item.Persons!.Count,
|
||||||
TotalPrice = (eventProject.Price / 100m) * item.Persons.Count,
|
TotalPrice = (eventProject.Price / 100m) * item.Persons.Count,
|
||||||
CreatedUserId = userId,
|
CreatedUserId = userId,
|
||||||
CreatedTime = DateTime.Now
|
CreatedTime = DateTime.Now
|
||||||
@ -554,7 +640,7 @@ public class EventClientController : IDynamicApiController
|
|||||||
IdCard = pInput.IdCardNumber,
|
IdCard = pInput.IdCardNumber,
|
||||||
BirthDate = pInput.BirthDate,
|
BirthDate = pInput.BirthDate,
|
||||||
Phone = pInput.Mobile,
|
Phone = pInput.Mobile,
|
||||||
GroupName = item.GroupName,
|
GroupName = groupName,
|
||||||
ProjectName = project.Name,
|
ProjectName = project.Name,
|
||||||
CreatedUserId = userId,
|
CreatedUserId = userId,
|
||||||
CreatedTime = DateTime.Now
|
CreatedTime = DateTime.Now
|
||||||
@ -607,66 +693,86 @@ public class EventClientController : IDynamicApiController
|
|||||||
|
|
||||||
if (reg.PaymentMethod == 2 && reg.TotalAmount > 0)
|
if (reg.PaymentMethod == 2 && reg.TotalAmount > 0)
|
||||||
{
|
{
|
||||||
WeChatPayConfig? payConfig = null;
|
// 根据赛事信息获取对应的商户支付配置
|
||||||
if (evt.MerchantId.HasValue)
|
var payConfig = await GetWeChatPayConfigByEventAsync(evt);
|
||||||
{
|
|
||||||
payConfig = await _weChatPayConfigRepository.GetFirstAsync(x =>
|
|
||||||
x.MerchantId == evt.MerchantId.Value &&
|
|
||||||
x.Status == AvailabilityStatus.Enable &&
|
|
||||||
!x.DeleteMark);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payConfig == null)
|
if (payConfig == null)
|
||||||
{
|
{
|
||||||
payConfig = await _weChatPayConfigRepository.GetFirstAsync(x =>
|
_logger.LogError("未找到可用的微信支付配置,赛事ID={EventId},商户ID={MerchantId}", evt.Id, evt.MerchantId);
|
||||||
x.Status == AvailabilityStatus.Enable && !x.DeleteMark);
|
throw Oops.Bah("未找到可用的微信支付配置,请联系赛事管理员");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payConfig != null)
|
_logger.LogInformation("开始创建微信 JSAPI 支付订单,订单号={OrderNo},OpenId={OpenId},商户ID={MerchantId}",
|
||||||
{
|
reg.OrderNo, input.OpenId, payConfig.MerchantId);
|
||||||
_logger.LogInformation("开始创建微信 JSAPI 支付订单,订单号={OrderNo},OpenId={OpenId}", reg.OrderNo, input.OpenId);
|
|
||||||
|
|
||||||
var jsapiRequest = new CreateJsApiPayRequest
|
var jsapiRequest = new CreateJsApiPayRequest
|
||||||
{
|
{
|
||||||
OutTradeNo = reg.OrderNo,
|
OutTradeNo = reg.OrderNo,
|
||||||
AppId = configOptions.AppId,
|
AppId = configOptions.AppId,
|
||||||
Description = $"{evt.EventName} - 报名费",
|
Description = $"{evt.EventName} - 报名费",
|
||||||
NotifyUrl = configOptions.NotifyUrl,
|
NotifyUrl = configOptions.NotifyUrl,
|
||||||
TotalAmount = (int)(reg.TotalAmount * 100),
|
TotalAmount = (int)(reg.TotalAmount * 100),
|
||||||
ExpireTime = DateTimeOffset.Now.AddMinutes(30),
|
ExpireTime = DateTimeOffset.Now.AddMinutes(30),
|
||||||
OpenId = input.OpenId
|
OpenId = input.OpenId
|
||||||
};
|
};
|
||||||
|
|
||||||
var payResult = await _weChatPayService.CreateJsApiPayAsync(payConfig, jsapiRequest);
|
var payResult = await _weChatPayService.CreateJsApiPayAsync(payConfig, jsapiRequest);
|
||||||
if (payResult.Success && payResult.JsApiParameters != null)
|
if (payResult.Success && payResult.JsApiParameters != null)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("微信 JSAPI 支付下单成功,订单号={OrderNo}", reg.OrderNo);
|
||||||
|
paymentInfo.WechatPayParams = new
|
||||||
{
|
{
|
||||||
_logger.LogInformation("微信 JSAPI 支付下单成功,订单号={OrderNo}", reg.OrderNo);
|
appId = payResult.JsApiParameters.AppId,
|
||||||
paymentInfo.WechatPayParams = new
|
timeStamp = payResult.JsApiParameters.TimeStamp,
|
||||||
{
|
nonceStr = payResult.JsApiParameters.NonceStr,
|
||||||
appId = payResult.JsApiParameters.AppId,
|
package = payResult.JsApiParameters.Package,
|
||||||
timeStamp = payResult.JsApiParameters.TimeStamp,
|
signType = payResult.JsApiParameters.SignType,
|
||||||
nonceStr = payResult.JsApiParameters.NonceStr,
|
paySign = payResult.JsApiParameters.PaySign
|
||||||
package = payResult.JsApiParameters.Package,
|
};
|
||||||
signType = payResult.JsApiParameters.SignType,
|
|
||||||
paySign = payResult.JsApiParameters.PaySign
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogError("微信 JSAPI 支付下单失败:订单号={OrderNo},错误信息={Message}", reg.OrderNo, payResult.Message);
|
|
||||||
throw Oops.Bah($"微信支付下单失败:{payResult.Message}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogError("未找到可用的微信支付配置,赛事ID={EventId}", evt.Id);
|
_logger.LogError("微信 JSAPI 支付下单失败:订单号={OrderNo},错误信息={Message}", reg.OrderNo, payResult.Message);
|
||||||
throw Oops.Bah("未找到可用的微信支付配置");
|
throw Oops.Bah($"微信支付下单失败:{payResult.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return paymentInfo;
|
return paymentInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据赛事ID精确获取对应的微信支付配置
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 1. 通过赛事信息中的 MerchantId 查找对应的微信支付配置
|
||||||
|
/// 2. 必须确保赛事已关联商户配置,不使用兜底策略
|
||||||
|
/// 3. 只返回启用状态且未删除的配置
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="eventInfo">赛事信息,必须包含有效的 MerchantId</param>
|
||||||
|
/// <returns>微信支付配置,如果未找到则返回 null</returns>
|
||||||
|
/// <exception cref="ArgumentException">当赛事没有关联商户配置时抛出</exception>
|
||||||
|
private async Task<WeChatPayConfig?> GetWeChatPayConfigByEventAsync(EventInfo eventInfo)
|
||||||
|
{
|
||||||
|
if (!eventInfo.MerchantId.HasValue)
|
||||||
|
{
|
||||||
|
_logger.LogError("赛事未关联商户配置,赛事ID={EventId},赛事名称={EventName}",
|
||||||
|
eventInfo.Id, eventInfo.EventName);
|
||||||
|
throw new ArgumentException($"赛事[{eventInfo.EventName}]未关联商户配置,请联系管理员配置", nameof(eventInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
var payConfig = await _weChatPayConfigRepository.GetFirstAsync(x =>
|
||||||
|
x.MerchantId == eventInfo.MerchantId.Value &&
|
||||||
|
x.Status == AvailabilityStatus.Enable &&
|
||||||
|
!x.DeleteMark);
|
||||||
|
|
||||||
|
if (payConfig == null)
|
||||||
|
{
|
||||||
|
_logger.LogError("未找到赛事关联的商户支付配置,赛事ID={EventId},商户ID={MerchantId}",
|
||||||
|
eventInfo.Id, eventInfo.MerchantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return payConfig;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上传支付凭证(线下支付)
|
/// 上传支付凭证(线下支付)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -927,23 +1033,14 @@ public class EventClientController : IDynamicApiController
|
|||||||
var evt = await _eventRepository.GetFirstAsync(e => e.Id == reg.EventId);
|
var evt = await _eventRepository.GetFirstAsync(e => e.Id == reg.EventId);
|
||||||
if (evt == null) throw Oops.Bah("赛事不存在");
|
if (evt == null) throw Oops.Bah("赛事不存在");
|
||||||
|
|
||||||
WeChatPayConfig? payConfig = null;
|
// 根据赛事信息获取对应的商户支付配置
|
||||||
if (evt.MerchantId.HasValue)
|
var payConfig = await GetWeChatPayConfigByEventAsync(evt);
|
||||||
{
|
|
||||||
payConfig = await _weChatPayConfigRepository.GetFirstAsync(x =>
|
|
||||||
x.MerchantId == evt.MerchantId.Value &&
|
|
||||||
x.Status == AvailabilityStatus.Enable &&
|
|
||||||
!x.DeleteMark);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payConfig == null)
|
if (payConfig == null)
|
||||||
{
|
{
|
||||||
payConfig = await _weChatPayConfigRepository.GetFirstAsync(x =>
|
_logger.LogError("未找到可用的微信支付配置,赛事ID={EventId},商户ID={MerchantId}", evt.Id, evt.MerchantId);
|
||||||
x.Status == AvailabilityStatus.Enable && !x.DeleteMark);
|
throw Oops.Bah("未找到可用的微信支付配置,请联系赛事管理员");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payConfig == null) throw Oops.Bah("微信支付配置未设置");
|
|
||||||
|
|
||||||
var queryResult = await _weChatPayService.QueryOrderAsync(payConfig, reg.OrderNo);
|
var queryResult = await _weChatPayService.QueryOrderAsync(payConfig, reg.OrderNo);
|
||||||
if (queryResult.Success && queryResult.TradeState == "SUCCESS")
|
if (queryResult.Success && queryResult.TradeState == "SUCCESS")
|
||||||
{
|
{
|
||||||
@ -966,11 +1063,21 @@ public class EventClientController : IDynamicApiController
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 微信支付回调通知处理
|
/// 微信支付回调通知处理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 处理流程:
|
||||||
|
/// 1. 接收微信支付平台回调通知
|
||||||
|
/// 2. 使用平台级别的支付配置验证签名和解密数据(微信平台证书是平台级别)
|
||||||
|
/// 3. 获取订单号后查找报名记录
|
||||||
|
/// 4. 根据报名记录获取赛事信息
|
||||||
|
/// 5. 根据赛事信息获取商户级别的支付配置(用于后续可能的退款等操作)
|
||||||
|
/// 6. 更新订单支付状态
|
||||||
|
/// </remarks>
|
||||||
|
/// <returns>微信支付平台要求的响应格式</returns>
|
||||||
[HttpPost("wechat-pay-notify")]
|
[HttpPost("wechat-pay-notify")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
[NonAction]
|
[DisplayName("微信支付回调通知")]
|
||||||
public async Task<object> WeChatPayNotify()
|
public async Task<IActionResult> WeChatPayNotify()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -987,16 +1094,17 @@ public class EventClientController : IDynamicApiController
|
|||||||
string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(serialNumber))
|
string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(serialNumber))
|
||||||
{
|
{
|
||||||
_logger.LogWarning("微信支付回调通知参数不完整");
|
_logger.LogWarning("微信支付回调通知参数不完整");
|
||||||
return new { code = "FAIL", message = "参数不完整" };
|
return new JsonResult(new { code = "FAIL", message = "参数不完整" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取默认支付配置(后续可根据订单号找到对应的商户配置)
|
// 第一步:使用平台级别的支付配置验证签名和解密数据
|
||||||
var payConfig = await _weChatPayConfigRepository.GetFirstAsync(x =>
|
// 注意:微信支付签名验证使用的是微信平台证书(平台级别),所有商户共用
|
||||||
|
var platformPayConfig = await _weChatPayConfigRepository.GetFirstAsync(x =>
|
||||||
x.Status == AvailabilityStatus.Enable && !x.DeleteMark);
|
x.Status == AvailabilityStatus.Enable && !x.DeleteMark);
|
||||||
if (payConfig == null)
|
if (platformPayConfig == null)
|
||||||
{
|
{
|
||||||
_logger.LogError("微信支付配置未找到");
|
_logger.LogError("未找到平台级别的微信支付配置");
|
||||||
return new { code = "FAIL", message = "支付配置未找到" };
|
return new JsonResult(new { code = "FAIL", message = "支付配置未找到" });
|
||||||
}
|
}
|
||||||
|
|
||||||
var notifyRequest = new WeChatPayNotifyRequest
|
var notifyRequest = new WeChatPayNotifyRequest
|
||||||
@ -1009,19 +1117,19 @@ public class EventClientController : IDynamicApiController
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 验证签名
|
// 验证签名
|
||||||
var isValid = await _weChatPayService.VerifyPayNotifyAsync(payConfig, notifyRequest);
|
var isValid = await _weChatPayService.VerifyPayNotifyAsync(platformPayConfig, notifyRequest);
|
||||||
if (!isValid)
|
if (!isValid)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("微信支付回调签名验证失败");
|
_logger.LogWarning("微信支付回调签名验证失败");
|
||||||
return new { code = "FAIL", message = "签名验证失败" };
|
return new JsonResult(new { code = "FAIL", message = "签名验证失败" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解密通知数据
|
// 解密通知数据
|
||||||
var notifyData = await _weChatPayService.DecryptPayNotifyDataAsync(payConfig, notifyRequest);
|
var notifyData = await _weChatPayService.DecryptPayNotifyDataAsync(platformPayConfig, notifyRequest);
|
||||||
if (notifyData == null || notifyData.TradeState != "SUCCESS")
|
if (notifyData == null || notifyData.TradeState != "SUCCESS")
|
||||||
{
|
{
|
||||||
_logger.LogWarning("微信支付回调数据无效或交易未成功");
|
_logger.LogWarning("微信支付回调数据无效或交易未成功");
|
||||||
return new { code = "SUCCESS", message = "成功" };
|
return new JsonResult(new { code = "SUCCESS", message = "成功" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据商户订单号查找报名记录
|
// 根据商户订单号查找报名记录
|
||||||
@ -1029,15 +1137,35 @@ public class EventClientController : IDynamicApiController
|
|||||||
if (reg == null)
|
if (reg == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning($"未找到订单:{notifyData.OutTradeNo}");
|
_logger.LogWarning($"未找到订单:{notifyData.OutTradeNo}");
|
||||||
return new { code = "SUCCESS", message = "成功" };
|
return new JsonResult(new { code = "SUCCESS", message = "成功" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reg.PaymentStatus == 1)
|
if (reg.PaymentStatus == 1)
|
||||||
{
|
{
|
||||||
_logger.LogInformation($"订单 {reg.OrderNo} 已处理过支付回调");
|
_logger.LogInformation($"订单 {reg.OrderNo} 已处理过支付回调");
|
||||||
return new { code = "SUCCESS", message = "成功" };
|
return new JsonResult(new { code = "SUCCESS", message = "成功" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 第二步:获取赛事信息并验证商户配置
|
||||||
|
var evt = await _eventRepository.GetFirstAsync(e => e.Id == reg.EventId);
|
||||||
|
if (evt == null)
|
||||||
|
{
|
||||||
|
_logger.LogError($"未找到赛事信息,订单号:{reg.OrderNo},赛事ID:{reg.EventId}");
|
||||||
|
return new JsonResult(new { code = "FAIL", message = "赛事信息不存在" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证赛事是否关联了有效的商户支付配置
|
||||||
|
var merchantPayConfig = await GetWeChatPayConfigByEventAsync(evt);
|
||||||
|
if (merchantPayConfig == null)
|
||||||
|
{
|
||||||
|
_logger.LogError("赛事未配置有效的商户支付配置,订单号={OrderNo},赛事ID={EventId}",
|
||||||
|
reg.OrderNo, evt.Id);
|
||||||
|
return new JsonResult(new { code = "FAIL", message = "商户支付配置异常" });
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("订单支付成功,订单号={OrderNo},赛事ID={EventId},商户ID={MerchantId}",
|
||||||
|
reg.OrderNo, evt.Id, evt.MerchantId);
|
||||||
|
|
||||||
// 更新支付状态
|
// 更新支付状态
|
||||||
reg.PaymentStatus = 1;
|
reg.PaymentStatus = 1;
|
||||||
reg.PaymentTime = notifyData.SuccessTime?.DateTime;
|
reg.PaymentTime = notifyData.SuccessTime?.DateTime;
|
||||||
@ -1048,12 +1176,17 @@ public class EventClientController : IDynamicApiController
|
|||||||
await _registrationRepository.UpdateAsync(reg);
|
await _registrationRepository.UpdateAsync(reg);
|
||||||
_logger.LogInformation($"订单 {reg.OrderNo} 支付成功,微信交易号:{notifyData.TransactionId}");
|
_logger.LogInformation($"订单 {reg.OrderNo} 支付成功,微信交易号:{notifyData.TransactionId}");
|
||||||
|
|
||||||
return new { code = "SUCCESS", message = "成功" };
|
return new JsonResult(new { code = "SUCCESS", message = "成功" });
|
||||||
|
}
|
||||||
|
catch (ArgumentException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "处理微信支付回调通知时参数验证失败");
|
||||||
|
return new JsonResult(new { code = "FAIL", message = "业务验证失败" });
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "处理微信支付回调通知异常");
|
_logger.LogError(ex, "处理微信支付回调通知异常");
|
||||||
return new { code = "FAIL", message = "处理失败" };
|
return new JsonResult(new { code = "FAIL", message = "处理失败" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
48
YD_Event.Application/Client/WeChatController.cs
Normal file
48
YD_Event.Application/Client/WeChatController.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using YD_Event.Application.Client.Dtos;
|
||||||
|
using YD_Event.Core.Infrastructure.WeChat;
|
||||||
|
|
||||||
|
namespace YD_Event.Application.Client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 微信小程序接口
|
||||||
|
/// </summary>
|
||||||
|
[ApiDescriptionSettings("微信小程序接口")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public class WeChatController : IDynamicApiController
|
||||||
|
{
|
||||||
|
private readonly WeChatService _weChatService;
|
||||||
|
private readonly ILogger<WeChatController>_logger;
|
||||||
|
|
||||||
|
public WeChatController(
|
||||||
|
WeChatService weChatService,
|
||||||
|
ILogger<WeChatController>logger)
|
||||||
|
{
|
||||||
|
_weChatService = weChatService;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 小程序 code 换取 openid 和 session_key
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">包含 code 的输入参数</param>
|
||||||
|
/// <returns>openid、session_key 等信息</returns>
|
||||||
|
[HttpPost("jscode2session")]
|
||||||
|
[DisplayName("小程序 code 换取 openid")]
|
||||||
|
public async Task<WeChatCode2SessionOutput> Code2Session([FromBody] WeChatCode2SessionInput input)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("开始调用微信 code2session 接口");
|
||||||
|
|
||||||
|
var result = await _weChatService.Code2Session(input.Code);
|
||||||
|
|
||||||
|
_logger.LogInformation("微信 code2session 接口调用成功,OpenId={OpenId}", result.OpenId);
|
||||||
|
|
||||||
|
return new WeChatCode2SessionOutput
|
||||||
|
{
|
||||||
|
OpenId = result.OpenId,
|
||||||
|
SessionKey = result.SessionKey,
|
||||||
|
UnionId = result.UnionId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -473,6 +473,11 @@ public class EventFeeConfigInput
|
|||||||
/// 营业执照图片URL (显示字段)
|
/// 营业执照图片URL (显示字段)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? BusinessLicenseUrl { get; set; }
|
public string? BusinessLicenseUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 项目价格字典 (key: 项目ID, value: 价格)
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<long, decimal>? ProjectPrices { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -264,6 +264,16 @@ public class EventService : BaseService<EventInfo>, ITransient
|
|||||||
output.FeeConfig.BusinessLicenseUrl = merchant.BusinessLicenseUrl;
|
output.FeeConfig.BusinessLicenseUrl = merchant.BusinessLicenseUrl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从 EventProject 表中读取项目价格并填充到 ProjectPrices
|
||||||
|
if (eventProjects.Any())
|
||||||
|
{
|
||||||
|
output.FeeConfig.ProjectPrices = new Dictionary<long, decimal>();
|
||||||
|
foreach (var ep in eventProjects)
|
||||||
|
{
|
||||||
|
output.FeeConfig.ProjectPrices[ep.Id] = ep.Price / 100m;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
|||||||
@ -437,12 +437,6 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
|
|||||||
|
|
||||||
var eventInfo = await _eventRepository.GetByIdAsync(input.EventId);
|
var eventInfo = await _eventRepository.GetByIdAsync(input.EventId);
|
||||||
if (eventInfo == null) throw Oops.Bah("赛事不存在");
|
if (eventInfo == null) throw Oops.Bah("赛事不存在");
|
||||||
|
|
||||||
// 验证1:一次报名只能选择一个项目
|
|
||||||
if (input.ProjectIds != null && input.ProjectIds.Count > 1)
|
|
||||||
{
|
|
||||||
throw Oops.Bah("一次报名只能选择一个项目");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证2:一次报名只能选择单个团队内的运动员
|
// 验证2:一次报名只能选择单个团队内的运动员
|
||||||
if (input.MemberIds != null && input.MemberIds.Count > 0)
|
if (input.MemberIds != null && input.MemberIds.Count > 0)
|
||||||
|
|||||||
@ -1958,12 +1958,12 @@
|
|||||||
</member>
|
</member>
|
||||||
<member name="P:YD_Event.Application.Client.Dtos.SubmitRegistrationInput.ContactName">
|
<member name="P:YD_Event.Application.Client.Dtos.SubmitRegistrationInput.ContactName">
|
||||||
<summary>
|
<summary>
|
||||||
联系人姓名
|
联系人姓名 (个人报名时可自动从用户信息获取)
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="P:YD_Event.Application.Client.Dtos.SubmitRegistrationInput.ContactPhone">
|
<member name="P:YD_Event.Application.Client.Dtos.SubmitRegistrationInput.ContactPhone">
|
||||||
<summary>
|
<summary>
|
||||||
联系人电话
|
联系人电话 (个人报名时可自动从用户信息获取)
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="P:YD_Event.Application.Client.Dtos.SubmitRegistrationInput.Items">
|
<member name="P:YD_Event.Application.Client.Dtos.SubmitRegistrationInput.Items">
|
||||||
@ -1993,7 +1993,7 @@
|
|||||||
</member>
|
</member>
|
||||||
<member name="P:YD_Event.Application.Client.Dtos.SubmitRegistrationItemInput.Persons">
|
<member name="P:YD_Event.Application.Client.Dtos.SubmitRegistrationItemInput.Persons">
|
||||||
<summary>
|
<summary>
|
||||||
参赛人员列表
|
参赛人员列表 (个人报名时可自动从用户信息获取)
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="T:YD_Event.Application.Client.Dtos.PersonInput">
|
<member name="T:YD_Event.Application.Client.Dtos.PersonInput">
|
||||||
@ -3029,6 +3029,36 @@
|
|||||||
报名项目集合
|
报名项目集合
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="T:YD_Event.Application.Client.Dtos.WeChatCode2SessionInput">
|
||||||
|
<summary>
|
||||||
|
微信小程序 code2session 输入参数
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:YD_Event.Application.Client.Dtos.WeChatCode2SessionInput.Code">
|
||||||
|
<summary>
|
||||||
|
登录时获取的 code
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:YD_Event.Application.Client.Dtos.WeChatCode2SessionOutput">
|
||||||
|
<summary>
|
||||||
|
微信小程序 code2session 输出参数
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:YD_Event.Application.Client.Dtos.WeChatCode2SessionOutput.OpenId">
|
||||||
|
<summary>
|
||||||
|
用户唯一标识
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:YD_Event.Application.Client.Dtos.WeChatCode2SessionOutput.SessionKey">
|
||||||
|
<summary>
|
||||||
|
会话密钥
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:YD_Event.Application.Client.Dtos.WeChatCode2SessionOutput.UnionId">
|
||||||
|
<summary>
|
||||||
|
用户在开放平台的唯一标识符(在满足 UnionID 下发条件的情况下会返回)
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="T:YD_Event.Application.Client.EventClientController">
|
<member name="T:YD_Event.Application.Client.EventClientController">
|
||||||
<summary>
|
<summary>
|
||||||
小程序-赛事接口
|
小程序-赛事接口
|
||||||
@ -3079,6 +3109,19 @@
|
|||||||
<param name="input">支付信息查询输入参数,包含报名记录ID和OpenId</param>
|
<param name="input">支付信息查询输入参数,包含报名记录ID和OpenId</param>
|
||||||
<returns>支付信息,包括微信支付参数(如果是微信支付)</returns>
|
<returns>支付信息,包括微信支付参数(如果是微信支付)</returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:YD_Event.Application.Client.EventClientController.GetWeChatPayConfigByEventAsync(YD_Event.Core.Entities.EventInfo)">
|
||||||
|
<summary>
|
||||||
|
根据赛事ID精确获取对应的微信支付配置
|
||||||
|
</summary>
|
||||||
|
<remarks>
|
||||||
|
1. 通过赛事信息中的 MerchantId 查找对应的微信支付配置
|
||||||
|
2. 必须确保赛事已关联商户配置,不使用兜底策略
|
||||||
|
3. 只返回启用状态且未删除的配置
|
||||||
|
</remarks>
|
||||||
|
<param name="eventInfo">赛事信息,必须包含有效的 MerchantId</param>
|
||||||
|
<returns>微信支付配置,如果未找到则返回 null</returns>
|
||||||
|
<exception cref="T:System.ArgumentException">当赛事没有关联商户配置时抛出</exception>
|
||||||
|
</member>
|
||||||
<member name="M:YD_Event.Application.Client.EventClientController.UploadPaymentVoucher(System.Int64,YD_Event.Application.Client.Dtos.UploadVoucherInput)">
|
<member name="M:YD_Event.Application.Client.EventClientController.UploadPaymentVoucher(System.Int64,YD_Event.Application.Client.Dtos.UploadVoucherInput)">
|
||||||
<summary>
|
<summary>
|
||||||
上传支付凭证(线下支付)
|
上传支付凭证(线下支付)
|
||||||
@ -3135,6 +3178,16 @@
|
|||||||
<summary>
|
<summary>
|
||||||
微信支付回调通知处理
|
微信支付回调通知处理
|
||||||
</summary>
|
</summary>
|
||||||
|
<remarks>
|
||||||
|
处理流程:
|
||||||
|
1. 接收微信支付平台回调通知
|
||||||
|
2. 使用平台级别的支付配置验证签名和解密数据(微信平台证书是平台级别)
|
||||||
|
3. 获取订单号后查找报名记录
|
||||||
|
4. 根据报名记录获取赛事信息
|
||||||
|
5. 根据赛事信息获取商户级别的支付配置(用于后续可能的退款等操作)
|
||||||
|
6. 更新订单支付状态
|
||||||
|
</remarks>
|
||||||
|
<returns>微信支付平台要求的响应格式</returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:YD_Event.Application.Client.EventClientController.GetOpenIdFromRequest">
|
<member name="M:YD_Event.Application.Client.EventClientController.GetOpenIdFromRequest">
|
||||||
<summary>
|
<summary>
|
||||||
@ -3274,6 +3327,18 @@
|
|||||||
生成自定义 Token
|
生成自定义 Token
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="T:YD_Event.Application.Client.WeChatController">
|
||||||
|
<summary>
|
||||||
|
微信小程序接口
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:YD_Event.Application.Client.WeChatController.Code2Session(YD_Event.Application.Client.Dtos.WeChatCode2SessionInput)">
|
||||||
|
<summary>
|
||||||
|
小程序 code 换取 openid 和 session_key
|
||||||
|
</summary>
|
||||||
|
<param name="input">包含 code 的输入参数</param>
|
||||||
|
<returns>openid、session_key 等信息</returns>
|
||||||
|
</member>
|
||||||
<member name="T:YD_Event.Application.Common.Dtos.AthleteOcrInput">
|
<member name="T:YD_Event.Application.Common.Dtos.AthleteOcrInput">
|
||||||
<summary>
|
<summary>
|
||||||
OCR识别输入
|
OCR识别输入
|
||||||
@ -4039,6 +4104,11 @@
|
|||||||
营业执照图片URL (显示字段)
|
营业执照图片URL (显示字段)
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:YD_Event.Application.Event.Dtos.EventFeeConfigInput.ProjectPrices">
|
||||||
|
<summary>
|
||||||
|
项目价格字典 (key: 项目ID, value: 价格)
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="T:YD_Event.Application.Event.Dtos.UpdateEventInput">
|
<member name="T:YD_Event.Application.Event.Dtos.UpdateEventInput">
|
||||||
<summary>
|
<summary>
|
||||||
更新赛事输入
|
更新赛事输入
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user