Beispiel #1
0
        private async Task <PaymentStatusUpdateErrorCodes> ChangeStatusAsync
            (string paymentRequestId, PaymentRequestStatus requiredStatus, PaymentRequestStatus updateStatus)
        {
            if (string.IsNullOrEmpty(paymentRequestId))
            {
                throw new ArgumentNullException(nameof(paymentRequestId));
            }

            return(await _transactionScopeHandler.WithTransactionAsync(async() =>
            {
                var payment = await _paymentsRepository.GetByPaymentRequestIdAsync(paymentRequestId);

                if (payment == null)
                {
                    return PaymentStatusUpdateErrorCodes.PaymentDoesNotExist;
                }

                if (payment.Status != requiredStatus)
                {
                    return PaymentStatusUpdateErrorCodes.PaymentIsInInvalidStatus;
                }

                await _paymentsRepository.SetStatusAsync(paymentRequestId, updateStatus);

                await _statusUpdatePublisher.PublishAsync(new PartnersPaymentStatusUpdatedEvent
                {
                    PaymentRequestId = paymentRequestId,
                    Status = updateStatus.ToContractModel()
                });

                return PaymentStatusUpdateErrorCodes.None;
            }));
        }
Beispiel #2
0
        public void MapToString_FromPaymentRequestStatusEnum_ExpectedResult(PaymentRequestStatus input, string expectedResult)
        {
            // Arrange + Act
            var result = input.MapToString();

            // Assert
            Assert.AreEqual(expectedResult, result);
        }
 public static PaymentRequestResult Succeeded(PaymentRequestStatus status, string paymentRequestId)
 {
     return(new PaymentRequestResult
     {
         Status = status,
         PaymentRequestId = paymentRequestId
     });
 }
Beispiel #4
0
        public static PartnerPaymentStatus ToContractModel(this PaymentRequestStatus src)
        {
            switch (src)
            {
            case PaymentRequestStatus.Created:
                return(PartnerPaymentStatus.Created);

            case PaymentRequestStatus.RequestExpired:
                return(PartnerPaymentStatus.RequestExpired);

            case PaymentRequestStatus.RejectedByCustomer:
                return(PartnerPaymentStatus.RejectedByCustomer);

            case PaymentRequestStatus.TokensBurnFailed:
                return(PartnerPaymentStatus.TokensBurnFailed);

            case PaymentRequestStatus.TokensBurnStarted:
                return(PartnerPaymentStatus.TokensBurnStarted);

            case PaymentRequestStatus.TokensBurnSucceeded:
                return(PartnerPaymentStatus.TokensBurnSucceeded);

            case PaymentRequestStatus.TokensRefundFailed:
                return(PartnerPaymentStatus.TokensRefundFailed);

            case PaymentRequestStatus.TokensRefundStarted:
                return(PartnerPaymentStatus.TokensRefundStarted);

            case PaymentRequestStatus.TokensRefundSucceeded:
                return(PartnerPaymentStatus.TokensRefundSucceeded);

            case PaymentRequestStatus.TokensTransferFailed:
                return(PartnerPaymentStatus.TokensTransferFailed);

            case PaymentRequestStatus.TokensTransferStarted:
                return(PartnerPaymentStatus.TokensTransferStarted);

            case PaymentRequestStatus.TokensTransferSucceeded:
                return(PartnerPaymentStatus.TokensTransferSucceeded);

            case PaymentRequestStatus.ExpirationTokensRefundFailed:
                return(PartnerPaymentStatus.ExpirationTokensRefundFailed);

            case PaymentRequestStatus.ExpirationTokensRefundStarted:
                return(PartnerPaymentStatus.ExpirationTokensRefundStarted);

            case PaymentRequestStatus.ExpirationTokensRefundSucceeded:
                return(PartnerPaymentStatus.ExpirationTokensRefundSucceeded);

            case PaymentRequestStatus.CancelledByPartner:
                return(PartnerPaymentStatus.CancelledByPartner);

            default:
                throw new ArgumentOutOfRangeException(nameof(src), $"Payment request status value was not expected: {src.ToString()}");
            }
        }
