public MerchantWithdrawal Withdrawal(Guid accountId, decimal amount, int cryptoId, string address, string tag, string clientIP)
        {
            SecurityVerify.Verify <WithdrawVerify>(new CustomVerifier("MerchantWithdraw"), SystemPlatform.FiiiPOS, accountId.ToString(), (model) =>
            {
                return(model.PinVerified && model.CombinedVerified);
            });

            var cryptocurrency = new CryptocurrencyDAC().GetById(cryptoId);

            CryptoAddressValidation.ValidateAddress(cryptocurrency.Code, address);

            if (!string.IsNullOrEmpty(tag))
            {
                CryptoAddressValidation.ValidateTag(cryptocurrency.Code, tag);
            }

            var account = new MerchantAccountDAC().GetById(accountId);

            if (!new ProfileComponent().ValidateLv1(accountId))
            {
                throw new CommonException(ReasonCode.NOT_VERIFY_LV1, Resources.需要Lv1认证才能使用相关功能);
            }

            if (!account.IsAllowWithdrawal)
            {
                throw new CommonException(ReasonCode.Not_Allow_Withdrawal, Resources.禁止提币);
            }

            if (!cryptocurrency.Status.HasFlag(CryptoStatus.Withdrawal))
            {
                throw new CommonException(ReasonCode.CURRENCY_FORBIDDEN, Resources.CurrencyForbidden);
            }

            if (cryptocurrency.Enable == (byte)CurrencyStatus.Forbidden)
            {
                throw new CommonException(ReasonCode.CURRENCY_FORBIDDEN, Resources.CurrencyForbidden);
            }

            var fromWallet = new MerchantWalletDAC().GetByAccountId(accountId, cryptoId);

            if (fromWallet == null)
            {
                throw new CommonException(ReasonCode.Not_Allow_Withdrawal, Resources.禁止提币);
            }

            if (fromWallet.Balance < amount)
            {
                throw new CommonException(ReasonCode.GENERAL_ERROR, Resources.余额不足);
            }

            var profileAgent = new MerchantProfileAgent();
            var profile      = profileAgent.GetMerchantProfile(accountId);

            int level = profile.L2VerifyStatus == VerifyStatus.Certified ? 2 : profile.L1VerifyStatus == VerifyStatus.Certified ? 1 : 0;

            var masterSetting = GetMerchantWithdrawalMasterSettingWithCrypto(cryptocurrency, level);

            if (amount > masterSetting.PerTxLimit)
            {
                throw new CommonException(ReasonCode.GENERAL_ERROR, Resources.能高于单次提币量);
            }
            var     dac             = new MerchantWithdrawalDAC();
            var     today           = DateTime.UtcNow.Date;
            decimal dailyWithdrawal = dac.DailyWithdrawal(accountId, cryptoId, today);

            if (amount > masterSetting.PerDayLimit - dailyWithdrawal)
            {
                throw new CommonException(ReasonCode.GENERAL_ERROR, Resources.今日提币达到限额);
            }
            decimal monthlyWithdrawal = dac.MonthlyWithdrawal(accountId, cryptoId, new DateTime(today.Year, today.Month, 1));

            if (amount > masterSetting.PerMonthLimit - monthlyWithdrawal)
            {
                throw new CommonException(ReasonCode.GENERAL_ERROR, Resources.本月提币达到限额);
            }

            var fromWithdraw = new MerchantWithdrawal
            {
                MerchantAccountId = accountId,
                MerchantWalletId  = fromWallet.Id,
                Address           = address,
                Tag        = tag,
                Amount     = amount,
                Status     = TransactionStatus.UnSubmit,
                Timestamp  = DateTime.UtcNow,
                OrderNo    = NumberGenerator.GenerateUnixOrderNo(),
                CryptoId   = fromWallet.CryptoId,
                CryptoCode = fromWallet.CryptoCode
            };

            var merchantWalletDac = new MerchantWalletDAC();
            var userWalletDac     = new UserWalletDAC();

            //是否是商户地址
            var toMerchantWallet = merchantWalletDac.GetByAddress(address, cryptocurrency.NeedTag ? tag : null);

            if (toMerchantWallet != null)
            {
                //if (toMerchantWallet.CryptoId != cryptoId)
                //    throw new CommonException(10000, string.Format(Resources.提币地址不是有效的地址, cryptocurrency.Code));
                //if (toMerchantWallet.MerchantAccountId == accountId)
                //    throw new CommonException(ReasonCode.CANNOT_TRANSFER_TO_YOURSELF, Resources.提币地址不能是自己账户的地址);
                //return WithdrawalToMerchantAccount(fromWallet, fromWithdraw, toMerchantWallet);
                // 042018
                throw new CommonException(ReasonCode.CAN_NOT_WITHDRAW_TO_FiiiPOS, Resources.FiiiPOSCantWithdrawToFiiiPOS);
            }

            //是否是用户地址
            var toUserWallet = userWalletDac.GetByAddressAndCrypto(cryptoId, address, cryptocurrency.NeedTag ? tag : null);

            if (toUserWallet != null)
            {
                if (toUserWallet.CryptoId != cryptoId)
                {
                    throw new CommonException(ReasonCode.GENERAL_ERROR, FiiiPay.Framework.Component.Properties.GeneralResources.EMInvalidAddress);
                }

                if (amount < masterSetting.ToUserMinAmount)
                {
                    throw new CommonException(10000, Resources.能低于最低提币量);
                }

                var fee = (fromWithdraw.Amount * masterSetting.ToUserHandleFeeTier).ToSpecificDecimal(cryptocurrency.DecimalPlace);
                if (amount <= fee)
                {
                    throw new CommonException(ReasonCode.GENERAL_ERROR, Resources.到账数量不能为零或者负数);
                }

                return(WithdrawalToUserAccount(fromWallet, fromWithdraw, toUserWallet, fee));
            }

            //平台内提币如果tag不对,创建一条失败记录
            if (cryptocurrency.NeedTag && (userWalletDac.IsUserWalletAddress(address) || merchantWalletDac.IsMerchantWalletAddress(address)))
            {
                return(CancelWithdrawal(fromWithdraw));
            }

            //如果都不是,提币到场外
            if (amount < masterSetting.ToOutsideMinAmount)
            {
                throw new CommonException(ReasonCode.GENERAL_ERROR, Resources.能低于最低提币量);
            }

            var baseFee  = cryptocurrency.Withdrawal_Fee ?? 0;
            var tier     = cryptocurrency.Withdrawal_Tier ?? 0;
            var fee1     = (amount * tier).ToSpecificDecimal(cryptocurrency.DecimalPlace);
            var totalFee = baseFee + fee1;

            if (amount <= totalFee)
            {
                throw new CommonException(ReasonCode.GENERAL_ERROR, Resources.到账数量不能为零或者负数);
            }

            return(WithdrawalToOutside(fromWallet, fromWithdraw, cryptocurrency, account, amount, totalFee, address, tag, clientIP));
        }
        public WithdrawOM Withdraw(UserAccount user, WithdrawIM im, string clientIP)
        {
            SecurityVerify.Verify <WithdrawVerify>(new CustomVerifier("UserWithdraw"), SystemPlatform.FiiiPay, user.Id.ToString(), (model) =>
            {
                return(model.PinVerified && model.CombinedVerified);
            });


            if (user.L1VerifyStatus != VerifyStatus.Certified)
            {
                throw new CommonException(ReasonCode.NOT_VERIFY_LV1, Resources.EMNeedLV1Verfied);
            }

            var cryptocurrency = new CryptocurrencyDAC().GetById(im.CoinId);

            if (!cryptocurrency.Status.HasFlag(CryptoStatus.Withdrawal) || cryptocurrency.Enable == 0)
            {
                throw new CommonException(ReasonCode.CURRENCY_FORBIDDEN, MessageResources.CurrencyForbidden);
            }

            CryptoAddressValidation.ValidateAddress(cryptocurrency.Code, im.Address);
            if (!string.IsNullOrEmpty(im.Tag))
            {
                CryptoAddressValidation.ValidateTag(cryptocurrency.Code, im.Tag);
            }
            //else if (cryptocurrency.NeedTag)
            //{
            //    throw new CommonException(ReasonCode.NEED_INPUT_TAG, GeneralResources.EMNeedInputTag);
            //}

            if (im.Amount <= 0)
            {
                throw new ApplicationException(MessageResources.AmountGreater);
            }

            var IsAllowWithdrawal = user.IsAllowWithdrawal ?? true;

            if (!IsAllowWithdrawal)
            {
                throw new CommonException(ReasonCode.Not_Allow_Withdrawal, MessageResources.WithdrawalDisabled);
            }

            var fromWallet = new UserWalletDAC().GetByAccountId(user.Id, im.CoinId);


            //var profile = new UserProfileAgent().GetUserProfile(user.Id);

            //标准化这个金额,防止超过8位
            im.Amount = im.Amount.ToSpecificDecimal(cryptocurrency.DecimalPlace);

            var mastSettings = new MasterSettingDAC().SelectByGroup("UserWithdrawal");

            var Withdrawal_PerTx_Limit_User_NotVerified    = Convert.ToDecimal(mastSettings.First(e => e.Name == "Withdrawal_PerTx_Limit_User_NotVerified").Value);
            var Withdrawal_PerDay_Limit_User_NotVerified   = Convert.ToDecimal(mastSettings.First(e => e.Name == "Withdrawal_PerDay_Limit_User_NotVerified").Value);
            var Withdrawal_PerMonth_Limit_User_NotVerified = Convert.ToDecimal(mastSettings.First(e => e.Name == "Withdrawal_PerMonth_Limit_User_NotVerified").Value);

            var Withdrawal_PerTx_Limit_User_Lv1Verified    = Convert.ToDecimal(mastSettings.First(e => e.Name == "Withdrawal_PerTx_Limit_User_Lv1Verified").Value);
            var Withdrawal_PerDay_Limit_User_Lv1Verified   = Convert.ToDecimal(mastSettings.First(e => e.Name == "Withdrawal_PerDay_Limit_User_Lv1Verified").Value);
            var Withdrawal_PerMonth_Limit_User_Lv1Verified = Convert.ToDecimal(mastSettings.First(e => e.Name == "Withdrawal_PerMonth_Limit_User_Lv1Verified").Value);

            var Withdrawal_PerTx_Limit_User_Lv2Verified    = Convert.ToDecimal(mastSettings.First(e => e.Name == "Withdrawal_PerTx_Limit_User_Lv2Verified").Value);
            var Withdrawal_PerDay_Limit_User_Lv2Verified   = Convert.ToDecimal(mastSettings.First(e => e.Name == "Withdrawal_PerDay_Limit_User_Lv2Verified").Value);
            var Withdrawal_PerMonth_Limit_User_Lv2Verified = Convert.ToDecimal(mastSettings.First(e => e.Name == "Withdrawal_PerMonth_Limit_User_Lv2Verified").Value);

            var MinAmount = Convert.ToDecimal(mastSettings.First(e => e.Name == "Withdrawal_MinAmount").Value);

            var merchantWalletDac = new MerchantWalletDAC();
            var userWalletDac     = new UserWalletDAC();

            var isWithdrawToInside = (merchantWalletDac.IsMerchantWalletAddress(im.Address) || userWalletDac.IsUserWalletAddress(im.Address));

            if (isWithdrawToInside)
            {
                MinAmount = Convert.ToDecimal(mastSettings.First(e => e.Name == "UserWithdrawal_ToUser_MinAmount").Value);
            }

            var exchangeRate = GetMarketPrice(user.CountryId, cryptocurrency.Code, "USD");

            var _minAmount = (MinAmount / exchangeRate).ToSpecificDecimal(cryptocurrency.DecimalPlace);

            if (im.Amount < _minAmount)
            {
                throw new ApplicationException(MessageResources.MinWidrawalError);
            }

            var     withdrawalDAC     = new UserWithdrawalDAC();
            var     today             = DateTime.UtcNow.Date;
            decimal dailyWithdrawal   = withdrawalDAC.DailyWithdrawal(user.Id, im.CoinId, today);
            decimal monthlyWithdrawal = withdrawalDAC.MonthlyWithdrawal(user.Id, im.CoinId, new DateTime(today.Year, today.Month, 1));

            var PerDayLimit   = 0M;
            var PerMonthLimit = 0M;

            if (user.L1VerifyStatus == VerifyStatus.Certified && user.L2VerifyStatus == VerifyStatus.Certified)
            {
                PerDayLimit   = Withdrawal_PerDay_Limit_User_Lv2Verified;
                PerMonthLimit = Withdrawal_PerMonth_Limit_User_Lv2Verified;
            }
            else if (user.L1VerifyStatus == VerifyStatus.Certified && user.L2VerifyStatus != VerifyStatus.Certified)
            {
                PerDayLimit   = Withdrawal_PerDay_Limit_User_Lv1Verified;
                PerMonthLimit = Withdrawal_PerMonth_Limit_User_Lv1Verified;
            }
            else if (user.L1VerifyStatus != VerifyStatus.Certified && user.L2VerifyStatus != VerifyStatus.Certified)
            {
                PerDayLimit   = Withdrawal_PerDay_Limit_User_NotVerified;
                PerMonthLimit = Withdrawal_PerMonth_Limit_User_NotVerified;
            }
            if ((PerDayLimit / exchangeRate - dailyWithdrawal).ToSpecificDecimal(cryptocurrency.DecimalPlace) < im.Amount)
            {
                throw new ApplicationException(MessageResources.TodayWidrawalLimit);
            }
            if ((PerMonthLimit / exchangeRate - monthlyWithdrawal).ToSpecificDecimal(cryptocurrency.DecimalPlace) < im.Amount)
            {
                throw new ApplicationException(MessageResources.MonthWithdrawalLimit);
            }

            var fromWithdraw = new UserWithdrawal
            {
                UserAccountId = user.Id,
                UserWalletId  = fromWallet.Id,
                Address       = im.Address,
                Tag           = im.Tag,
                Amount        = im.Amount,
                Status        = TransactionStatus.UnSubmit,
                Timestamp     = DateTime.UtcNow,
                OrderNo       = IdentityHelper.OrderNo(),
                CryptoCode    = fromWallet.CryptoCode,
                CryptoId      = fromWallet.CryptoId
            };

            //是否是商户地址
            var toMerchantWallet = merchantWalletDac.GetByAddressAndCrypto(im.CoinId, im.Address, im.Tag);

            if (toMerchantWallet != null)
            {
                //if (toMerchantWallet.CryptoId != im.CoinId)
                //    throw new CommonException(ReasonCode.GENERAL_ERROR, GeneralResources.EMInvalidAddress);
                //return WithdrawalToMerchantAccount(fromWallet, fromWithdraw, fromWithdrawFee, toMerchantWallet, cryptocurrency);
                //042018
                throw new CommonException(ReasonCode.CAN_NOT_WITHDRAW_TO_FiiiPOS, MessageResources.FiiiPayCantWithdrawToFiiiPOS);
            }

            //是否是用户地址
            var toUserWallet = userWalletDac.GetByAddressAndCrypto(im.CoinId, im.Address, im.Tag);

            if (toUserWallet != null)
            {
                if (toUserWallet.UserAccountId == user.Id)
                {
                    throw new CommonException(ReasonCode.CANNOT_TRANSFER_TO_YOURSELF, MessageResources.WithdrawalToSelfError);
                }

                var toFiiiPayMinWithdrawAmount = GetToFiiiPayMinAmount(Convert.ToDecimal(mastSettings.First(e => e.Name == "UserWithdrawal_ToUser_MinAmount").Value), exchangeRate, cryptocurrency.DecimalPlace);
                if (im.Amount < toFiiiPayMinWithdrawAmount)
                {
                    throw new CommonException(10000, MessageResources.MinWidrawalError);
                }

                return(WithdrawalToUserAccount(fromWallet, fromWithdraw, toUserWallet));
            }

            var tier = cryptocurrency.Withdrawal_Tier ?? 0;
            var fee  = im.Amount * tier + (cryptocurrency.Withdrawal_Fee ?? 0);

            fee = fee.ToSpecificDecimal(cryptocurrency.DecimalPlace);
            var actualAmount = im.Amount - fee;

            if (fromWallet.Balance < im.Amount)
            {
                throw new CommonException(ReasonCode.INSUFFICIENT_BALANCE, MessageResources.InsufficientBalance);
            }
            if (actualAmount <= 0)
            {
                throw new CommonException(ReasonCode.GENERAL_ERROR, MessageResources.ArrivalAmountError);
            }

            //地址是FiiiPay的地址,但tag找不到
            if (cryptocurrency.NeedTag && isWithdrawToInside)
            {
                return(CancelWithdrawal(fromWithdraw));
            }

            ILog _logger = LogManager.GetLogger("LogicError");

            //如果都不是,向Finance申请提现

            //var agent = new FiiiFinanceAgent();

            var requestModel = new CreateWithdrawModel
            {
                AccountID        = user.Id,
                AccountType      = AccountTypeEnum.User,
                CryptoName       = cryptocurrency.Code,
                ReceivingAddress = im.Address,
                DestinationTag   = im.Tag,
                Amount           = actualAmount,
                IPAddress        = clientIP,
                TransactionFee   = fee
            };

            using (var scope = new TransactionScope())
            {
                try
                {
                    fromWithdraw.Id = withdrawalDAC.Create(fromWithdraw);
                    var fromWithdrawFee = new UserWithdrawalFee
                    {
                        Amount       = im.Amount,
                        Fee          = fee,
                        Timestamp    = DateTime.UtcNow,
                        WithdrawalId = fromWithdraw.Id
                    };

                    new UserTransactionDAC().Insert(new UserTransaction
                    {
                        Id         = Guid.NewGuid(),
                        AccountId  = fromWithdraw.UserAccountId,
                        CryptoId   = fromWithdraw.CryptoId,
                        CryptoCode = fromWithdraw.CryptoCode,
                        Type       = UserTransactionType.Withdrawal,
                        DetailId   = fromWithdraw.Id.ToString(),
                        Status     = (byte)fromWithdraw.Status,
                        Timestamp  = fromWithdraw.Timestamp,
                        Amount     = fromWithdraw.Amount,
                        OrderNo    = fromWithdraw.OrderNo
                    });

                    new UserWithdrawalFeeDAC().Create(fromWithdrawFee);

                    userWalletDac.Freeze(fromWallet.Id, im.Amount);

                    new UserWalletStatementDAC().Insert(new UserWalletStatement
                    {
                        WalletId      = fromWallet.Id,
                        Action        = UserWalletStatementAction.Withdrawal,
                        Amount        = 0 - im.Amount,
                        Balance       = fromWallet.Balance - im.Amount,
                        FrozenAmount  = im.Amount,
                        FrozenBalance = fromWallet.FrozenBalance + im.Amount,
                        Timestamp     = DateTime.UtcNow
                    });

                    requestModel.WithdrawalId = fromWithdraw.Id;
                    Framework.Queue.RabbitMQSender.SendMessage("WithdrawSubmit", requestModel);

                    scope.Complete();
                }
                catch (CommonException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    _logger.Info($"Withdraw CreateWithdrawRequest faild.WithdrawID:{fromWithdraw.Id},OrderNo:{fromWithdraw.OrderNo}.request Parameter - {requestModel}. Error message:{ex.Message}");
                    throw;
                }
            }

            return(new WithdrawOM
            {
                OrderId = fromWithdraw.Id,
                OrderNo = fromWithdraw.OrderNo,
                Timestamp = fromWithdraw.Timestamp.ToUnixTime().ToString()
            });
        }
