예제 #1
0
        public async Task <IPaymentRequestTransaction> CreateTransactionAsync(ICreateTransactionCommand command)
        {
            IPaymentRequestTransaction transaction = await _transactionsService.CreateTransactionAsync(command);

            await _paymentRequestService.UpdateStatusAsync(command.WalletAddress);

            return(transaction);
        }
예제 #2
0
        public async Task <ActionResult <AuthenticatedUser> > Register([FromBody] RegisterCredentials credentials)
        {
            var isAdded = await _users.AddUserAsync(credentials);

            if (isAdded)
            {
                var user = await _authenticateService.Authenticate(new LoginCredentials()
                {
                    Email = credentials.Email, Password = credentials.Password
                });

                if (user != null)
                {
                    var transaction = await _transactions.CreateTransactionAsync(null, user.Id, REGISTRATION_BONUS, DateTime.Now);

                    if (transaction != null)
                    {
                        var result = await _transactions.CommitTransactionAsync(transaction);

                        if (result != null)
                        {
                            return(Ok(user));
                        }
                    }
                }
                return(BadRequest(new { message = "Registration failed" }));
            }
            else
            {
                return(BadRequest(new { message = "User already registered" }));
            }
        }
예제 #3
0
        public async Task <TransactionDto?> CreateNewTransactionAsync(ITransactionModel transactionModel)
        {
            if (
                string.IsNullOrEmpty(transactionModel.ProfileId) ||
                transactionModel.AccountId == Guid.Empty.ToString() ||
                !await _licenseManager.EvaluateNewEntityAsync(transactionModel)
                )
            {
                return(null);
            }

            // Optimistic Concurrency Control: set the sequential number
            var sequencedTransaction = await _concurrencyManager.SetNextSequentialNumber(transactionModel);

            var newTransaction = await _transactionsService.CreateTransactionAsync(
                sequencedTransaction.ToTransactionEntity()
                );

            if (newTransaction != null)
            {
                await _publishEndpoint.Publish(newTransaction.ToTransactionModel <TransactionCreatedEvent>());

                await _publishEndpoint.Publish(newTransaction.ToTransactionModel <TransactionCheckCommand>());

                return(newTransaction.ToTransactionModel <TransactionDto>());
            }

            return(null);
        }
        private async Task RegisterTransferTxsAsync(TransferResult transfer, bool outgoing = true)
        {
            foreach (var transferTransactionResult in transfer.Transactions)
            {
                string historyId = outgoing
                    ? await _walletHistoryService.PublishOutgoingExchangeAsync(Mapper
                                                                               .Map <WalletHistoryOutgoingExchangeCommand>(transferTransactionResult).Map(transfer))
                    : await _walletHistoryService.PublishIncomingExchangeAsync(Mapper
                                                                               .Map <WalletHistoryIncomingExchangeCommand>(transferTransactionResult).Map(transfer));

                await _transactionsService.CreateTransactionAsync(
                    new CreateTransactionCommand
                {
                    Amount                = transferTransactionResult.Amount,
                    Blockchain            = transfer.Blockchain,
                    AssetId               = transferTransactionResult.AssetId,
                    Confirmations         = 0,
                    Hash                  = transferTransactionResult.Hash,
                    IdentityType          = transferTransactionResult.IdentityType,
                    Identity              = transferTransactionResult.Identity,
                    TransferId            = transfer.Id,
                    Type                  = TransactionType.Exchange,
                    SourceWalletAddresses = transferTransactionResult.Sources.ToArray(),
                    ContextData           = new ExchangeTransactonContext {
                        HistoryOperationId = historyId
                    }.ToJson()
                });
            }
        }
예제 #5
0
        public async Task <ActionResult <Transaction> > CreateTransaction([FromBody] UserTransaction userTransaction)
        {
            var transaction = await _transactions.CreateTransactionAsync(UserId, userTransaction.RecipientId, userTransaction.Amount, DateTime.Now);

            if (transaction != null)
            {
                return(Ok(transaction));
            }

            return(BadRequest(new { message = $"Could not create transaction" }));
        }
