public async Task TryToProcessPaymentTransferEvent_BurnRuleNotFound_SuccessfullyProcessedWithMissingName()
        {
            _campaignClientMock.Setup(x => x.History.GetBurnRuleByIdAsync(It.IsAny <Guid>()))
            .ReturnsAsync(new BurnRuleResponse {
                ErrorCode = CampaignServiceErrorCodes.EntityNotFound
            });

            _paymentTransferRepoMock.Setup(x => x.AddPaymentTransferAsync(It.IsAny <PaymentTransferDto>()))
            .Returns(Task.CompletedTask)
            .Verifiable();

            var paymentTransfer = new PaymentTransferDto
            {
                AssetSymbol = "asset",
                TransferId  = "transferId",
                Amount      = 100,
                CustomerId  = "customerId",
                InvoiceId   = "invoiceId",
                BurnRuleId  = "3cfd1d93-34e4-44a5-9ccd-96734b24faa4",
            };

            var operationsService = CreateSutInstance();

            await operationsService.ProcessPaymentTransferTokensReservedEventAsync(paymentTransfer);

            _paymentTransferRepoMock.Verify(x => x.AddPaymentTransferAsync(It.Is <PaymentTransferDto>(o => o.BurnRuleName == "N/A")));
        }
        public async Task TryToProcessPaymentTransferEventEvent_InvalidBalance_SuccessfullyProcessedWithWarning(long amount)
        {
            var campaignResponseModel = new BurnRuleResponse {
                Title = "burnRule"
            };

            _campaignClientMock.Setup(x => x.History.GetBurnRuleByIdAsync(It.IsAny <Guid>()))
            .ReturnsAsync(campaignResponseModel);

            _paymentTransferRepoMock.Setup(x => x.AddPaymentTransferAsync(It.IsAny <PaymentTransferDto>()))
            .Returns(Task.CompletedTask)
            .Verifiable();

            var paymentTransfer = new PaymentTransferDto
            {
                AssetSymbol = "asset",
                TransferId  = "transferId",
                Amount      = amount,
                CustomerId  = "customerId",
                InvoiceId   = "invoiceId",
                BurnRuleId  = "3cfd1d93-34e4-44a5-9ccd-96734b24faa4",
            };

            var operationsService = CreateSutInstance();

            await operationsService.ProcessPaymentTransferTokensReservedEventAsync(paymentTransfer);

            _paymentTransferRepoMock.Verify();
        }
        public async Task AddPaymentTransferRefundAsync(PaymentTransferDto paymentTransfer)
        {
            using (var context = _contextFactory.CreateDataContext())
            {
                var historyEntity  = TransactionHistoryEntity.CreateForPaymentTransferRefund(paymentTransfer);
                var transferEntity = PaymentTransferRefundEntity.Create(paymentTransfer);

                var burnRule =
                    await context.BurnRules.FindAsync(paymentTransfer.BurnRuleId);

                if (burnRule != null && burnRule.BurnRuleName != paymentTransfer.BurnRuleName)
                {
                    burnRule.BurnRuleName = paymentTransfer.BurnRuleName;
                }

                if (burnRule == null)
                {
                    burnRule = BurnRuleEntity.Create(paymentTransfer.BurnRuleId, paymentTransfer.BurnRuleName);
                }

                transferEntity.BurnRule = burnRule;

                context.RefundedPaymentTransfers.Add(transferEntity);
                context.TransactionHistories.Add(historyEntity);

                await context.SaveChangesAsync();
            }
        }
        public async Task CustomerTriesToMakePaymentTransfer_InvalidAmountInFiat_ErrorReturned()
        {
            _cpClient.Setup(x => x.CustomerProfiles.GetByCustomerIdAsync(It.IsAny <string>(), It.IsAny <bool>(), It.IsAny <bool>()))
            .ReturnsAsync(new CustomerProfileResponse
            {
                Profile = new CustomerProfile.Client.Models.Responses.CustomerProfile()
            });

            _wmClient
            .Setup(x => x.Api.GetCustomerWalletBlockStateAsync(It.IsAny <string>()))
            .ReturnsAsync(new CustomerWalletBlockStatusResponse {
                Status = CustomerWalletActivityStatus.Active
            });

            _campaignClient.Setup(x => x.BurnRules.GetByIdAsync(It.IsAny <Guid>()))
            .ReturnsAsync(new BurnRuleResponse {
                ErrorCode = CampaignServiceErrorCodes.None, Vertical = Vertical.RealEstate
            });

            var requestDto = new PaymentTransferDto
            {
                CustomerId   = FakeCustomerId,
                SpendRuleId  = FakeCampaignId,
                AmountInFiat = 0
            };

            var sut = CreateSutInstance();

            var result = await sut.PaymentTransferAsync(requestDto);

            Assert.Equal(PaymentTransfersErrorCodes.InvalidFiatAmount, result);
        }
