public override async Task OnPaymentStartedAsync(Payment payment, ExtraPropertyDictionary configurations)
        {
            if (payment.ActualPaymentAmount <= decimal.Zero)
            {
                throw new PaymentAmountInvalidException(payment.ActualPaymentAmount, PaymentMethod);
            }

            if (!Guid.TryParse(configurations.GetOrDefault("AccountId") as string, out var accountId))
            {
                throw new ArgumentNullException("AccountId");
            }

            var account = await _accountRepository.GetAsync(accountId);

            if (account.UserId != _currentUser.GetId())
            {
                throw new UserIsNotAccountOwnerException(_currentUser.GetId(), accountId);
            }

            payment.SetProperty("AccountId", accountId);

            var accountGroupConfiguration = _accountGroupConfigurationProvider.Get(account.AccountGroupName);

            if (!accountGroupConfiguration.AllowedUsingToTopUpOtherAccounts &&
                payment.PaymentItems.Any(x => x.ItemType == PrepaymentConsts.TopUpPaymentItemType))
            {
                throw new AccountTopingUpOtherAccountsIsNotAllowedException(account.AccountGroupName);
            }

            if (payment.PaymentItems.Any(x =>
                                         x.ItemType == PrepaymentConsts.TopUpPaymentItemType && x.ItemKey == accountId.ToString()))
            {
                throw new SelfTopUpException();
            }

            if (payment.Currency != accountGroupConfiguration.Currency)
            {
                throw new CurrencyNotSupportedException(payment.Currency);
            }

            var accountChangedBalance = -1 * payment.ActualPaymentAmount;

            var transaction = new Transaction(_guidGenerator.Create(), _currentTenant.Id, account.Id, account.UserId,
                                              payment.Id, TransactionType.Credit, PrepaymentConsts.PaymentActionName, payment.PaymentMethod,
                                              payment.ExternalTradingCode, accountGroupConfiguration.Currency, accountChangedBalance,
                                              account.Balance);

            await _transactionRepository.InsertAsync(transaction, true);

            account.ChangeBalance(accountChangedBalance);

            await _accountRepository.UpdateAsync(account, true);

            await _paymentManager.CompletePaymentAsync(payment);

            await _paymentRepository.UpdateAsync(payment, true);
        }
        public override async Task OnPaymentStartedAsync(Payment payment, ExtraPropertyDictionary configurations)
        {
            if (payment.Currency != "CNY")
            {
                throw new CurrencyNotSupportedException(payment.PaymentMethod, payment.Currency);
            }

            if (payment.ActualPaymentAmount <= decimal.Zero)
            {
                throw new PaymentAmountInvalidException(payment.ActualPaymentAmount, PaymentMethod);
            }

            var payeeAccount = configurations.GetOrDefault("PayeeAccount") as string ??
                               await _settingProvider.GetOrNullAsync(WeChatPaySettings.MchId);

            Check.NotNullOrWhiteSpace(payeeAccount, "PayeeAccount");

            payment.SetPayeeAccount(payeeAccount);

            var appId = configurations.GetOrDefault("appid") as string;

            var openId = await _paymentOpenIdProvider.FindUserOpenIdAsync(appId, payment.UserId);

            var outTradeNo = payment.Id.ToString("N");

            var result = await _serviceProviderPayService.UnifiedOrderAsync(
                appId : appId,
                subAppId : null,
                mchId : payment.PayeeAccount,
                subMchId : null,
                deviceInfo : PaymentServiceWeChatPayConsts.DeviceInfo,
                body : configurations.GetOrDefault("body") as string ?? "EasyAbpPaymentService",
                detail : configurations.GetOrDefault("detail") as string,
                attach : configurations.GetOrDefault("attach") as string,
                outTradeNo : outTradeNo,
                feeType : payment.Currency,
                totalFee : _weChatPayFeeConverter.ConvertToWeChatPayFee(payment.ActualPaymentAmount),
                billCreateIp : "127.0.0.1",
                timeStart : null,
                timeExpire : null,
                goodsTag : configurations.GetOrDefault("goods_tag") as string,
                notifyUrl : configurations.GetOrDefault("notify_url") as string
                ?? await _settingProvider.GetOrNullAsync(WeChatPaySettings.NotifyUrl),
                tradeType : configurations.GetOrDefault("trade_type") as string,
                productId : null,
                limitPay : configurations.GetOrDefault("limit_pay") as string,
                openId : openId,
                subOpenId : null,
                receipt : configurations.GetOrDefault("receipt") as string ?? "N",
                sceneInfo : null);

            var dict = result.SelectSingleNode("xml").ToDictionary() ?? throw new NullReferenceException();

            if (dict.GetOrDefault("return_code") != "SUCCESS")
            {
                throw new UnifiedOrderFailedException(dict.GetOrDefault("return_code"), dict.GetOrDefault("return_msg"));
            }

            if (dict.GetOrDefault("result_code") != "SUCCESS")
            {
                throw new UnifiedOrderFailedException(
                          dict.GetOrDefault("return_code"),
                          dict.GetOrDefault("return_msg"),
                          dict.GetOrDefault("err_code_des"),
                          dict.GetOrDefault("err_code")
                          );
            }

            payment.SetProperty("appid", configurations.GetOrDefault("appid") as string);

            payment.SetProperty("trade_type", dict.GetOrDefault("trade_type"));
            payment.SetProperty("prepay_id", dict.GetOrDefault("prepay_id"));
            payment.SetProperty("code_url", dict.GetOrDefault("code_url"));

            await _paymentRecordRepository.InsertAsync(
                new PaymentRecord(_guidGenerator.Create(), _currentTenant.Id, payment.Id), true);

            await _paymentRepository.UpdateAsync(payment, true);
        }