Exemplo n.º 1
0
        public int Insert(OrderWithdrawalFee fee)
        {
            var sql = "INSERT INTO OrderWithdrawalFee (CryptoId, Amount, Timestamp, OrderId) VALUES (@CryptoId, @Amount, @Timestamp, @OrderId);SELECT SCOPE_IDENTITY()";

            using (var con = ReadConnection())
            {
                return(con.Query <int>(sql, new { fee.CryptoId, fee.Amount, fee.Timestamp, fee.OrderId }).FirstOrDefault());
            }
        }
Exemplo n.º 2
0
        //private static readonly bool IsPushProduction = Convert.ToBoolean(ConfigurationManager.AppSettings.Get("PushProduction"));

        public PayOrderOM PayExistedOrder(UserAccount user, string orderNo, string pin)
        {
            new SecurityComponent().VerifyPin(user, pin);

            var isAllowExpense = user.IsAllowExpense ?? true;

            if (!isAllowExpense)
            {
                throw new CommonException(ReasonCode.Not_Allow_Expense, MessageResources.PaymentForbidden);
            }

            var order = RedisHelper.Get <RedisOrderDTO>($"fiiipos:order:{orderNo}");

            RedisHelper.KeyDelete($"fiiipos:order:{orderNo}");
            if (order == null)
            {
                throw new ApplicationException(MessageResources.OrderNotFound);
            }

            //if (new OrderDAC().GetByOrderNo(orderNo) != null)
            //{
            //    throw new ApplicationException(Resources.订单已完成或者已退款);
            //}

            if (order.UserId != Guid.Empty)
            {
                if (order.UserId != user.Id)
                {
                    throw new ApplicationException(MessageResources.AccountNotMatch);
                }
            }

            var coin = new CryptocurrencyDAC().GetById(order.CryptoId);

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

            var userWallet = new UserWalletDAC().GetByAccountId(user.Id, order.CryptoId);

            if (userWallet == null)
            {
                throw new CommonException(ReasonCode.INSUFFICIENT_BALANCE, MessageResources.InsufficientBalance);
            }
            var     exchangeRate    = GetExchangeRate(order.CountryId, order.FiatCurrency, coin);
            decimal fiatTotalAmount = (order.FiatAmount * (1 + order.Markup)).ToSpecificDecimal(4);
            decimal cryptoAmount    = (fiatTotalAmount / exchangeRate).ToSpecificDecimal(coin.DecimalPlace);

            if (userWallet.Balance < cryptoAmount)
            {
                throw new CommonException(ReasonCode.INSUFFICIENT_BALANCE, MessageResources.InsufficientBalance);
            }

            var merchantAccount = new MerchantAccountDAC().GetById(order.MerchantGuid);

            if (!merchantAccount.IsAllowAcceptPayment || merchantAccount.Status == AccountStatus.Locked)
            {
                throw new CommonException(ReasonCode.Not_Allow_Withdrawal, MessageResources.MerchantExceptionTransClose);
            }

            var merchantWallet = new MerchantWalletDAC().GetByAccountId(order.MerchantGuid, order.CryptoId);

            if (merchantWallet == null || !merchantWallet.SupportReceipt)
            {
                throw new ApplicationException(MessageResources.MerchantNotSupperCrypto);
            }
            var country   = new CountryComponent().GetById(merchantAccount.CountryId);
            var orderData = new Order
            {
                Id                  = Guid.NewGuid(),
                OrderNo             = order.OrderNo,
                MerchantAccountId   = merchantAccount.Id,
                CryptoId            = coin.Id,
                CryptoCode          = coin.Code,
                FiatAmount          = order.FiatAmount,
                PaymentType         = order.Type,
                FiatCurrency        = order.FiatCurrency,
                Status              = OrderStatus.Completed,
                ExpiredTime         = DateTime.UtcNow.AddMinutes(30),
                Markup              = merchantAccount.Markup,
                ExchangeRate        = GetExchangeRate(merchantAccount.CountryId, order.FiatCurrency, coin),
                UnifiedExchangeRate = GetExchangeRate(merchantAccount.CountryId, country.FiatCurrency ?? "USD", coin),
                UnifiedFiatCurrency = country.FiatCurrency ?? "USD",
                MerchantIP          = order.ClientIP,
                PaymentTime         = DateTime.UtcNow,
                Timestamp           = DateTime.UtcNow,
                UserAccountId       = user.Id
            };

            if (merchantAccount.WithdrawalFeeType != WithdrawalFeeType.FiiiCoin)
            {
                var calcModel =
                    CalculateAmount(order.FiatAmount, merchantAccount.Markup, merchantAccount.Receivables_Tier, orderData.ExchangeRate, coin);

                orderData.ActualFiatAmount   = calcModel.FiatTotalAmount;
                orderData.CryptoAmount       = calcModel.CryptoAmount;
                orderData.TransactionFee     = calcModel.TransactionFee;
                orderData.ActualCryptoAmount = calcModel.ActualCryptoAmount;

                var model = CalculateUnifiedAmount(orderData.CryptoAmount, orderData.ActualCryptoAmount, orderData.UnifiedExchangeRate);
                orderData.UnifiedFiatAmount       = model.UnifiedFiatAmount;
                orderData.UnifiedActualFiatAmount = model.UnifiedActualFiatAmount;
                var orderWithdrawalFee = new OrderWithdrawalFee()
                {
                    Timestamp = DateTime.UtcNow
                };
                orderWithdrawalFee.CryptoId = orderData.CryptoId;
                orderWithdrawalFee.Amount   = 0;

                Execute(orderWithdrawalFee);
            }
            else
            {
                var orderWithdrawalFee = CalculateOrderAmount(ref orderData, order, merchantAccount, coin);
                var wallet             = new MerchantWalletDAC().GetByAccountId(merchantAccount.Id, new CryptocurrencyDAC().GetByCode("FIII").Id);
                if (orderWithdrawalFee.Amount != 0)
                {
                    using (var scope = new TransactionScope())
                    {
                        var dbOrder = new OrderDAC().Create(orderData);
                        new UserTransactionDAC().Insert(new UserTransaction
                        {
                            Id           = Guid.NewGuid(),
                            AccountId    = userWallet.UserAccountId,
                            CryptoId     = userWallet.CryptoId,
                            CryptoCode   = userWallet.CryptoCode,
                            Type         = UserTransactionType.Order,
                            DetailId     = dbOrder.Id.ToString(),
                            Status       = (byte)dbOrder.Status,
                            Timestamp    = dbOrder.Timestamp,
                            Amount       = dbOrder.CryptoAmount,
                            OrderNo      = dbOrder.OrderNo,
                            MerchantName = merchantAccount.MerchantName
                        });

                        orderWithdrawalFee.OrderId = dbOrder.Id;
                        var id = new OrderWithdrawalFeeDAC().Insert(orderWithdrawalFee);
                        new MerchantWalletDAC().Decrease(wallet.Id, orderWithdrawalFee.Amount);
                        new MerchantWalletDAC().Increase(merchantWallet.Id, orderData.ActualCryptoAmount);
                        new UserWalletDAC().Decrease(userWallet.Id, cryptoAmount);
                        new UserWalletStatementDAC().Insert(new UserWalletStatement
                        {
                            Action        = UserWalletStatementAction.Consume,
                            Amount        = orderData.CryptoAmount,
                            Balance       = userWallet.Balance - orderData.CryptoAmount,
                            FrozenAmount  = 0,
                            FrozenBalance = userWallet.FrozenBalance,
                            Remark        = null,
                            Timestamp     = DateTime.UtcNow,
                            WalletId      = userWallet.Id
                        });
                        new MerchantWalletStatementDAC().Insert(new MerchantWalletStatement
                        {
                            Action    = MerchantWalletStatementAction.Receipt,
                            Amount    = orderData.ActualCryptoAmount,
                            Balance   = merchantWallet.Balance + orderData.ActualCryptoAmount,
                            Remark    = null,
                            Timestamp = DateTime.UtcNow,
                            WalletId  = merchantWallet.Id
                        });
                        new MerchantWalletStatementDAC().Insert(new MerchantWalletStatement
                        {
                            Action    = MerchantWalletStatementAction.Withdrawal,
                            Amount    = orderWithdrawalFee.Amount,
                            Balance   = merchantWallet.Balance - orderData.ActualCryptoAmount,
                            Remark    = null,
                            Timestamp = DateTime.UtcNow,
                            WalletId  = wallet.Id
                        });
                        scope.Complete();
                    }
                }
                else
                {
                    Execute(orderWithdrawalFee);
                }
            }
            void Execute(OrderWithdrawalFee orderWithdrawalFee)
            {
                using (var scope = new TransactionScope())
                {
                    var dbOrder = new OrderDAC().Create(orderData);
                    new UserTransactionDAC().Insert(new UserTransaction
                    {
                        Id           = Guid.NewGuid(),
                        AccountId    = userWallet.UserAccountId,
                        CryptoId     = userWallet.CryptoId,
                        CryptoCode   = userWallet.CryptoCode,
                        Type         = UserTransactionType.Order,
                        DetailId     = dbOrder.Id.ToString(),
                        Status       = (byte)dbOrder.Status,
                        Timestamp    = dbOrder.Timestamp,
                        Amount       = dbOrder.CryptoAmount,
                        OrderNo      = dbOrder.OrderNo,
                        MerchantName = merchantAccount.MerchantName
                    });
                    orderWithdrawalFee.OrderId = dbOrder.Id;
                    var id = new OrderWithdrawalFeeDAC().Insert(orderWithdrawalFee);
                    new UserWalletDAC().Decrease(userWallet.Id, cryptoAmount);
                    new MerchantWalletDAC().Increase(merchantWallet.Id, orderData.ActualCryptoAmount);
                    new UserWalletStatementDAC().Insert(new UserWalletStatement
                    {
                        Action        = UserWalletStatementAction.Consume,
                        Amount        = orderData.CryptoAmount,
                        Balance       = userWallet.Balance - orderData.CryptoAmount,
                        FrozenAmount  = 0,
                        FrozenBalance = userWallet.FrozenBalance,
                        Remark        = null,
                        Timestamp     = DateTime.UtcNow,
                        WalletId      = userWallet.Id
                    });
                    new MerchantWalletStatementDAC().Insert(new MerchantWalletStatement
                    {
                        Action    = MerchantWalletStatementAction.Receipt,
                        Amount    = orderData.ActualCryptoAmount,
                        Balance   = merchantWallet.Balance + orderData.ActualCryptoAmount,
                        Remark    = null,
                        Timestamp = DateTime.UtcNow,
                        WalletId  = merchantWallet.Id
                    });
                    scope.Complete();
                }
            }

            UserMSMQ.PubOrderPayed(orderData.Id, 0);
            //PushConsume(orderData.Id);
            UserMSMQ.PubConsumeOrder(orderData.Id);

            return(new PayOrderOM
            {
                Amount = orderData.CryptoAmount.ToString(coin.DecimalPlace),
                Currency = new CryptocurrencyDAC().GetById(order.CryptoId).Code,
                OrderId = orderData.Id.ToString(),
                OrderNo = orderData.OrderNo,
                Timestamp = orderData.PaymentTime?.ToUnixTime().ToString()
            });
        }
