From 0241ade3260a951d558f9e0fdfaf6356de9bd3d5 Mon Sep 17 00:00:00 2001 From: Michael Bromley Date: Wed, 7 Apr 2021 11:38:07 +0200 Subject: [PATCH] feat(core): Allow setting PaymentState on failure to settle Payment Closes #809 --- .../core/e2e/fixtures/test-payment-methods.ts | 1 + packages/core/e2e/order.e2e-spec.ts | 5 ++- .../config/payment/payment-method-handler.ts | 37 +++++++++++++++++-- .../src/service/services/payment.service.ts | 6 +++ 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/packages/core/e2e/fixtures/test-payment-methods.ts b/packages/core/e2e/fixtures/test-payment-methods.ts index deb50eb070..61bba00d22 100644 --- a/packages/core/e2e/fixtures/test-payment-methods.ts +++ b/packages/core/e2e/fixtures/test-payment-methods.ts @@ -121,6 +121,7 @@ export const failsToSettlePaymentMethod = new PaymentMethodHandler({ settlePayment: () => { return { success: false, + state: 'Cancelled', errorMessage: 'Something went horribly wrong', metadata: { privateSettlePaymentData: 'secret', diff --git a/packages/core/e2e/order.e2e-spec.ts b/packages/core/e2e/order.e2e-spec.ts index 2fef5e9e19..9e32de2ff4 100644 --- a/packages/core/e2e/order.e2e-spec.ts +++ b/packages/core/e2e/order.e2e-spec.ts @@ -17,7 +17,7 @@ import gql from 'graphql-tag'; import path from 'path'; import { initialData } from '../../../e2e-common/e2e-initial-data'; -import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config'; +import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config'; import { failsToSettlePaymentMethod, @@ -83,9 +83,9 @@ import { DELETE_SHIPPING_METHOD, GET_CUSTOMER_LIST, GET_ORDER, + GET_ORDERS_LIST, GET_ORDER_FULFILLMENTS, GET_ORDER_HISTORY, - GET_ORDERS_LIST, GET_PRODUCT_WITH_VARIANTS, GET_STOCK_MOVEMENT, SETTLE_PAYMENT, @@ -356,6 +356,7 @@ describe('Orders resolver', () => { }); expect(result.order!.state).toBe('PaymentAuthorized'); + expect(result.order!.payments![0].state).toBe('Cancelled'); firstOrderCode = order.code; firstOrderId = order.id; }); diff --git a/packages/core/src/config/payment/payment-method-handler.ts b/packages/core/src/config/payment/payment-method-handler.ts index cfeba98494..74bb095058 100644 --- a/packages/core/src/config/payment/payment-method-handler.ts +++ b/packages/core/src/config/payment/payment-method-handler.ts @@ -42,6 +42,9 @@ export interface CreatePaymentResult { * * In a single-step payment flow, this should be set to `'Settled'`. * In a two-step flow, this should be set to `'Authorized'`. + * + * If using a {@link CustomOrderProcess}, may be something else + * entirely according to your business logic. */ state: Exclude; /** @@ -101,13 +104,41 @@ export interface CreateRefundResult { /** * @description - * This object is the return value of the {@link SettlePaymentFn} + * This object is the return value of the {@link SettlePaymentFn} when the Payment + * has been successfully settled. * * @docsCategory payment * @docsPage Payment Method Types */ export interface SettlePaymentResult { - success: boolean; + success: true; + metadata?: PaymentMetadata; +} + +/** + * @description + * This object is the return value of the {@link SettlePaymentFn} when the Payment + * could not be settled. + * + * @docsCategory payment + * @docsPage Payment Method Types + */ +export interface SettlePaymentErrorResult { + success: false; + /** + * @description + * The state to transition this Payment to upon unsuccessful settlement. + * Defaults to `Error`. Note that if using a different state, it must be + * legal to transition to that state from the `Authorized` state according + * to the PaymentState config (which can be customized using the + * {@link CustomPaymentProcess}). + */ + state?: Exclude; + /** + * @description + * The message that will be returned when attempting to settle the payment, and will + * also be persisted as `Payment.errorMessage`. + */ errorMessage?: string; metadata?: PaymentMetadata; } @@ -141,7 +172,7 @@ export type SettlePaymentFn = ( order: Order, payment: Payment, args: ConfigArgValues, -) => SettlePaymentResult | Promise; +) => SettlePaymentResult | SettlePaymentErrorResult | Promise; /** * @description diff --git a/packages/core/src/service/services/payment.service.ts b/packages/core/src/service/services/payment.service.ts index d9aafbb33f..9c88523452 100644 --- a/packages/core/src/service/services/payment.service.ts +++ b/packages/core/src/service/services/payment.service.ts @@ -150,6 +150,12 @@ export class PaymentService { } else { payment.errorMessage = settlePaymentResult.errorMessage; payment.metadata = this.mergePaymentMetadata(payment.metadata, settlePaymentResult.metadata); + await this.paymentStateMachine.transition( + ctx, + payment.order, + payment, + settlePaymentResult.state || 'Error', + ); await this.connection.getRepository(ctx, Payment).save(payment, { reload: false }); } return payment;