Exemplo n.º 1
0
        private async Task <PaymentRequestStatusInfo> GetStatusForPayment(IPaymentRequest paymentRequest)
        {
            IReadOnlyList <IPaymentRequestTransaction> txs =
                (await _transactionsService.GetByWalletAsync(paymentRequest.WalletAddress)).Where(x => x.IsPayment()).ToList();

            if (!txs.Any())
            {
                return((paymentRequest.DueDate < DateTime.UtcNow)
                    ? PaymentRequestStatusInfo.Error(PaymentRequestProcessingError.PaymentExpired)
                    : PaymentRequestStatusInfo.New());
            }

            decimal btcPaid;

            var assetId = txs.GetAssetId();

            switch (assetId)
            {
            case LykkeConstants.SatoshiAsset:
                btcPaid = txs.GetTotal().SatoshiToBtc();
                break;

            default:
                btcPaid = txs.GetTotal();
                break;
            }

            bool allConfirmed = txs.All(x => x.Confirmed(_transactionConfirmationCount));

            var paidDate = txs.GetLatestDate();

            if (paidDate > paymentRequest.DueDate)
            {
                if (allConfirmed)
                {
                    return(PaymentRequestStatusInfo.Error(PaymentRequestProcessingError.LatePaid, btcPaid, paidDate));
                }

                return(paymentRequest.GetCurrentStatusInfo());
            }

            IOrder actualOrder = await _orderService.GetActualAsync(paymentRequest.Id, paidDate) ??
                                 await _orderService.GetLatestOrCreateAsync(paymentRequest);

            if (!allConfirmed)
            {
                return(PaymentRequestStatusInfo.InProcess());
            }

            decimal btcToBePaid = actualOrder.PaymentAmount;

            var fulfillment = await _calculationService.CalculateBtcAmountFullfillmentAsync(btcToBePaid, btcPaid);

            switch (fulfillment)
            {
            case AmountFullFillmentStatus.Below:
                return(PaymentRequestStatusInfo.Error(PaymentRequestProcessingError.PaymentAmountBelow, btcPaid, paidDate));

            case AmountFullFillmentStatus.Exact:
                return(PaymentRequestStatusInfo.Confirmed(btcPaid, paidDate));

            case AmountFullFillmentStatus.Above:
                return(PaymentRequestStatusInfo.Error(PaymentRequestProcessingError.PaymentAmountAbove, btcPaid, paidDate));

            default: throw new Exception("Unexpected amount fullfillment status");
            }
        }
Exemplo n.º 2
0
        public async Task <PaymentResult> PayAsync(PaymentCommand cmd)
        {
            IPaymentRequest paymentRequest = await _paymentRequestRepository.GetAsync(cmd.MerchantId, cmd.PaymentRequestId);

            if (paymentRequest == null)
            {
                throw new PaymentRequestNotFoundException(cmd.MerchantId, cmd.PaymentRequestId);
            }

            string payerWalletAddress = (await _merchantWalletService.GetDefaultAsync(
                                             cmd.PayerMerchantId,
                                             paymentRequest.PaymentAssetId,
                                             PaymentDirection.Outgoing)).WalletAddress;

            string destinationWalletAddress = await _walletsManager.ResolveBlockchainAddressAsync(
                paymentRequest.WalletAddress,
                paymentRequest.PaymentAssetId);

            bool locked = await _paymentLocksService.TryAcquireLockAsync(
                paymentRequest.Id,
                cmd.MerchantId,
                paymentRequest.DueDate);

            if (!locked)
            {
                throw new DistributedLockAcquireException(paymentRequest.Id);
            }

            TransferResult transferResult;

            try
            {
                await UpdateStatusAsync(paymentRequest.WalletAddress, PaymentRequestStatusInfo.InProcess());

                transferResult = await _paymentRetryPolicy
                                 .ExecuteAsync(() => _transferService.PayThrowFail(
                                                   paymentRequest.PaymentAssetId,
                                                   payerWalletAddress,
                                                   destinationWalletAddress,
                                                   cmd.Amount));

                foreach (var transferResultTransaction in transferResult.Transactions)
                {
                    IPaymentRequestTransaction paymentTx = await _transactionsService.CreateTransactionAsync(
                        new CreateTransactionCommand
                    {
                        Amount                = transferResultTransaction.Amount,
                        Blockchain            = transferResult.Blockchain,
                        AssetId               = transferResultTransaction.AssetId,
                        WalletAddress         = paymentRequest.WalletAddress,
                        DueDate               = paymentRequest.DueDate,
                        IdentityType          = transferResultTransaction.IdentityType,
                        Identity              = transferResultTransaction.Identity,
                        Confirmations         = 0,
                        Hash                  = transferResultTransaction.Hash,
                        TransferId            = transferResult.Id,
                        Type                  = TransactionType.Payment,
                        SourceWalletAddresses = transferResultTransaction.Sources.ToArray()
                    });

                    await _transactionPublisher.PublishAsync(paymentTx);
                }
            }
            catch (Exception e)
            {
                PaymentRequestStatusInfo newStatus = e is InsufficientFundsException
                    ? PaymentRequestStatusInfo.New()
                    : PaymentRequestStatusInfo.Error(PaymentRequestProcessingError.UnknownPayment);

                await UpdateStatusAsync(paymentRequest.WalletAddress, newStatus);

                await _paymentLocksService.ReleaseLockAsync(paymentRequest.Id, cmd.MerchantId);

                throw;
            }

            return(new PaymentResult
            {
                PaymentRequestId = paymentRequest.Id,
                PaymentRequestWalletAddress = paymentRequest.WalletAddress,
                AssetId = transferResult.Transactions.Unique(x => x.AssetId).Single(),
                Amount = transferResult.GetSuccedeedTxs().Sum(x => x.Amount)
            });
        }