From 782f211758127fa2fd7d094a994f0d0f8557df2b Mon Sep 17 00:00:00 2001 From: Oleg Zhuk Date: Mon, 2 Oct 2023 14:55:32 +0200 Subject: [PATCH] PT-13737: Allows to Update and Retry Rejected Capture and Rufund documents (#381) feat: Allows to Update and Retry Rejected Capture document with same Transaction Id. feat: Allows to Update and Retry Rejected Refund document with same Transaction Id. feat: Added Transaction Id and Outer Id on Capture details blade. feat: Added Transaction Id and Outer Id on Refund details blade. --- .../Services/PaymentFlowService.cs | 65 ++++++++- .../en.VirtoCommerce.Orders.json | 136 ++++++++++-------- .../Scripts/order.js | 25 ++++ 3 files changed, 158 insertions(+), 68 deletions(-) diff --git a/src/VirtoCommerce.OrdersModule.Data/Services/PaymentFlowService.cs b/src/VirtoCommerce.OrdersModule.Data/Services/PaymentFlowService.cs index 9f41d40c..caea3a90 100644 --- a/src/VirtoCommerce.OrdersModule.Data/Services/PaymentFlowService.cs +++ b/src/VirtoCommerce.OrdersModule.Data/Services/PaymentFlowService.cs @@ -93,10 +93,23 @@ protected virtual async Task CreateRefundDocument(Refu return result; } - var refund = CreateRefund(paymentInfo.Payment, paymentInfo.Store, request); - paymentInfo.Payment.Refunds ??= new List(); - paymentInfo.Payment.Refunds.Add(refund); + + // Allows to Update and Retry Rejected Refund document. + var refund = paymentInfo.Payment.Refunds.FirstOrDefault(r => r.TransactionId == request.TransactionId + && r.Status == RefundStatus.Rejected.ToString()); + + if (refund != null) + { + UpdateRefund(refund, paymentInfo.Payment, paymentInfo.Store, request); + } + else + { + refund = CreateRefund(paymentInfo.Payment, paymentInfo.Store, request); + paymentInfo.Payment.Refunds.Add(refund); + } + + await _customerOrderService.SaveChangesAsync(new[] { paymentInfo.CustomerOrder }); result.Succeeded = true; @@ -157,10 +170,22 @@ public virtual async Task CreateCaptureDocument(Captu return result; } - var capture = CreateCapture(paymentInfo.Payment, paymentInfo.Store, request); - paymentInfo.Payment.Captures ??= new List(); - paymentInfo.Payment.Captures.Add(capture); + + // Allows to Update and Retry Rejected Capture document. + var capture = paymentInfo.Payment.Captures.FirstOrDefault(c => c.TransactionId == request.TransactionId + && c.Status == CaptureStatus.Rejected.ToString()); + + if (capture != null) + { + UpdateCapture(capture, paymentInfo.Payment, paymentInfo.Store, request); + } + else + { + capture = CreateCapture(paymentInfo.Payment, paymentInfo.Store, request); + paymentInfo.Payment.Captures.Add(capture); + } + await _customerOrderService.SaveChangesAsync(new[] { paymentInfo.CustomerOrder }); result.Succeeded = true; @@ -314,6 +339,21 @@ protected virtual Refund CreateRefund(PaymentIn payment, Store store, RefundOrde return refund; } + protected virtual void UpdateRefund(Refund refund, PaymentIn payment, Store store, RefundOrderPaymentRequest request) + { + refund.Amount = request.Amount ?? payment.Sum; + refund.ReasonCode = EnumUtility.SafeParse(request.ReasonCode, RefundReasonCode.Other); + refund.ReasonMessage = request.ReasonMessage; + refund.Comment = request.ReasonMessage; + refund.OuterId = request.OuterId; + refund.TransactionId = request.TransactionId; + + refund.Status = RefundStatus.Pending.ToString(); + refund.Currency = payment.Currency; + refund.CustomerOrderId = payment.OrderId; + refund.VendorId = payment.VendorId; + } + protected virtual Capture CreateCapture(PaymentIn payment, Store store, CaptureOrderPaymentRequest request) { var capture = AbstractTypeFactory.TryCreateInstance(); @@ -334,6 +374,19 @@ protected virtual Capture CreateCapture(PaymentIn payment, Store store, CaptureO return capture; } + protected virtual void UpdateCapture(Capture capture, PaymentIn payment, Store store, CaptureOrderPaymentRequest request) + { + capture.Amount = request.Amount ?? payment.Sum; + capture.Comment = request.CaptureDetails; + capture.OuterId = request.OuterId; + capture.TransactionId = request.TransactionId; + + capture.Status = CaptureStatus.Pending.ToString(); + capture.Currency = payment.Currency; + capture.CustomerOrderId = payment.OrderId; + capture.VendorId = payment.VendorId; + } + private static void FillPaymentRequestBase(OrderPaymentInfo paymentInfo, PaymentRequestBase result) { result.OrderId = paymentInfo.CustomerOrder.Id; diff --git a/src/VirtoCommerce.OrdersModule.Web/Localizations/en.VirtoCommerce.Orders.json b/src/VirtoCommerce.OrdersModule.Web/Localizations/en.VirtoCommerce.Orders.json index 15c056ec..dbd8561b 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Localizations/en.VirtoCommerce.Orders.json +++ b/src/VirtoCommerce.OrdersModule.Web/Localizations/en.VirtoCommerce.Orders.json @@ -143,6 +143,12 @@ "reasonCode": "Select..." } }, + "refund-detail": { + "labels": { + "transactionId": "Transaction Id", + "outerId": "Outer Id" + } + }, "capture-add": { "title": "Create a capture", "labels": { @@ -170,72 +176,78 @@ "vendor": "Select..." } }, - "transactions-list": { - "title": "Payment gateway transactions", - "subtitle": "List of all payment gateway interactions" - }, - "transaction-detail": { - "title": "Transaction details", - "subtitle": "Raw transaction data " - }, - "shipment-detail": { - "title": "Shipment #{{number}}", - "title-new": "New shipment", - "subtitle": "Edit shipment details", + "capture-detail": { "labels": { - "shipment-method": "Shipment method", - "approved": "Approved", - "shipment-number": "Shipment ID", - "status": "Status", - "fulfillment-center": "Fulfillment center", - "from": "Date and time", - "employee": "Assigned to", - "price": "Shipment amount", - "price-with-tax": "Shipment amount with tax", - "shipment-method-option": "Shipment method", - "tracking-number": "Tracking number", - "tracking-url": "Tracking URL", - "delivery-date": "Delivery date", - "vendor": "Vendor" + "transactionId": "Transaction Id", + "outerId": "Outer Id" + } + }, + "transactions-list": { + "title": "Payment gateway transactions", + "subtitle": "List of all payment gateway interactions" + }, + "transaction-detail": { + "title": "Transaction details", + "subtitle": "Raw transaction data " + }, + "shipment-detail": { + "title": "Shipment #{{number}}", + "title-new": "New shipment", + "subtitle": "Edit shipment details", + "labels": { + "shipment-method": "Shipment method", + "approved": "Approved", + "shipment-number": "Shipment ID", + "status": "Status", + "fulfillment-center": "Fulfillment center", + "from": "Date and time", + "employee": "Assigned to", + "price": "Shipment amount", + "price-with-tax": "Shipment amount with tax", + "shipment-method-option": "Shipment method", + "tracking-number": "Tracking number", + "tracking-url": "Tracking URL", + "delivery-date": "Delivery date", + "vendor": "Vendor" + }, + "placeholders": { + "status": "Select...", + "shipment-method": "Select...", + "fulfillment-center": "Select or search a center...", + "shipment-method-option": "Default shipment method", + "vendor": "Select..." + } + }, + "shipment-items": { + "title": "{{title}} line items", + "subtitle": "Edit shipment items", + "labels": { + "item": "Item", + "status": "Status", + "quantity": "Qty", + "price": "Price", + "total": "Total" + } + }, + "catalog-items-select": { + "title": "Add item to order" + }, + "newOperation-wizard": { + "title": "New operation", + "subtitle": "Select operation type", + "menu": { + "shipment-operation": { + "description": "Shipment document" }, - "placeholders": { - "status": "Select...", - "shipment-method": "Select...", - "fulfillment-center": "Select or search a center...", - "shipment-method-option": "Default shipment method", - "vendor": "Select..." - } - }, - "shipment-items": { - "title": "{{title}} line items", - "subtitle": "Edit shipment items", - "labels": { - "item": "Item", - "status": "Status", - "quantity": "Qty", - "price": "Price", - "total": "Total" - } - }, - "catalog-items-select": { - "title": "Add item to order" - }, - "newOperation-wizard": { - "title": "New operation", - "subtitle": "Select operation type", - "menu": { - "shipment-operation": { - "description": "Shipment document" - }, - "payment-operation": { - "description": "Incoming payment document" - }, - "refund-operation": { - "description": "Refund document" - } + "payment-operation": { + "description": "Incoming payment document" + }, + "refund-operation": { + "description": "Refund document" } } - }, + } + }, "widgets": { "notifications": { "title": "Notification feed", diff --git a/src/VirtoCommerce.OrdersModule.Web/Scripts/order.js b/src/VirtoCommerce.OrdersModule.Web/Scripts/order.js index 6f4eac21..ad2d5c7c 100644 --- a/src/VirtoCommerce.OrdersModule.Web/Scripts/order.js +++ b/src/VirtoCommerce.OrdersModule.Web/Scripts/order.js @@ -212,6 +212,18 @@ angular.module(moduleName, [ isReadOnly: true, title: "orders.blades.payment-detail.labels.from", valueType: "DateTime" + }, + { + name: 'transactionId', + isReadOnly: true, + title: "orders.blades.refund-detail.labels.transactionId", + valueType: "ShortText" + }, + { + name: 'outerId', + isReadOnly: true, + title: "orders.blades.refund-detail.labels.outerId", + valueType: "ShortText" } ] } @@ -235,6 +247,19 @@ angular.module(moduleName, [ isReadOnly: true, title: "orders.blades.payment-detail.labels.from", valueType: "DateTime" + }, + { + name: 'transactionId', + isReadOnly: true, + title: "orders.blades.capture-detail.labels.transactionId", + valueType: "ShortText" + } + , + { + name: 'outerId', + isReadOnly: true, + title: "orders.blades.capture-detail.labels.outerId", + valueType: "ShortText" } ] }