Răsfoiți Sursa

feat(core): De-couple PaymentMethod from PaymentMethodHandler

Relates to #671

BREAKING CHANGE: The PaymentMethod entity and type has changed. Previously, a PaymentMethod was
coupled to the configured PaymentMethodHandlers 1-to-1. Now the PaymentMethodHandler is just
a configurable _property_ of the PaymentMethod, much in the same way that a ShippingCalculator
relates to a ShippingMethod. Any existing PaymentMethod entities will need to be migrated to the
new structure.
Michael Bromley 5 ani în urmă
părinte
comite
ee9ba23a0d
35 a modificat fișierele cu 549 adăugiri și 156 ștergeri
  1. 5 1
      e2e-common/e2e-initial-data.ts
  2. 29 3
      packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts
  3. 1 0
      packages/common/src/generated-shop-types.ts
  4. 36 9
      packages/common/src/generated-types.ts
  5. 9 1
      packages/core/e2e/fulfillment-process.e2e-spec.ts
  6. 66 3
      packages/core/e2e/graphql/generated-e2e-admin-types.ts
  7. 1 0
      packages/core/e2e/graphql/generated-e2e-shop-types.ts
  8. 9 1
      packages/core/e2e/order-fulfillment.e2e-spec.ts
  9. 9 1
      packages/core/e2e/order-modification.e2e-spec.ts
  10. 9 1
      packages/core/e2e/order-process.e2e-spec.ts
  11. 9 1
      packages/core/e2e/order-promotion.e2e-spec.ts
  12. 9 1
      packages/core/e2e/order-taxes.e2e-spec.ts
  13. 17 1
      packages/core/e2e/order.e2e-spec.ts
  14. 135 0
      packages/core/e2e/payment-method.e2e-spec.ts
  15. 17 1
      packages/core/e2e/shop-order.e2e-spec.ts
  16. 13 1
      packages/core/e2e/stock-control.e2e-spec.ts
  17. 5 1
      packages/core/mock-data/data-sources/initial-data.ts
  18. 11 0
      packages/core/src/api/resolvers/admin/payment-method.resolver.ts
  19. 0 8
      packages/core/src/api/resolvers/entity/payment-method-entity.resolver.ts
  20. 14 1
      packages/core/src/api/schema/admin-api/payment-method.api.graphql
  21. 3 2
      packages/core/src/api/schema/admin-api/payment-method.type.graphql
  22. 1 0
      packages/core/src/config/index.ts
  23. 61 0
      packages/core/src/config/payment-method/dummy-payment-method-handler.ts
  24. 19 1
      packages/core/src/data-import/providers/populator/populator.ts
  25. 2 1
      packages/core/src/data-import/types.ts
  26. 7 3
      packages/core/src/entity/payment-method/payment-method.entity.ts
  27. 1 1
      packages/core/src/service/helpers/config-arg/config-arg.service.ts
  28. 0 3
      packages/core/src/service/initializer.service.ts
  29. 16 101
      packages/core/src/service/services/payment-method.service.ts
  30. 2 2
      packages/create/templates/vendure-config.hbs
  31. 2 1
      packages/dev-server/dev-config.ts
  32. 2 3
      packages/dev-server/load-testing/load-test-config.ts
  33. 29 3
      packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts
  34. 0 0
      schema-admin.json
  35. 0 0
      schema-shop.json

+ 5 - 1
e2e-common/e2e-initial-data.ts

@@ -9,7 +9,11 @@ export const initialData: InitialData = {
         { name: 'Reduced Tax', percentage: 10 },
         { name: 'Reduced Tax', percentage: 10 },
         { name: 'Zero Tax', percentage: 0 },
         { name: 'Zero Tax', percentage: 0 },
     ],
     ],
