Example #1
0
        /// <summary>
        /// Verifies request that comes from a gateway.
        /// </summary>
        /// <param name="httpContext">HttpContext object of current request.</param>
        /// <param name="verifyInvoiceHandler">This handler would be called before verifying the invoice. You can check the invoice with your database and decide whether or not you should call the Cancel method.</param>
        public static async Task <VerifyResult> VerifyAsync(HttpContext httpContext, Action <VerifyInvoice> verifyInvoiceHandler = null)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            //  1) Get PaymentData's ID from HttpRequest
            if (!httpContext.Request.TryGetPaymentDataId(out var paymentId))
            {
                return(new VerifyResult(0, string.Empty, string.Empty, VerifyResultStatus.NotValid, "Payment's ID is not valid."));
            }

            //  2) Load PaymentData by ID from storage
            var paymentData = await SelectPaymentDataByIdAsync(paymentId);

            if (paymentData == null)
            {
                return(new VerifyResult(0, string.Empty, string.Empty, VerifyResultStatus.NotValid, $"No payment found with ID: {paymentId:N}."));
            }

            ThrowExceptionIfGatewayIsNotConfigured(paymentData.Gateway);

            //  Some paymentData's checks...

            if (paymentData.Status != PaymentDataStatus.Requested)
            {
                if (paymentData.Status == PaymentDataStatus.Verified)
                {
                    return(new VerifyResult(paymentData.Gateway, paymentData.ReferenceId, paymentData.TransactionId, VerifyResultStatus.AlreadyVerified, "The payment is already verified."));
                }

                return(new VerifyResult(paymentData.Gateway, paymentData.ReferenceId, paymentData.TransactionId, VerifyResultStatus.NotValid, "Payment is not valid."));
            }

            if (paymentData.IsExpired())
            {
                return(new VerifyResult(paymentData.Gateway, paymentData.ReferenceId, paymentData.TransactionId, VerifyResultStatus.PaymentTimeExpired, "The time of payment is expired."));
            }

            //  Let developer decides to continue or stop the payment
            if (verifyInvoiceHandler != null)
            {
                var paymentVerify = new VerifyInvoice(paymentData.Gateway, paymentData.OrderNumber, paymentData.ReferenceId);

                verifyInvoiceHandler.Invoke(paymentVerify);

                if (paymentVerify.IsCanceled)
                {
                    //  Log canceling
                    TryLog(() => new Log
                    {
                        Type          = LogType.Verify,
                        Gateway       = paymentData.Gateway,
                        OrderNumber   = paymentData.OrderNumber,
                        Amount        = paymentData.Amount,
                        Message       = paymentVerify.CancellationReason,
                        CreatedOn     = DateTime.Now,
                        ReferenceId   = paymentData.ReferenceId,
                        TransactionId = string.Empty,
                        Status        = VerifyResultStatus.CanceledProgrammatically.ToString()
                    });

                    paymentData.Status = PaymentDataStatus.Failed;

                    //  Update PaymentData in the Storage
                    await UpdatePaymentDataAsync(paymentData);

                    return(new VerifyResult(paymentData.Gateway, paymentData.ReferenceId, paymentData.TransactionId, VerifyResultStatus.CanceledProgrammatically, paymentVerify.CancellationReason));
                }
            }

            //  Making ready data to verify

            //  Incoming request parameters
            IRequestParameters requestParameters = new RequestParameters(httpContext.Request);

            //  Create VerifyContext
            var gatewayVerifyPaymentContext = CreateGatewayVerifyPaymentContext(paymentData, requestParameters);

            //  4) Create gateway from PaymentData's Gateway
            var gateway = GatewayFactory.CreateGateway(paymentData.Gateway);

            //  5) Verify
            var verifyResult = await gateway.VerifyAsync(gatewayVerifyPaymentContext);

            //  Log Verify
            TryLog(() => new Log
            {
                Type          = LogType.Verify,
                Gateway       = paymentData.Gateway,
                OrderNumber   = paymentData.OrderNumber,
                Amount        = paymentData.Amount,
                Message       = verifyResult.Message,
                CreatedOn     = DateTime.Now,
                ReferenceId   = paymentData.ReferenceId,
                TransactionId = verifyResult.TransactionId,
                Status        = verifyResult.Status.ToString()
            });

            //  Update PaymentData
            paymentData.TransactionId = verifyResult.TransactionId;
            paymentData.Status        = verifyResult.IsSuccess() ? PaymentDataStatus.Verified : PaymentDataStatus.Failed;

            //  Save PaymentData to the storage
            await UpdatePaymentDataAsync(paymentData);

            return(verifyResult);
        }
Example #2
0
        /// <summary>
        /// Refunds a specific payment by its Order Number.
        /// </summary>
        /// <param name="refundInvoice">RefundInvoice object</param>
        public static async Task <RefundResult> RefundAsync(RefundInvoice refundInvoice)
        {
            if (refundInvoice == null)
            {
                throw new ArgumentNullException(nameof(refundInvoice));
            }

            var paymentData = await SelectPaymentDataByOrderNumberAsync(refundInvoice.OrderNumber);

            if (paymentData == null)
            {
                return(new RefundResult(0, 0, RefundResultStatus.Failed, $"No payment found with order's number: {refundInvoice.OrderNumber}"));
            }

            if (refundInvoice.AmountToRefund > paymentData.Amount)
            {
                return(new RefundResult(0, 0, RefundResultStatus.Failed, $"Amount To Refund cannot be greater than original amount. Original Amount: {paymentData.Amount:N0}. Amount To Refund: {refundInvoice.AmountToRefund:N0}"));
            }

            ThrowExceptionIfGatewayIsNotConfigured(paymentData.Gateway);

            var gateway = GatewayFactory.CreateGateway(paymentData.Gateway);

            var amountToRefund = refundInvoice.AmountToRefund > 0 ? refundInvoice.AmountToRefund : paymentData.Amount;

            var gatewayRefundPaymentContext = new GatewayRefundPaymentContext(paymentData.OrderNumber, amountToRefund, paymentData.ReferenceId, paymentData.TransactionId, paymentData.AdditionalData);

            try
            {
                var refundResult = await gateway.RefundAsync(gatewayRefundPaymentContext);

                //  Log
                TryLog(() => new Log
                {
                    Type          = LogType.Refund,
                    Gateway       = paymentData.Gateway,
                    OrderNumber   = paymentData.OrderNumber,
                    Amount        = paymentData.Amount,
                    Message       = refundResult.Message,
                    CreatedOn     = DateTime.Now,
                    ReferenceId   = paymentData.ReferenceId,
                    TransactionId = paymentData.TransactionId,
                    Status        = refundResult.Status.ToString()
                });

                if (refundResult.IsSuccess())
                {
                    paymentData.Status = PaymentDataStatus.Refunded;

                    await UpdatePaymentDataAsync(paymentData);
                }

                return(refundResult);
            }
            catch (Exception exception)
            {
                //  Log
                TryLog(() => new Log
                {
                    Type          = LogType.Error,
                    Gateway       = paymentData.Gateway,
                    OrderNumber   = paymentData.OrderNumber,
                    Amount        = amountToRefund,
                    Message       = exception.Message,
                    CreatedOn     = DateTime.Now,
                    ReferenceId   = paymentData.ReferenceId,
                    TransactionId = paymentData.TransactionId,
                    Status        = string.Empty
                });

                return(new RefundResult(paymentData.Gateway, 0, RefundResultStatus.Failed, exception.Message));
            }
        }
Example #3
0
        /// <summary>
        /// Sends pay request to selected gateway.
        /// </summary>
        /// <param name="gateway">Gateway to pay</param>
        /// <param name="invoice">Invoice object</param>
        public static RequestResult Request(Gateway gateway, Invoice invoice)
        {
            if (invoice == null)
            {
                throw new ArgumentNullException(nameof(invoice));
            }

            ThrowExceptionIfGatewayIsNotConfigured(gateway);

            var paymentData = SelectPaymentDataByOrderNumber(invoice.OrderNumber);

            if (paymentData != null)
            {
                //  Log
                TryLog(() => new Log
                {
                    Type          = LogType.Request,
                    Gateway       = gateway,
                    OrderNumber   = invoice.OrderNumber,
                    Amount        = invoice.Amount,
                    Message       = $"Order Number ({invoice.OrderNumber}) is used before and you cannot use it again. It must be unique for each requests.",
                    CreatedOn     = DateTime.Now,
                    ReferenceId   = string.Empty,
                    TransactionId = string.Empty,
                    Status        = RequestResultStatus.DuplicateOrderNumber.ToString()
                });

                return(new RequestResult(RequestResultStatus.DuplicateOrderNumber, $"The order number ({invoice.OrderNumber}) is already exists and you cannot use it again. It must be unique for each requests."));
            }

            var gatewayBase = GatewayFactory.CreateGateway(gateway);

            paymentData = invoice.CreatePaymentData(gateway);

            invoice.CallbackUrl = CreateCallbackUrl(invoice.CallbackUrl, paymentData.Id);

            try
            {
                var result = gatewayBase.Request(invoice);

                //  Set ReferenceId
                paymentData.ReferenceId    = result.ReferenceId;
                paymentData.AdditionalData = result.AdditionalData;

                if (!result.IsSuccess())
                {
                    paymentData.Status = PaymentDataStatus.Failed;
                }

                //  Log
                TryLog(() => new Log
                {
                    Type          = LogType.Request,
                    Gateway       = gateway,
                    OrderNumber   = invoice.OrderNumber,
                    Amount        = invoice.Amount,
                    Message       = result.Message,
                    CreatedOn     = DateTime.Now,
                    ReferenceId   = result.ReferenceId,
                    TransactionId = string.Empty,
                    Status        = result.Status.ToString()
                });

                return(result);
            }
            catch (Exception exception)
            {
                //  Log
                TryLog(() => new Log
                {
                    Type        = LogType.Error,
                    Gateway     = gateway,
                    OrderNumber = invoice.OrderNumber,
                    Amount      = invoice.Amount,
                    Message     = exception.Message,
                    CreatedOn   = DateTime.Now
                });

                paymentData.Status = PaymentDataStatus.Failed;

                return(new RequestResult(RequestResultStatus.Failed, "An error occurred."));
            }
            finally
            {
                InsertPaymentData(paymentData);
            }
        }