Sfoglia il codice sorgente

feat(core): Add eligiblePaymentMethods query to Shop API

Relates to #469
Michael Bromley 5 anni fa
parent
commit
e528c099fe

+ 7 - 0
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts

@@ -2693,6 +2693,13 @@ export type ShippingMethodQuote = {
     metadata?: Maybe<Scalars['JSON']>;
 };
 
+export type PaymentMethodQuote = {
+    id: Scalars['ID'];
+    code: Scalars['String'];
+    isEligible: Scalars['Boolean'];
+    eligibilityMessage?: Maybe<Scalars['String']>;
+};
+
 export type Country = Node & {
     id: Scalars['ID'];
     createdAt: Scalars['DateTime'];

+ 10 - 0
packages/common/src/generated-shop-types.ts

@@ -36,6 +36,8 @@ export type Query = {
     collection?: Maybe<Collection>;
     /** Returns a list of eligible shipping methods based on the current active Order */
     eligibleShippingMethods: Array<ShippingMethodQuote>;
+    /** Returns a list of payment methods and their eligibility based on the current active Order */
+    eligiblePaymentMethods: Array<PaymentMethodQuote>;
     /** Returns information about the current authenticated User */
     me?: Maybe<CurrentUser>;
     /** Returns the possible next states that the activeOrder can transition to */
@@ -800,6 +802,14 @@ export type ShippingMethodQuote = {
     metadata?: Maybe<Scalars['JSON']>;
 };
 
+export type PaymentMethodQuote = {
+    __typename?: 'PaymentMethodQuote';
+    id: Scalars['ID'];
+    code: Scalars['String'];
+    isEligible: Scalars['Boolean'];
+    eligibilityMessage?: Maybe<Scalars['String']>;
+};
+
 export type Country = Node & {
     __typename?: 'Country';
     id: Scalars['ID'];

+ 8 - 0
packages/common/src/generated-types.ts

@@ -2890,6 +2890,14 @@ export type ShippingMethodQuote = {
   metadata?: Maybe<Scalars['JSON']>;
 };
 
+export type PaymentMethodQuote = {
+  __typename?: 'PaymentMethodQuote';
+  id: Scalars['ID'];
+  code: Scalars['String'];
+  isEligible: Scalars['Boolean'];
+  eligibilityMessage?: Maybe<Scalars['String']>;
+};
+
 export type Country = Node & {
   __typename?: 'Country';
   id: Scalars['ID'];

+ 7 - 0
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -2693,6 +2693,13 @@ export type ShippingMethodQuote = {
     metadata?: Maybe<Scalars['JSON']>;
 };
 
+export type PaymentMethodQuote = {
+    id: Scalars['ID'];
+    code: Scalars['String'];
+    isEligible: Scalars['Boolean'];
+    eligibilityMessage?: Maybe<Scalars['String']>;
+};
+
 export type Country = Node & {
     id: Scalars['ID'];
     createdAt: Scalars['DateTime'];

+ 25 - 0
packages/core/e2e/graphql/generated-e2e-shop-types.ts

@@ -35,6 +35,8 @@ export type Query = {
     collection?: Maybe<Collection>;
     /** Returns a list of eligible shipping methods based on the current active Order */
     eligibleShippingMethods: Array<ShippingMethodQuote>;
+    /** Returns a list of payment methods and their eligibility based on the current active Order */
+    eligiblePaymentMethods: Array<PaymentMethodQuote>;
     /** Returns information about the current authenticated User */
     me?: Maybe<CurrentUser>;
     /** Returns the possible next states that the activeOrder can transition to */
@@ -770,6 +772,13 @@ export type ShippingMethodQuote = {
     metadata?: Maybe<Scalars['JSON']>;
 };
 
+export type PaymentMethodQuote = {
+    id: Scalars['ID'];
+    code: Scalars['String'];
+    isEligible: Scalars['Boolean'];
+    eligibilityMessage?: Maybe<Scalars['String']>;
+};
+
 export type Country = Node & {
     id: Scalars['ID'];
     createdAt: Scalars['DateTime'];
@@ -3153,6 +3162,14 @@ export type RemoveAllOrderLinesMutation = {
     removeAllOrderLines: TestOrderFragmentFragment | Pick<OrderModificationError, 'errorCode' | 'message'>;
 };
 
+export type GetEligiblePaymentMethodsQueryVariables = Exact<{ [key: string]: never }>;
+
+export type GetEligiblePaymentMethodsQuery = {
+    eligiblePaymentMethods: Array<
+        Pick<PaymentMethodQuote, 'id' | 'code' | 'eligibilityMessage' | 'isEligible'>
+    >;
+};
+
 type DiscriminateUnion<T, U> = T extends U ? T : never;
 
 export namespace TestOrderFragment {
@@ -3664,3 +3681,11 @@ export namespace RemoveAllOrderLines {
         { __typename?: 'ErrorResult' }
     >;
 }
+
+export namespace GetEligiblePaymentMethods {
+    export type Variables = GetEligiblePaymentMethodsQueryVariables;
+    export type Query = GetEligiblePaymentMethodsQuery;
+    export type EligiblePaymentMethods = NonNullable<
+        NonNullable<GetEligiblePaymentMethodsQuery['eligiblePaymentMethods']>[number]
+    >;
+}

+ 11 - 0
packages/core/e2e/graphql/shop-definitions.ts

@@ -669,3 +669,14 @@ export const REMOVE_ALL_ORDER_LINES = gql`
     }
     ${TEST_ORDER_FRAGMENT}
 `;
+
+export const GET_ELIGIBLE_PAYMENT_METHODS = gql`
+    query GetEligiblePaymentMethods {
+        eligiblePaymentMethods {
+            id
+            code
+            eligibilityMessage
+            isEligible
+        }
+    }
+`;

+ 54 - 19
packages/core/e2e/payment-method.e2e-spec.ts

@@ -21,9 +21,10 @@ import {
     AddItemToOrder,
     AddPaymentToOrder,
     ErrorCode,
+    GetEligiblePaymentMethods,
     TestOrderWithPaymentsFragment,
 } from './graphql/generated-e2e-shop-types';
-import { ADD_ITEM_TO_ORDER, ADD_PAYMENT } from './graphql/shop-definitions';
+import { ADD_ITEM_TO_ORDER, ADD_PAYMENT, GET_ELIGIBLE_PAYMENT_METHODS } from './graphql/shop-definitions';
 import { proceedToArrangingPayment } from './utils/test-order-utils';
 
 const checkerSpy = jest.fn();
@@ -116,7 +117,6 @@ describe('PaymentMethod resolver', () => {
             input: {
                 id: 'T_1',
                 description: 'modified',
-                enabled: false,
                 handler: {
                     code: dummyPaymentHandler.code,
                     arguments: [{ name: 'automaticSettle', value: 'false' }],
@@ -129,7 +129,7 @@ describe('PaymentMethod resolver', () => {
             name: 'No Checker',
             code: 'no-checks',
             description: 'modified',
-            enabled: false,
+            enabled: true,
             handler: {
                 args: [
                     {
@@ -168,25 +168,40 @@ describe('PaymentMethod resolver', () => {
 
     describe('eligibility checks', () => {
         beforeAll(async () => {
-            const { createPaymentMethod } = await adminClient.query<
-                CreatePaymentMethod.Mutation,
-                CreatePaymentMethod.Variables
-            >(CREATE_PAYMENT_METHOD, {
-                input: {
-                    code: 'price-check',
-                    name: 'With Min Price Checker',
-                    description: 'Order total must be more than 2k',
-                    enabled: true,
-                    checker: {
-                        code: minPriceChecker.code,
-                        arguments: [{ name: 'minPrice', value: '200000' }],
+            await adminClient.query<CreatePaymentMethod.Mutation, CreatePaymentMethod.Variables>(
+                CREATE_PAYMENT_METHOD,
+                {
+                    input: {
+                        code: 'price-check',
+                        name: 'With Min Price Checker',
+                        description: 'Order total must be more than 2k',
+                        enabled: true,
+                        checker: {
+                            code: minPriceChecker.code,
+                            arguments: [{ name: 'minPrice', value: '200000' }],
+                        },
+                        handler: {
+                            code: dummyPaymentHandler.code,
+                            arguments: [{ name: 'automaticSettle', value: 'true' }],
+                        },
                     },
-                    handler: {
-                        code: dummyPaymentHandler.code,
-                        arguments: [{ name: 'automaticSettle', value: 'true' }],
+                },
+            );
+            await adminClient.query<CreatePaymentMethod.Mutation, CreatePaymentMethod.Variables>(
+                CREATE_PAYMENT_METHOD,
+                {
+                    input: {
+                        code: 'disabled-method',
+                        name: 'Disabled ones',
+                        description: 'This method is disabled',
+                        enabled: false,
+                        handler: {
+                            code: dummyPaymentHandler.code,
+                            arguments: [{ name: 'automaticSettle', value: 'true' }],
+                        },
                     },
                 },
-            });
+            );
 
             await shopClient.asUserWithCredentials('hayden.zieme12@hotmail.com', 'test');
             await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
@@ -197,6 +212,26 @@ describe('PaymentMethod resolver', () => {
             await proceedToArrangingPayment(shopClient);
         });
 
+        it('eligiblePaymentMethods', async () => {
+            const { eligiblePaymentMethods } = await shopClient.query<GetEligiblePaymentMethods.Query>(
+                GET_ELIGIBLE_PAYMENT_METHODS,
+            );
+            expect(eligiblePaymentMethods).toEqual([
+                {
+                    id: 'T_1',
+                    code: 'no-checks',
+                    isEligible: true,
+                    eligibilityMessage: null,
+                },
+                {
+                    id: 'T_2',
+                    code: 'price-check',
+                    isEligible: false,
+                    eligibilityMessage: 'Order total too low',
+                },
+            ]);
+        });
+
         it('addPaymentToOrder does not allow ineligible method', async () => {
             checkerSpy.mockClear();
             const { addPaymentToOrder } = await shopClient.query<

+ 13 - 0
packages/core/src/api/resolvers/shop/shop-order.resolver.ts

@@ -14,6 +14,7 @@ import {
     MutationSetOrderShippingAddressArgs,
     MutationSetOrderShippingMethodArgs,
     MutationTransitionOrderToStateArgs,
+    PaymentMethodQuote,
     Permission,
     QueryOrderArgs,
     QueryOrderByCodeArgs,
@@ -180,6 +181,18 @@ export class ShopOrderResolver {
         return [];
     }
 
+    @Query()
+    @Allow(Permission.Owner)
+    async eligiblePaymentMethods(@Ctx() ctx: RequestContext): Promise<PaymentMethodQuote[]> {
+        if (ctx.authorizedAsOwnerOnly) {
+            const sessionOrder = await this.getOrderFromContext(ctx);
+            if (sessionOrder) {
+                return this.orderService.getEligiblePaymentMethods(ctx, sessionOrder.id);
+            }
+        }
+        return [];
+    }
+
     @Transaction()
     @Mutation()
     @Allow(Permission.Owner)

+ 7 - 0
packages/core/src/api/schema/common/common-types.graphql

@@ -183,3 +183,10 @@ type ShippingMethodQuote {
     "Any optional metadata returned by the ShippingCalculator in the ShippingCalculationResult"
     metadata: JSON
 }
+
+type PaymentMethodQuote {
+    id: ID!
+    code: String!
+    isEligible: Boolean!
+    eligibilityMessage: String
+}

+ 2 - 0
packages/core/src/api/schema/shop-api/shop.api.graphql

@@ -17,6 +17,8 @@ type Query {
     collection(id: ID, slug: String): Collection
     "Returns a list of eligible shipping methods based on the current active Order"
     eligibleShippingMethods: [ShippingMethodQuote!]!
+    "Returns a list of payment methods and their eligibility based on the current active Order"
+    eligiblePaymentMethods: [PaymentMethodQuote!]!
     "Returns information about the current authenticated User"
     me: CurrentUser
     "Returns the possible next states that the activeOrder can transition to"

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

@@ -3,6 +3,7 @@ import {
     AddPaymentToOrderResult,
     ApplyCouponCodeResult,
     PaymentInput,
+    PaymentMethodQuote,
     RemoveOrderItemsResult,
     SetOrderShippingMethodResult,
     UpdateOrderItemsResult,
@@ -599,6 +600,11 @@ export class OrderService {
         });
     }
 
+    async getEligiblePaymentMethods(ctx: RequestContext, orderId: ID): Promise<PaymentMethodQuote[]> {
+        const order = await this.getOrderOrThrow(ctx, orderId);
+        return this.paymentMethodService.getEligiblePaymentMethods(ctx, order);
+    }
+
     async setShippingMethod(
         ctx: RequestContext,
         orderId: ID,

+ 30 - 0
packages/core/src/service/services/payment-method.service.ts

@@ -1,4 +1,5 @@
 import { Injectable } from '@nestjs/common';
+import { PaymentMethodQuote } from '@vendure/common/lib/generated-shop-types';
 import {
     ConfigurableOperationDefinition,
     CreatePaymentMethodInput,
@@ -99,6 +100,35 @@ export class PaymentMethodService {
         return this.configArgService.getDefinitions('PaymentMethodHandler').map(x => x.toGraphQlType(ctx));
     }
 
+    async getEligiblePaymentMethods(ctx: RequestContext, order: Order): Promise<PaymentMethodQuote[]> {
+        const paymentMethods = await this.connection
+            .getRepository(ctx, PaymentMethod)
+            .find({ where: { enabled: true } });
+        const results: PaymentMethodQuote[] = [];
+        for (const method of paymentMethods) {
+            let isEligible = true;
+            let eligibilityMessage: string | undefined;
+            if (method.checker) {
+                const checker = this.configArgService.getByCode(
+                    'PaymentMethodEligibilityChecker',
+                    method.checker.code,
+                );
+                const eligible = await checker.check(ctx, order, method.checker.args);
+                if (eligible === false || typeof eligible === 'string') {
+                    isEligible = false;
+                    eligibilityMessage = typeof eligible === 'string' ? eligible : undefined;
+                }
+            }
+            results.push({
+                id: method.id,
+                code: method.code,
+                isEligible,
+                eligibilityMessage,
+            });
+        }
+        return results;
+    }
+
     async createPayment(
         ctx: RequestContext,
         order: Order,

+ 7 - 0
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts

@@ -2693,6 +2693,13 @@ export type ShippingMethodQuote = {
     metadata?: Maybe<Scalars['JSON']>;
 };
 
+export type PaymentMethodQuote = {
+    id: Scalars['ID'];
+    code: Scalars['String'];
+    isEligible: Scalars['Boolean'];
+    eligibilityMessage?: Maybe<Scalars['String']>;
+};
+
 export type Country = Node & {
     id: Scalars['ID'];
     createdAt: Scalars['DateTime'];

File diff suppressed because it is too large
+ 0 - 0
schema-admin.json


File diff suppressed because it is too large
+ 0 - 0
schema-shop.json


Some files were not shown because too many files changed in this diff