public async Task <OperationStatusUpdateResultModel> AcceptAsync(Guid id, string hash) { if (Guid.Empty == id) { _log.Warning("Operation id is empty"); return(OperationStatusUpdateResultModel.Failed(OperationStatusUpdateError.OperationNotFound)); } if (string.IsNullOrEmpty(hash)) { _log.Warning("Transaction hash is empty"); return(OperationStatusUpdateResultModel.Failed(OperationStatusUpdateError.InvalidTransactionHash)); } if (!hash.IsValidTransactionHash()) { _log.Warning("Transaction hash has invalid format"); return(OperationStatusUpdateResultModel.Failed(OperationStatusUpdateError.InvalidTransactionHash)); } return(await _transactionScopeHandler.WithTransactionAsync(async() => { var operationRequest = await _operationRequestsRepository.GetByIdAsync(id); if (operationRequest == null) { var operation = await _operationsRepository.GetByIdAsync(id); if (operation == null) { _log.Warning("Operation request not found by id", context: id); return OperationStatusUpdateResultModel.Failed(OperationStatusUpdateError.OperationNotFound); } if (operation.Status == OperationStatus.Accepted) { _log.Warning("Operation has already been accepted", context: id); } return OperationStatusUpdateResultModel.Succeeded(); } if (operationRequest.Status != OperationStatus.Created) { _log.Error(message: "Current operation request status is not expected", context: new { id = operationRequest.Id, currentStatus = operationRequest.Status.ToString() }); return OperationStatusUpdateResultModel.Failed(OperationStatusUpdateError.InvalidStatus); } await _operationRequestsRepository.AcceptAsync( operationRequest.Id, operationRequest.CustomerId, operationRequest.Nonce, operationRequest.MasterWalletAddress, operationRequest.Type, operationRequest.ContextJson, operationRequest.CreatedAt, hash); return OperationStatusUpdateResultModel.Succeeded(); })); }
public async Task HandleWalletLinkingAsync( string eventId, string customerId, string masterWalletAddress, string internalAddress, string publicAddress, Money18 fee) { await _transactionScopeHandler.WithTransactionAsync(async() => { if (!string.IsNullOrEmpty(eventId)) { var isDuplicate = await _deduplicationLog.IsDuplicateAsync(eventId); if (isDuplicate) { _log.Warning("There is already wallet linking operation with the same id", context: eventId); return; } } await _operationRequestsProducer.AddAsync(customerId, OperationType.WalletLinking, new WalletLinkingContext { InternalWalletAddress = internalAddress, PublicWalletAddress = publicAddress, LinkingFee = fee }, masterWalletAddress); }); }
public async Task <CustomerWalletCreationResultModel> CreateCustomerWalletAsync(string customerId) { #region Validation if (string.IsNullOrEmpty(customerId)) { return(CustomerWalletCreationResultModel.Failed(CustomerWalletCreationError.InvalidCustomerId)); } var walletAlreadyAssigned = await _walletOwnersRepository.GetByOwnerIdAsync(customerId); if (walletAlreadyAssigned != null) { _log.Info("Customer already has wallet assigned", walletAlreadyAssigned); return(CustomerWalletCreationResultModel.Failed(CustomerWalletCreationError.AlreadyCreated)); } #endregion var createWalletResponse = await _quorumTransactionSignerClient.WalletsApi.CreateWalletAsync(); try { await _transactionScopeHandler.WithTransactionAsync(async() => { await _walletOwnersRepository.AddAsync(customerId, createWalletResponse.Address); await _operationRequestsProducer.AddAsync( customerId, OperationType.CustomerWalletCreation, new CustomerWalletContext { CustomerId = customerId, WalletAddress = createWalletResponse.Address }); }); } catch (Exception e) { if (e is WalletOwnerDuplicateException) { return(CustomerWalletCreationResultModel.Failed(CustomerWalletCreationError.AlreadyCreated)); } throw; } return(CustomerWalletCreationResultModel.Succeeded()); }
public Task HandleAsync(string operationId, Money18 amount, string reason) { return(_transactionScopeHandler.WithTransactionAsync(async() => { var isDuplicate = await _deduplicationLogRepository.IsDuplicateAsync(operationId); if (isDuplicate) { _log.Warning("There is already seize operation with the same operation id", context: $"{nameof(operationId)}: {operationId}"); return; } await _operationRequestsProducer.AddAsync(OperationType.SeizeToInternal, new SeizeToInternalContext(_privateBlockchainGatewayContractAddress, amount, reason)); })); }
public async Task <GenericOperationResultModel> AddGenericOperationAsync(string operationId, string encodedData, string sourceAddress, string targetAddress) { if (string.IsNullOrEmpty(encodedData)) { throw new ArgumentNullException(nameof(encodedData)); } if (string.IsNullOrEmpty(sourceAddress)) { throw new ArgumentNullException(nameof(sourceAddress)); } if (string.IsNullOrEmpty(targetAddress)) { throw new ArgumentNullException(nameof(targetAddress)); } return(await _transactionScopeHandler.WithTransactionAsync(async() => { if (!string.IsNullOrEmpty(operationId)) { var isDuplicate = await _deduplicationLog.IsDuplicateAsync(operationId); if (isDuplicate) { _log.Warning("There is already generic operation with the same id", context: operationId); return GenericOperationResultModel.Failed(AddOperationError.DuplicateRequest); } } var result = await _operationRequestsProducer.AddAsync( OperationType.GenericOperation, new GenericOperationContext { Data = encodedData, SourceWalletAddress = sourceAddress, TargetWalletAddress = targetAddress }, sourceAddress); return GenericOperationResultModel.Succeeded(result); })); }
public async Task <PaymentStatusUpdateErrorCodes> ApproveByCustomerAsync(string paymentRequestId, Money18 sendingAmount, string customerId) { if (string.IsNullOrEmpty(paymentRequestId)) { throw new ArgumentNullException(nameof(paymentRequestId)); } if (string.IsNullOrEmpty(customerId)) { throw new ArgumentNullException(nameof(customerId)); } if (sendingAmount <= 0) { return(PaymentStatusUpdateErrorCodes.InvalidAmount); } var customerBlockStatusResponse = await _walletManagementClient.Api.GetCustomerWalletBlockStateAsync(customerId); if (customerBlockStatusResponse.Status != CustomerWalletActivityStatus.Active) { return(PaymentStatusUpdateErrorCodes.CustomerWalletIsBlocked); } return(await _transactionScopeHandler.WithTransactionAsync(async() => { var payment = await _paymentsRepository.GetByPaymentRequestIdAsync(paymentRequestId); if (payment == null) { return PaymentStatusUpdateErrorCodes.PaymentDoesNotExist; } if (payment.CustomerId != customerId) { return PaymentStatusUpdateErrorCodes.CustomerIdDoesNotMatch; } if (payment.Status != PaymentRequestStatus.Created) { return PaymentStatusUpdateErrorCodes.PaymentIsInInvalidStatus; } if (payment.TokensAmount < sendingAmount) { return PaymentStatusUpdateErrorCodes.InvalidAmount; } var calculatedFiatAmountFromPartnerRate = await CalculateAmountFromPartnerRate(Guid.Parse(payment.CustomerId), Guid.Parse(payment.PartnerId), sendingAmount, _tokenSymbol, payment.Currency); if (calculatedFiatAmountFromPartnerRate.ErrorCode == EligibilityEngineErrors.PartnerNotFound) { return PaymentStatusUpdateErrorCodes.PartnerDoesNotExist; } if (calculatedFiatAmountFromPartnerRate.ErrorCode == EligibilityEngineErrors.ConversionRateNotFound) { return PaymentStatusUpdateErrorCodes.InvalidTokensOrCurrencyRateInPartner; } payment.Status = PaymentRequestStatus.TokensTransferStarted; payment.TokensSendingAmount = sendingAmount; payment.FiatSendingAmount = (decimal)calculatedFiatAmountFromPartnerRate.Amount; await _paymentsRepository.UpdatePaymentAsync(payment); await _statusUpdatePublisher.PublishAsync(new PartnersPaymentStatusUpdatedEvent { PaymentRequestId = paymentRequestId, Status = payment.Status.ToContractModel() }); var encodedPaymentData = _blockchainEncodingService.EncodePaymentRequestData(payment.PartnerId, payment.LocationId, payment.Timestamp.ConvertToLongMs(), payment.CustomerId, payment.PaymentRequestId); var pbfTransferResponse = await _pbfClient.GenericTransfersApi.GenericTransferAsync( new GenericTransferRequestModel { Amount = sendingAmount, AdditionalData = encodedPaymentData, RecipientAddress = _settingsService.GetPartnersPaymentsAddress(), SenderCustomerId = customerId, TransferId = paymentRequestId }); if (pbfTransferResponse.Error != TransferError.None) { return (PaymentStatusUpdateErrorCodes)pbfTransferResponse.Error; } await _paymentRequestBlockchainRepository.UpsertAsync(paymentRequestId, pbfTransferResponse.OperationId); return PaymentStatusUpdateErrorCodes.None; })); }
public async Task <BonusRewardResultModel> RewardAsync(string customerId, Money18 amount, string rewardId, string bonusReason, string campaignId, string conditionId) { if (string.IsNullOrEmpty(customerId)) { return(BonusRewardResultModel.Failed(BonusRewardError.InvalidCustomerId)); } if (string.IsNullOrEmpty(bonusReason)) { return(BonusRewardResultModel.Failed(BonusRewardError.MissingBonusReason)); } if (string.IsNullOrEmpty(campaignId)) { return(BonusRewardResultModel.Failed(BonusRewardError.InvalidCampaignId)); } if (amount <= 0) { return(BonusRewardResultModel.Failed(BonusRewardError.InvalidAmount)); } var customerWalletAddressResult = await _walletsService.GetCustomerWalletAsync(customerId); switch (customerWalletAddressResult.Error) { case CustomerWalletAddressError.None: break; case CustomerWalletAddressError.CustomerWalletMissing: return(BonusRewardResultModel.Failed(BonusRewardError.CustomerWalletMissing)); case CustomerWalletAddressError.InvalidCustomerId: return(BonusRewardResultModel.Failed(BonusRewardError.InvalidCustomerId)); default: throw new ArgumentOutOfRangeException(nameof(customerWalletAddressResult.Error)); } return(await _transactionScopeHandler.WithTransactionAsync(async() => { var isDuplicate = await _deduplicationLog.IsDuplicateAsync(rewardId); if (isDuplicate) { _log.Warning("There is already bonus reward operation with the same id", context: rewardId); return BonusRewardResultModel.Failed(BonusRewardError.DuplicateRequest); } await _operationRequestsProducer.AddAsync( customerId, OperationType.CustomerBonusReward, new CustomerBonusRewardContext { CustomerId = customerId, Amount = amount, // todo: temporary solution MinterAddress = _walletCreationMasterWalletAddress, WalletAddress = customerWalletAddressResult.WalletAddress, RequestId = rewardId, BonusReason = bonusReason, CampaignId = campaignId, ConditionId = conditionId }); return BonusRewardResultModel.Succeeded(); })); }
public async Task <PaymentTransfersErrorCodes> ProcessPaymentTransferAsync(string transferId) { if (string.IsNullOrEmpty(transferId)) { _log.Error(message: "TransferId is empty when trying to update payment transfer status"); throw new ArgumentNullException(nameof(transferId)); } return(await _transactionScopeHandler.WithTransactionAsync(async() => { var paymentTransfer = await _paymentTransfersRepository.GetByTransferIdAsync(transferId); if (paymentTransfer == null) { _log.Warning("Payment transfer not find by transfer id", context: transferId); return PaymentTransfersErrorCodes.PaymentTransferNotFound; } if (paymentTransfer.Status == PaymentTransferStatus.Processing) { return PaymentTransfersErrorCodes.PaymentTransferAlreadyProcessing; } if (paymentTransfer.Status != PaymentTransferStatus.Pending) { _log.Info("Payment transfer's status cannot be updated because it is not in Pending status"); return PaymentTransfersErrorCodes.InvalidStatus; } var walletAddressResponse = await _pbfClient.CustomersApi.GetWalletAddress(Guid.Parse(paymentTransfer.CustomerId)); if (walletAddressResponse.Error != CustomerWalletAddressError.None) { _log.Warning("Customer transfer does not have a BC wallet when payment transfer is being processed"); return PaymentTransfersErrorCodes.CustomerWalletDoesNotExist; } var payInvoiceResponse = await _realEstateIntegrationClient.Api.PayInvoiceAsync(new InvoicePayRequestModel { CurrencyCode = paymentTransfer.Currency, ReceiptNumber = paymentTransfer.ReceiptNumber, CustomerAccountNumber = paymentTransfer.CustomerAccountNumber, CustomerTrxId = paymentTransfer.CustomerTrxId, LocationCode = paymentTransfer.LocationCode, InstallmentType = paymentTransfer.InstallmentType, PaidAmount = paymentTransfer.AmountInFiat, OrgId = paymentTransfer.OrgId, }); _log.Info("Pay invoice response", context: new { payInvoiceResponse.Status, payInvoiceResponse.ErrorCode, payInvoiceResponse.ReceiptNumber, paymentTransfer.TransferId }); var status = payInvoiceResponse.Status.ToLower() == PaidInvoiceSuccessfullyStatus ? PaymentTransferStatus.Accepted : PaymentTransferStatus.Rejected; await CreateApproveOrRejectTransactionInPbf(paymentTransfer.SpendRuleId, paymentTransfer.InvoiceId, paymentTransfer.TransferId, status); await _paymentTransfersRepository.SetStatusAsync(paymentTransfer.TransferId, PaymentTransferStatus.Processing); _log.Info($"Successfully changed payment transfer's status to Processing", transferId); return PaymentTransfersErrorCodes.None; })); }
public async Task <TransferResultModel> P2PTransferAsync( string senderId, string recipientId, Money18 amount, string transferRequestId) { #region Validation if (string.IsNullOrEmpty(recipientId)) { return(TransferResultModel.Failed(TransferError.InvalidRecipientId)); } if (string.IsNullOrEmpty(senderId)) { return(TransferResultModel.Failed(TransferError.InvalidSenderId)); } if (amount <= 0) { return(TransferResultModel.Failed(TransferError.InvalidAmount)); } var senderAddressResult = await _walletsService.GetCustomerWalletAsync(senderId); var senderAddressValidationResultError = ValidateSenderAddress(senderAddressResult.Error); if (senderAddressValidationResultError != TransferError.None) { return(TransferResultModel.Failed(senderAddressValidationResultError)); } var recipientAddressResult = await _walletsService.GetCustomerWalletAsync(recipientId); var recipientAddressValidationResultError = ValidateRecipientAddress(recipientAddressResult.Error); if (recipientAddressValidationResultError != TransferError.None) { return(TransferResultModel.Failed(recipientAddressValidationResultError)); } var senderBalanceValidationError = await ValidateSenderBalance(senderId, amount); if (senderBalanceValidationError != TransferError.None) { return(TransferResultModel.Failed(senderBalanceValidationError)); } #endregion return(await _transactionScopeHandler.WithTransactionAsync(async() => { if (!string.IsNullOrEmpty(transferRequestId)) { var isDuplicate = await _deduplicationLog.IsDuplicateAsync(transferRequestId); if (isDuplicate) { _log.Warning("There is already transfer operation with the same request id", context: transferRequestId); return TransferResultModel.Failed(TransferError.DuplicateRequest); } } var result = await _operationRequestsProducer.AddAsync( senderId, OperationType.TokensTransfer, new TokensTransferContext { SenderWalletAddress = senderAddressResult.WalletAddress, RecipientWalletAddress = recipientAddressResult.WalletAddress, Amount = amount, RequestId = transferRequestId, }, senderAddressResult.WalletAddress); return TransferResultModel.Succeeded(result); })); }