Beispiel #5
0
 private static bool VerifyReceivedTransferModelParameterValues(PaymentTransferDto model)
 {
     return(model.TransferId == FakeTransferId &&
            model.AmountInFiat == FakeAmount &&
            model.SpendRuleId == FakeCampaignId &&
            model.CustomerId == FakeCustomerId &&
            model.InvoiceId == FakeInvoiceId &&
            model.Status == PaymentTransferStatus.Pending);
 }
 public static TransactionHistoryEntity CreateForPaymentTransferRefund(PaymentTransferDto paymentTransfer)
 {
     return(new TransactionHistoryEntity
     {
         CustomerId = paymentTransfer.CustomerId,
         Timestamp = paymentTransfer.Timestamp,
         TransactionId = paymentTransfer.TransferId,
         AssetSymbol = paymentTransfer.AssetSymbol,
         Type = OperationType.PaymentTransferRefunded.ToString(),
     });
 }
Beispiel #7
0
        public async Task AddAsync(PaymentTransferDto paymentTransfer)
        {
            using (var context = _contextFactory.CreateDataContext())
            {
                var entity = PaymentTransferEntity.Create(paymentTransfer);

                context.PaymentTransfers.Add(entity);

                await context.SaveChangesAsync();
            }
        }
Beispiel #8
0
        public async Task ProcessRefundPaymentTransferEventAsync(PaymentTransferDto paymentTransferDto)
        {
            var isValid = ValidatePaymentTransfer(paymentTransferDto);

            if (!isValid)
            {
                return;
            }

            await AddAdditionalDataToPaymentTransfer(paymentTransferDto);

            await _paymentTransfersRepository.AddPaymentTransferRefundAsync(paymentTransferDto);
        }
        public async Task CustomerTriesToMakePaymentTransfer_SuccessfullyCreated()
        {
            _cpClient.Setup(x => x.CustomerProfiles.GetByCustomerIdAsync(It.IsAny <string>(), It.IsAny <bool>(), It.IsAny <bool>()))
            .ReturnsAsync(new CustomerProfileResponse
            {
                Profile = new CustomerProfile.Client.Models.Responses.CustomerProfile()
            });

            _wmClient
            .Setup(x => x.Api.GetCustomerWalletBlockStateAsync(It.IsAny <string>()))
            .ReturnsAsync(new CustomerWalletBlockStatusResponse {
                Status = CustomerWalletActivityStatus.Active
            });

            _campaignClient.Setup(x => x.BurnRules.GetByIdAsync(It.IsAny <Guid>()))
            .ReturnsAsync(new BurnRuleResponse {
                ErrorCode = CampaignServiceErrorCodes.None, Vertical = Vertical.RealEstate
            });

            _eligibilityEngineClientMock.Setup(x =>
                                               x.ConversionRate.GetAmountBySpendRuleAsync(It.IsAny <ConvertAmountBySpendRuleRequest>()))
            .ReturnsAsync(new ConvertAmountBySpendRuleResponse
            {
                ErrorCode = EligibilityEngineErrors.None,
                Amount    = 2 * ValidAmount
            });

            _pbfClientMock.Setup(x =>
                                 x.GenericTransfersApi.GenericTransferAsync(It.IsAny <GenericTransferRequestModel>()))
            .ReturnsAsync(new TransferResponseModel {
                Error = TransferError.None
            });

            var requestDto = new PaymentTransferDto
            {
                CustomerId   = FakeCustomerId,
                SpendRuleId  = FakeCampaignId,
                AmountInFiat = ValidAmount
            };

            var amountInTokens = 2 * ValidAmount;

            var sut = CreateSutInstance();

            var result = await sut.PaymentTransferAsync(requestDto);

            Assert.Equal(PaymentTransfersErrorCodes.None, result);
            _paymentTransfersRepositoryMock.Verify(x => x.AddAsync(It.Is <PaymentTransferDto>(p =>
                                                                                              p.AmountInTokens == amountInTokens && p.AmountInFiat == ValidAmount && p.ReceiptNumber.Length <= 30)));
            _pbfClientMock.Verify(x => x.GenericTransfersApi.GenericTransferAsync(It.Is <GenericTransferRequestModel>(t => t.Amount == amountInTokens)), Times.Once);
        }