Beispiel #5
0
        private async Task UpdateStatusAsync(IPaymentRequest paymentRequest, PaymentRequestStatusInfo statusInfo = null)
        {
            PaymentRequestStatusInfo newStatusInfo =
                statusInfo ?? await _paymentRequestStatusResolver.GetStatus(paymentRequest.WalletAddress);

            PaymentRequestStatus          previousStatus          = paymentRequest.Status;
            PaymentRequestProcessingError previousProcessingError = paymentRequest.ProcessingError;

            paymentRequest.Status   = newStatusInfo.Status;
            paymentRequest.PaidDate = newStatusInfo.Date;

            if (newStatusInfo.Amount.HasValue)
            {
                paymentRequest.PaidAmount = newStatusInfo.Amount.Value;
            }

            paymentRequest.ProcessingError = (paymentRequest.Status == PaymentRequestStatus.Error || paymentRequest.Status == PaymentRequestStatus.SettlementError)
                ? newStatusInfo.ProcessingError
                : PaymentRequestProcessingError.None;

            await _paymentRequestRepository.UpdateAsync(paymentRequest);

            // if we are updating status from "InProcess" to any other - we have to release the lock
            if (previousStatus == PaymentRequestStatus.InProcess)
            {
                await _paymentLocksService.ReleaseLockAsync(paymentRequest.Id, paymentRequest.MerchantId);
            }

            PaymentRequestRefund refundInfo = await GetRefundInfoAsync(paymentRequest.WalletAddress);

            if (paymentRequest.Status != previousStatus ||
                (paymentRequest.Status == PaymentRequestStatus.Error &&
                 paymentRequest.ProcessingError != previousProcessingError))
            {
                await _paymentRequestPublisher.PublishAsync(paymentRequest, refundInfo);

                IAssetGeneralSettings assetSettings =
                    await _assetSettingsService.GetGeneralAsync(paymentRequest.PaymentAssetId);

                // doing auto settlement only once
                // Some flows assume we can get updates from blockchain multiple times for the same transaction
                // which leads to the same payment request status
                if (paymentRequest.StatusValidForSettlement() && (assetSettings?.AutoSettle ?? false))
                {
                    if (paymentRequest.Status != PaymentRequestStatus.Confirmed &&
                        !_autoSettleSettingsResolver.AllowToMakePartialAutoSettle(paymentRequest.PaymentAssetId))
                    {
                        return;
                    }

                    await SettleAsync(paymentRequest.MerchantId, paymentRequest.Id);
                }
            }
        }
        public static InvoiceStatus Convert(PaymentRequestStatus status, PaymentRequestProcessingError error)
        {
            switch (status)
            {
            case PaymentRequestStatus.New:
                return(InvoiceStatus.Unpaid);

            case PaymentRequestStatus.Cancelled:
                return(InvoiceStatus.Removed);

            case PaymentRequestStatus.InProcess:
                return(InvoiceStatus.InProgress);

            case PaymentRequestStatus.Confirmed:
                return(InvoiceStatus.Paid);

            case PaymentRequestStatus.RefundInProgress:
                return(InvoiceStatus.RefundInProgress);

            case PaymentRequestStatus.Refunded:
                return(InvoiceStatus.Refunded);

            case PaymentRequestStatus.Error:
                switch (error)
                {
                case PaymentRequestProcessingError.PaymentExpired:
                    return(InvoiceStatus.PastDue);

                case PaymentRequestProcessingError.LatePaid:
                    return(InvoiceStatus.LatePaid);

                case PaymentRequestProcessingError.PaymentAmountBelow:
                    return(InvoiceStatus.Underpaid);

                case PaymentRequestProcessingError.PaymentAmountAbove:
                    return(InvoiceStatus.Overpaid);

                case PaymentRequestProcessingError.RefundNotConfirmed:
                    return(InvoiceStatus.NotConfirmed);

                case PaymentRequestProcessingError.UnknownPayment:
                case PaymentRequestProcessingError.UnknownRefund:
                    return(InvoiceStatus.InternalError);

                default:
                    throw new Exception($"Unknown payment request error '{error}'");
                }

            default:
                throw new Exception($"Unknown payment request status '{status}'");
            }
        }