-    shippingMethods: [{ name: 'Standard Shipping', price: 500 }, { name: 'Express Shipping', price: 1000 }],
+    shippingMethods: [
+        { name: 'Standard Shipping', price: 500 },
+        { name: 'Express Shipping', price: 1000 },
+    ],
+    paymentMethods: [],
     countries: [
     countries: [
         { name: 'Australia', code: 'AU', zone: 'Oceania' },
         { name: 'Australia', code: 'AU', zone: 'Oceania' },
         { name: 'Austria', code: 'AT', zone: 'Europe' },
         { name: 'Austria', code: 'AT', zone: 'Europe' },

+ 29 - 3
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts

@@ -49,6 +49,7 @@ export type Query = {
     orders: OrderList;
     orders: OrderList;
     paymentMethods: PaymentMethodList;
     paymentMethods: PaymentMethodList;
     paymentMethod?: Maybe<PaymentMethod>;
     paymentMethod?: Maybe<PaymentMethod>;
+    paymentMethodHandlers: Array<ConfigurableOperationDefinition>;
     productOptionGroups: Array<ProductOptionGroup>;
     productOptionGroups: Array<ProductOptionGroup>;
     productOptionGroup?: Maybe<ProductOptionGroup>;
     productOptionGroup?: Maybe<ProductOptionGroup>;
     search: SearchResponse;
     search: SearchResponse;
@@ -366,6 +367,8 @@ export type Mutation = {
      * Payment.
      * Payment.
      */
      */
     addManualPaymentToOrder: AddManualPaymentToOrderResult;
     addManualPaymentToOrder: AddManualPaymentToOrderResult;
+    /** Create existing PaymentMethod */
+    createPaymentMethod: PaymentMethod;
     /** Update an existing PaymentMethod */
     /** Update an existing PaymentMethod */
     updatePaymentMethod: PaymentMethod;
     updatePaymentMethod: PaymentMethod;
     /** Create a new ProductOptionGroup */
     /** Create a new ProductOptionGroup */
@@ -693,6 +696,10 @@ export type MutationAddManualPaymentToOrderArgs = {
     input: ManualPaymentInput;
     input: ManualPaymentInput;
 };
 };
 
 
+export type MutationCreatePaymentMethodArgs = {
+    input: CreatePaymentMethodInput;
+};
+
 export type MutationUpdatePaymentMethodArgs = {
 export type MutationUpdatePaymentMethodArgs = {
     input: UpdatePaymentMethodInput;
     input: UpdatePaymentMethodInput;
 };
 };
@@ -959,6 +966,7 @@ export type CreateChannelInput = {
     currencyCode: CurrencyCode;
     currencyCode: CurrencyCode;
     defaultTaxZoneId: Scalars['ID'];
     defaultTaxZoneId: Scalars['ID'];
     defaultShippingZoneId: Scalars['ID'];
     defaultShippingZoneId: Scalars['ID'];
+    customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 export type UpdateChannelInput = {
 export type UpdateChannelInput = {
@@ -970,6 +978,7 @@ export type UpdateChannelInput = {
     currencyCode?: Maybe<CurrencyCode>;
     currencyCode?: Maybe<CurrencyCode>;
     defaultTaxZoneId?: Maybe<Scalars['ID']>;
     defaultTaxZoneId?: Maybe<Scalars['ID']>;
     defaultShippingZoneId?: Maybe<Scalars['ID']>;
     defaultShippingZoneId?: Maybe<Scalars['ID']>;
+    customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 /** Returned if attempting to set a Channel's defaultLanguageCode to a language which is not enabled in GlobalSettings */
 /** Returned if attempting to set a Channel's defaultLanguageCode to a language which is not enabled in GlobalSettings */
@@ -1721,21 +1730,32 @@ export type PaymentMethodList = PaginatedList & {
     totalItems: Scalars['Int'];
     totalItems: Scalars['Int'];
 };
 };
 
 
+export type CreatePaymentMethodInput = {
+    name: Scalars['String'];
+    code: Scalars['String'];
+    description?: Maybe<Scalars['String']>;
+    enabled: Scalars['Boolean'];
+    handler: ConfigurableOperationInput;
+};
+
 export type UpdatePaymentMethodInput = {
 export type UpdatePaymentMethodInput = {
     id: Scalars['ID'];
     id: Scalars['ID'];
+    name?: Maybe<Scalars['String']>;
     code?: Maybe<Scalars['String']>;
     code?: Maybe<Scalars['String']>;
+    description?: Maybe<Scalars['String']>;
     enabled?: Maybe<Scalars['Boolean']>;
     enabled?: Maybe<Scalars['Boolean']>;
-    configArgs?: Maybe<Array<ConfigArgInput>>;
+    handler?: Maybe<ConfigurableOperationInput>;
 };
 };
 
 
 export type PaymentMethod = Node & {
 export type PaymentMethod = Node & {
     id: Scalars['ID'];
     id: Scalars['ID'];
     createdAt: Scalars['DateTime'];
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
+    name: Scalars['String'];
     code: Scalars['String'];
     code: Scalars['String'];
+    description: Scalars['String'];
     enabled: Scalars['Boolean'];
     enabled: Scalars['Boolean'];
-    configArgs: Array<ConfigArg>;
-    definition: ConfigurableOperationDefinition;
+    handler: ConfigurableOperation;
 };
 };
 
 
 export type Product = Node & {
 export type Product = Node & {
@@ -2279,6 +2299,7 @@ export type Channel = Node & {
     defaultLanguageCode: LanguageCode;
     defaultLanguageCode: LanguageCode;
     currencyCode: CurrencyCode;
     currencyCode: CurrencyCode;
     pricesIncludeTax: Scalars['Boolean'];
     pricesIncludeTax: Scalars['Boolean'];
+    customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 export type CollectionBreadcrumb = {
 export type CollectionBreadcrumb = {
@@ -4260,7 +4281,9 @@ export type OrderSortParameter = {
 export type PaymentMethodFilterParameter = {
 export type PaymentMethodFilterParameter = {
     createdAt?: Maybe<DateOperators>;
     createdAt?: Maybe<DateOperators>;
     updatedAt?: Maybe<DateOperators>;
     updatedAt?: Maybe<DateOperators>;
+    name?: Maybe<StringOperators>;
     code?: Maybe<StringOperators>;
     code?: Maybe<StringOperators>;
+    description?: Maybe<StringOperators>;
     enabled?: Maybe<BooleanOperators>;
     enabled?: Maybe<BooleanOperators>;
 };
 };
 
 
@@ -4268,7 +4291,9 @@ export type PaymentMethodSortParameter = {
     id?: Maybe<SortOrder>;
     id?: Maybe<SortOrder>;
     createdAt?: Maybe<SortOrder>;
     createdAt?: Maybe<SortOrder>;
     updatedAt?: Maybe<SortOrder>;
     updatedAt?: Maybe<SortOrder>;
+    name?: Maybe<SortOrder>;
     code?: Maybe<SortOrder>;
     code?: Maybe<SortOrder>;
+    description?: Maybe<SortOrder>;
 };
 };
 
 
 export type ProductFilterParameter = {
 export type ProductFilterParameter = {
@@ -4431,6 +4456,7 @@ export type NativeAuthInput = {
 
 
 export type CustomFields = {
 export type CustomFields = {
     Address: Array<CustomFieldConfig>;
     Address: Array<CustomFieldConfig>;
+    Channel: Array<CustomFieldConfig>;
     Collection: Array<CustomFieldConfig>;
     Collection: Array<CustomFieldConfig>;
     Customer: Array<CustomFieldConfig>;
     Customer: Array<CustomFieldConfig>;
     Facet: Array<CustomFieldConfig>;
     Facet: Array<CustomFieldConfig>;

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

@@ -371,6 +371,7 @@ export type Channel = Node & {
     defaultLanguageCode: LanguageCode;
     defaultLanguageCode: LanguageCode;
     currencyCode: CurrencyCode;
     currencyCode: CurrencyCode;
     pricesIncludeTax: Scalars['Boolean'];
     pricesIncludeTax: Scalars['Boolean'];
+    customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 export type Collection = Node & {
 export type Collection = Node & {

+ 36 - 9
packages/common/src/generated-types.ts

@@ -50,6 +50,7 @@ export type Query = {
   orders: OrderList;
   orders: OrderList;
   paymentMethods: PaymentMethodList;
   paymentMethods: PaymentMethodList;
   paymentMethod?: Maybe<PaymentMethod>;
   paymentMethod?: Maybe<PaymentMethod>;
+  paymentMethodHandlers: Array<ConfigurableOperationDefinition>;
   productOptionGroups: Array<ProductOptionGroup>;
   productOptionGroups: Array<ProductOptionGroup>;
   productOptionGroup?: Maybe<ProductOptionGroup>;
   productOptionGroup?: Maybe<ProductOptionGroup>;
   search: SearchResponse;
   search: SearchResponse;
@@ -411,6 +412,8 @@ export type Mutation = {
    * Payment.
    * Payment.
    */
    */
   addManualPaymentToOrder: AddManualPaymentToOrderResult;
   addManualPaymentToOrder: AddManualPaymentToOrderResult;
+  /** Create existing PaymentMethod */
+  createPaymentMethod: PaymentMethod;
   /** Update an existing PaymentMethod */
   /** Update an existing PaymentMethod */
   updatePaymentMethod: PaymentMethod;
   updatePaymentMethod: PaymentMethod;
   /** Create a new ProductOptionGroup */
   /** Create a new ProductOptionGroup */
@@ -797,6 +800,11 @@ export type MutationAddManualPaymentToOrderArgs = {
 };
 };
 
 
 
 
+export type MutationCreatePaymentMethodArgs = {
+  input: CreatePaymentMethodInput;
+};
+
+
 export type MutationUpdatePaymentMethodArgs = {
 export type MutationUpdatePaymentMethodArgs = {
   input: UpdatePaymentMethodInput;
   input: UpdatePaymentMethodInput;
 };
 };
@@ -1106,6 +1114,7 @@ export type CreateChannelInput = {
   currencyCode: CurrencyCode;
   currencyCode: CurrencyCode;
   defaultTaxZoneId: Scalars['ID'];
   defaultTaxZoneId: Scalars['ID'];
   defaultShippingZoneId: Scalars['ID'];
   defaultShippingZoneId: Scalars['ID'];
+  customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 export type UpdateChannelInput = {
 export type UpdateChannelInput = {
@@ -1117,6 +1126,7 @@ export type UpdateChannelInput = {
   currencyCode?: Maybe<CurrencyCode>;
   currencyCode?: Maybe<CurrencyCode>;
   defaultTaxZoneId?: Maybe<Scalars['ID']>;
   defaultTaxZoneId?: Maybe<Scalars['ID']>;
   defaultShippingZoneId?: Maybe<Scalars['ID']>;
   defaultShippingZoneId?: Maybe<Scalars['ID']>;
+  customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 /** Returned if attempting to set a Channel's defaultLanguageCode to a language which is not enabled in GlobalSettings */
 /** Returned if attempting to set a Channel's defaultLanguageCode to a language which is not enabled in GlobalSettings */
@@ -1428,7 +1438,7 @@ export type ImportInfo = {
 /**
 /**
  * @description
  * @description
  * The state of a Job in the JobQueue
  * The state of a Job in the JobQueue
- *
+ * 
  * @docsCategory common
  * @docsCategory common
  */
  */
 export enum JobState {
 export enum JobState {
@@ -1878,11 +1888,21 @@ export type PaymentMethodList = PaginatedList & {
   totalItems: Scalars['Int'];
   totalItems: Scalars['Int'];
 };
 };
 
 
+export type CreatePaymentMethodInput = {
+  name: Scalars['String'];
+  code: Scalars['String'];
+  description?: Maybe<Scalars['String']>;
+  enabled: Scalars['Boolean'];
+  handler: ConfigurableOperationInput;
+};
+
 export type UpdatePaymentMethodInput = {
 export type UpdatePaymentMethodInput = {
   id: Scalars['ID'];
   id: Scalars['ID'];
+  name?: Maybe<Scalars['String']>;
   code?: Maybe<Scalars['String']>;
   code?: Maybe<Scalars['String']>;
+  description?: Maybe<Scalars['String']>;
   enabled?: Maybe<Scalars['Boolean']>;
   enabled?: Maybe<Scalars['Boolean']>;
-  configArgs?: Maybe<Array<ConfigArgInput>>;
+  handler?: Maybe<ConfigurableOperationInput>;
 };
 };
 
 
 export type PaymentMethod = Node & {
 export type PaymentMethod = Node & {
@@ -1890,10 +1910,11 @@ export type PaymentMethod = Node & {
   id: Scalars['ID'];
   id: Scalars['ID'];
   createdAt: Scalars['DateTime'];
   createdAt: Scalars['DateTime'];
   updatedAt: Scalars['DateTime'];
   updatedAt: Scalars['DateTime'];
+  name: Scalars['String'];
   code: Scalars['String'];
   code: Scalars['String'];
+  description: Scalars['String'];
   enabled: Scalars['Boolean'];
   enabled: Scalars['Boolean'];
-  configArgs: Array<ConfigArg>;
-  definition: ConfigurableOperationDefinition;
+  handler: ConfigurableOperation;
 };
 };
 
 
 export type Product = Node & {
 export type Product = Node & {
@@ -2452,6 +2473,7 @@ export type Channel = Node & {
   defaultLanguageCode: LanguageCode;
   defaultLanguageCode: LanguageCode;
   currencyCode: CurrencyCode;
   currencyCode: CurrencyCode;
   pricesIncludeTax: Scalars['Boolean'];
   pricesIncludeTax: Scalars['Boolean'];
+  customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 export type CollectionBreadcrumb = {
 export type CollectionBreadcrumb = {
@@ -2506,7 +2528,7 @@ export enum DeletionResult {
  * @description
  * @description
  * Permissions for administrators and customers. Used to control access to
  * Permissions for administrators and customers. Used to control access to
  * GraphQL resolvers via the {@link Allow} decorator.
  * GraphQL resolvers via the {@link Allow} decorator.
- *
+ * 
  * @docsCategory common
  * @docsCategory common
  */
  */
 export enum Permission {
 export enum Permission {
@@ -2883,7 +2905,7 @@ export type CountryList = PaginatedList & {
 /**
 /**
  * @description
  * @description
  * ISO 4217 currency code
  * ISO 4217 currency code
- *
+ * 
  * @docsCategory common
  * @docsCategory common
  */
  */
 export enum CurrencyCode {
 export enum CurrencyCode {
@@ -3420,7 +3442,7 @@ export type HistoryEntryList = PaginatedList & {
  * region or script modifier (e.g. de_AT). The selection available is based
  * region or script modifier (e.g. de_AT). The selection available is based
  * on the [Unicode CLDR summary list](https://unicode-org.github.io/cldr-staging/charts/37/summary/root.html)
  * on the [Unicode CLDR summary list](https://unicode-org.github.io/cldr-staging/charts/37/summary/root.html)
  * and includes the major spoken languages of the world and any widely-used variants.
  * and includes the major spoken languages of the world and any widely-used variants.
- *
+ * 
  * @docsCategory common
  * @docsCategory common
  */
  */
 export enum LanguageCode {
 export enum LanguageCode {
@@ -3809,7 +3831,7 @@ export type OrderItem = Node & {
   unitPriceWithTax: Scalars['Int'];
   unitPriceWithTax: Scalars['Int'];
   /**
   /**
    * The price of a single unit including discounts, excluding tax.
    * The price of a single unit including discounts, excluding tax.
-   *
+   * 
    * If Order-level discounts have been applied, this will not be the
    * If Order-level discounts have been applied, this will not be the
    * actual taxable unit price (see `proratedUnitPrice`), but is generally the
    * actual taxable unit price (see `proratedUnitPrice`), but is generally the
    * correct price to display to customers to avoid confusion
    * correct price to display to customers to avoid confusion
@@ -3849,7 +3871,7 @@ export type OrderLine = Node & {
   unitPriceWithTax: Scalars['Int'];
   unitPriceWithTax: Scalars['Int'];
   /**
   /**
    * The price of a single unit including discounts, excluding tax.
    * The price of a single unit including discounts, excluding tax.
-   *
+   * 
    * If Order-level discounts have been applied, this will not be the
    * If Order-level discounts have been applied, this will not be the
    * actual taxable unit price (see `proratedUnitPrice`), but is generally the
    * actual taxable unit price (see `proratedUnitPrice`), but is generally the
    * correct price to display to customers to avoid confusion
    * correct price to display to customers to avoid confusion
@@ -4506,7 +4528,9 @@ export type OrderSortParameter = {
 export type PaymentMethodFilterParameter = {
 export type PaymentMethodFilterParameter = {
   createdAt?: Maybe<DateOperators>;
   createdAt?: Maybe<DateOperators>;
   updatedAt?: Maybe<DateOperators>;
   updatedAt?: Maybe<DateOperators>;
+  name?: Maybe<StringOperators>;
   code?: Maybe<StringOperators>;
   code?: Maybe<StringOperators>;
+  description?: Maybe<StringOperators>;
   enabled?: Maybe<BooleanOperators>;
   enabled?: Maybe<BooleanOperators>;
 };
 };
 
 
@@ -4514,7 +4538,9 @@ export type PaymentMethodSortParameter = {
   id?: Maybe<SortOrder>;
   id?: Maybe<SortOrder>;
   createdAt?: Maybe<SortOrder>;
   createdAt?: Maybe<SortOrder>;
   updatedAt?: Maybe<SortOrder>;
   updatedAt?: Maybe<SortOrder>;
+  name?: Maybe<SortOrder>;
   code?: Maybe<SortOrder>;
   code?: Maybe<SortOrder>;
+  description?: Maybe<SortOrder>;
 };
 };
 
 
 export type ProductFilterParameter = {
 export type ProductFilterParameter = {
@@ -4678,6 +4704,7 @@ export type NativeAuthInput = {
 export type CustomFields = {
 export type CustomFields = {
   __typename?: 'CustomFields';
   __typename?: 'CustomFields';
   Address: Array<CustomFieldConfig>;
   Address: Array<CustomFieldConfig>;
+  Channel: Array<CustomFieldConfig>;
   Collection: Array<CustomFieldConfig>;
   Collection: Array<CustomFieldConfig>;
   Customer: Array<CustomFieldConfig>;
   Customer: Array<CustomFieldConfig>;
   Facet: Array<CustomFieldConfig>;
   Facet: Array<CustomFieldConfig>;

+ 9 - 1
packages/core/e2e/fulfillment-process.e2e-spec.ts

@@ -90,7 +90,15 @@ describe('Fulfillment process', () => {
 
 
     beforeAll(async () => {
     beforeAll(async () => {
         await server.init({
         await server.init({
-            initialData,
+            initialData: {
+                ...initialData,
+                paymentMethods: [
+                    {
+                        name: testSuccessfulPaymentMethod.code,
+                        handler: { code: testSuccessfulPaymentMethod.code, arguments: [] },
+                    },
+                ],
+            },
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 1,
             customerCount: 1,
         });
         });

+ 66 - 3
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -49,6 +49,7 @@ export type Query = {
     orders: OrderList;
     orders: OrderList;
     paymentMethods: PaymentMethodList;
     paymentMethods: PaymentMethodList;
     paymentMethod?: Maybe<PaymentMethod>;
     paymentMethod?: Maybe<PaymentMethod>;
+    paymentMethodHandlers: Array<ConfigurableOperationDefinition>;
     productOptionGroups: Array<ProductOptionGroup>;
     productOptionGroups: Array<ProductOptionGroup>;
     productOptionGroup?: Maybe<ProductOptionGroup>;
     productOptionGroup?: Maybe<ProductOptionGroup>;
     search: SearchResponse;
     search: SearchResponse;
@@ -366,6 +367,8 @@ export type Mutation = {
      * Payment.
      * Payment.
      */
      */
     addManualPaymentToOrder: AddManualPaymentToOrderResult;
     addManualPaymentToOrder: AddManualPaymentToOrderResult;
+    /** Create existing PaymentMethod */
+    createPaymentMethod: PaymentMethod;
     /** Update an existing PaymentMethod */
     /** Update an existing PaymentMethod */
     updatePaymentMethod: PaymentMethod;
     updatePaymentMethod: PaymentMethod;
     /** Create a new ProductOptionGroup */
     /** Create a new ProductOptionGroup */
@@ -693,6 +696,10 @@ export type MutationAddManualPaymentToOrderArgs = {
     input: ManualPaymentInput;
     input: ManualPaymentInput;
 };
 };
 
 
+export type MutationCreatePaymentMethodArgs = {
+    input: CreatePaymentMethodInput;
+};
+
 export type MutationUpdatePaymentMethodArgs = {
 export type MutationUpdatePaymentMethodArgs = {
     input: UpdatePaymentMethodInput;
     input: UpdatePaymentMethodInput;
 };
 };
@@ -959,6 +966,7 @@ export type CreateChannelInput = {
     currencyCode: CurrencyCode;
     currencyCode: CurrencyCode;
     defaultTaxZoneId: Scalars['ID'];
     defaultTaxZoneId: Scalars['ID'];
     defaultShippingZoneId: Scalars['ID'];
     defaultShippingZoneId: Scalars['ID'];
+    customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 export type UpdateChannelInput = {
 export type UpdateChannelInput = {
@@ -970,6 +978,7 @@ export type UpdateChannelInput = {
     currencyCode?: Maybe<CurrencyCode>;
     currencyCode?: Maybe<CurrencyCode>;
     defaultTaxZoneId?: Maybe<Scalars['ID']>;
     defaultTaxZoneId?: Maybe<Scalars['ID']>;
     defaultShippingZoneId?: Maybe<Scalars['ID']>;
     defaultShippingZoneId?: Maybe<Scalars['ID']>;
+    customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 /** Returned if attempting to set a Channel's defaultLanguageCode to a language which is not enabled in GlobalSettings */
 /** Returned if attempting to set a Channel's defaultLanguageCode to a language which is not enabled in GlobalSettings */
@@ -1721,21 +1730,32 @@ export type PaymentMethodList = PaginatedList & {
     totalItems: Scalars['Int'];
     totalItems: Scalars['Int'];
 };
 };
 
 
+export type CreatePaymentMethodInput = {
+    name: Scalars['String'];
+    code: Scalars['String'];
+    description?: Maybe<Scalars['String']>;
+    enabled: Scalars['Boolean'];
+    handler: ConfigurableOperationInput;
+};
+
 export type UpdatePaymentMethodInput = {
 export type UpdatePaymentMethodInput = {
     id: Scalars['ID'];
     id: Scalars['ID'];
+    name?: Maybe<Scalars['String']>;
     code?: Maybe<Scalars['String']>;
     code?: Maybe<Scalars['String']>;
+    description?: Maybe<Scalars['String']>;
     enabled?: Maybe<Scalars['Boolean']>;
     enabled?: Maybe<Scalars['Boolean']>;
-    configArgs?: Maybe<Array<ConfigArgInput>>;
+    handler?: Maybe<ConfigurableOperationInput>;
 };
 };
 
 
 export type PaymentMethod = Node & {
 export type PaymentMethod = Node & {
     id: Scalars['ID'];
     id: Scalars['ID'];
     createdAt: Scalars['DateTime'];
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
+    name: Scalars['String'];
     code: Scalars['String'];
     code: Scalars['String'];
+    description: Scalars['String'];
     enabled: Scalars['Boolean'];
     enabled: Scalars['Boolean'];
-    configArgs: Array<ConfigArg>;
-    definition: ConfigurableOperationDefinition;
+    handler: ConfigurableOperation;
 };
 };
 
 
 export type Product = Node & {
 export type Product = Node & {
@@ -2279,6 +2299,7 @@ export type Channel = Node & {
     defaultLanguageCode: LanguageCode;
     defaultLanguageCode: LanguageCode;
     currencyCode: CurrencyCode;
     currencyCode: CurrencyCode;
     pricesIncludeTax: Scalars['Boolean'];
     pricesIncludeTax: Scalars['Boolean'];
+    customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 export type CollectionBreadcrumb = {
 export type CollectionBreadcrumb = {
@@ -4260,7 +4281,9 @@ export type OrderSortParameter = {
 export type PaymentMethodFilterParameter = {
 export type PaymentMethodFilterParameter = {
     createdAt?: Maybe<DateOperators>;
     createdAt?: Maybe<DateOperators>;
     updatedAt?: Maybe<DateOperators>;
     updatedAt?: Maybe<DateOperators>;
+    name?: Maybe<StringOperators>;
     code?: Maybe<StringOperators>;
     code?: Maybe<StringOperators>;
+    description?: Maybe<StringOperators>;
     enabled?: Maybe<BooleanOperators>;
     enabled?: Maybe<BooleanOperators>;
 };
 };
 
 
@@ -4268,7 +4291,9 @@ export type PaymentMethodSortParameter = {
     id?: Maybe<SortOrder>;
     id?: Maybe<SortOrder>;
     createdAt?: Maybe<SortOrder>;
     createdAt?: Maybe<SortOrder>;
     updatedAt?: Maybe<SortOrder>;
     updatedAt?: Maybe<SortOrder>;
+    name?: Maybe<SortOrder>;
     code?: Maybe<SortOrder>;
     code?: Maybe<SortOrder>;
+    description?: Maybe<SortOrder>;
 };
 };
 
 
 export type ProductFilterParameter = {
 export type ProductFilterParameter = {
@@ -4431,6 +4456,7 @@ export type NativeAuthInput = {
 
 
 export type CustomFields = {
 export type CustomFields = {
     Address: Array<CustomFieldConfig>;
     Address: Array<CustomFieldConfig>;
+    Channel: Array<CustomFieldConfig>;
     Collection: Array<CustomFieldConfig>;
     Collection: Array<CustomFieldConfig>;
     Customer: Array<CustomFieldConfig>;
     Customer: Array<CustomFieldConfig>;
     Facet: Array<CustomFieldConfig>;
     Facet: Array<CustomFieldConfig>;
@@ -5996,6 +6022,23 @@ export type GetOrderListWithQtyQuery = {
     };
     };
 };
 };
 
 
+export type PaymentMethodFragment = Pick<
+    PaymentMethod,
+    'id' | 'code' | 'name' | 'description' | 'enabled'
+> & { handler: Pick<ConfigurableOperation, 'code'> & { args: Array<Pick<ConfigArg, 'name' | 'value'>> } };
+
+export type CreatePaymentMethodMutationVariables = Exact<{
+    input: CreatePaymentMethodInput;
+}>;
+
+export type CreatePaymentMethodMutation = { createPaymentMethod: PaymentMethodFragment };
+
+export type UpdatePaymentMethodMutationVariables = Exact<{
+    input: UpdatePaymentMethodInput;
+}>;
+
+export type UpdatePaymentMethodMutation = { updatePaymentMethod: PaymentMethodFragment };
+
 export type UpdateProductOptionGroupMutationVariables = Exact<{
 export type UpdateProductOptionGroupMutationVariables = Exact<{
     input: UpdateProductOptionGroupInput;
     input: UpdateProductOptionGroupInput;
 }>;
 }>;
@@ -8079,6 +8122,26 @@ export namespace GetOrderListWithQty {
     >;
     >;
 }
 }
 
 
+export namespace PaymentMethod {
+    export type Fragment = PaymentMethodFragment;
+    export type Handler = NonNullable<PaymentMethodFragment['handler']>;
+    export type Args = NonNullable<
+        NonNullable<NonNullable<PaymentMethodFragment['handler']>['args']>[number]
+    >;
+}
+
+export namespace CreatePaymentMethod {
+    export type Variables = CreatePaymentMethodMutationVariables;
+    export type Mutation = CreatePaymentMethodMutation;
+    export type CreatePaymentMethod = NonNullable<CreatePaymentMethodMutation['createPaymentMethod']>;
+}
+
+export namespace UpdatePaymentMethod {
+    export type Variables = UpdatePaymentMethodMutationVariables;
+    export type Mutation = UpdatePaymentMethodMutation;
+    export type UpdatePaymentMethod = NonNullable<UpdatePaymentMethodMutation['updatePaymentMethod']>;
+}
+
 export namespace UpdateProductOptionGroup {
 export namespace UpdateProductOptionGroup {
     export type Variables = UpdateProductOptionGroupMutationVariables;
     export type Variables = UpdateProductOptionGroupMutationVariables;
     export type Mutation = UpdateProductOptionGroupMutation;
     export type Mutation = UpdateProductOptionGroupMutation;

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

@@ -362,6 +362,7 @@ export type Channel = Node & {
     defaultLanguageCode: LanguageCode;
     defaultLanguageCode: LanguageCode;
     currencyCode: CurrencyCode;
     currencyCode: CurrencyCode;
     pricesIncludeTax: Scalars['Boolean'];
     pricesIncludeTax: Scalars['Boolean'];
+    customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 export type Collection = Node & {
 export type Collection = Node & {

+ 9 - 1
packages/core/e2e/order-fulfillment.e2e-spec.ts

@@ -83,7 +83,15 @@ describe('Order fulfillments', () => {
 
 
     beforeAll(async () => {
     beforeAll(async () => {
         await server.init({
         await server.init({
-            initialData,
+            initialData: {
+                ...initialData,
+                paymentMethods: [
+                    {
+                        name: testSuccessfulPaymentMethod.code,
+                        handler: { code: testSuccessfulPaymentMethod.code, arguments: [] },
+                    },
+                ],
+            },
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 2,
             customerCount: 2,
         });
         });

+ 9 - 1
packages/core/e2e/order-modification.e2e-spec.ts

@@ -102,7 +102,15 @@ describe('Order modification', () => {
 
 
     beforeAll(async () => {
     beforeAll(async () => {
         await server.init({
         await server.init({
-            initialData,
+            initialData: {
+                ...initialData,
+                paymentMethods: [
+                    {
+                        name: testSuccessfulPaymentMethod.code,
+                        handler: { code: testSuccessfulPaymentMethod.code, arguments: [] },
+                    },
+                ],
+            },
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 2,
             customerCount: 2,
         });
         });

+ 9 - 1
packages/core/e2e/order-process.e2e-spec.ts

@@ -98,7 +98,15 @@ describe('Order process', () => {
 
 
     beforeAll(async () => {
     beforeAll(async () => {
         await server.init({
         await server.init({
-            initialData,
+            initialData: {
+                ...initialData,
+                paymentMethods: [
+                    {
+                        name: testSuccessfulPaymentMethod.code,
+                        handler: { code: testSuccessfulPaymentMethod.code, arguments: [] },
+                    },
+                ],
+            },
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 1,
             customerCount: 1,
         });
         });

+ 9 - 1
packages/core/e2e/order-promotion.e2e-spec.ts

@@ -110,7 +110,15 @@ describe('Promotions applied to Orders', () => {
 
 
     beforeAll(async () => {
     beforeAll(async () => {
         await server.init({
         await server.init({
-            initialData,
+            initialData: {
+                ...initialData,
+                paymentMethods: [
+                    {
+                        name: testSuccessfulPaymentMethod.code,
+                        handler: { code: testSuccessfulPaymentMethod.code, arguments: [] },
+                    },
+                ],
+            },
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-promotions.csv'),
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-promotions.csv'),
             customerCount: 2,
             customerCount: 2,
         });
         });

+ 9 - 1
packages/core/e2e/order-taxes.e2e-spec.ts

@@ -35,7 +35,15 @@ describe('Order taxes', () => {
 
 
     beforeAll(async () => {
     beforeAll(async () => {
         await server.init({
         await server.init({
-            initialData,
+            initialData: {
+                ...initialData,
+                paymentMethods: [
+                    {
+                        name: testSuccessfulPaymentMethod.code,
+                        handler: { code: testSuccessfulPaymentMethod.code, arguments: [] },
+                    },
+                ],
+            },
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-order-taxes.csv'),
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-order-taxes.csv'),
             customerCount: 2,
             customerCount: 2,
         });
         });

+ 17 - 1
packages/core/e2e/order.e2e-spec.ts

@@ -111,7 +111,23 @@ describe('Orders resolver', () => {
 
 
     beforeAll(async () => {
     beforeAll(async () => {
         await server.init({
         await server.init({
-            initialData,
+            initialData: {
+                ...initialData,
+                paymentMethods: [
+                    {
+                        name: twoStagePaymentMethod.code,
+                        handler: { code: twoStagePaymentMethod.code, arguments: [] },
+                    },
+                    {
+                        name: failsToSettlePaymentMethod.code,
+                        handler: { code: failsToSettlePaymentMethod.code, arguments: [] },
+                    },
+                    {
+                        name: singleStageRefundablePaymentMethod.code,
+                        handler: { code: singleStageRefundablePaymentMethod.code, arguments: [] },
+                    },
+                ],
+            },
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 3,
             customerCount: 3,
         });
         });

+ 135 - 0
packages/core/e2e/payment-method.e2e-spec.ts

@@ -0,0 +1,135 @@
+import { dummyPaymentHandler } from '@vendure/core';
+import { createTestEnvironment } from '@vendure/testing';
+import gql from 'graphql-tag';
+import path from 'path';
+
+import { initialData } from '../../../e2e-common/e2e-initial-data';
+import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
+
+import { CreatePaymentMethod, UpdatePaymentMethod } from './graphql/generated-e2e-admin-types';
+
+describe('PaymentMethod resolver', () => {
+    const { server, adminClient, shopClient } = createTestEnvironment({
+        ...testConfig,
+        paymentOptions: {
+            paymentMethodHandlers: [dummyPaymentHandler],
+        },
+    });
+
+    beforeAll(async () => {
+        await server.init({
+            initialData,
+            productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
+            customerCount: 2,
+        });
+        await adminClient.asSuperAdmin();
+    }, TEST_SETUP_TIMEOUT_MS);
+
+    afterAll(async () => {
+        await server.destroy();
+    });
+
+    it('create', async () => {
+        const { createPaymentMethod } = await adminClient.query<
+            CreatePaymentMethod.Mutation,
+            CreatePaymentMethod.Variables
+        >(CREATE_PAYMENT_METHOD, {
+            input: {
+                code: 'test-method',
+                name: 'Test Method',
+                description: 'This is a test payment method',
+                enabled: true,
+                handler: {
+                    code: dummyPaymentHandler.code,
+                    arguments: [{ name: 'automaticSettle', value: 'true' }],
+                },
+            },
+        });
+
+        expect(createPaymentMethod).toEqual({
+            id: 'T_1',
+            name: 'Test Method',
+            code: 'test-method',
+            description: 'This is a test payment method',
+            enabled: true,
+            handler: {
+                args: [
+                    {
+                        name: 'automaticSettle',
+                        value: 'true',
+                    },
+                ],
+                code: 'dummy-payment-handler',
+            },
+        });
+    });
+
+    it('update', async () => {
+        const { updatePaymentMethod } = await adminClient.query<
+            UpdatePaymentMethod.Mutation,
+            UpdatePaymentMethod.Variables
+        >(UPDATE_PAYMENT_METHOD, {
+            input: {
+                id: 'T_1',
+                description: 'modified',
+                enabled: false,
+                handler: {
+                    code: dummyPaymentHandler.code,
+                    arguments: [{ name: 'automaticSettle', value: 'false' }],
+                },
+            },
+        });
+
+        expect(updatePaymentMethod).toEqual({
+            id: 'T_1',
+            name: 'Test Method',
+            code: 'test-method',
+            description: 'modified',
+            enabled: false,
+            handler: {
+                args: [
+                    {
+                        name: 'automaticSettle',
+                        value: 'false',
+                    },
+                ],
+                code: 'dummy-payment-handler',
+            },
+        });
+    });
+});
+
+export const PAYMENT_METHOD_FRAGMENT = gql`
+    fragment PaymentMethod on PaymentMethod {
+        id
+        code
+        name
+        description
+        enabled
+        handler {
+            code
+            args {
+                name
+                value
+            }
+        }
+    }
+`;
+
+export const CREATE_PAYMENT_METHOD = gql`
+    mutation CreatePaymentMethod($input: CreatePaymentMethodInput!) {
+        createPaymentMethod(input: $input) {
+            ...PaymentMethod
+        }
+    }
+    ${PAYMENT_METHOD_FRAGMENT}
+`;
+
+export const UPDATE_PAYMENT_METHOD = gql`
+    mutation UpdatePaymentMethod($input: UpdatePaymentMethodInput!) {
+        updatePaymentMethod(input: $input) {
+            ...PaymentMethod
+        }
+    }
+    ${PAYMENT_METHOD_FRAGMENT}
+`;

+ 17 - 1
packages/core/e2e/shop-order.e2e-spec.ts

@@ -109,7 +109,23 @@ describe('Shop orders', () => {
 
 
     beforeAll(async () => {
     beforeAll(async () => {
         await server.init({
         await server.init({
-            initialData,
+            initialData: {
+                ...initialData,
+                paymentMethods: [
+                    {
+                        name: testSuccessfulPaymentMethod.code,
+                        handler: { code: testSuccessfulPaymentMethod.code, arguments: [] },
+                    },
+                    {
+                        name: testFailingPaymentMethod.code,
+                        handler: { code: testFailingPaymentMethod.code, arguments: [] },
+                    },
+                    {
+                        name: testErrorPaymentMethod.code,
+                        handler: { code: testErrorPaymentMethod.code, arguments: [] },
+                    },
+                ],
+            },
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 3,
             customerCount: 3,
         });
         });

+ 13 - 1
packages/core/e2e/stock-control.e2e-spec.ts

@@ -82,7 +82,19 @@ describe('Stock control', () => {
 
 
     beforeAll(async () => {
     beforeAll(async () => {
         await server.init({
         await server.init({
-            initialData,
+            initialData: {
+                ...initialData,
+                paymentMethods: [
+                    {
+                        name: testSuccessfulPaymentMethod.code,
+                        handler: { code: testSuccessfulPaymentMethod.code, arguments: [] },
+                    },
+                    {
+                        name: twoStagePaymentMethod.code,
+                        handler: { code: twoStagePaymentMethod.code, arguments: [] },
+                    },
+                ],
+            },
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-stock-control.csv'),
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-stock-control.csv'),
             customerCount: 3,
             customerCount: 3,
         });
         });

+ 5 - 1
packages/core/mock-data/data-sources/initial-data.ts

@@ -10,7 +10,11 @@ export const initialData: InitialData = {
         { name: 'Reduced Tax', percentage: 10 },
         { name: 'Reduced Tax', percentage: 10 },
         { name: 'Zero Tax', percentage: 0 },
         { name: 'Zero Tax', percentage: 0 },
     ],
     ],
-    shippingMethods: [{ name: 'Standard Shipping', price: 500 }, { name: 'Express Shipping', price: 1000 }],
+    shippingMethods: [
+        { name: 'Standard Shipping', price: 500 },
+        { name: 'Express Shipping', price: 1000 },
+    ],
+    paymentMethods: [{ name: 'Standard Payment', handler: { code: 'dummy-payment-handler', arguments: [] } }],
     collections: [
     collections: [
         {
         {
             name: 'Electronics',
             name: 'Electronics',

+ 11 - 0
packages/core/src/api/resolvers/admin/payment-method.resolver.ts

@@ -1,5 +1,6 @@
 import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
 import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
 import {
 import {
+    MutationCreatePaymentMethodArgs,
     MutationUpdatePaymentMethodArgs,
     MutationUpdatePaymentMethodArgs,
     Permission,
     Permission,
     QueryPaymentMethodArgs,
     QueryPaymentMethodArgs,
@@ -36,6 +37,16 @@ export class PaymentMethodResolver {
         return this.paymentMethodService.findOne(ctx, args.id);
         return this.paymentMethodService.findOne(ctx, args.id);
     }
     }
 
 
+    @Transaction()
+    @Mutation()
+    @Allow(Permission.CreateSettings)
+    createPaymentMethod(
+        @Ctx() ctx: RequestContext,
+        @Args() args: MutationCreatePaymentMethodArgs,
+    ): Promise<PaymentMethod> {
+        return this.paymentMethodService.create(ctx, args.input);
+    }
+
     @Transaction()
     @Transaction()
     @Mutation()
     @Mutation()
     @Allow(Permission.UpdateSettings)
     @Allow(Permission.UpdateSettings)

+ 0 - 8
packages/core/src/api/resolvers/entity/payment-method-entity.resolver.ts

@@ -9,12 +9,4 @@ import { Ctx } from '../../decorators/request-context.decorator';
 @Resolver('PaymentMethod')
 @Resolver('PaymentMethod')
 export class PaymentMethodEntityResolver {
 export class PaymentMethodEntityResolver {
     constructor(private paymentMethodService: PaymentMethodService) {}
     constructor(private paymentMethodService: PaymentMethodService) {}
-
-    @ResolveField()
-    async definition(
-        @Ctx() ctx: RequestContext,
-        @Parent() paymentMethod: PaymentMethod,
-    ): Promise<ConfigurableOperationDefinition> {
-        return this.paymentMethodService.getPaymentMethodHandler(paymentMethod.code).toGraphQlType(ctx);
-    }
 }
 }

+ 14 - 1
packages/core/src/api/schema/admin-api/payment-method.api.graphql

@@ -1,9 +1,12 @@
 type Query {
 type Query {
     paymentMethods(options: PaymentMethodListOptions): PaymentMethodList!
     paymentMethods(options: PaymentMethodListOptions): PaymentMethodList!
     paymentMethod(id: ID!): PaymentMethod
     paymentMethod(id: ID!): PaymentMethod
+    paymentMethodHandlers: [ConfigurableOperationDefinition!]!
 }
 }
 
 
 type Mutation {
 type Mutation {
+    "Create existing PaymentMethod"
+    createPaymentMethod(input: CreatePaymentMethodInput!): PaymentMethod!
     "Update an existing PaymentMethod"
     "Update an existing PaymentMethod"
     updatePaymentMethod(input: UpdatePaymentMethodInput!): PaymentMethod!
     updatePaymentMethod(input: UpdatePaymentMethodInput!): PaymentMethod!
 }
 }
@@ -16,9 +19,19 @@ type PaymentMethodList implements PaginatedList {
 # generated by generateListOptions function
 # generated by generateListOptions function
 input PaymentMethodListOptions
 input PaymentMethodListOptions
 
 
+input CreatePaymentMethodInput {
+    name: String!
+    code: String!
+    description: String
+    enabled: Boolean!
+    handler: ConfigurableOperationInput!
+}
+
 input UpdatePaymentMethodInput {
 input UpdatePaymentMethodInput {
     id: ID!
     id: ID!
+    name: String
     code: String
     code: String
+    description: String
     enabled: Boolean
     enabled: Boolean
-    configArgs: [ConfigArgInput!]
+    handler: ConfigurableOperationInput
 }
 }

+ 3 - 2
packages/core/src/api/schema/admin-api/payment-method.type.graphql

@@ -2,8 +2,9 @@ type PaymentMethod implements Node {
     id: ID!
     id: ID!
     createdAt: DateTime!
     createdAt: DateTime!
     updatedAt: DateTime!
     updatedAt: DateTime!
+    name: String!
     code: String!
     code: String!
+    description: String!
     enabled: Boolean!
     enabled: Boolean!
-    configArgs: [ConfigArg!]!
-    definition: ConfigurableOperationDefinition!
+    handler: ConfigurableOperation!
 }
 }

+ 1 - 0
packages/core/src/config/index.ts

@@ -25,6 +25,7 @@ export * from './order/order-code-strategy';
 export * from './order/order-merge-strategy';
 export * from './order/order-merge-strategy';
 export * from './order/order-item-price-calculation-strategy';
 export * from './order/order-item-price-calculation-strategy';
 export * from './order/stock-allocation-strategy';
 export * from './order/stock-allocation-strategy';
+export * from './payment-method/dummy-payment-method-handler';
 export * from './payment-method/example-payment-method-handler';
 export * from './payment-method/example-payment-method-handler';
 export * from './payment-method/payment-method-handler';
 export * from './payment-method/payment-method-handler';
 export * from './promotion';
 export * from './promotion';

+ 61 - 0
packages/core/src/config/payment-method/dummy-payment-method-handler.ts

@@ -0,0 +1,61 @@
+import { LanguageCode } from '@vendure/common/lib/generated-types';
+
+import { CreatePaymentResult, PaymentMethodHandler } from './payment-method-handler';
+
+/**
+ * @description
+ * A dummy PaymentMethodHandler which simply creates a Payment without any integration
+ * with an external payment provider. Intended only for use in development.
+ */
+export const dummyPaymentHandler = new PaymentMethodHandler({
+    code: 'dummy-payment-handler',
+    description: [
+        {
+            languageCode: LanguageCode.en,
+            value: 'A dummy payment provider intended for testing and development only.',
+        },
+    ],
+    args: {
+        automaticSettle: {
+            type: 'boolean',
+            label: [
+                {
+                    languageCode: LanguageCode.en,
+                    value: 'Authorize and settle in 1 step',
+                },
+            ],
+            description: [
+                {
+                    languageCode: LanguageCode.en,
+                    value: 'If enabled, Payments will be created in the "Settled" state.',
+                },
+            ],
+            required: true,
+            defaultValue: false,
+        },
+    },
+    createPayment: async (ctx, order, amount, args, metadata): Promise<CreatePaymentResult> => {
+        if (metadata.shouldDecline) {
+            return {
+                amount,
+                state: 'Declined' as 'Declined',
+                metadata: {
+                    errorMessage: 'Simulated error',
+                },
+            };
+        } else {
+            return {
+                amount,
+                state: args.automaticSettle ? 'Settled' : 'Authorized',
+                transactionId: Math.random().toString(36).substr(3),
+                metadata,
+            };
+        }
+    },
+    settlePayment: async (ctx, order, payment, args) => {
+        return {
+            success: true,
+            metadata: {},
+        };
+    },
+});

+ 19 - 1
packages/core/src/data-import/providers/populator/populator.ts

@@ -7,7 +7,12 @@ import { RequestContext } from '../../../api/common/request-context';
 import { defaultShippingCalculator, defaultShippingEligibilityChecker } from '../../../config';
 import { defaultShippingCalculator, defaultShippingEligibilityChecker } from '../../../config';
 import { manualFulfillmentHandler } from '../../../config/fulfillment/manual-fulfillment-handler';
 import { manualFulfillmentHandler } from '../../../config/fulfillment/manual-fulfillment-handler';
 import { Channel, Collection, FacetValue, TaxCategory } from '../../../entity';
 import { Channel, Collection, FacetValue, TaxCategory } from '../../../entity';
-import { CollectionService, FacetValueService, ShippingMethodService } from '../../../service';
+import {
+    CollectionService,
+    FacetValueService,
+    PaymentMethodService,
+    ShippingMethodService,
+} from '../../../service';
 import { ChannelService } from '../../../service/services/channel.service';
 import { ChannelService } from '../../../service/services/channel.service';
 import { CountryService } from '../../../service/services/country.service';
 import { CountryService } from '../../../service/services/country.service';
 import { SearchService } from '../../../service/services/search.service';
 import { SearchService } from '../../../service/services/search.service';
@@ -29,6 +34,7 @@ export class Populator {
         private taxRateService: TaxRateService,
         private taxRateService: TaxRateService,
         private taxCategoryService: TaxCategoryService,
         private taxCategoryService: TaxCategoryService,
         private shippingMethodService: ShippingMethodService,
         private shippingMethodService: ShippingMethodService,
+        private paymentMethodService: PaymentMethodService,
         private collectionService: CollectionService,
         private collectionService: CollectionService,
         private facetValueService: FacetValueService,
         private facetValueService: FacetValueService,
         private searchService: SearchService,
         private searchService: SearchService,
@@ -45,6 +51,7 @@ export class Populator {
         const zoneMap = await this.populateCountries(ctx, data.countries);
         const zoneMap = await this.populateCountries(ctx, data.countries);
         await this.populateTaxRates(ctx, data.taxRates, zoneMap);
         await this.populateTaxRates(ctx, data.taxRates, zoneMap);
         await this.populateShippingMethods(ctx, data.shippingMethods);
         await this.populateShippingMethods(ctx, data.shippingMethods);
+        await this.populatePaymentMethods(ctx, data.paymentMethods);
         await this.setChannelDefaults(zoneMap, data, channel);
         await this.setChannelDefaults(zoneMap, data, channel);
     }
     }
 
 
@@ -215,4 +222,15 @@ export class Populator {
             });
             });
         }
         }
     }
     }
+
+    private async populatePaymentMethods(ctx: RequestContext, paymentMethods: InitialData['paymentMethods']) {
+        for (const method of paymentMethods) {
+            await this.paymentMethodService.create(ctx, {
+                name: method.name,
+                code: normalizeString(method.name, '-'),
+                enabled: true,
+                handler: method.handler,
+            });
+        }
+    }
 }
 }

+ 2 - 1
packages/core/src/data-import/types.ts

@@ -1,4 +1,4 @@
-import { LanguageCode } from '@vendure/common/lib/generated-types';
+import { ConfigurableOperationInput, LanguageCode } from '@vendure/common/lib/generated-types';
 import { ID } from '@vendure/common/lib/shared-types';
 import { ID } from '@vendure/common/lib/shared-types';
 
 
 import { Zone } from '../entity/zone/zone.entity';
 import { Zone } from '../entity/zone/zone.entity';
@@ -43,5 +43,6 @@ export interface InitialData {
     countries: CountryDefinition[];
     countries: CountryDefinition[];
     taxRates: Array<{ name: string; percentage: number }>;
     taxRates: Array<{ name: string; percentage: number }>;
     shippingMethods: Array<{ name: string; price: number }>;
     shippingMethods: Array<{ name: string; price: number }>;
+    paymentMethods: Array<{ name: string; handler: ConfigurableOperationInput }>;
     collections: CollectionDefinition[];
     collections: CollectionDefinition[];
 }
 }

+ 7 - 3
packages/core/src/entity/payment-method/payment-method.entity.ts

@@ -1,4 +1,4 @@
-import { ConfigArg } from '@vendure/common/lib/generated-types';
+import { ConfigArg, ConfigurableOperation } from '@vendure/common/lib/generated-types';
 import { DeepPartial } from '@vendure/common/lib/shared-types';
 import { DeepPartial } from '@vendure/common/lib/shared-types';
 import { Column, Entity } from 'typeorm';
 import { Column, Entity } from 'typeorm';
 
 
@@ -17,9 +17,13 @@ export class PaymentMethod extends VendureEntity {
         super(input);
         super(input);
     }
     }
 
 
-    @Column() code: string;
+    @Column({ default: '' }) name: string;
+
+    @Column({ default: '' }) code: string;
+
+    @Column({ default: '' }) description: string;
 
 
     @Column() enabled: boolean;
     @Column() enabled: boolean;
 
 
-    @Column('simple-json') configArgs: ConfigArg[];
+    @Column('simple-json') handler: ConfigurableOperation;
 }
 }

+ 1 - 1
packages/core/src/service/helpers/config-arg/config-arg.service.ts

@@ -96,7 +96,7 @@ export class ConfigArgService {
                         // ignore
                         // ignore
                     }
                     }
                 }
                 }
-                if (!val) {
+                if (val == null) {
                     throw new UserInputError('error.configurable-argument-is-required', {
                     throw new UserInputError('error.configurable-argument-is-required', {
                         name,
                         name,
                         value: String(val),
                         value: String(val),

+ 0 - 3
packages/core/src/service/initializer.service.ts

@@ -3,7 +3,6 @@ import { Injectable } from '@nestjs/common';
 import { AdministratorService } from './services/administrator.service';
 import { AdministratorService } from './services/administrator.service';
 import { ChannelService } from './services/channel.service';
 import { ChannelService } from './services/channel.service';
 import { GlobalSettingsService } from './services/global-settings.service';
 import { GlobalSettingsService } from './services/global-settings.service';
-import { PaymentMethodService } from './services/payment-method.service';
 import { RoleService } from './services/role.service';
 import { RoleService } from './services/role.service';
 import { ShippingMethodService } from './services/shipping-method.service';
 import { ShippingMethodService } from './services/shipping-method.service';
 import { TaxRateService } from './services/tax-rate.service';
 import { TaxRateService } from './services/tax-rate.service';
@@ -20,7 +19,6 @@ export class InitializerService {
         private administratorService: AdministratorService,
         private administratorService: AdministratorService,
         private taxRateService: TaxRateService,
         private taxRateService: TaxRateService,
         private shippingMethodService: ShippingMethodService,
         private shippingMethodService: ShippingMethodService,
-        private paymentMethodService: PaymentMethodService,
         private globalSettingsService: GlobalSettingsService,
         private globalSettingsService: GlobalSettingsService,
     ) {}
     ) {}
 
 
@@ -37,6 +35,5 @@ export class InitializerService {
         await this.administratorService.initAdministrators();
         await this.administratorService.initAdministrators();
         await this.taxRateService.initTaxRates();
         await this.taxRateService.initTaxRates();
         await this.shippingMethodService.initShippingMethods();
         await this.shippingMethodService.initShippingMethods();
-        await this.paymentMethodService.initPaymentMethods();
     }
     }
 }
 }

+ 16 - 101
packages/core/src/service/services/payment-method.service.ts

@@ -1,14 +1,13 @@
 import { Injectable } from '@nestjs/common';
 import { Injectable } from '@nestjs/common';
 import {
 import {
-    ConfigArg,
-    ConfigArgInput,
+    CreatePaymentMethodInput,
     ManualPaymentInput,
     ManualPaymentInput,
     RefundOrderInput,
     RefundOrderInput,
     UpdatePaymentMethodInput,
     UpdatePaymentMethodInput,
 } from '@vendure/common/lib/generated-types';
 } from '@vendure/common/lib/generated-types';
 import { omit } from '@vendure/common/lib/omit';
 import { omit } from '@vendure/common/lib/omit';
-import { ConfigArgType, ID, PaginatedList } from '@vendure/common/lib/shared-types';
-import { assertNever, summate } from '@vendure/common/lib/shared-utils';
+import { ID, PaginatedList } from '@vendure/common/lib/shared-types';
+import { summate } from '@vendure/common/lib/shared-utils';
 
 
 import { RequestContext } from '../../api/common/request-context';
 import { RequestContext } from '../../api/common/request-context';
 import { UserInputError } from '../../common/error/errors';
 import { UserInputError } from '../../common/error/errors';
@@ -43,10 +42,6 @@ export class PaymentMethodService {
         private configArgService: ConfigArgService,
         private configArgService: ConfigArgService,
     ) {}
     ) {}
 
 
-    async initPaymentMethods() {
-        await this.ensurePaymentMethodsExist();
-    }
-
     findAll(
     findAll(
         ctx: RequestContext,
         ctx: RequestContext,
         options?: ListQueryOptions<PaymentMethod>,
         options?: ListQueryOptions<PaymentMethod>,
@@ -64,19 +59,17 @@ export class PaymentMethodService {
         return this.connection.getRepository(ctx, PaymentMethod).findOne(paymentMethodId);
         return this.connection.getRepository(ctx, PaymentMethod).findOne(paymentMethodId);
     }
     }
 
 
+    async create(ctx: RequestContext, input: CreatePaymentMethodInput): Promise<PaymentMethod> {
+        const paymentMethod = new PaymentMethod(input);
+        paymentMethod.handler = this.configArgService.parseInput('PaymentMethodHandler', input.handler);
+        return this.connection.getRepository(ctx, PaymentMethod).save(paymentMethod);
+    }
+
     async update(ctx: RequestContext, input: UpdatePaymentMethodInput): Promise<PaymentMethod> {
     async update(ctx: RequestContext, input: UpdatePaymentMethodInput): Promise<PaymentMethod> {
         const paymentMethod = await this.connection.getEntityOrThrow(ctx, PaymentMethod, input.id);
         const paymentMethod = await this.connection.getEntityOrThrow(ctx, PaymentMethod, input.id);
-        const updatedPaymentMethod = patchEntity(paymentMethod, omit(input, ['configArgs']));
-        if (input.configArgs) {
-            const handler = this.configService.paymentOptions.paymentMethodHandlers.find(
-                h => h.code === paymentMethod.code,
-            );
-            if (handler) {
-                function handlerHasArgDefinition(arg: ConfigArgInput): boolean {
-                    return !!handler?.args.hasOwnProperty(arg.name);
-                }
-                updatedPaymentMethod.configArgs = input.configArgs.filter(handlerHasArgDefinition);
-            }
+        const updatedPaymentMethod = patchEntity(paymentMethod, omit(input, ['handler']));
+        if (input.handler) {
+            paymentMethod.handler = this.configArgService.parseInput('PaymentMethodHandler', input.handler);
         }
         }
         return this.connection.getRepository(ctx, PaymentMethod).save(updatedPaymentMethod);
         return this.connection.getRepository(ctx, PaymentMethod).save(updatedPaymentMethod);
     }
     }
@@ -93,7 +86,7 @@ export class PaymentMethodService {
             ctx,
             ctx,
             order,
             order,
             amount,
             amount,
-            paymentMethod.configArgs,
+            paymentMethod.handler.args,
             metadata || {},
             metadata || {},
         );
         );
         const initialState = 'Created';
         const initialState = 'Created';
@@ -132,7 +125,7 @@ export class PaymentMethodService {
 
 
     async settlePayment(ctx: RequestContext, payment: Payment, order: Order) {
     async settlePayment(ctx: RequestContext, payment: Payment, order: Order) {
         const { paymentMethod, handler } = await this.getMethodAndHandler(ctx, payment.method);
         const { paymentMethod, handler } = await this.getMethodAndHandler(ctx, payment.method);
-        return handler.settlePayment(ctx, order, payment, paymentMethod.configArgs);
+        return handler.settlePayment(ctx, order, payment, paymentMethod.handler.args);
     }
     }
 
 
     async createRefund(
     async createRefund(
@@ -163,7 +156,7 @@ export class PaymentMethodService {
             refundAmount,
             refundAmount,
             order,
             order,
             payment,
             payment,
-            paymentMethod.configArgs,
+            paymentMethod.handler.args,
         );
         );
         if (createRefundResult) {
         if (createRefundResult) {
             refund.transactionId = createRefundResult.transactionId || '';
             refund.transactionId = createRefundResult.transactionId || '';
@@ -185,10 +178,6 @@ export class PaymentMethodService {
         return refund;
         return refund;
     }
     }
 
 
-    getPaymentMethodHandler(code: string): PaymentMethodHandler {
-        return this.configArgService.getByCode('PaymentMethodHandler', code);
-    }
-
     private async getMethodAndHandler(
     private async getMethodAndHandler(
         ctx: RequestContext,
         ctx: RequestContext,
         method: string,
         method: string,
@@ -202,81 +191,7 @@ export class PaymentMethodService {
         if (!paymentMethod) {
         if (!paymentMethod) {
             throw new UserInputError(`error.payment-method-not-found`, { method });
             throw new UserInputError(`error.payment-method-not-found`, { method });
         }
         }
-        const handler = this.getPaymentMethodHandler(paymentMethod.code);
+        const handler = this.configArgService.getByCode('PaymentMethodHandler', paymentMethod.handler.code);
         return { paymentMethod, handler };
         return { paymentMethod, handler };
     }
     }
-
-    private async ensurePaymentMethodsExist() {
-        const paymentMethodRepo = await this.connection.getRepository(PaymentMethod);
-        const paymentMethodHandlers = this.configService.paymentOptions.paymentMethodHandlers;
-        const existingPaymentMethods = await paymentMethodRepo.find();
-        const toCreate = paymentMethodHandlers.filter(
-            h => !existingPaymentMethods.find(pm => pm.code === h.code),
-        );
-        const toRemove = existingPaymentMethods.filter(
-            h => !paymentMethodHandlers.find(pm => pm.code === h.code),
-        );
-        const toUpdate = existingPaymentMethods.filter(
-            h => !toCreate.find(x => x.code === h.code) && !toRemove.find(x => x.code === h.code),
-        );
-
-        for (const paymentMethod of toUpdate) {
-            const handler = paymentMethodHandlers.find(h => h.code === paymentMethod.code);
-            if (!handler) {
-                continue;
-            }
-            paymentMethod.configArgs = this.buildConfigArgsArray(handler, paymentMethod.configArgs);
-            await paymentMethodRepo.save(paymentMethod, { reload: false });
-        }
-        for (const handler of toCreate) {
-            let paymentMethod = existingPaymentMethods.find(pm => pm.code === handler.code);
-
-            if (!paymentMethod) {
-                paymentMethod = new PaymentMethod({
-                    code: handler.code,
-                    enabled: true,
-                    configArgs: [],
-                });
-            }
-            paymentMethod.configArgs = this.buildConfigArgsArray(handler, paymentMethod.configArgs);
-            await paymentMethodRepo.save(paymentMethod, { reload: false });
-        }
-        await paymentMethodRepo.remove(toRemove);
-    }
-
-    private buildConfigArgsArray(
-        handler: PaymentMethodHandler,
-        existingConfigArgs: ConfigArg[],
-    ): ConfigArg[] {
-        let configArgs: ConfigArg[] = [];
-        for (const [name, def] of Object.entries(handler.args)) {
-            if (!existingConfigArgs.find(ca => ca.name === name)) {
-                configArgs.push({
-                    name,
-                    value: this.getDefaultValue(def.type),
-                });
-            }
-        }
-        configArgs = configArgs.filter(ca => handler.args.hasOwnProperty(ca.name));
-        return [...existingConfigArgs, ...configArgs];
-    }
-
-    private getDefaultValue(type: ConfigArgType): string {
-        switch (type) {
-            case 'string':
-                return '';
-            case 'boolean':
-                return 'false';
-            case 'int':
-            case 'float':
-                return '0';
-            case 'ID':
-                return '';
-            case 'datetime':
-                return new Date().toISOString();
-            default:
-                assertNever(type);
-                return '';
-        }
-    }
 }
 }

+ 2 - 2
packages/create/templates/vendure-config.hbs

@@ -1,5 +1,5 @@
 {{#if isTs }}import{{ else }}const{{/if}} {
 {{#if isTs }}import{{ else }}const{{/if}} {
-    examplePaymentHandler,
+    dummyPaymentHandler,
     DefaultJobQueuePlugin,
     DefaultJobQueuePlugin,
     DefaultSearchPlugin,{{#if isTs}}
     DefaultSearchPlugin,{{#if isTs}}
     VendureConfig,{{/if}}
     VendureConfig,{{/if}}
@@ -71,7 +71,7 @@ const path = require('path');
         migrations: [path.join(__dirname, '../migrations/*.ts')],
         migrations: [path.join(__dirname, '../migrations/*.ts')],
     },
     },
     paymentOptions: {
     paymentOptions: {
-        paymentMethodHandlers: [examplePaymentHandler],
+        paymentMethodHandlers: [dummyPaymentHandler],
     },
     },
     customFields: {},
     customFields: {},
     plugins: [
     plugins: [

+ 2 - 1
packages/dev-server/dev-config.ts

@@ -6,6 +6,7 @@ import {
     DefaultJobQueuePlugin,
     DefaultJobQueuePlugin,
     DefaultLogger,
     DefaultLogger,
     DefaultSearchPlugin,
     DefaultSearchPlugin,
+    dummyPaymentHandler,
     examplePaymentHandler,
     examplePaymentHandler,
     LogLevel,
     LogLevel,
     manualFulfillmentHandler,
     manualFulfillmentHandler,
@@ -50,7 +51,7 @@ export const devConfig: VendureConfig = {
         ...getDbConfig(),
         ...getDbConfig(),
     },
     },
     paymentOptions: {
     paymentOptions: {
-        paymentMethodHandlers: [examplePaymentHandler],
+        paymentMethodHandlers: [dummyPaymentHandler],
     },
     },
     customFields: {},
     customFields: {},
     logger: new DefaultLogger({ level: LogLevel.Info }),
     logger: new DefaultLogger({ level: LogLevel.Info }),

+ 2 - 3
packages/dev-server/load-testing/load-test-config.ts

@@ -4,11 +4,10 @@ import {
     defaultConfig,
     defaultConfig,
     DefaultLogger,
     DefaultLogger,
     DefaultSearchPlugin,
     DefaultSearchPlugin,
-    examplePaymentHandler,
+    dummyPaymentHandler,
     InMemorySessionCacheStrategy,
     InMemorySessionCacheStrategy,
     LogLevel,
     LogLevel,
     mergeConfig,
     mergeConfig,
-    NoopSessionCacheStrategy,
     VendureConfig,
     VendureConfig,
 } from '@vendure/core';
 } from '@vendure/core';
 import path from 'path';
 import path from 'path';
@@ -41,7 +40,7 @@ export function getLoadTestConfig(tokenMethod: 'cookie' | 'bearer'): Required<Ve
     const count = getProductCount();
     const count = getProductCount();
     return mergeConfig(defaultConfig, {
     return mergeConfig(defaultConfig, {
         paymentOptions: {
         paymentOptions: {
-            paymentMethodHandlers: [examplePaymentHandler],
+            paymentMethodHandlers: [dummyPaymentHandler],
         },
         },
         orderOptions: {
         orderOptions: {
             orderItemsLimit: 99999,
             orderItemsLimit: 99999,

+ 29 - 3
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts

@@ -49,6 +49,7 @@ export type Query = {
     orders: OrderList;
     orders: OrderList;
     paymentMethods: PaymentMethodList;
     paymentMethods: PaymentMethodList;
     paymentMethod?: Maybe<PaymentMethod>;
     paymentMethod?: Maybe<PaymentMethod>;
+    paymentMethodHandlers: Array<ConfigurableOperationDefinition>;
     productOptionGroups: Array<ProductOptionGroup>;
     productOptionGroups: Array<ProductOptionGroup>;
     productOptionGroup?: Maybe<ProductOptionGroup>;
     productOptionGroup?: Maybe<ProductOptionGroup>;
     search: SearchResponse;
     search: SearchResponse;
@@ -366,6 +367,8 @@ export type Mutation = {
      * Payment.
      * Payment.
      */
      */
     addManualPaymentToOrder: AddManualPaymentToOrderResult;
     addManualPaymentToOrder: AddManualPaymentToOrderResult;
+    /** Create existing PaymentMethod */
+    createPaymentMethod: PaymentMethod;
     /** Update an existing PaymentMethod */
     /** Update an existing PaymentMethod */
     updatePaymentMethod: PaymentMethod;
     updatePaymentMethod: PaymentMethod;
     /** Create a new ProductOptionGroup */
     /** Create a new ProductOptionGroup */
@@ -693,6 +696,10 @@ export type MutationAddManualPaymentToOrderArgs = {
     input: ManualPaymentInput;
     input: ManualPaymentInput;
 };
 };
 
 
+export type MutationCreatePaymentMethodArgs = {
+    input: CreatePaymentMethodInput;
+};
+
 export type MutationUpdatePaymentMethodArgs = {
 export type MutationUpdatePaymentMethodArgs = {
     input: UpdatePaymentMethodInput;
     input: UpdatePaymentMethodInput;
 };
 };
@@ -959,6 +966,7 @@ export type CreateChannelInput = {
     currencyCode: CurrencyCode;
     currencyCode: CurrencyCode;
     defaultTaxZoneId: Scalars['ID'];
     defaultTaxZoneId: Scalars['ID'];
     defaultShippingZoneId: Scalars['ID'];
     defaultShippingZoneId: Scalars['ID'];
+    customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 export type UpdateChannelInput = {
 export type UpdateChannelInput = {
@@ -970,6 +978,7 @@ export type UpdateChannelInput = {
     currencyCode?: Maybe<CurrencyCode>;
     currencyCode?: Maybe<CurrencyCode>;
     defaultTaxZoneId?: Maybe<Scalars['ID']>;
     defaultTaxZoneId?: Maybe<Scalars['ID']>;
     defaultShippingZoneId?: Maybe<Scalars['ID']>;
     defaultShippingZoneId?: Maybe<Scalars['ID']>;
+    customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 /** Returned if attempting to set a Channel's defaultLanguageCode to a language which is not enabled in GlobalSettings */
 /** Returned if attempting to set a Channel's defaultLanguageCode to a language which is not enabled in GlobalSettings */
@@ -1721,21 +1730,32 @@ export type PaymentMethodList = PaginatedList & {
     totalItems: Scalars['Int'];
     totalItems: Scalars['Int'];
 };
 };
 
 
+export type CreatePaymentMethodInput = {
+    name: Scalars['String'];
+    code: Scalars['String'];
+    description?: Maybe<Scalars['String']>;
+    enabled: Scalars['Boolean'];
+    handler: ConfigurableOperationInput;
+};
+
 export type UpdatePaymentMethodInput = {
 export type UpdatePaymentMethodInput = {
     id: Scalars['ID'];
     id: Scalars['ID'];
+    name?: Maybe<Scalars['String']>;
     code?: Maybe<Scalars['String']>;
     code?: Maybe<Scalars['String']>;
+    description?: Maybe<Scalars['String']>;
     enabled?: Maybe<Scalars['Boolean']>;
     enabled?: Maybe<Scalars['Boolean']>;
-    configArgs?: Maybe<Array<ConfigArgInput>>;
+    handler?: Maybe<ConfigurableOperationInput>;
 };
 };
 
 
 export type PaymentMethod = Node & {
 export type PaymentMethod = Node & {
     id: Scalars['ID'];
     id: Scalars['ID'];
     createdAt: Scalars['DateTime'];
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
+    name: Scalars['String'];
     code: Scalars['String'];
     code: Scalars['String'];
+    description: Scalars['String'];
     enabled: Scalars['Boolean'];
     enabled: Scalars['Boolean'];
-    configArgs: Array<ConfigArg>;
-    definition: ConfigurableOperationDefinition;
+    handler: ConfigurableOperation;
 };
 };
 
 
 export type Product = Node & {
 export type Product = Node & {
@@ -2279,6 +2299,7 @@ export type Channel = Node & {
     defaultLanguageCode: LanguageCode;
     defaultLanguageCode: LanguageCode;
     currencyCode: CurrencyCode;
     currencyCode: CurrencyCode;
     pricesIncludeTax: Scalars['Boolean'];
     pricesIncludeTax: Scalars['Boolean'];
+    customFields?: Maybe<Scalars['JSON']>;
 };
 };
 
 
 export type CollectionBreadcrumb = {
 export type CollectionBreadcrumb = {
@@ -4260,7 +4281,9 @@ export type OrderSortParameter = {
 export type PaymentMethodFilterParameter = {
 export type PaymentMethodFilterParameter = {
     createdAt?: Maybe<DateOperators>;
     createdAt?: Maybe<DateOperators>;
     updatedAt?: Maybe<DateOperators>;
     updatedAt?: Maybe<DateOperators>;
+    name?: Maybe<StringOperators>;
     code?: Maybe<StringOperators>;
     code?: Maybe<StringOperators>;
+    description?: Maybe<StringOperators>;
     enabled?: Maybe<BooleanOperators>;
     enabled?: Maybe<BooleanOperators>;
 };
 };
 
 
@@ -4268,7 +4291,9 @@ export type PaymentMethodSortParameter = {
     id?: Maybe<SortOrder>;
     id?: Maybe<SortOrder>;
     createdAt?: Maybe<SortOrder>;
     createdAt?: Maybe<SortOrder>;
     updatedAt?: Maybe<SortOrder>;
     updatedAt?: Maybe<SortOrder>;
+    name?: Maybe<SortOrder>;
     code?: Maybe<SortOrder>;
     code?: Maybe<SortOrder>;
+    description?: Maybe<SortOrder>;
 };
 };
 
 
 export type ProductFilterParameter = {
 export type ProductFilterParameter = {
@@ -4431,6 +4456,7 @@ export type NativeAuthInput = {
 
 
 export type CustomFields = {
 export type CustomFields = {
     Address: Array<CustomFieldConfig>;
     Address: Array<CustomFieldConfig>;
+    Channel: Array<CustomFieldConfig>;
     Collection: Array<CustomFieldConfig>;
     Collection: Array<CustomFieldConfig>;
     Customer: Array<CustomFieldConfig>;
     Customer: Array<CustomFieldConfig>;
     Facet: Array<CustomFieldConfig>;
     Facet: Array<CustomFieldConfig>;

Fișier diff suprimat deoarece este prea mare
+ 0 - 0
schema-admin.json


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
schema-shop.json


Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff