public async Task <PaymentRequestStatusInfo> GetStatus(string walletAddress) { IPaymentRequest paymentRequest = await _paymentRequestRepository.FindAsync(walletAddress); if (paymentRequest == null) { throw new PaymentRequestNotFoundException(walletAddress); } IReadOnlyList <IPaymentRequestTransaction> txs = await _transactionsService.GetByWalletAsync(paymentRequest.WalletAddress); PaymentRequestStatusInfo paymentStatusInfo; if (txs.Any(x => x.IsSettlement())) { paymentStatusInfo = await GetStatusForSettlement(paymentRequest); } else if (txs.Any(x => x.IsRefund())) { paymentStatusInfo = await GetStatusForRefund(paymentRequest); } else if (txs.Any(x => x.IsPayment())) { paymentStatusInfo = await GetStatusForPayment(paymentRequest); } else { throw new Exception("Inconsistent paymentRequest status"); } return(paymentStatusInfo); }
public async Task <PaymentRequestRefund> GetRefundInfoAsync(string walletAddress) { IReadOnlyList <IPaymentRequestTransaction> transactions = (await _transactionsService.GetByWalletAsync(walletAddress)).Where(x => x.IsRefund()).ToList(); if (!transactions.Any()) { return(null); } IEnumerable <string> transferIds = transactions.Unique(x => x.TransferId).ToList(); if (transferIds.MoreThanOne()) { throw new MultiTransactionRefundNotSupportedException(); } Transfer transfer = await _transferService.GetAsync(transferIds.Single()); IPaymentRequest paymentRequest = await FindAsync(walletAddress); return(new PaymentRequestRefund { Amount = transfer.Amounts.Sum(x => x.Amount ?? 0), Timestamp = transfer.CreatedOn, Address = transfer.Amounts.Unique(x => x.Destination).Single(), DueDate = transactions.OrderByDescending(x => x.DueDate).First().DueDate ?? paymentRequest.DueDate, Transactions = Mapper.Map <IEnumerable <PaymentRequestRefundTransaction> >(transactions) }); }
public async Task <IEnumerable <IWalletState> > GetNotExpiredStateAsync() { IReadOnlyList <IVirtualWallet> wallets = await _virtualWalletService.GetNotExpiredAsync(); var transactions = new List <IPaymentRequestTransaction>(); foreach (IEnumerable <IVirtualWallet> batch in wallets.Batch(BatchPieceSize)) { await Task.WhenAll(batch.Select(x => _transactionsService.GetByWalletAsync(x.Id) .ContinueWith(t => { lock (transactions) { transactions.AddRange(t.Result); } }))); } var walletStateResult = new List <WalletState>(); foreach (IVirtualWallet virtualWallet in wallets) { walletStateResult.AddRange(virtualWallet.BlockchainWallets.Select(bcnWallet => new WalletState { DueDate = virtualWallet.DueDate, Address = bcnWallet.Address, Blockchain = bcnWallet.Blockchain, Transactions = transactions.Where(t => t.WalletAddress == virtualWallet.Id && t.Blockchain == bcnWallet.Blockchain) })); } return(walletStateResult); }
public async Task <TResult> Build <TResult, TOrder, TTransaction, TRefund>(IPaymentRequest paymentRequest, PaymentRequestRefund refundInfo) { IOrder order = await _orderService.GetAsync(paymentRequest.Id, paymentRequest.OrderId); IReadOnlyList <IPaymentRequestTransaction> transactions = await _transactionsService.GetByWalletAsync(paymentRequest.WalletAddress); IEnumerable <IPaymentRequestTransaction> paymentTransactions = transactions.Where(x => x.IsPayment()); dynamic model = Mapper.Map <TResult>(paymentRequest); model.Order = Mapper.Map <TOrder>(order); model.Transactions = Mapper.Map <List <TTransaction> >(paymentTransactions); model.Refund = Mapper.Map <TRefund>(refundInfo); return(model); }
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)); }