예제 #1
0
        public async Task <RefundResult> ExecuteAsync(string merchantId, string paymentRequestId,
                                                      string destinationWalletAddress)
        {
            IPaymentRequest paymentRequest =
                await _paymentRequestService.GetAsync(merchantId, paymentRequestId);

            if (paymentRequest == null)
            {
                throw new RefundValidationException(RefundErrorType.PaymentRequestNotFound);
            }

            if (!paymentRequest.StatusValidForRefund())
            {
                throw new RefundValidationException(RefundErrorType.NotAllowedInStatus);
            }

            IEnumerable <IPaymentRequestTransaction> paymentTxs =
                (await _transactionsService.GetByWalletAsync(paymentRequest.WalletAddress)).Where(x => x.IsPayment()).ToList();

            if (!paymentTxs.Any())
            {
                throw new RefundValidationException(RefundErrorType.NoPaymentTransactions);
            }

            if (paymentTxs.MoreThanOne())
            {
                throw new RefundValidationException(RefundErrorType.MultitransactionNotSupported);
            }

            IPaymentRequestTransaction tx = paymentTxs.Single();

            bool isValidAddress = string.IsNullOrWhiteSpace(destinationWalletAddress) ||
                                  await _blockchainAddressValidator.Execute(destinationWalletAddress, tx.Blockchain);

            if (!isValidAddress)
            {
                throw new RefundValidationException(RefundErrorType.InvalidDestinationAddress);
            }

            if (!tx.SourceWalletAddresses.Any())
            {
                throw new RefundValidationException(RefundErrorType.InvalidDestinationAddress);
            }

            if (string.IsNullOrWhiteSpace(destinationWalletAddress))
            {
                if (tx.SourceWalletAddresses.MoreThanOne())
                {
                    throw new RefundValidationException(RefundErrorType.InvalidDestinationAddress);
                }
            }

            //validation finished, refund request accepted
            await _paymentRequestService.UpdateStatusAsync(paymentRequest.WalletAddress,
                                                           PaymentRequestStatusInfo.RefundInProgress());

            TransferResult transferResult;

            DateTime refundDueDate;

            try
            {
                TransferCommand refundTransferCommand = Mapper.Map <TransferCommand>(tx,
                                                                                     opts => opts.Items["destinationAddress"] = destinationWalletAddress);

                transferResult = await _transferService.ExecuteAsync(refundTransferCommand);

                refundDueDate = transferResult.Timestamp.Add(_refundExpirationPeriod);

                foreach (var transferResultTransaction in transferResult.Transactions)
                {
                    if (!string.IsNullOrEmpty(transferResultTransaction.Error))
                    {
                        await _log.WriteWarningAsync(nameof(RefundService), nameof(ExecuteAsync),
                                                     transferResultTransaction.ToJson(), "Transaction failed");

                        continue;
                    }

                    IPaymentRequestTransaction refundTransaction = await _transactionsService.CreateTransactionAsync(
                        new CreateTransactionCommand
                    {
                        Amount        = transferResultTransaction.Amount,
                        AssetId       = transferResultTransaction.AssetId,
                        Confirmations = 0,
                        Hash          = transferResultTransaction.Hash,
                        WalletAddress = paymentRequest.WalletAddress,
                        Type          = TransactionType.Refund,
                        Blockchain    = transferResult.Blockchain,
                        FirstSeen     = null,
                        DueDate       = refundDueDate,
                        TransferId    = transferResult.Id,
                        IdentityType  = transferResultTransaction.IdentityType,
                        Identity      = transferResultTransaction.Identity
                    });

                    await _transactionPublisher.PublishAsync(refundTransaction);
                }

                if (transferResult.Transactions.All(x => x.HasError))
                {
                    throw new RefundOperationFailedException {
                              TransferErrors = transferResult.Transactions.Select(x => x.Error)
                    }
                }
                ;

                IEnumerable <TransferTransactionResult> errorTransactions =
                    transferResult.Transactions.Where(x => x.HasError).ToList();

                if (errorTransactions.Any())
                {
                    throw new RefundOperationPartiallyFailedException(errorTransactions.Select(x => x.Error));
                }
            }
            catch (Exception)
            {
                await _paymentRequestService.UpdateStatusAsync(paymentRequest.WalletAddress,
                                                               PaymentRequestStatusInfo.Error(PaymentRequestProcessingError.UnknownRefund));

                throw;
            }

            return(await PrepareRefundResult(paymentRequest, transferResult, refundDueDate));
        }
예제 #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)
            });
        }