Beispiel #10
0
 public static PaymentTransferRefundEntity Create(PaymentTransferDto paymentTransfer)
 {
     return(new PaymentTransferRefundEntity
     {
         Amount = paymentTransfer.Amount,
         Timestamp = paymentTransfer.Timestamp,
         CustomerId = paymentTransfer.CustomerId,
         BurnRuleId = paymentTransfer.BurnRuleId,
         InvoiceId = paymentTransfer.InvoiceId,
         TransferId = paymentTransfer.TransferId,
         AssetSymbol = paymentTransfer.AssetSymbol,
         InstalmentName = paymentTransfer.InstalmentName,
         LocationCode = paymentTransfer.LocationCode,
     });
 }
        public async Task CustomerTriesToMakePaymentTransfer_ErrorFromPbf_ErrorReturned
            (TransferError pbfError, PaymentTransfersErrorCodes expectedError)
        {
            _cpClient.Setup(x => x.CustomerProfiles.GetByCustomerIdAsync(It.IsAny <string>(), It.IsAny <bool>(), It.IsAny <bool>()))
            .ReturnsAsync(new CustomerProfileResponse
            {
                Profile = new CustomerProfile.Client.Models.Responses.CustomerProfile()
            });

            _wmClient
            .Setup(x => x.Api.GetCustomerWalletBlockStateAsync(It.IsAny <string>()))
            .ReturnsAsync(new CustomerWalletBlockStatusResponse {
                Status = CustomerWalletActivityStatus.Active
            });

            _campaignClient.Setup(x => x.BurnRules.GetByIdAsync(It.IsAny <Guid>()))
            .ReturnsAsync(new BurnRuleResponse {
                ErrorCode = CampaignServiceErrorCodes.None, Vertical = Vertical.RealEstate
            });

            _eligibilityEngineClientMock.Setup(x =>
                                               x.ConversionRate.GetAmountBySpendRuleAsync(It.IsAny <ConvertAmountBySpendRuleRequest>()))
            .ReturnsAsync(new ConvertAmountBySpendRuleResponse
            {
                ErrorCode = EligibilityEngineErrors.None,
                Amount    = ValidAmount
            });

            _pbfClientMock.Setup(x =>
                                 x.GenericTransfersApi.GenericTransferAsync(It.IsAny <GenericTransferRequestModel>()))
            .ReturnsAsync(new TransferResponseModel {
                Error = pbfError
            });

            var requestDto = new PaymentTransferDto
            {
                CustomerId   = FakeCustomerId,
                SpendRuleId  = FakeCampaignId,
                AmountInFiat = ValidAmount
            };

            var sut = CreateSutInstance();

            var result = await sut.PaymentTransferAsync(requestDto);

            Assert.Equal(expectedError, result);
        }
