lingling
2025-03-17 995b00fc03d611c8a7f96a48bafe4fb66b62ef7b
添加回调
5个文件已添加
2个文件已修改
646 ■■■■■ 已修改文件
LifePayment/LifePayment.Application/User/OssSTSHelper.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LifePayment/LifePayment.Domain/WeChat/WxRechargeInfo.cs 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LifePayment/LifePayment.Host/LifePaymentService.HttpApi.xml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LifePayment/LifePayment.HttpApi/LifePay/ACOOLYNotifyController.cs 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LifePayment/LifePayment.HttpApi/LifePay/AccountController.cs 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LifePayment/LifePayment.HttpApi/LifePay/AliPayNotifyController.cs 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LifePayment/LifePayment.HttpApi/LifePay/WxPayNotifyController.cs 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LifePayment/LifePayment.Application/User/OssSTSHelper.cs
New file
@@ -0,0 +1,62 @@
using Aliyun.Acs.Core;
using Aliyun.Acs.Core.Auth.Sts;
using Aliyun.Acs.Core.Http;
using Aliyun.Acs.Core.Profile;
using LifePayment.Domain.Shared;
namespace LifePayment.Application
{
    public class OssSTSHelper
    {
        private const int TokenExpireTime = 3600;
        private const string RoleSessionName = "SessionTest";
        private const string PolicyFile = @"{
    ""Version"": ""1"",
    ""Statement"": [
        {
            ""Effect"": ""Allow"",
            ""Action"": ""oss:PutObject"",
            ""Resource"": [
                ""acs:oss:*:*:waterdroptest2/*"",
                ""acs:oss:*:*:parkmanagement/*"",
                ""acs:oss:*:*:jurenlian/*""
            ]
        }
    ]
}";
        private readonly OssSettings ossSettings;
        public OssSTSHelper(OssSettings ossSettings)
        {
            this.ossSettings = ossSettings;
        }
        public OssSTSReponse GetOssSTS()
        {
            string regionId = "cn-hangzhou";
            IClientProfile profile = DefaultProfile.GetProfile(regionId, ossSettings.OssAccessKeyId, ossSettings.OssAccessSecret);
            DefaultAcsClient client = new DefaultAcsClient(profile);
            AssumeRoleRequest request = new AssumeRoleRequest();
            request.Method = MethodType.POST;
            request.RoleArn = ossSettings.OssRoleRan;
            request.RoleSessionName = RoleSessionName;
            request.Policy = PolicyFile;
            request.DurationSeconds = TokenExpireTime;
            AssumeRoleResponse response = client.GetAcsResponse(request);
            OssSTSReponse result = new OssSTSReponse()
            {
                Expiration = response.Credentials.Expiration,
                OssAccessKeyId = response.Credentials.AccessKeyId,
                OssAccessSecret = response.Credentials.AccessKeySecret,
                SecurityToken = response.Credentials.SecurityToken,
                RequestId = response.RequestId
            };
            return result;
        }
    }
}
LifePayment/LifePayment.Domain/WeChat/WxRechargeInfo.cs
New file
@@ -0,0 +1,219 @@
using LifePayment.Domain.Shared;
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
namespace LifePayment.Application.Contracts
{
    public class WxRechargeInfo
    {
        /// <summary>
        /// 充值单号
        /// </summary>
        public string RechargeNo { get; set; }
        /// <summary>
        /// 充值码地址
        /// </summary>
        public string RechargeCodeUrl { get; set; }
    }
    public class WxRechargeInput
    {
        /// <summary>
        /// 充值金额,单位为元。
        /// </summary>
        public decimal Amount { get; set; }
        /// <summary>
        /// 商品描述
        /// </summary>
        public string Description { get; set; } = "微信充值";
        /// <summary>
        /// 备注
        /// </summary>
        public string Remark { get; set; }
    }
    public class WxRechargeNotifyInput
    {
        /// <summary>
        /// 通知Id
        /// </summary>
        [JsonProperty("id")]
        [Required]
        public string Id { get; set; }
        /// <summary>
        /// 通知创建时间
        /// </summary>
        [JsonProperty("create_time")]
        [Required]
        public string CreateTime { get; set; }
        /// <summary>
        /// 通知类型
        /// </summary>
        [JsonProperty("event_type")]
        [Required]
        public string EventType { get; set; }
        /// <summary>
        /// 通知数据类型
        /// </summary>
        [JsonProperty("resource_type")]
        [Required]
        public string ResourceType { get; set; }
        /// <summary>
        /// 通知数据
        /// </summary>
        [JsonProperty("resource")]
        [Required]
        public Resource Resource { get; set; }
        /// <summary>
        /// 回调摘要
        /// </summary>
        [JsonProperty("summary")]
        [Required]
        public string Summary { get; set; }
    }
    public class Resource
    {
        /// <summary>
        /// 加密算法类型
        /// </summary>
        [JsonProperty("algorithm")]
        [Required]
        public string Algorithm { get; set; }
        /// <summary>
        /// 数据密文
        /// </summary>
        [JsonProperty("Ciphertext")]
        [Required]
        public string Ciphertext { get; set; }
        /// <summary>
        /// 附加数据
        /// </summary>
        [JsonProperty("associated_data")]
        public string AssociatedData { get; set; }
        /// <summary>
        /// 原始类型
        /// </summary>
        [JsonProperty("original_type")]
        [Required]
        public string OriginalType { get; set; }
        /// <summary>
        /// 随机串
        /// </summary>
        [JsonProperty("nonce")]
        [Required]
        public string Nonce { get; set; }
    }
    public class WxPayNotice
    {
        /// <summary>
        /// 应用Id
        /// </summary>
        [JsonProperty("appid")]
        public string Appid { get; set; }
        /// <summary>
        /// 商户号
        /// </summary>
        [JsonProperty("mchid")]
        public string Mchid { get; set; }
        /// <summary>
        /// 商户订单号
        /// </summary>
        [JsonProperty("out_trade_no")]
        public string OutTradeNo { get; set; }
        /// <summary>
        /// 微信支付订单号
        /// </summary>
        [JsonProperty("transaction_id")]
        public string TransactionId { get; set; }
        /// <summary>
        /// 交易类型
        /// </summary>
        [JsonProperty("trade_type")]
        public string TradeType { get; set; }
        /// <summary>
        /// 交易状态
        /// </summary>
        [JsonProperty("trade_state")]
        public string TradeState { get; set; }
        /// <summary>
        /// 交易状态描述
        /// </summary>
        [JsonProperty("trade_state_desc")]
        public string TradeStateDesc { get; set; }
        /// <summary>
        /// 付款银行
        /// </summary>
        [JsonProperty("bank_type")]
        public string BankType { get; set; }
        /// <summary>
        /// 附加数据
        /// </summary>
        [JsonProperty("attach")]
        public string Attach { get; set; }
        /// <summary>
        /// 支付完成时间
        /// </summary>
        [JsonProperty("success_time")]
        public string SuccessTime { get; set; }
        /// <summary>
        /// 支付者
        /// </summary>
        [JsonProperty("payer")]
        public Payer Payer { get; set; }
        /// <summary>
        /// 订单金额
        /// </summary>
        [JsonProperty("amount")]
        public Amount Amount { get; set; }
        /// <summary>
        /// 场景信息
        /// </summary>
        [JsonProperty("scene_info")]
        public SceneInfo SceneInfo { get; set; }
        /// <summary>
        /// 优惠功能
        /// </summary>
        [JsonProperty("promotion_detail")]
        public PromotionDetail PromotionDetail { get; set; }
    }
    public class WxRechargeNotifyResult
    {
        /// <summary>
        /// 返回状态码(错误码,SUCCESS为接收成功,其他错误码为失败)
        /// </summary>
        public string Code { get; set; }
        /// <summary>
        /// 返回信息,如非空,为错误原因
        /// </summary>
        public string Message { get; set; }
    }
}
LifePayment/LifePayment.Host/LifePaymentService.HttpApi.xml
@@ -19,6 +19,18 @@
            <returns></returns>
            <exception cref="T:Volo.Abp.UserFriendlyException"></exception>
        </member>
        <member name="M:LifePayment.HttpApi.ACOOLYNotifyController.ACOOLYNotify">
            <summary>
            ACOOLY回调通知
            </summary>
            <returns></returns>
        </member>
        <member name="M:LifePayment.HttpApi.AliPayNotifyController.AliRechargeNotify">
            <summary>
            支付宝充值回调通知
            </summary>
            <returns></returns>
        </member>
        <member name="M:LifePayment.HttpApi.AreaController.GetAreaList">
            <summary>
            获取省市区
@@ -266,5 +278,12 @@
            <param name="input"></param>
            <returns></returns>
        </member>
        <member name="M:LifePayment.HttpApi.WxPayNotifyController.WxRechargeNotify(LifePayment.Application.Contracts.WxRechargeNotifyInput)">
            <summary>
            微信支付回调通知
            </summary>
            <param name="input"></param>
            <returns></returns>
        </member>
    </members>
</doc>
LifePayment/LifePayment.HttpApi/LifePay/ACOOLYNotifyController.cs
New file
@@ -0,0 +1,124 @@
using LifePayment.Application.Contracts;
using LifePayment.Domain.Shared;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Uow;
namespace LifePayment.HttpApi
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    [IgnoreAntiforgeryToken]
    public class ACOOLYNotifyController : AbpController
    {
        private readonly ILifePayService _lifePayService;
        private readonly ILogger<ACOOLYNotifyController> _logger;
        public ACOOLYNotifyController(
               ILogger<ACOOLYNotifyController> logger,
               ILifePayService lifePayService)
        {
            _logger = logger;
            _lifePayService = lifePayService;
        }
        /// <summary>
        /// ACOOLY回调通知
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [UnitOfWork]
        [AllowAnonymous]
        public async Task ACOOLYNotify()
        {
            _logger.LogError("ACOOLY回调通知开始进入");
            var body = string.Empty;
            using (var reader = new StreamReader(Request.Body, Encoding.UTF8))
            {
                body = await reader.ReadToEndAsync();
            }
            if (body.IsNullOrEmpty())
            {
                _logger.LogError("ACOOLY回调通知Input为null");
                throw new UserFriendlyException("ACOOLY回调通知Input为null");
            }
            var baseInfo = JsonConvert.DeserializeObject<ACOOLYRequestBaseResponse>(body);
            var orderNo = string.Empty;
            var status = LifePayOrderStatusEnum.待确认;
            var acoolyOrderNo = string.Empty;
            _logger.LogError("ACOOLY回调通处理类型:" + baseInfo.Service);
            _logger.LogError("ACOOLY回调内容:" + body);
            switch (baseInfo.Service)
            {
                case ACOOLYConstant.Sevice.ConfirmElectricOrder:
                    var confirmElectricOrderResponse = JsonConvert.DeserializeObject<ConfirmElectricOrderResponse>(body);
                    orderNo = confirmElectricOrderResponse.ElectricChargeOrder.OutOrderNo;
                    acoolyOrderNo = confirmElectricOrderResponse.ElectricChargeOrder.BusiOrderNo;
                    _logger.LogError("ACOOLY回调通处理结果状态:" + confirmElectricOrderResponse.ElectricChargeOrder.Status);
                    if (!confirmElectricOrderResponse.Success
                    || (confirmElectricOrderResponse.Code != ACOOLYConstant.Code.SUCCESS && confirmElectricOrderResponse.Code != ACOOLYConstant.Code.PROCESSING))
                    {
                        status = LifePayOrderStatusEnum.已失败;
                    }
                    else
                    if (confirmElectricOrderResponse.ElectricChargeOrder.Status == ACOOLYConstant.Code.SUCCESS)
                    {
                        status = LifePayOrderStatusEnum.已完成;
                    }
                    else
                    if (confirmElectricOrderResponse.ElectricChargeOrder.Status == ACOOLYConstant.Code.PROCESSING)
                    {
                        status = LifePayOrderStatusEnum.待确认;
                    }
                    else
                    {
                        status = LifePayOrderStatusEnum.已失败;
                    }
                    break;
                case ACOOLYConstant.Sevice.ConfirmPhoneOrder:
                    var confirmPhoneOrderResponse = JsonConvert.DeserializeObject<ConfirmPhoneOrderResponse>(body);
                    orderNo = confirmPhoneOrderResponse.PhoneChargeOrder.OutOrderNo;
                    acoolyOrderNo = confirmPhoneOrderResponse.PhoneChargeOrder.BusiOrderNo;
                    _logger.LogError("ACOOLY回调通处理结果状态:" + confirmPhoneOrderResponse.PhoneChargeOrder.Status);
                    if (!confirmPhoneOrderResponse.Success
                    || (confirmPhoneOrderResponse.Code != ACOOLYConstant.Code.SUCCESS && confirmPhoneOrderResponse.Code != ACOOLYConstant.Code.PROCESSING))
                    {
                        status = LifePayOrderStatusEnum.已失败;
                    }
                    else
                    if (confirmPhoneOrderResponse.PhoneChargeOrder.Status == ACOOLYConstant.Code.SUCCESS)
                    {
                        status = LifePayOrderStatusEnum.已完成;
                    }
                    else
                    if (confirmPhoneOrderResponse.PhoneChargeOrder.Status == ACOOLYConstant.Code.PROCESSING)
                    {
                        status = LifePayOrderStatusEnum.待确认;
                    }
                    else
                    {
                        status = LifePayOrderStatusEnum.已失败;
                    }
                    break;
                default:
                    break;
            }
            await _lifePayService.ACOOLYOrderNotifyHandler(orderNo, acoolyOrderNo, status);
        }
    }
}
LifePayment/LifePayment.HttpApi/LifePay/AccountController.cs
@@ -1,4 +1,6 @@
using LifePayment.Application.Contracts;
using LifePayment.Application;
using LifePayment.Application.Contracts;
using LifePayment.Domain.Shared;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
@@ -15,19 +17,19 @@
    public class AccountController : AbpController
    {
        private readonly IAccountService _accountService;
        private readonly OssSettings ossSettings;
        private readonly IWebClientInfoProvider _webClientInfoProvider;
        public AccountController(
               IAccountService accountService,
               IOptionsMonitor<OssSettings> optionsMonitor,
               IWebClientInfoProvider webClientInfoProvider
          )
        {
            _accountService = accountService;
            this.ossSettings = optionsMonitor.CurrentValue;
            _webClientInfoProvider = webClientInfoProvider;
        }
@@ -60,7 +62,12 @@
            return await _accountService.LifePayPhoneMesssageCodeLogin(input);
        }
        [HttpPost]
        public OssSTSReponse GetOssSTS()
        {
            OssSTSHelper ossSTSHelper = new OssSTSHelper(this.ossSettings);
            return ossSTSHelper.GetOssSTS();
        }
        #endregion
LifePayment/LifePayment.HttpApi/LifePay/AliPayNotifyController.cs
New file
@@ -0,0 +1,130 @@
using LifePayment.Application.Contracts;
using LifePayment.Domain.Shared;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Uow;
namespace LifePayment.HttpApi
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    [IgnoreAntiforgeryToken]
    //[Authorize(AuthenticationSchemes = AliPaySignAuthenticationDefaults.AuthenticationScheme)]
    public class AliPayNotifyController : AbpController
    {
        private readonly ILifePayService _lifePayService;
        private readonly ILogger<AliPayNotifyController> _logger;
        public AliPayNotifyController(
               ILogger<AliPayNotifyController> logger,
               ILifePayService lifePayService)
        {
            _logger = logger;
            _lifePayService = lifePayService;
        }
        /// <summary>
        /// 支付宝充值回调通知
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [UnitOfWork]
        public async Task<ContentResult> AliRechargeNotify()
        {
            _logger.LogError($"生活管家支付宝充值回调通知:进入支付宝回调");
            var request = await this.HttpContext.Request.ReadFormAsync();
            var requestToDic = request.ToDictionary(r => r.Key, r => r.Value.ToString());
            var serializeRequest = JsonConvert.SerializeObject(requestToDic);
            _logger.LogError($"生活管家支付宝充值回调通知:{serializeRequest}");
            //var serializeRequest = "{\"gmt_create\":\"2025-02-25 13:18:59\",\"charset\":\"UTF-8\",\"seller_email\":\"zfb1@818nb.com\",\"subject\":\"生活缴费-话费\",\"sign\":\"Sp06G1GxrAfDvoHPz9l3DJ20SxhvRzEGFeCGu4LHrSWmEG4OY7MHMx+iJi54ETbdXV0YsyDH9JZD7PWN3HCpEq/wGO4Wh4VSYSe7lqxD6r4f/HFiB0YlrdQoSzjZgYPzLjy6bcdlKRRHOeDkgs2i7TfvIsxWxHs9t0xuS0RlkpdZfb7d7m0EuZ/3v2Cbsj5AHjxb1S2PkO0oQyriYgGQdmkPqILZHwieST+tNEHS4dGFKYu2nkfctAGjWIDv/hKQNY7jEUxsEeG0SnK4TPU8zNplFR9/aKM0Wfwp1pdlaiP2u/d8vOtNh5q+emvaYbKrUkIEFBok8pDLNDta7ZjtVw==\",\"invoice_amount\":\"0.01\",\"buyer_open_id\":\"071xYXDfXBLAI9U11jg_WrH1K6hWq8HYGz0u85xBivf3Sce\",\"notify_id\":\"2025022501222131904087711494539601\",\"fund_bill_list\":\"[{\\\"amount\\\":\\\"0.01\\\",\\\"fundChannel\\\":\\\"ALIPAYACCOUNT\\\"}]\",\"notify_type\":\"trade_status_sync\",\"trade_status\":\"TRADE_SUCCESS\",\"receipt_amount\":\"0.01\",\"buyer_pay_amount\":\"0.01\",\"app_id\":\"2021004171602214\",\"sign_type\":\"RSA2\",\"seller_id\":\"2088050542042301\",\"gmt_payment\":\"2025-02-25 13:19:03\",\"notify_time\":\"2025-02-25 13:44:34\",\"merchant_app_id\":\"2021004171602214\",\"version\":\"1.0\",\"out_trade_no\":\"JF202502251318555214671\",\"total_amount\":\"0.01\",\"trade_no\":\"2025022522001487711401331043\",\"auth_app_id\":\"2021004171602214\",\"buyer_logon_id\":\"130****2238\",\"point_amount\":\"0.00\"}";
            var input = JsonConvert.DeserializeObject<AliRechargeNotifyInput>(serializeRequest);
            if (input.OutTradeNo.StartsWith("JF") && Regex.IsMatch(input.OutTradeNo, @"^JF\d+$"))
            {
                if (input.TradeStatus == LifePaymentConstant.AliPayStatus.支付成功)
                {
                    await _lifePayService.LifePaySuccessHandler(input.OutTradeNo, input.TradeNo);
                }
            }
            return new ContentResult
            {
                Content = "success",
            };
        }
#if DEBUG
        [HttpGet]
        [UnitOfWork]
        [AllowAnonymous]
        public async Task<ContentResult> TestAliRechargeNotify(string outTradeNo, string tradeNo, bool success)
        {
            if (outTradeNo.StartsWith("JF") && Regex.IsMatch(outTradeNo, @"^JF\d+$"))
            {
                if (success)
                {
                    await _lifePayService.LifePaySuccessHandler(outTradeNo, tradeNo);
                }
            }
            return new ContentResult
            {
                Content = "success",
            };
        }
#endif
        /// <summary>
        /// 单笔转账到支付宝账户、单笔转账到银行卡、C2C现金红包、B2C现金红包单据状态变更后触发的通知
        /// </summary>
        /// <returns></returns>
        //[HttpPost]
        //[UnitOfWork]
        //public async Task<ContentResult> NotifyOrderChanged()
        //{
        //    var request = await this.HttpContext.Request.ReadFormAsync();
        //    var requestToDic = request.ToDictionary(r => r.Key, r => r.Value.ToString());
        //    var serializeRequest = JsonConvert.SerializeObject(requestToDic);
        //    _logger.LogError($"单笔转账回调:{serializeRequest}");
        //    if (requestToDic["msg_method"] == "alipay.fund.trans.order.changed")
        //    {
        //        var input = JsonConvert.DeserializeObject<FundOrderChangedInput>(serializeRequest);
        //        if (input.BizContent != null)
        //        {
        //            if (input.BizContent.OutBizNo.Contains(RequestCode.AlipayCharge))
        //            {
        //                await _walletService.FundTransOrderChangedForRecharge(input.BizContent);
        //            }
        //            else
        //            {
        //                await _walletService.FundTransOrderChangedForSingleTransfer(input.BizContent);
        //            }
        //        }
        //    }
        //    return new ContentResult
        //    {
        //        Content = "success",
        //    };
        //}
    }
}
LifePayment/LifePayment.HttpApi/LifePay/WxPayNotifyController.cs
New file
@@ -0,0 +1,75 @@
using LifePayment.Application.Contracts;
using LifePayment.Domain;
using LifePayment.Domain.Shared;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Uow;
namespace LifePayment.HttpApi
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    [IgnoreAntiforgeryToken]
    // [Authorize(AuthenticationSchemes = WxPaySignAuthenticationDefaults.AuthenticationScheme)]
    public class WxPayNotifyController : AbpController
    {
        private readonly IWxPayApi _wxPayApi;
        private readonly ILifePayService _lifePayService;
        private readonly ILogger<WxPayNotifyController> _logger;
        public WxPayNotifyController(
               IWxPayApi wxPayApi,
               ILogger<WxPayNotifyController> logger,
               ILifePayService lifePayService)
        {
            _wxPayApi = wxPayApi;
            _logger = logger;
            _lifePayService = lifePayService;
        }
        /// <summary>
        /// 微信支付回调通知
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost]
        [UnitOfWork]
        public async Task<WxRechargeNotifyResult> WxRechargeNotify(WxRechargeNotifyInput input)
        {
            try
            {
                _logger.LogError($"生活管家微信充值回调通知:进入微信回调");
                var data = _wxPayApi.AesGcmDecrypt(input.Resource.AssociatedData, input.Resource.Nonce, input.Resource.Ciphertext);
                var wxPayNotice = JsonConvert.DeserializeObject<WxPayNotice>(data);
                if (wxPayNotice.OutTradeNo.StartsWith("JF") && Regex.IsMatch(wxPayNotice.OutTradeNo, @"^JF\d+$"))
                {
                    if (wxPayNotice.TradeState == LifePaymentConstant.WxPayStatus.支付成功)
                    {
                        await _lifePayService.LifePaySuccessHandler(wxPayNotice.OutTradeNo, wxPayNotice.TransactionId);
                    }
                }
            }
            catch (Exception ex)
            {
                await CurrentUnitOfWork.RollbackAsync();
                return new WxRechargeNotifyResult
                {
                    Code = "FAIL",
                    Message = ex.Message,
                };
            }
            return new WxRechargeNotifyResult
            {
                Code = "SUCCESS",
            };
        }
    }
}