Beispiel #7
0
        public static InvoiceStatus ToInvoiceStatus(this PaymentRequestStatus status, PaymentRequestProcessingError error)
        {
            switch (status)
            {
            case PaymentRequestStatus.New:
                return(InvoiceStatus.Unpaid);

            case PaymentRequestStatus.Cancelled:
                return(InvoiceStatus.Removed);

            case PaymentRequestStatus.InProcess:
                return(InvoiceStatus.InProgress);

            case PaymentRequestStatus.Confirmed:
                return(InvoiceStatus.Paid);

            case PaymentRequestStatus.RefundInProgress:
                return(InvoiceStatus.RefundInProgress);

            case PaymentRequestStatus.Refunded:
                return(InvoiceStatus.Refunded);

            case PaymentRequestStatus.Error:
                switch (error)
                {
                case PaymentRequestProcessingError.PaymentExpired:
                    return(InvoiceStatus.PastDue);

                case PaymentRequestProcessingError.LatePaid:
                    return(InvoiceStatus.LatePaid);

                case PaymentRequestProcessingError.PaymentAmountBelow:
                    return(InvoiceStatus.Underpaid);

                case PaymentRequestProcessingError.PaymentAmountAbove:
                    return(InvoiceStatus.Overpaid);

                case PaymentRequestProcessingError.RefundNotConfirmed:
                    return(InvoiceStatus.NotConfirmed);

                case PaymentRequestProcessingError.UnknownPayment:
                case PaymentRequestProcessingError.UnknownRefund:
                    return(InvoiceStatus.InternalError);

                default:
                    return(InvoiceStatus.None);
                }

            default:
                return(InvoiceStatus.None);
            }
        }
Beispiel #8
0
        public async Task <PaymentStatusUpdateErrorCodes> ApproveByReceptionistAsync(string paymentRequestId)
        {
            if (string.IsNullOrEmpty(paymentRequestId))
            {
                throw new ArgumentNullException(nameof(paymentRequestId));
            }

            return(await _transactionScopeHandler.WithTransactionAsync(async() =>
            {
                var payment = await _paymentsRepository.GetByPaymentRequestIdAsync(paymentRequestId);

                if (payment == null)
                {
                    return PaymentStatusUpdateErrorCodes.PaymentDoesNotExist;
                }

                if (payment.Status != PaymentRequestStatus.TokensTransferSucceeded)
                {
                    return PaymentStatusUpdateErrorCodes.PaymentIsInInvalidStatus;
                }

                const PaymentRequestStatus newStatus = PaymentRequestStatus.TokensBurnStarted;

                await _paymentsRepository.SetStatusAsync(paymentRequestId, newStatus);

                await _statusUpdatePublisher.PublishAsync(new PartnersPaymentStatusUpdatedEvent
                {
                    PaymentRequestId = paymentRequestId,
                    Status = newStatus.ToContractModel()
                });

                var encodedData = _blockchainEncodingService.EncodeAcceptRequestData
                                      (payment.PartnerId, payment.LocationId, payment.Timestamp.ConvertToLongMs(), payment.CustomerId,
                                      payment.PaymentRequestId);

                var pbfResponse = await _pbfClient.OperationsApi.AddGenericOperationAsync(new GenericOperationRequest
                {
                    Data = encodedData,
                    SourceAddress = _settingsService.GetMasterWalletAddress(),
                    TargetAddress = _settingsService.GetPartnersPaymentsAddress()
                });

                await _paymentRequestBlockchainRepository.UpsertAsync(paymentRequestId,
                                                                      pbfResponse.OperationId.ToString());
                return PaymentStatusUpdateErrorCodes.None;
            }));
        }
        /// <summary>
        /// Maps the <see cref="PaymentRequestStatus"/> to its corresponding Tikkie API string.
        /// </summary>
        /// <param name="paymentRequestStatus">The payment request status as enum.</param>
        /// <returns>The payment request status in string format.</returns>
        /// <exception cref="ArgumentException">When the payment request status enum is not recognized.</exception>
        public static string MapToString(this PaymentRequestStatus paymentRequestStatus)
        {
            switch (paymentRequestStatus)
            {
            case PaymentRequestStatus.Open: return("OPEN");

            case PaymentRequestStatus.Closed: return("CLOSED");

            case PaymentRequestStatus.Expired: return("EXPIRED");

            case PaymentRequestStatus.MaxYieldReached: return("MAX_YIELD_REACHED");

            case PaymentRequestStatus.MaxSuccessfulPaymentsReached: return("MAX_SUCCESSFUL_PAYMENTS_REACHED");

            default: throw new ArgumentException($"Not recognized {nameof(paymentRequestStatus)}");
            }
        }