Beispiel #12
0
        private bool ValidatePaymentTransfer(PaymentTransferDto paymentTransfer)
        {
            var isValid = true;

            if (paymentTransfer.Amount <= 0)
            {
                _log.Warning("Processed payment transfer event with invalid balance", context: paymentTransfer);
            }

            if (string.IsNullOrEmpty(paymentTransfer.InstalmentName))
            {
                _log.Warning("Processed payment transfer with missing Instalment name, Default name will be used", context: paymentTransfer);
            }

            if (string.IsNullOrEmpty(paymentTransfer.LocationCode))
            {
                _log.Warning("Processed payment transfer with missing LocationCode", context: paymentTransfer);
            }

            if (string.IsNullOrEmpty(paymentTransfer.TransferId))
            {
                isValid = false;
                _log.Warning("Payment transfer event without transfer id", context: paymentTransfer);
            }

            if (string.IsNullOrEmpty(paymentTransfer.CustomerId))
            {
                isValid = false;
                _log.Warning("Payment transfer event without customer id", context: paymentTransfer);
            }

            if (string.IsNullOrEmpty(paymentTransfer.InvoiceId))
            {
                isValid = false;
                _log.Warning("Payment transfer event without invoice id type", context: paymentTransfer);
            }

            if (string.IsNullOrEmpty(paymentTransfer.BurnRuleId))
            {
                isValid = false;
                _log.Warning("Payment transfer event without campaign id", context: paymentTransfer);
            }

            return(isValid);
        }
Beispiel #13
0
        private async Task AddAdditionalDataToPaymentTransfer(PaymentTransferDto paymentTransferDto)
        {
            paymentTransferDto.AssetSymbol = _tokenSymbol;

            paymentTransferDto.InstalmentName = string.IsNullOrEmpty(paymentTransferDto.InstalmentName)
                ? "DefaultInstalmentName"
                : paymentTransferDto.InstalmentName;

            var burnRule = await _campaignClient.History.GetBurnRuleByIdAsync(Guid.Parse(paymentTransferDto.BurnRuleId));

            if (burnRule.ErrorCode == CampaignServiceErrorCodes.EntityNotFound)
            {
                _log.Warning("Burn rule not found for payment transfer event", context: paymentTransferDto);
                paymentTransferDto.BurnRuleName = "N/A";
            }
            else
            {
                paymentTransferDto.BurnRuleName = burnRule.Title;
            }
        }
        public async Task TryToPaymentTransferEvent_MissingDataInEvent_NotProcessed
            (string transferId, string customerId, string invoiceId, string burnRuleId)
        {
            _paymentTransferRepoMock.Setup(x => x.AddPaymentTransferAsync(It.IsAny <PaymentTransferDto>()))
            .Returns(Task.CompletedTask)
            .Verifiable();

            var paymentTransfer = new PaymentTransferDto
            {
                TransferId = transferId,
                Amount     = 100,
                CustomerId = customerId,
                InvoiceId  = invoiceId,
                BurnRuleId = burnRuleId,
            };

            var operationsService = CreateSutInstance();

            await operationsService.ProcessPaymentTransferTokensReservedEventAsync(paymentTransfer);

            _paymentTransferRepoMock.Verify(x => x.AddPaymentTransferAsync(It.IsAny <PaymentTransferDto>()), Times.Never);
        }
 public static PaymentTransferEntity Create(PaymentTransferDto model)
 {
     return(new PaymentTransferEntity
     {
         InvoiceId = model.InvoiceId,
         SpendRuleId = model.SpendRuleId,
         CustomerId = model.CustomerId,
         Status = model.Status,
         Timestamp = model.Timestamp,
         TransferId = model.TransferId,
         AmountInTokens = model.AmountInTokens.Value,
         CustomerAccountNumber = model.CustomerAccountNumber,
         CustomerTrxId = model.CustomerTrxId,
         InstallmentType = model.InstallmentType,
         LocationCode = model.LocationCode,
         ReceiptNumber = model.ReceiptNumber,
         AmountInFiat = model.AmountInFiat.Value,
         Currency = model.Currency,
         OrgId = model.OrgId,
         InstalmentName = model.InstalmentName,
     });
 }