예제 #6
0
        public async void Should_CreateTransactionAsync_Valid()
        {
            var newTransactionEntity = new TransactionEntity
            {
                Amount    = 1,
                Approved  = true,
                ProfileId = 1.ToString(),
                Version   = 0
            };
            var newCreatedTransactionsEntity = await _service.CreateTransactionAsync(newTransactionEntity);

            Assert.NotNull(newCreatedTransactionsEntity);
        }
        public async Task <ActionResult <CreateTransactionResponse> > Post([FromBody] CreateTransactionRequest model)
        {
            var serviceModel = Mapper.Map <CreateTransactionModel>(model);

            try
            {
                var transaction = await _transactionsService.CreateTransactionAsync(serviceModel);

                var transactionItem = Mapper.Map <TransactionItem>(transaction);

                return(Ok(new CreateTransactionResponse(transactionItem)));
            }
            catch (CreateTransactionException ex)
            {
                if (ex.Error == CreateTransactionErrors.NotEnoughBalance)
                {
                    return(BadRequest("Balance is low"));
                }

                throw;
            }
        }
예제 #8
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));
        }
예제 #9
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)
            });
        }
예제 #10
0
        public async Task <CashoutResult> ExecuteAsync(CashoutCommand cmd)
        {
            BlockchainType network = await _assetSettingsService.GetNetworkAsync(cmd.SourceAssetId);

            string hotwallet = _bcnSettingsResolver.GetCashoutHotWallet(network);

            if (string.IsNullOrEmpty(hotwallet))
            {
                throw new CashoutHotwalletNotDefinedException(network);
            }

            string sourceAddress = await GetSourceAddressAsync(cmd);

            await _walletBalanceValidator.ValidateTransfer(sourceAddress, cmd.SourceAssetId, cmd.SourceAmount);

            TransferResult toHotWallet = await _retryPolicy
                                         .ExecuteAsync(() => _transferService.CashoutThrowFail(
                                                           cmd.SourceAssetId,
                                                           sourceAddress,
                                                           hotwallet,
                                                           cmd.SourceAmount));

            foreach (var transferTransactionResult in toHotWallet.Transactions)
            {
                string historyId = await _walletHistoryService.PublishCashoutAsync(new WalletHistoryCashoutCommand
                {
                    Blockchain      = toHotWallet.Blockchain,
                    AssetId         = transferTransactionResult.AssetId,
                    Amount          = transferTransactionResult.Amount,
                    DesiredAsset    = cmd.DesiredAsset,
                    EmployeeEmail   = cmd.EmployeeEmail,
                    TransactionHash = transferTransactionResult.Hash,
                    WalletAddress   = string.Join(Constants.Separator, transferTransactionResult.Sources)
                });

                await _transactionsService.CreateTransactionAsync(new CreateTransactionCommand
                {
                    Amount                = transferTransactionResult.Amount,
                    Blockchain            = toHotWallet.Blockchain,
                    AssetId               = transferTransactionResult.AssetId,
                    Confirmations         = 0,
                    Hash                  = transferTransactionResult.Hash,
                    IdentityType          = transferTransactionResult.IdentityType,
                    Identity              = transferTransactionResult.Identity,
                    TransferId            = toHotWallet.Id,
                    Type                  = TransactionType.CashOut,
                    SourceWalletAddresses = transferTransactionResult.Sources.ToArray(),
                    ContextData           = new CashoutTransactionContext
                    {
                        DesiredAsset       = cmd.DesiredAsset,
                        EmployeeEmail      = cmd.EmployeeEmail,
                        HistoryOperationId = historyId
                    }.ToJson()
                });
            }

            return(new CashoutResult
            {
                Amount = cmd.SourceAmount,
                AssetId = cmd.SourceAssetId,
                SourceWalletAddress = sourceAddress,
                DestWalletAddress = hotwallet
            });
        }