Example #3
0
        public LoginOM NewDeviceLoginBySMSCode(NewDeviceLoginBySMSCodeIM im, string deviceNumber)
        {
            var user           = CheckUser(im.CountryId, im.Cellphone);
            var prevVerifier   = new LoginCellphoneVerifier();
            var customVerifier = new CustomVerifier("NewDeviceLogin");

            var hadOpenedGoogleAuth = ValidationFlagComponent.CheckSecurityOpened(user.ValidationFlag, ValidationFlag.GooogleAuthenticator);

            SecurityVerify.Verify <LoginBySMSVerify>(customVerifier, SystemPlatform.FiiiPay, user.Id.ToString(), (model) =>
            {
                bool result = model.CellphoneVerified;
                if (user.L1VerifyStatus == VerifyStatus.Certified)
                {
                    var identityNo = new UserProfileComponent().PreVerifyLv1(user).IdentityDocNo;
                    result         = result && new IDNumberVerifier().Verify(SystemPlatform.FiiiPay, user.Id.ToString(), identityNo, im.IdentityDocNo);
                    if (!result)
                    {
                        var errorCountKey = customVerifier.GetErrorCountKey(SystemPlatform.FiiiPay, user.Id.ToString());
                        var errorCount    = SecurityVerify.CheckErrorCount(customVerifier, errorCountKey);
                        new IDNumberVerifier().VerifyFaild(Constant.VIRIFY_FAILD_TIMES_LIMIT - errorCount - 1);
                    }
                }
                if (!string.IsNullOrEmpty(user.Pin))
                {
                    result = result && new PinVerifier().Verify(SystemPlatform.FiiiPay, user.Id.ToString(), user.Pin, AES128.Decrypt(im.Pin, AES128.DefaultKey));
                    if (!result)
                    {
                        var errorCountKey = customVerifier.GetErrorCountKey(SystemPlatform.FiiiPay, user.Id.ToString());
                        var errorCount    = SecurityVerify.CheckErrorCount(customVerifier, errorCountKey);
                        new PinVerifier().VerifyFaild(Constant.VIRIFY_FAILD_TIMES_LIMIT - errorCount - 1);
                    }
                }
                if (SecurityVerify.CheckSecurityOpened(user.ValidationFlag, ValidationFlag.GooogleAuthenticator))
                {
                    var googleVerifier = new GoogleVerifier();
                    if (string.IsNullOrEmpty(im.GoogleCode))
                    {
                        result = false;
                    }
                    result = result && SecurityVerify.CheckCodeValid(googleVerifier, SystemPlatform.FiiiPay, user.Id.ToString(), im.GoogleCode);
                    result = result && googleVerifier.Verify(user.AuthSecretKey, im.GoogleCode);
                    if (!result)
                    {
                        var errorCountKey = customVerifier.GetErrorCountKey(SystemPlatform.FiiiPay, user.Id.ToString());
                        var errorCount    = SecurityVerify.CheckErrorCount(customVerifier, errorCountKey);
                        googleVerifier.VerifyFaild(Constant.VIRIFY_FAILD_TIMES_LIMIT - errorCount - 1);
                    }
                }

                return(result);
            });

            new UserDeviceDAC().Insert(new UserDevice()
            {
                DeviceNumber = deviceNumber, Name = " ", UserAccountId = user.Id, LastActiveTime = DateTime.UtcNow
            });

            var loginOm = IssueAccessToken(user);

            return(loginOm);
        }