Beispiel #10
0
        public async Task <PaymentStatusUpdateErrorCodes> RejectByCustomerAsync(string paymentRequestId, string customerId)
        {
            if (string.IsNullOrEmpty(paymentRequestId))
            {
                throw new ArgumentNullException(nameof(paymentRequestId));
            }

            if (string.IsNullOrEmpty(customerId))
            {
                throw new ArgumentNullException(nameof(customerId));
            }

            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;
                }

                const PaymentRequestStatus newStatus = PaymentRequestStatus.RejectedByCustomer;

                await _paymentsRepository.SetStatusAsync(paymentRequestId, newStatus);

                await _statusUpdatePublisher.PublishAsync(new PartnersPaymentStatusUpdatedEvent
                {
                    PaymentRequestId = paymentRequestId,
                    Status = newStatus.ToContractModel()
                });

                return PaymentStatusUpdateErrorCodes.None;
            }));
        }
Beispiel #11
0
        public static PartnerPaymentPublicStatus ConvertToPublicStatus(this PaymentRequestStatus partnerPaymentStatus)
        {
            switch (partnerPaymentStatus)
            {
            case PaymentRequestStatus.Created:
            case PaymentRequestStatus.TokensTransferStarted:
                return(PartnerPaymentPublicStatus.Pending);

            case PaymentRequestStatus.RejectedByCustomer:
            case PaymentRequestStatus.TokensRefundSucceeded:
            case PaymentRequestStatus.CancelledByPartner:
                return(PartnerPaymentPublicStatus.Cancelled);

            case PaymentRequestStatus.TokensTransferSucceeded:
            case PaymentRequestStatus.TokensBurnStarted:
            case PaymentRequestStatus.TokensRefundStarted:
                return(PartnerPaymentPublicStatus.Confirmed);

            case PaymentRequestStatus.TokensBurnSucceeded:
                return(PartnerPaymentPublicStatus.Completed);

            case PaymentRequestStatus.TokensBurnFailed:
            case PaymentRequestStatus.TokensRefundFailed:
            case PaymentRequestStatus.TokensTransferFailed:
            case PaymentRequestStatus.ExpirationTokensRefundFailed:
                return(PartnerPaymentPublicStatus.Failed);

            case PaymentRequestStatus.RequestExpired:
                return(PartnerPaymentPublicStatus.RequestExpired);

            case PaymentRequestStatus.ExpirationTokensRefundStarted:
            case PaymentRequestStatus.ExpirationTokensRefundSucceeded:
                return(PartnerPaymentPublicStatus.PaymentExpired);

            default:
                throw new ArgumentOutOfRangeException(nameof(partnerPaymentStatus), partnerPaymentStatus, null);
            }
        }
        public async Task SetStatusAsync(string paymentRequestId, PaymentRequestStatus status)
        {
            using (var context = _contextFactory.CreateDataContext())
            {
                var entity = new PartnerPaymentEntity {
                    PaymentRequestId = paymentRequestId
                };

                context.PartnersPayments.Attach(entity);

                entity.Status = status;
                entity.LastUpdatedTimestamp = DateTime.UtcNow;

                try
                {
                    await context.SaveChangesAsync();
                }
                catch (DbUpdateException)
                {
                    throw new InvalidOperationException("Entity was not found during status update");
                }
            }
        }
Beispiel #13
0
        public async Task <PaymentStatusUpdateErrorCodes> StartExpireRefundAsync(string paymentRequestId)
        {
            if (string.IsNullOrEmpty(paymentRequestId))
            {
                throw new ArgumentNullException(nameof(paymentRequestId));
            }

            return(await _transactionScopeHandler.WithTransactionAsync(async() =>
            {
                var payment = await _paymentsRepository.GetByPaymentRequestIdAsync(paymentRequestId);

                if (payment == null)
                {
                    return PaymentStatusUpdateErrorCodes.PaymentDoesNotExist;
                }

                if (payment.Status != PaymentRequestStatus.TokensTransferSucceeded)
                {
                    return PaymentStatusUpdateErrorCodes.PaymentIsInInvalidStatus;
                }

                const PaymentRequestStatus newStatus = PaymentRequestStatus.ExpirationTokensRefundStarted;

                await _paymentsRepository.SetStatusAsync(paymentRequestId, newStatus);

                await _statusUpdatePublisher.PublishAsync(new PartnersPaymentStatusUpdatedEvent
                {
                    PaymentRequestId = paymentRequestId,
                    Status = newStatus.ToContractModel()
                });

                await BlockchainRefundAsync(paymentRequestId, payment);

                return PaymentStatusUpdateErrorCodes.None;
            }));
        }