Exemplo n.º 3
0
        /// <summary>
        /// 用户主动支付
        /// </summary>
        /// <param name="user"></param>
        /// <param name="im"></param>
        /// <returns></returns>
        public PayOrderOM Pay(UserAccount user, PayIM im)
        {
            if (im.Amount <= 0)
            {
                throw new CommonException(ReasonCode.GENERAL_ERROR, MessageResources.AmountGreater);
            }

            new SecurityComponent().VerifyPin(user, im.Pin);

            var isAllowExpense = user.IsAllowExpense ?? true;

            if (!isAllowExpense)
            {
                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 merchantAccount = GetMerchantAccountByIdOrCode(im.MerchantId, im.MerchantCode);

            if (merchantAccount == null)
            {
                throw new CommonException(ReasonCode.INVALID_QRCODE, MessageResources.InvalidQRCode);
            }

            if (!merchantAccount.IsAllowAcceptPayment || merchantAccount.Status == AccountStatus.Locked)
            {
                throw new CommonException(ReasonCode.Not_Allow_AcceptPayment, MessageResources.MerchantExceptionTransClose);
            }

            var country = new CountryComponent().GetById(merchantAccount.CountryId);

            var orderData = new Order
            {
                Id                  = Guid.NewGuid(),
                OrderNo             = IdentityHelper.OrderNo(),
                MerchantAccountId   = merchantAccount.Id,
                CryptoId            = coin.Id,
                CryptoCode          = coin.Code,
                FiatAmount          = im.Amount,
                PaymentType         = im.PaymentType,
                FiatCurrency        = merchantAccount.FiatCurrency,
                Status              = OrderStatus.Completed,
                ExpiredTime         = DateTime.UtcNow.AddMinutes(30),
                Markup              = merchantAccount.Markup,
                ExchangeRate        = GetExchangeRate(merchantAccount.CountryId, merchantAccount.FiatCurrency, coin),
                UnifiedExchangeRate = GetExchangeRate(merchantAccount.CountryId, country.FiatCurrency ?? "USD", coin),
                UnifiedFiatCurrency = country.FiatCurrency ?? "USD",
                MerchantIP          = null,
                PaymentTime         = DateTime.UtcNow,
                UserAccountId       = user.Id,
                Timestamp           = DateTime.UtcNow
            };

            var order = Generate(merchantAccount, coin, country.FiatCurrency ?? "USD", im.Amount, im.PaymentType);

            order.UserAccountId = user.Id;

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

            if (userWallet.Balance < order.CryptoAmount)
            {
                throw new CommonException(ReasonCode.INSUFFICIENT_BALANCE, MessageResources.InsufficientBalance);
            }

            order.Status      = OrderStatus.Completed;
            order.PaymentTime = DateTime.UtcNow;

            var merchantWallet = new MerchantWalletDAC().GetByAccountId(order.MerchantAccountId, order.CryptoId);

            if (merchantWallet == null || !merchantWallet.SupportReceipt)
            {
                throw new ApplicationException(MessageResources.MerchantNotSupperCrypto);
            }

            if (merchantAccount.WithdrawalFeeType != WithdrawalFeeType.FiiiCoin)
            {
                var calcModel =
                    CalculateAmount(im.Amount, merchantAccount.Markup, merchantAccount.Receivables_Tier, orderData.ExchangeRate, coin);

                orderData.ActualFiatAmount   = calcModel.FiatTotalAmount;
                orderData.CryptoAmount       = calcModel.CryptoAmount;
                orderData.TransactionFee     = calcModel.TransactionFee;
                orderData.ActualCryptoAmount = calcModel.ActualCryptoAmount;

                var model = CalculateUnifiedAmount(orderData.CryptoAmount, orderData.ActualCryptoAmount, orderData.UnifiedExchangeRate);
                orderData.UnifiedFiatAmount       = model.UnifiedFiatAmount;
                orderData.UnifiedActualFiatAmount = model.UnifiedActualFiatAmount;
                var orderWithdrawalFee = new OrderWithdrawalFee
                {
                    Timestamp = DateTime.UtcNow,
                    CryptoId  = orderData.CryptoId,
                    Amount    = 0
                };

                Execute(orderWithdrawalFee);
            }
            else
            {
                var orderWithdrawalFee = CalculateOrderAmount(ref orderData, new RedisOrderDTO()
                {
                    CountryId = merchantAccount.CountryId, FiatAmount = im.Amount
                }, merchantAccount, coin);
                var wallet = new MerchantWalletDAC().GetByAccountId(merchantAccount.Id, new CryptocurrencyDAC().GetByCode("FIII").Id);
                if (orderWithdrawalFee.Amount != 0)
                {
                    using (var scope = new TransactionScope())
                    {
                        var dbOrder = new OrderDAC().Create(orderData);
                        new UserTransactionDAC().Insert(new UserTransaction
                        {
                            Id           = Guid.NewGuid(),
                            AccountId    = userWallet.UserAccountId,
                            CryptoId     = userWallet.CryptoId,
                            CryptoCode   = userWallet.CryptoCode,
                            Type         = UserTransactionType.Order,
                            DetailId     = dbOrder.Id.ToString(),
                            Status       = (byte)dbOrder.Status,
                            Timestamp    = dbOrder.Timestamp,
                            Amount       = dbOrder.CryptoAmount,
                            OrderNo      = dbOrder.OrderNo,
                            MerchantName = merchantAccount.MerchantName
                        });
                        orderWithdrawalFee.OrderId = dbOrder.Id;
                        var id = new OrderWithdrawalFeeDAC().Insert(orderWithdrawalFee);

                        new MerchantWalletDAC().Decrease(wallet.Id, orderWithdrawalFee.Amount);
                        new MerchantWalletDAC().Increase(merchantWallet.Id, orderData.ActualCryptoAmount);
                        new UserWalletDAC().Decrease(userWallet.Id, orderData.ActualCryptoAmount);
                        new UserWalletStatementDAC().Insert(new UserWalletStatement
                        {
                            Action        = UserWalletStatementAction.Consume,
                            Amount        = orderData.CryptoAmount,
                            Balance       = userWallet.Balance - orderData.CryptoAmount,
                            FrozenAmount  = 0,
                            FrozenBalance = userWallet.FrozenBalance,
                            Remark        = null,
                            Timestamp     = DateTime.UtcNow,
                            WalletId      = userWallet.Id
                        });
                        new MerchantWalletStatementDAC().Insert(new MerchantWalletStatement
                        {
                            Action    = MerchantWalletStatementAction.Receipt,
                            Amount    = orderData.ActualCryptoAmount,
                            Balance   = merchantWallet.Balance + orderData.ActualCryptoAmount,
                            Remark    = null,
                            Timestamp = DateTime.UtcNow,
                            WalletId  = merchantWallet.Id
                        });
                        new MerchantWalletStatementDAC().Insert(new MerchantWalletStatement
                        {
                            Action    = MerchantWalletStatementAction.Withdrawal,
                            Amount    = orderWithdrawalFee.Amount,
                            Balance   = merchantWallet.Balance - orderData.ActualCryptoAmount,
                            Remark    = null,
                            Timestamp = DateTime.UtcNow,
                            WalletId  = wallet.Id
                        });
                        scope.Complete();
                    }
                }
                else
                {
                    Execute(orderWithdrawalFee);
                }
            }
            void Execute(OrderWithdrawalFee orderWithdrawalFee)
            {
                using (var scope = new TransactionScope())
                {
                    var dbOrder = new OrderDAC().Create(orderData);
                    new UserTransactionDAC().Insert(new UserTransaction
                    {
                        Id           = Guid.NewGuid(),
                        AccountId    = userWallet.UserAccountId,
                        CryptoId     = userWallet.CryptoId,
                        CryptoCode   = userWallet.CryptoCode,
                        Type         = UserTransactionType.Order,
                        DetailId     = dbOrder.Id.ToString(),
                        Status       = (byte)dbOrder.Status,
                        Timestamp    = dbOrder.Timestamp,
                        Amount       = dbOrder.CryptoAmount,
                        OrderNo      = dbOrder.OrderNo,
                        MerchantName = merchantAccount.MerchantName
                    });
                    orderWithdrawalFee.OrderId = dbOrder.Id;
                    var id = new OrderWithdrawalFeeDAC().Insert(orderWithdrawalFee);

                    new UserWalletDAC().Decrease(userWallet.Id, orderData.CryptoAmount);
                    new MerchantWalletDAC().Increase(merchantWallet.Id, orderData.ActualCryptoAmount);
                    new UserWalletStatementDAC().Insert(new UserWalletStatement
                    {
                        Action        = UserWalletStatementAction.Consume,
                        Amount        = orderData.CryptoAmount,
                        Balance       = userWallet.Balance - orderData.CryptoAmount,
                        FrozenAmount  = 0,
                        FrozenBalance = userWallet.FrozenBalance,
                        Remark        = null,
                        Timestamp     = DateTime.UtcNow,
                        WalletId      = userWallet.Id
                    });
                    new MerchantWalletStatementDAC().Insert(new MerchantWalletStatement
                    {
                        Action    = MerchantWalletStatementAction.Receipt,
                        Amount    = orderData.ActualCryptoAmount,
                        Balance   = merchantWallet.Balance + orderData.ActualCryptoAmount,
                        Remark    = null,
                        Timestamp = DateTime.UtcNow,
                        WalletId  = merchantWallet.Id
                    });
                    scope.Complete();
                }
            }

            UserMSMQ.PubOrderPayed(orderData.Id, 0);
            //PushConsume(order.Id);
            UserMSMQ.PubConsumeOrder(orderData.Id);

            return(new PayOrderOM
            {
                Amount = orderData.CryptoAmount.ToString(coin.DecimalPlace),
                Currency = new CryptocurrencyDAC().GetById(order.CryptoId).Code,
                OrderId = orderData.Id.ToString(),
                OrderNo = orderData.OrderNo,
                Timestamp = orderData.PaymentTime?.ToUnixTime().ToString()
            });
        }
Exemplo n.º 4
0
        private OrderWithdrawalFee CalculateOrderAmount(ref Order order, RedisOrderDTO orderDto, MerchantAccount account, Cryptocurrency coin)
        {
            var orderWithdrawalFee = new OrderWithdrawalFee()
            {
                Timestamp = DateTime.UtcNow
            };

            var wallet = new MerchantWalletDAC().GetByAccountId(account.Id, new CryptocurrencyDAC().GetByCode("FIII").Id);

            if (wallet == null || !wallet.SupportReceipt)
            {
                Excute(ref order);
            }
            else
            {
                var exchangeFiiiRate = GetExchangeRate(orderDto.CountryId, order.FiatCurrency, new CryptocurrencyDAC().GetById(wallet.CryptoId));
                var fiiiCoin         = new CryptocurrencyDAC().GetById(wallet.CryptoId);

                //var tempFiatActualAmount = orderDto.FiatAmount * (1 + account.Markup);
                var tempFiatActualAmount = orderDto.FiatAmount + (orderDto.FiatAmount * account.Markup).ToSpecificDecimal(4);

                var fiiiTransactionFee = ((orderDto.FiatAmount + orderDto.FiatAmount * account.Markup) * account.Receivables_Tier / exchangeFiiiRate)
                                         .ToSpecificDecimal(
                    fiiiCoin.DecimalPlace);
                if (wallet.Balance < fiiiTransactionFee)
                {
                    Excute(ref order);
                }
                else
                {
                    orderWithdrawalFee.CryptoId = fiiiCoin.Id;
                    orderWithdrawalFee.Amount   = fiiiTransactionFee;

                    order.ActualFiatAmount   = tempFiatActualAmount.ToSpecificDecimal(4);
                    order.CryptoAmount       = ((orderDto.FiatAmount + orderDto.FiatAmount * account.Markup) / order.ExchangeRate).ToSpecificDecimal(coin.DecimalPlace);
                    order.TransactionFee     = 0;
                    order.ActualCryptoAmount = order.CryptoAmount;

                    var model = CalculateUnifiedAmount(order.CryptoAmount, order.ActualCryptoAmount, order.UnifiedExchangeRate);
                    order.UnifiedFiatAmount       = model.UnifiedFiatAmount;
                    order.UnifiedActualFiatAmount = model.UnifiedActualFiatAmount;
                }
            }
            void Excute(ref Order inOrder)
            {
                var calcModel =
                    CalculateAmount(orderDto.FiatAmount, account.Markup, account.Receivables_Tier, inOrder.ExchangeRate, coin);

                inOrder.ActualFiatAmount   = calcModel.FiatTotalAmount;
                inOrder.CryptoAmount       = calcModel.CryptoAmount;
                inOrder.TransactionFee     = calcModel.TransactionFee;
                inOrder.ActualCryptoAmount = calcModel.ActualCryptoAmount;

                var model = CalculateUnifiedAmount(inOrder.CryptoAmount, inOrder.ActualCryptoAmount, inOrder.UnifiedExchangeRate);

                inOrder.UnifiedFiatAmount       = model.UnifiedFiatAmount;
                inOrder.UnifiedActualFiatAmount = model.UnifiedActualFiatAmount;

                orderWithdrawalFee.CryptoId = inOrder.CryptoId;
                orderWithdrawalFee.Amount   = 0;
            }

            return(orderWithdrawalFee);
        }