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