Beispiel #16
0
        public async Task <PaymentTransfersErrorCodes> PaymentTransferAsync(PaymentTransferDto paymentTransfer)
        {
            #region Validation

            if (!await CheckIfCustomerExists(paymentTransfer.CustomerId))
            {
                return(PaymentTransfersErrorCodes.CustomerDoesNotExist);
            }

            if (await CheckIfCustomerWalletIsBlocked(paymentTransfer.CustomerId))
            {
                return(PaymentTransfersErrorCodes.CustomerWalletBlocked);
            }

            var isCampaignIdGuid = Guid.TryParse(paymentTransfer.SpendRuleId, out var campaignIdAsGuid);
            if (!isCampaignIdGuid)
            {
                return(PaymentTransfersErrorCodes.InvalidSpendRuleId);
            }

            var campaignResult = await _campaignClient.BurnRules.GetByIdAsync(campaignIdAsGuid);

            if (campaignResult.ErrorCode == CampaignServiceErrorCodes.EntityNotFound)
            {
                return(PaymentTransfersErrorCodes.SpendRuleNotFound);
            }

            if (campaignResult.Vertical != Vertical.RealEstate)
            {
                return(PaymentTransfersErrorCodes.InvalidVerticalInSpendRule);
            }

            //We can have either amount in Tokens or in Fiat
            if (paymentTransfer.AmountInFiat != null && paymentTransfer.AmountInTokens != null)
            {
                return(PaymentTransfersErrorCodes.CannotPassBothFiatAndTokensAmount);
            }

            //Fiat or Tokens Amount must be provided
            if (paymentTransfer.AmountInFiat == null && paymentTransfer.AmountInTokens == null)
            {
                return(PaymentTransfersErrorCodes.EitherFiatOrTokensAmountShouldBePassed);
            }

            if (paymentTransfer.AmountInTokens != null && paymentTransfer.AmountInTokens <= 0)
            {
                return(PaymentTransfersErrorCodes.InvalidTokensAmount);
            }

            if (paymentTransfer.AmountInFiat != null && paymentTransfer.AmountInFiat <= 0)
            {
                return(PaymentTransfersErrorCodes.InvalidFiatAmount);
            }

            #endregion

            var conversionRequest = new ConvertAmountBySpendRuleRequest
            {
                CustomerId   = Guid.Parse(paymentTransfer.CustomerId),
                Amount       = paymentTransfer.AmountInTokens ?? paymentTransfer.AmountInFiat.Value,
                FromCurrency = paymentTransfer.AmountInTokens.HasValue ? _settingsService.GetTokenCurrencyCode() : paymentTransfer.Currency,
                ToCurrency   = paymentTransfer.AmountInTokens.HasValue ? paymentTransfer.Currency : _settingsService.GetTokenCurrencyCode(),
                SpendRuleId  = campaignIdAsGuid
            };

            var conversionResponse = await _eligibilityEngineClient.ConversionRate.GetAmountBySpendRuleAsync(conversionRequest);

            if (conversionResponse.ErrorCode != EligibilityEngineErrors.None)
            {
                _log.Warning("Invalid amount conversion when trying to add payment transfer", context: conversionRequest);
                return(PaymentTransfersErrorCodes.InvalidAmountConversion);
            }

            if (paymentTransfer.AmountInFiat == null)
            {
                paymentTransfer.AmountInFiat = (decimal)conversionResponse.Amount;
            }
            else
            {
                paymentTransfer.AmountInTokens = conversionResponse.Amount;
            }

            var transferId         = Guid.NewGuid();
            var transferIdAsString = transferId.ToString();
            var receiptNumber      = await GenerateReceiptNumberAsync();

            paymentTransfer.TransferId    = transferIdAsString;
            paymentTransfer.ReceiptNumber = receiptNumber;
            paymentTransfer.InvoiceId     = receiptNumber;
            paymentTransfer.Timestamp     = DateTime.UtcNow;

            var encodedData = EncodePaymentTransferData(paymentTransfer.SpendRuleId, receiptNumber, transferIdAsString);

            var pbfTransferResponse = await _pbfClient.GenericTransfersApi.GenericTransferAsync(new GenericTransferRequestModel
            {
                Amount           = paymentTransfer.AmountInTokens.Value,
                AdditionalData   = encodedData,
                RecipientAddress = _settingsService.GetPaymentTransfersAddress(),
                SenderCustomerId = paymentTransfer.CustomerId,
                TransferId       = transferIdAsString
            });

            if (pbfTransferResponse.Error == TransferError.SenderWalletMissing)
            {
                return(PaymentTransfersErrorCodes.CustomerWalletDoesNotExist);
            }

            if (pbfTransferResponse.Error == TransferError.NotEnoughFunds)
            {
                return(PaymentTransfersErrorCodes.NotEnoughFunds);
            }

            await _paymentTransfersRepository.AddAsync(paymentTransfer);

            return(PaymentTransfersErrorCodes.None);
        }