Example #4
0
 public bool CheckRegisterSMSCode(int countryId, string cellphone, string code)
 {
     SecurityVerify.Verify(new RegisterCellphoneVerifier(), SystemPlatform.FiiiPay, $"{countryId}:{cellphone}", code, true);
     return(true);
 }
Example #5
0
        public async Task <PayOrderOM> PayAsync(UserAccount user, StoreOrderPayIM im)
        {
            #region 验证
            if (im.FiatAmount <= 0)
            {
                throw new ApplicationException();
            }
            SecurityVerify.Verify(new PinVerifier(), SystemPlatform.FiiiPay, user.Id.ToString(), user.Pin, im.Pin);
            if (!user.IsAllowExpense.HasValue || !user.IsAllowExpense.Value)
            {
                throw new CommonException(ReasonCode.Not_Allow_Expense, MessageResources.PaymentForbidden);
            }

            var coin = new CryptocurrencyDAC().GetById(im.CoinId);
            if (!coin.Status.HasFlag(CryptoStatus.Pay) || coin.Enable == 0)
            {
                throw new CommonException(ReasonCode.CURRENCY_FORBIDDEN, MessageResources.CurrencyForbidden);
            }

            var merchantInfo = new MerchantInformationDAC().GetById(im.MerchantInfoId);
            if (merchantInfo.Status != Status.Enabled || merchantInfo.VerifyStatus != VerifyStatus.Certified || merchantInfo.IsPublic != Status.Enabled)
            {
                throw new CommonException(ReasonCode.Not_Allow_AcceptPayment, MessageResources.MerchantExceptionTransClose);
            }
            if (!merchantInfo.IsAllowExpense)
            {
                throw new CommonException(ReasonCode.Not_Allow_AcceptPayment, MessageResources.MerchantReceiveNotAllowed);
            }
            if (merchantInfo.AccountType == AccountType.Merchant)
            {
                throw new ApplicationException();
            }

            var storeAccount = new UserAccountDAC().GetById(merchantInfo.MerchantAccountId);
            if (storeAccount.Id == user.Id)
            {
                throw new CommonException(ReasonCode.Not_Allow_AcceptPayment, MessageResources.PaytoSelf);
            }
            if (storeAccount == null || storeAccount.Status.Value != (byte)AccountStatus.Active)
            {
                throw new CommonException(ReasonCode.Not_Allow_AcceptPayment, MessageResources.MerchantFiiipayAbnormal);
            }

            var paySetting = await new StorePaySettingDAC().GetByCountryIdAsync(merchantInfo.CountryId);
            if (paySetting != null)
            {
                if (im.FiatAmount > paySetting.LimitAmount)
                {
                    throw new CommonException(ReasonCode.TRANSFER_AMOUNT_OVERFLOW, string.Format(MessageResources.TransferAmountOverflow, paySetting.LimitAmount, paySetting.FiatCurrency));
                }
            }
            #endregion

            var walletDAC     = new UserWalletDAC();
            var statementDAC  = new UserWalletStatementDAC();
            var storeOrderDAC = new StoreOrderDAC();
            var utDAC         = new UserTransactionDAC();

            #region 计算
            decimal markup       = merchantInfo.Markup;
            decimal feeRate      = merchantInfo.FeeRate;
            var     exchangeRate = new PriceInfoDAC().GetPriceByName(storeAccount.FiatCurrency, coin.Code);

            decimal failTotalAmount    = im.FiatAmount + (im.FiatAmount * markup).ToSpecificDecimal(4);
            decimal transactionFiatFee = (im.FiatAmount * feeRate).ToSpecificDecimal(4);
            decimal transactionFee     = (transactionFiatFee / exchangeRate).ToSpecificDecimal(coin.DecimalPlace);
            decimal cryptoAmount       = (failTotalAmount / exchangeRate).ToSpecificDecimal(coin.DecimalPlace);

            var fromWallet = walletDAC.GetUserWallet(user.Id, im.CoinId);
            if (fromWallet == null || fromWallet.Balance < cryptoAmount)
            {
                throw new CommonException(ReasonCode.INSUFFICIENT_BALANCE, MessageResources.InsufficientBalance);
            }

            var toWallet = walletDAC.GetUserWallet(storeAccount.Id, im.CoinId);
            if (toWallet == null)
            {
                toWallet = new UserWalletComponent().GenerateWallet(storeAccount.Id, im.CoinId);
            }
            #endregion

            #region entity
            DateTime   dtNow = DateTime.UtcNow;
            StoreOrder order = new StoreOrder
            {
                Id                 = Guid.NewGuid(),
                OrderNo            = IdentityHelper.OrderNo(),
                Timestamp          = dtNow,
                Status             = OrderStatus.Completed,
                MerchantInfoId     = merchantInfo.Id,
                MerchantInfoName   = merchantInfo.MerchantName,
                UserAccountId      = user.Id,
                CryptoId           = im.CoinId,
                CryptoCode         = coin.Code,
                CryptoAmount       = cryptoAmount,
                CryptoActualAmount = cryptoAmount - transactionFee,
                ExchangeRate       = exchangeRate,
                Markup             = markup,
                FiatCurrency       = storeAccount.FiatCurrency,
                FiatAmount         = im.FiatAmount,
                FiatActualAmount   = failTotalAmount,
                FeeRate            = feeRate,
                TransactionFee     = transactionFee,
                PaymentTime        = dtNow
            };
            UserWalletStatement fromStatement = new UserWalletStatement
            {
                WalletId      = fromWallet.Id,
                Action        = UserWalletStatementAction.StoreOrderOut,
                Amount        = -order.CryptoAmount,
                Balance       = fromWallet.Balance - order.CryptoAmount,
                FrozenAmount  = 0,
                FrozenBalance = fromWallet.FrozenBalance,
                Timestamp     = dtNow
            };
            UserWalletStatement toStatement = new UserWalletStatement
            {
                WalletId      = toWallet.Id,
                Action        = UserWalletStatementAction.StoreOrderIn,
                Amount        = order.CryptoActualAmount,
                Balance       = toWallet.Balance + order.CryptoActualAmount,
                FrozenAmount  = 0,
                FrozenBalance = toWallet.FrozenBalance,
                Timestamp     = dtNow
            };
            #endregion

            using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
            {
                await storeOrderDAC.CreateAsync(order);

                await utDAC.InsertAsync(new UserTransaction
                {
                    Id           = Guid.NewGuid(),
                    AccountId    = fromWallet.UserAccountId,
                    CryptoId     = order.CryptoId,
                    CryptoCode   = order.CryptoCode,
                    Type         = UserTransactionType.StoreOrderConsume,
                    DetailId     = order.Id.ToString(),
                    Status       = (byte)order.Status,
                    Timestamp    = order.PaymentTime.Value,
                    Amount       = order.CryptoAmount,
                    OrderNo      = order.OrderNo,
                    MerchantName = order.MerchantInfoName
                });

                await utDAC.InsertAsync(new UserTransaction
                {
                    Id           = Guid.NewGuid(),
                    AccountId    = toWallet.UserAccountId,
                    CryptoId     = order.CryptoId,
                    CryptoCode   = order.CryptoCode,
                    Type         = UserTransactionType.StoreOrderIncome,
                    DetailId     = order.Id.ToString(),
                    Status       = (byte)order.Status,
                    Timestamp    = order.Timestamp,
                    Amount       = order.CryptoActualAmount,
                    OrderNo      = order.OrderNo,
                    MerchantName = order.MerchantInfoName
                });

                walletDAC.Decrease(fromWallet.Id, order.CryptoAmount);
                walletDAC.Increase(toWallet.Id, order.CryptoActualAmount);
                statementDAC.Insert(fromStatement);
                statementDAC.Insert(toStatement);
                scope.Complete();
            }

            var pushObj = new { order.Id, order.UserAccountId, order.CryptoCode };

            RabbitMQSender.SendMessage("StoreOrderPayed", new { order.Id, MerchantInfoId = merchantInfo.MerchantAccountId, order.UserAccountId, order.CryptoCode });

            return(new PayOrderOM
            {
                Amount = order.CryptoAmount.ToString(coin.DecimalPlace),
                Currency = coin.Code,
                OrderId = order.Id.ToString(),
                OrderNo = order.OrderNo,
                Timestamp = dtNow.ToUnixTime().ToString()
            });
        }