Beispiel #14
0
 public NotAllowedStatusException(PaymentRequestStatus status) : base("Not allowed status")
 {
     Status = status;
 }
Beispiel #15
0
 private async Task SaveRequestStatus(SqlConnection conn, int requestId, CheckPaymentRequestData data, PaymentRequestStatus status)
 {
     using (SqlCommand cmd = _sqlServer.GetSpCommand("payment.UpdateYandexKassaRequestStatus", conn))
     {
         cmd.AddIntParam("@RequestId", requestId);
         cmd.AddTinyIntParam("@Status", (byte)status);
         cmd.AddVarCharParam("@KassaPaymentId", 50, data.ResponseData.Response.Id);
         await cmd.ExecuteNonQueryAsync();
     }
 }
Beispiel #16
0
        private async Task <ProcessESBResult> ValidateEsbResponse(ProcessESBResponse esbResponse, PaymentRequestStatus paymentRequestStatus)
        {
            if (paymentRequestStatus != null)
            {
                var interfaceStatus = (InterfaceStatus)paymentRequestStatus.DocumentStatus;
                if (!(interfaceStatus == InterfaceStatus.StandBy || interfaceStatus == InterfaceStatus.ReadyToTransmit || interfaceStatus == InterfaceStatus.Interfaced))
                {
                    string message = "The payment request " + esbResponse.DocumentReference + " is not in status 'ready to transmit' or 'stand by' or 'interfaced' (current status: " + interfaceStatus + " )";
                    processESBResult.Error     = message;
                    processESBResult.IsSuccess = false;
                }
                else if (paymentRequestStatus.CounterParty != esbResponse.Counterparty)
                {
                    string message = "The Counterparty should be the same code as in the original payment order.";
                    processESBResult.Error     = message;
                    processESBResult.IsSuccess = false;
                }
                else
                {
                    processESBResult.IsSuccess = true;
                    if (!string.IsNullOrEmpty(paymentRequestStatus.UUID))
                    {
                        processESBResult.UUID = paymentRequestStatus.UUID;
                    }
                }
            }
            else
            {
                string message = "Invalid Cash Document Reference";
                processESBResult.Error     = message;
                processESBResult.IsSuccess = false;
            }

            return(processESBResult);
        }
Beispiel #17
0
        public void MapToPaymentRequestStatusEnum_FromString_ExpectedResult(string input, PaymentRequestStatus expectedResult)
        {
            // Arrange + Act
            var result = input.MapToPaymentRequestStatusEnum();

            // Assert
            Assert.AreEqual(expectedResult, result);
        }
        public async Task HandleAsync_PaymentIsNotInCorrectStatus_PaymentsStatusUpdaterNotCalled(PaymentRequestStatus status)
        {
            _paymentRequestBlockchainRepoMock.Setup(x => x.GetByOperationIdAsync(FakeOperationId))
            .ReturnsAsync(new PaymentRequestBlockchainEntity
            {
                PaymentRequestId = FakePaymentRequestId
            });

            _paymentsRepositoryMock.Setup(x => x.GetByPaymentRequestIdAsync(FakePaymentRequestId))
            .ReturnsAsync(new PaymentModel
            {
                Status = status
            });

            var sut = CreateSutInstance();

            await sut.HandleAsync(FakeOperationId);

            _paymentsStatusUpdaterMock.Verify();
        }
        public async Task GetPaymentDetailsById_StatusIsNotCreated_ZeroIsReturnedForCustomerExpirationTimeLeft(PaymentRequestStatus status)
        {
            _paymentsRepositoryMock.Setup(x => x.GetByPaymentRequestIdAsync(FakePaymentId))
            .ReturnsAsync(new PaymentModel
            {
                Status = status
            });

            var sut = CreateSutInstance();

            var result = await sut.GetPaymentDetailsByPaymentId(FakePaymentId);

            Assert.Equal(0, result.CustomerActionExpirationTimeLeftInSeconds);
        }