Browse Source

feat(core): Pass `amount` argument into createPayment method

BREAKING CHANGE: The `PaymentMethodHandler.createPayment()` method now takes a new `amount`
argument. Update any custom PaymentMethodHandlers to use account for this new parameter and use
it instead of `order.total` when creating a new payment.

    ```ts
    // before
    createPayment: async (ctx, order, args, metadata) {
      const transactionAmount = order.total;
      // ...
    }

    // after
    createPayment: async (ctx, order, amount, args, metadata) {
      const transactionAmount = amount;
      // ...
    }
    ```
Michael Bromley 5 years ago
parent
commit
0c85c762e6

+ 2 - 2
docs/content/docs/developer-guide/payment-integrations/index.md

@@ -46,11 +46,11 @@ const myPaymentIntegration = new PaymentMethodHandler({
   },
 
   /** This is called when the `addPaymentToOrder` mutation is executed */
-  createPayment: async (ctx, order, args, metadata): Promise<CreatePaymentResult> => {
+  createPayment: async (ctx, order, amount, args, metadata): Promise<CreatePaymentResult> => {
     try {
       const result = await sdk.charges.create({
+        amount,
         apiKey: args.apiKey,
-        amount: order.total,
         source: metadata.token,
       });
       return {

+ 14 - 14
packages/core/e2e/fixtures/test-payment-methods.ts

@@ -6,9 +6,9 @@ export const testSuccessfulPaymentMethod = new PaymentMethodHandler({
     code: 'test-payment-method',
     description: [{ languageCode: LanguageCode.en, value: 'Test Payment Method' }],
     args: {},
-    createPayment: (ctx, order, args, metadata) => {
+    createPayment: (ctx, order, amount, args, metadata) => {
         return {
-            amount: order.totalWithTax,
+            amount,
             state: 'Settled',
             transactionId: '12345',
             metadata: { public: metadata },
@@ -27,9 +27,9 @@ export const twoStagePaymentMethod = new PaymentMethodHandler({
     code: 'authorize-only-payment-method',
     description: [{ languageCode: LanguageCode.en, value: 'Test Payment Method' }],
     args: {},
-    createPayment: (ctx, order, args, metadata) => {
+    createPayment: (ctx, order, amount, args, metadata) => {
         return {
-            amount: order.totalWithTax,
+            amount,
             state: 'Authorized',
             transactionId: '12345',
             metadata: { public: metadata },
@@ -55,9 +55,9 @@ export const singleStageRefundablePaymentMethod = new PaymentMethodHandler({
     code: 'single-stage-refundable-payment-method',
     description: [{ languageCode: LanguageCode.en, value: 'Test Payment Method' }],
     args: {},
-    createPayment: (ctx, order, args, metadata) => {
+    createPayment: (ctx, order, amount, args, metadata) => {
         return {
-            amount: order.totalWithTax,
+            amount,
             state: 'Settled',
             transactionId: '12345',
             metadata,
@@ -66,9 +66,9 @@ export const singleStageRefundablePaymentMethod = new PaymentMethodHandler({
     settlePayment: () => {
         return { success: true };
     },
-    createRefund: (ctx, input, total, order, payment, args) => {
+    createRefund: (ctx, input, amount, order, payment, args) => {
         return {
-            amount: total,
+            amount,
             state: 'Settled',
             transactionId: 'abc123',
         };
@@ -82,9 +82,9 @@ export const failsToSettlePaymentMethod = new PaymentMethodHandler({
     code: 'fails-to-settle-payment-method',
     description: [{ languageCode: LanguageCode.en, value: 'Test Payment Method' }],
     args: {},
-    createPayment: (ctx, order, args, metadata) => {
+    createPayment: (ctx, order, amount, args, metadata) => {
         return {
-            amount: order.totalWithTax,
+            amount,
             state: 'Authorized',
             transactionId: '12345',
             metadata: {
@@ -112,9 +112,9 @@ export const testFailingPaymentMethod = new PaymentMethodHandler({
     code: 'test-failing-payment-method',
     description: [{ languageCode: LanguageCode.en, value: 'Test Failing Payment Method' }],
     args: {},
-    createPayment: (ctx, order, args, metadata) => {
+    createPayment: (ctx, order, amount, args, metadata) => {
         return {
-            amount: order.totalWithTax,
+            amount,
             state: 'Declined',
             errorMessage: 'Insufficient funds',
             metadata: { public: metadata },
@@ -128,9 +128,9 @@ export const testErrorPaymentMethod = new PaymentMethodHandler({
     code: 'test-error-payment-method',
     description: [{ languageCode: LanguageCode.en, value: 'Test Error Payment Method' }],
     args: {},
-    createPayment: (ctx, order, args, metadata) => {
+    createPayment: (ctx, order, amount, args, metadata) => {
         return {
-            amount: order.totalWithTax,
+            amount,
             state: 'Error',
             errorMessage: 'Something went horribly wrong',
             metadata,

+ 4 - 4
packages/core/src/config/payment-method/example-payment-method-handler.ts

@@ -29,22 +29,22 @@ export const examplePaymentHandler = new PaymentMethodHandler({
         automaticCapture: { type: 'boolean' },
         apiKey: { type: 'string' },
     },
-    createPayment: async (ctx, order, args, metadata): Promise<CreatePaymentResult> => {
+    createPayment: async (ctx, order, amount, args, metadata): Promise<CreatePaymentResult> => {
         try {
             const result = await gripeSDK.charges.create({
                 apiKey: args.apiKey,
-                amount: order.totalWithTax,
+                amount,
                 source: metadata.authToken,
             });
             return {
-                amount: order.totalWithTax,
+                amount,
                 state: args.automaticCapture ? 'Settled' : 'Authorized',
                 transactionId: result.id.toString(),
                 metadata,
             };
         } catch (err) {
             return {
-                amount: order.totalWithTax,
+                amount,
                 state: 'Declined' as 'Declined',
                 metadata: {
                     errorMessage: err.message,

+ 51 - 38
packages/core/src/config/payment-method/payment-method-handler.ts

@@ -124,6 +124,7 @@ export interface SettlePaymentResult {
 export type CreatePaymentFn<T extends ConfigArgs> = (
     ctx: RequestContext,
     order: Order,
+    amount: number,
     args: ConfigArgValues<T>,
     metadata: PaymentMetadata,
 ) => CreatePaymentResult | CreatePaymentErrorResult | Promise<CreatePaymentResult | CreatePaymentErrorResult>;
@@ -152,7 +153,7 @@ export type SettlePaymentFn<T extends ConfigArgs> = (
 export type CreateRefundFn<T extends ConfigArgs> = (
     ctx: RequestContext,
     input: RefundOrderInput,
-    total: number,
+    amount: number,
     order: Order,
     payment: Payment,
     args: ConfigArgValues<T>,
@@ -213,40 +214,40 @@ export interface PaymentMethodConfigOptions<T extends ConfigArgs> extends Config
  * import gripeSDK from 'gripe';
  *
  * export const examplePaymentHandler = new PaymentMethodHandler({
- *     code: 'example-payment-provider',
- *     description: [{
- *         languageCode: LanguageCode.en,
- *         value: 'Example Payment Provider',
- *     }],
- *     args: {
- *         apiKey: { type: 'string' },
- *     },
- *     createPayment: async (ctx, order, args, metadata): Promise<CreatePaymentResult> => {
- *         try {
- *             const result = await gripeSDK.charges.create({
- *                 apiKey: args.apiKey,
- *                 amount: order.total,
- *                 source: metadata.authToken,
- *             });
- *             return {
- *                 amount: order.total,
- *                 state: 'Settled' as const,
- *                 transactionId: result.id.toString(),
- *                 metadata: result.outcome,
- *             };
- *         } catch (err) {
- *             return {
- *                 amount: order.total,
- *                 state: 'Declined' as const,
- *                 metadata: {
- *                     errorMessage: err.message,
- *                 },
- *             };
- *         }
- *     },
- *     settlePayment: async (ctx, order, payment, args): Promise<SettlePaymentResult> => {
- *         return { success: true };
+ *   code: 'example-payment-provider',
+ *   description: [{
+ *     languageCode: LanguageCode.en,
+ *     value: 'Example Payment Provider',
+ *   }],
+ *   args: {
+ *     apiKey: { type: 'string' },
+ *   },
+ *   createPayment: async (ctx, order, amount, args, metadata): Promise<CreatePaymentResult> => {
+ *     try {
+ *       const result = await gripeSDK.charges.create({
+ *         amount,
+ *         apiKey: args.apiKey,
+ *         source: metadata.authToken,
+ *       });
+ *       return {
+ *         amount: order.total,
+ *         state: 'Settled' as const,
+ *         transactionId: result.id.toString(),
+ *         metadata: result.outcome,
+ *       };
+ *     } catch (err) {
+ *       return {
+ *         amount: order.total,
+ *         state: 'Declined' as const,
+ *         metadata: {
+ *           errorMessage: err.message,
+ *         },
+ *       };
  *     }
+ *   },
+ *   settlePayment: async (ctx, order, payment, args): Promise<SettlePaymentResult> => {
+ *     return { success: true };
+ *   }
  * });
  * ```
  *
@@ -273,8 +274,20 @@ export class PaymentMethodHandler<T extends ConfigArgs = ConfigArgs> extends Con
      *
      * @internal
      */
-    async createPayment(ctx: RequestContext, order: Order, args: ConfigArg[], metadata: PaymentMetadata) {
-        const paymentConfig = await this.createPaymentFn(ctx, order, this.argsArrayToHash(args), metadata);
+    async createPayment(
+        ctx: RequestContext,
+        order: Order,
+        amount: number,
+        args: ConfigArg[],
+        metadata: PaymentMetadata,
+    ) {
+        const paymentConfig = await this.createPaymentFn(
+            ctx,
+            order,
+            amount,
+            this.argsArrayToHash(args),
+            metadata,
+        );
         return {
             method: this.code,
             ...paymentConfig,
@@ -300,13 +313,13 @@ export class PaymentMethodHandler<T extends ConfigArgs = ConfigArgs> extends Con
     async createRefund(
         ctx: RequestContext,
         input: RefundOrderInput,
-        total: number,
+        amount: number,
         order: Order,
         payment: Payment,
         args: ConfigArg[],
     ) {
         return this.createRefundFn
-            ? this.createRefundFn(ctx, input, total, order, payment, this.argsArrayToHash(args))
+            ? this.createRefundFn(ctx, input, amount, order, payment, this.argsArrayToHash(args))
             : false;
     }
 

+ 11 - 0
packages/core/src/service/services/order.service.ts

@@ -131,6 +131,7 @@ export class OrderService {
         private promotionService: PromotionService,
         private eventBus: EventBus,
         private channelService: ChannelService,
+        private orderModifier: OrderModifier,
     ) {}
 
     getOrderProcessStates(): OrderProcessState[] {
@@ -251,6 +252,15 @@ export class OrderService {
         return refund.orderItems;
     }
 
+    getOrderModifications(ctx: RequestContext, orderId: ID): Promise<OrderModification[]> {
+        return this.connection.getRepository(ctx, OrderModification).find({
+            where: {
+                order: orderId,
+            },
+            relations: ['orderItems', 'payment', 'refund'],
+        });
+    }
+
     getPaymentRefunds(ctx: RequestContext, paymentId: ID): Promise<Refund[]> {
         return this.connection.getRepository(ctx, Refund).find({
             where: {
@@ -770,6 +780,7 @@ export class OrderService {
         const payment = await this.paymentMethodService.createPayment(
             ctx,
             order,
+            order.totalWithTax,
             input.method,
             input.metadata,
         );

+ 14 - 2
packages/core/src/service/services/payment-method.service.ts

@@ -78,9 +78,21 @@ export class PaymentMethodService {
         return this.connection.getRepository(ctx, PaymentMethod).save(updatedPaymentMethod);
     }
 
-    async createPayment(ctx: RequestContext, order: Order, method: string, metadata: any): Promise<Payment> {
+    async createPayment(
+        ctx: RequestContext,
+        order: Order,
+        amount: number,
+        method: string,
+        metadata: any,
+    ): Promise<Payment> {
         const { paymentMethod, handler } = await this.getMethodAndHandler(ctx, method);
-        const result = await handler.createPayment(ctx, order, paymentMethod.configArgs, metadata || {});
+        const result = await handler.createPayment(
+            ctx,
+            order,
+            amount,
+            paymentMethod.configArgs,
+            metadata || {},
+        );
         const initialState = 'Created';
         const payment = await this.connection
             .getRepository(ctx, Payment)