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() }); }