Forráskód Böngészése

Merge branch 'minor' into major

Michael Bromley 3 éve
szülő
commit
31b1a20654
28 módosított fájl, 5730 hozzáadás és 2249 törlés
  1. 2 1
      docs/content/plugins/extending-the-admin-ui/using-other-frameworks/_index.md
  2. 297 301
      packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts
  3. 29 4
      packages/asset-server-plugin/src/s3-asset-storage-strategy.ts
  4. 288 289
      packages/common/src/generated-shop-types.ts
  5. 297 301
      packages/core/e2e/graphql/generated-e2e-admin-types.ts
  6. 288 289
      packages/core/e2e/graphql/generated-e2e-shop-types.ts
  7. 2 1
      packages/core/src/api/resolvers/admin/global-settings.resolver.ts
  8. 297 301
      packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts
  9. 2 0
      packages/email-plugin/src/mock-events.ts
  10. 3 0
      packages/payments-plugin/README.md
  11. 17 0
      packages/payments-plugin/e2e/graphql/admin-queries.ts
  12. 297 301
      packages/payments-plugin/e2e/graphql/generated-admin-types.ts
  13. 288 289
      packages/payments-plugin/e2e/graphql/generated-shop-types.ts
  14. 72 24
      packages/payments-plugin/e2e/mollie-payment.e2e-spec.ts
  15. 4 3
      packages/payments-plugin/e2e/payment-helpers.ts
  16. 1 1
      packages/payments-plugin/package.json
  17. 0 5
      packages/payments-plugin/src/mollie/README.md
  18. 3174 0
      packages/payments-plugin/src/mollie/graphql/generated-shop-types.ts
  19. 18 0
      packages/payments-plugin/src/mollie/mollie-shop-schema.ts
  20. 10 55
      packages/payments-plugin/src/mollie/mollie.controller.ts
  21. 19 54
      packages/payments-plugin/src/mollie/mollie.handler.ts
  22. 22 20
      packages/payments-plugin/src/mollie/mollie.plugin.ts
  23. 33 0
      packages/payments-plugin/src/mollie/mollie.resolver.ts
  24. 191 0
      packages/payments-plugin/src/mollie/mollie.service.ts
  25. 4 1
      packages/payments-plugin/src/stripe/stripe.handler.ts
  26. 46 9
      packages/ui-devkit/src/compiler/compile.ts
  27. 23 0
      packages/ui-devkit/src/compiler/types.ts
  28. 6 0
      scripts/codegen/generate-graphql-types.ts

+ 2 - 1
docs/content/plugins/extending-the-admin-ui/using-other-frameworks/_index.md

@@ -94,6 +94,7 @@ export const config: VendureConfig = {
   // ...
   plugins: [
     AdminUiPlugin.init({
+      route: "admin",
       port: 3002,
       app: compileUiExtensions({
         outputPath: path.join(__dirname, '../admin-ui'),
@@ -120,7 +121,7 @@ export const config: VendureConfig = {
             // artifacts over to the Admin UI's `/static` directory. In this case we
             // also rename "build" to "react-app". This is why the `extensionUrl`
             // in the module config points to './assets/react-app/index.html'.
-            { path: path.join(__dirname, 'react-app/build'), rename: 'react-app' },
+            { path: path.join(__dirname, 'ui-extension/react-app/build'), rename: 'react-app' },
           ],
         }],
         devMode: true,

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 297 - 301
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts


+ 29 - 4
packages/asset-server-plugin/src/s3-asset-storage-strategy.ts

@@ -82,6 +82,32 @@ export interface S3Config {
  * }),
  * ```
  *
+ * ## Usage with MinIO
+ *
+ * Reference: [How to use AWS SDK for Javascript with MinIO Server](https://docs.min.io/docs/how-to-use-aws-sdk-for-javascript-with-minio-server.html)
+ *
+ * @example
+ * ```TypeScript
+ * plugins: [
+ *   AssetServerPlugin.init({
+ *     route: 'assets',
+ *     assetUploadDir: path.join(__dirname, 'assets'),
+ *     port: 5002,
+ *     namingStrategy: new DefaultAssetNamingStrategy(),
+ *     storageStrategyFactory: configureS3AssetStorage({
+ *       bucket: 'my-minio-bucket',
+ *       credentials: {
+ *         accessKeyId: process.env.MINIO_ACCESS_KEY_ID,
+ *         secretAccessKey: process.env.MINIO_SECRET_ACCESS_KEY,
+ *       },
+ *       nativeS3Configuration: {
+ *         endpoint: process.env.MINIO_ENDPOINT ?? 'http://localhost:9000',
+ *         s3ForcePathStyle: true,
+ *         signatureVersion: 'v4',
+ *       },
+ *     }),
+ * }),
+ * ```
  * @docsCategory asset-server-plugin
  * @docsPage S3AssetStorageStrategy
  */
@@ -114,10 +140,9 @@ export function configureS3AssetStorage(s3Config: S3Config) {
  *
  * **Note:** Rather than instantiating this manually, use the {@link configureS3AssetStorage} function.
  *
- * ## Use with S3-compatible services
- * This strategy will also work with any S3-compatible object storage solutions, such as [MinIO](https://min.io/):
- *
- * * [How to use AWS SDK for Javascript with MinIO Server](https://docs.min.io/docs/how-to-use-aws-sdk-for-javascript-with-minio-server.html)
+ * ## Use with S3-compatible services (MinIO)
+ * This strategy will also work with any S3-compatible object storage solutions, such as [MinIO](https://min.io/).
+ * See the {@link configureS3AssetStorage} for an example with MinIO.
  *
  * @docsCategory asset-server-plugin
  * @docsPage S3AssetStorageStrategy

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 288 - 289
packages/common/src/generated-shop-types.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 297 - 301
packages/core/e2e/graphql/generated-e2e-admin-types.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 288 - 289
packages/core/e2e/graphql/generated-e2e-shop-types.ts


+ 2 - 1
packages/core/src/api/resolvers/admin/global-settings.resolver.ts

@@ -11,6 +11,7 @@ import {
     GraphQLOutputType,
     GraphQLResolveInfo,
     isEnumType,
+    isInterfaceType,
     isListType,
     isNonNullType,
     isObjectType,
@@ -118,7 +119,7 @@ export class GlobalSettingsResolver {
     private getScalarFieldsOfType(info: GraphQLResolveInfo, typeName: string): string[] {
         const type = info.schema.getType(typeName);
 
-        if (type && isObjectType(type)) {
+        if (type && (isObjectType(type) || isInterfaceType(type))) {
             return Object.values(type.getFields())
                 .filter(field => {
                     const namedType = this.getNamedType(field.type);

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 297 - 301
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts


+ 2 - 0
packages/email-plugin/src/mock-events.ts

@@ -1,6 +1,7 @@
 import { AdjustmentType } from '@vendure/common/lib/generated-shop-types';
 import {
     AccountRegistrationEvent,
+    CurrencyCode,
     Customer,
     IdentifierChangeRequestEvent,
     NativeAuthenticationMethod,
@@ -20,6 +21,7 @@ export const mockOrderStateTransitionEvent = new OrderStateTransitionEvent(
     {} as any,
     new Order({
         id: '6',
+        currencyCode: CurrencyCode.USD,
         createdAt: '2018-10-31T11:18:29.261Z',
         updatedAt: '2018-10-31T15:24:17.000Z',
         orderPlacedAt: '2018-10-31T13:54:17.000Z',

+ 3 - 0
packages/payments-plugin/README.md

@@ -0,0 +1,3 @@
+# Payments plugin
+
+For documentation, see https://www.vendure.io/docs/typescript-api/payments-plugin

+ 17 - 0
packages/payments-plugin/e2e/graphql/admin-queries.ts

@@ -77,3 +77,20 @@ export const REFUND_ORDER = gql`
     }
     ${REFUND_FRAGMENT}
 `;
+
+export const GET_ORDER_PAYMENTS = gql`
+    query order($id: ID!) {
+        order(id: $id) {
+            id
+            payments {
+                id
+                transactionId
+                method
+                amount
+                state
+                errorMessage
+                metadata
+            }
+        }
+    }
+`;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 297 - 301
packages/payments-plugin/e2e/graphql/generated-admin-types.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 288 - 289
packages/payments-plugin/e2e/graphql/generated-shop-types.ts


+ 72 - 24
packages/payments-plugin/e2e/mollie-payment.e2e-spec.ts

@@ -4,11 +4,10 @@ import { DefaultLogger, LogLevel, mergeConfig } from '@vendure/core';
 import {
     createTestEnvironment,
     E2E_DEFAULT_CHANNEL_TOKEN,
-    registerInitializer,
     SimpleGraphQLClient,
-    SqljsInitializer,
     TestServer,
 } from '@vendure/testing';
+import gql from 'graphql-tag';
 import nock from 'nock';
 import fetch from 'node-fetch';
 import path from 'path';
@@ -18,7 +17,7 @@ import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-conf
 import { MolliePlugin } from '../src/mollie';
 import { molliePaymentHandler } from '../src/mollie/mollie.handler';
 
-import { CREATE_PAYMENT_METHOD, GET_CUSTOMER_LIST } from './graphql/admin-queries';
+import { CREATE_PAYMENT_METHOD, GET_CUSTOMER_LIST, GET_ORDER_PAYMENTS } from './graphql/admin-queries';
 import {
     CreatePaymentMethodMutation,
     CreatePaymentMethodMutationVariables,
@@ -35,7 +34,21 @@ import {
     TestOrderFragmentFragment,
 } from './graphql/generated-shop-types';
 import { ADD_ITEM_TO_ORDER, ADD_PAYMENT, GET_ORDER_BY_CODE } from './graphql/shop-queries';
-import { proceedToArrangingPayment, refundOne } from './payment-helpers';
+import { proceedToArrangingPayment, refundOne, setShipping } from './payment-helpers';
+
+export const CREATE_MOLLIE_PAYMENT_INTENT = gql`
+    mutation createMolliePaymentIntent($input: MolliePaymentIntentInput!) {
+        createMolliePaymentIntent(input: $input) {
+            ... on MolliePaymentIntent {
+                url
+            }
+            ... on MolliePaymentIntentError {
+                errorCode
+                message
+            }
+        }
+    }
+`;
 
 describe('Mollie payments', () => {
     const mockData = {
@@ -50,6 +63,14 @@ describe('Mollie payments', () => {
                     href: 'https://www.mollie.com/payscreen/select-method/mock-payment',
                 },
             },
+            resource: 'payment',
+            mode: 'test',
+            method: 'test-method',
+            profileId: '123',
+            settlementAmount: 'test amount',
+            customerId: '456',
+            authorizedAt: new Date(),
+            paidAt: new Date(),
         },
     };
     let shopClient: SimpleGraphQLClient;
@@ -93,6 +114,19 @@ describe('Mollie payments', () => {
         expect(customers).toHaveLength(2);
     });
 
+    it('Should prepare an order', async () => {
+        await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
+        const { addItemToOrder } = await shopClient.query<
+            AddItemToOrderMutation,
+            AddItemToOrderMutationVariables
+        >(ADD_ITEM_TO_ORDER, {
+            productVariantId: 'T_1',
+            quantity: 2,
+        });
+        order = addItemToOrder as TestOrderFragmentFragment;
+        expect(order.code).toBeDefined();
+    });
+
     it('Should add a Mollie paymentMethod', async () => {
         const { createPaymentMethod } = await adminClient.query<
             CreatePaymentMethodMutation,
@@ -115,7 +149,17 @@ describe('Mollie payments', () => {
         expect(createPaymentMethod.code).toBe(mockData.methodCode);
     });
 
-    it('Should add payment to order', async () => {
+    it('Should fail to create payment intent without shippingmethod', async () => {
+        await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
+        const { createMolliePaymentIntent: result } = await shopClient.query(CREATE_MOLLIE_PAYMENT_INTENT, {
+            input: {
+                paymentMethodCode: mockData.methodCode,
+            },
+        });
+        expect(result.errorCode).toBe('ORDER_PAYMENT_STATE_ERROR');
+    });
+
+    it('Should get payment url', async () => {
         let mollieRequest: any;
         nock('https://api.mollie.com/')
             .post(/.*/, body => {
@@ -124,22 +168,15 @@ describe('Mollie payments', () => {
             })
             .reply(200, mockData.mollieResponse);
         await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
-        await shopClient.query<AddItemToOrderMutation, AddItemToOrderMutationVariables>(ADD_ITEM_TO_ORDER, {
-            productVariantId: 'T_1',
-            quantity: 2,
-        });
-        await proceedToArrangingPayment(shopClient);
-        const { addPaymentToOrder } = await shopClient.query<
-            AddPaymentToOrderMutation,
-            AddPaymentToOrderMutationVariables
-        >(ADD_PAYMENT, {
+        await setShipping(shopClient);
+        const { createMolliePaymentIntent } = await shopClient.query(CREATE_MOLLIE_PAYMENT_INTENT, {
             input: {
-                method: mockData.methodCode,
-                metadata: {},
+                paymentMethodCode: mockData.methodCode,
             },
         });
-        order = addPaymentToOrder as TestOrderFragmentFragment;
-        expect(order.state).toEqual('PaymentAuthorized');
+        expect(createMolliePaymentIntent).toEqual({
+            url: 'https://www.mollie.com/payscreen/select-method/mock-payment',
+        });
         expect(mollieRequest?.metadata.orderCode).toEqual(order.code);
         expect(mollieRequest?.redirectUrl).toEqual(`${mockData.redirectUrl}/${order.code}`);
         expect(mollieRequest?.webhookUrl).toEqual(
@@ -147,10 +184,6 @@ describe('Mollie payments', () => {
         );
         expect(mollieRequest?.amount?.value).toBe('3127.60');
         expect(mollieRequest?.amount?.currency).toBeDefined();
-        // tslint:disable-next-line:no-non-null-assertion
-        const payment = order.payments![0];
-        expect(payment.transactionId).toEqual(mockData.mollieResponse.id);
-        expect(payment.metadata.public.redirectLink).toEqual(mockData.mollieResponse._links.checkout.href);
     });
 
     it('Should settle payment for order', async () => {
@@ -177,6 +210,21 @@ describe('Mollie payments', () => {
         expect(order.state).toBe('PaymentSettled');
     });
 
+    it('Should have Mollie metadata on payment', async () => {
+        const {
+            order: {
+                payments: [{ metadata }],
+            },
+        } = await adminClient.query(GET_ORDER_PAYMENTS, { id: order.id });
+        expect(metadata.mode).toBe(mockData.mollieResponse.mode);
+        expect(metadata.method).toBe(mockData.mollieResponse.method);
+        expect(metadata.profileId).toBe(mockData.mollieResponse.profileId);
+        expect(metadata.settlementAmount).toBe(mockData.mollieResponse.settlementAmount);
+        expect(metadata.customerId).toBe(mockData.mollieResponse.customerId);
+        expect(metadata.authorizedAt).toEqual(mockData.mollieResponse.authorizedAt.toISOString());
+        expect(metadata.paidAt).toEqual(mockData.mollieResponse.paidAt.toISOString());
+    });
+
     it('Should fail to refund', async () => {
         let mollieRequest;
         nock('https://api.mollie.com/')
@@ -184,7 +232,7 @@ describe('Mollie payments', () => {
                 mollieRequest = body;
                 return true;
             })
-            .reply(200, { status: 'failed' });
+            .reply(200, { status: 'failed', resource: 'payment' });
         const refund = await refundOne(adminClient, order.lines[0].id, order.payments![0].id);
         expect(refund.state).toBe('Failed');
     });
@@ -196,7 +244,7 @@ describe('Mollie payments', () => {
                 mollieRequest = body;
                 return true;
             })
-            .reply(200, { status: 'pending' });
+            .reply(200, { status: 'pending', resource: 'payment' });
         const refund = await refundOne(adminClient, order.lines[0].id, order.payments![0].id);
         expect(mollieRequest?.amount.value).toBe('1558.80');
         expect(refund.total).toBe(155880);

+ 4 - 3
packages/payments-plugin/e2e/payment-helpers.ts

@@ -16,7 +16,7 @@ import {
     TRANSITION_TO_STATE,
 } from './graphql/shop-queries';
 
-export async function proceedToArrangingPayment(shopClient: SimpleGraphQLClient): Promise<ID> {
+export async function setShipping(shopClient: SimpleGraphQLClient): Promise<void> {
     await shopClient.query(SET_SHIPPING_ADDRESS, {
         input: {
             fullName: 'name',
@@ -26,15 +26,16 @@ export async function proceedToArrangingPayment(shopClient: SimpleGraphQLClient)
             countryCode: 'US',
         },
     });
-
     const { eligibleShippingMethods } = await shopClient.query<GetShippingMethods.Query>(
         GET_ELIGIBLE_SHIPPING_METHODS,
     );
-
     await shopClient.query<SetShippingMethod.Mutation, SetShippingMethod.Variables>(SET_SHIPPING_METHOD, {
         id: eligibleShippingMethods[1].id,
     });
+}
 
+export async function proceedToArrangingPayment(shopClient: SimpleGraphQLClient): Promise<ID> {
+    await setShipping(shopClient);
     const { transitionOrderToState } = await shopClient.query<
         TransitionToState.Mutation,
         TransitionToState.Variables

+ 1 - 1
packages/payments-plugin/package.json

@@ -26,7 +26,7 @@
         "stripe": "8.x"
     },
     "devDependencies": {
-        "@mollie/api-client": "~3.5.1",
+        "@mollie/api-client": "^3.6.0",
         "@types/braintree": "^2.22.15",
         "@vendure/common": "^2.0.0-next.0",
         "@vendure/core": "^2.0.0-next.0",

+ 0 - 5
packages/payments-plugin/src/mollie/README.md

@@ -1,5 +0,0 @@
-# Mollie payment plugin
-
-Plugin to enable payments through the [Mollie platform](https://docs.mollie.com/).
-
-For documentation, see https://www.vendure.io/docs/typescript-api/payments-plugin/mollie-plugin

+ 3174 - 0
packages/payments-plugin/src/mollie/graphql/generated-shop-types.ts

@@ -0,0 +1,3174 @@
+// tslint:disable
+export type Maybe<T> = T | null;
+export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
+export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
+export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
+/** All built-in and custom scalars, mapped to their actual values */
+export type Scalars = {
+    ID: string;
+    String: string;
+    Boolean: boolean;
+    Int: number;
+    Float: number;
+    /** A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. */
+    DateTime: any;
+    /** The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
+    JSON: any;
+    /** The `Upload` scalar type represents a file upload. */
+    Upload: any;
+};
+
+export type ActiveOrderResult = Order | NoActiveOrderError;
+
+export type AddPaymentToOrderResult =
+    | Order
+    | OrderPaymentStateError
+    | IneligiblePaymentMethodError
+    | PaymentFailedError
+    | PaymentDeclinedError
+    | OrderStateTransitionError
+    | NoActiveOrderError;
+
+export type Address = Node & {
+    __typename?: 'Address';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    fullName?: Maybe<Scalars['String']>;
+    company?: Maybe<Scalars['String']>;
+    streetLine1: Scalars['String'];
+    streetLine2?: Maybe<Scalars['String']>;
+    city?: Maybe<Scalars['String']>;
+    province?: Maybe<Scalars['String']>;
+    postalCode?: Maybe<Scalars['String']>;
+    country: Country;
+    phoneNumber?: Maybe<Scalars['String']>;
+    defaultShippingAddress?: Maybe<Scalars['Boolean']>;
+    defaultBillingAddress?: Maybe<Scalars['Boolean']>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type Adjustment = {
+    __typename?: 'Adjustment';
+    adjustmentSource: Scalars['String'];
+    type: AdjustmentType;
+    description: Scalars['String'];
+    amount: Scalars['Int'];
+};
+
+export enum AdjustmentType {
+    PROMOTION = 'PROMOTION',
+    DISTRIBUTED_ORDER_PROMOTION = 'DISTRIBUTED_ORDER_PROMOTION',
+    OTHER = 'OTHER',
+}
+
+/** Returned when attempting to set the Customer for an Order when already logged in. */
+export type AlreadyLoggedInError = ErrorResult & {
+    __typename?: 'AlreadyLoggedInError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+export type ApplyCouponCodeResult =
+    | Order
+    | CouponCodeExpiredError
+    | CouponCodeInvalidError
+    | CouponCodeLimitError;
+
+export type Asset = Node & {
+    __typename?: 'Asset';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    name: Scalars['String'];
+    type: AssetType;
+    fileSize: Scalars['Int'];
+    mimeType: Scalars['String'];
+    width: Scalars['Int'];
+    height: Scalars['Int'];
+    source: Scalars['String'];
+    preview: Scalars['String'];
+    focalPoint?: Maybe<Coordinate>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type AssetList = PaginatedList & {
+    __typename?: 'AssetList';
+    items: Array<Asset>;
+    totalItems: Scalars['Int'];
+};
+
+export enum AssetType {
+    IMAGE = 'IMAGE',
+    VIDEO = 'VIDEO',
+    BINARY = 'BINARY',
+}
+
+export type AuthenticationInput = {
+    native?: Maybe<NativeAuthInput>;
+};
+
+export type AuthenticationMethod = Node & {
+    __typename?: 'AuthenticationMethod';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    strategy: Scalars['String'];
+};
+
+export type AuthenticationResult = CurrentUser | InvalidCredentialsError | NotVerifiedError;
+
+export type BooleanCustomFieldConfig = CustomField & {
+    __typename?: 'BooleanCustomFieldConfig';
+    name: Scalars['String'];
+    type: Scalars['String'];
+    list: Scalars['Boolean'];
+    label?: Maybe<Array<LocalizedString>>;
+    description?: Maybe<Array<LocalizedString>>;
+    readonly?: Maybe<Scalars['Boolean']>;
+    internal?: Maybe<Scalars['Boolean']>;
+    nullable?: Maybe<Scalars['Boolean']>;
+    ui?: Maybe<Scalars['JSON']>;
+};
+
+/** Operators for filtering on a list of Boolean fields */
+export type BooleanListOperators = {
+    inList: Scalars['Boolean'];
+};
+
+/** Operators for filtering on a Boolean field */
+export type BooleanOperators = {
+    eq?: Maybe<Scalars['Boolean']>;
+};
+
+export type Channel = Node & {
+    __typename?: 'Channel';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    code: Scalars['String'];
+    token: Scalars['String'];
+    defaultTaxZone?: Maybe<Zone>;
+    defaultShippingZone?: Maybe<Zone>;
+    defaultLanguageCode: LanguageCode;
+    currencyCode: CurrencyCode;
+    pricesIncludeTax: Scalars['Boolean'];
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type Collection = Node & {
+    __typename?: 'Collection';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode?: Maybe<LanguageCode>;
+    name: Scalars['String'];
+    slug: Scalars['String'];
+    breadcrumbs: Array<CollectionBreadcrumb>;
+    position: Scalars['Int'];
+    description: Scalars['String'];
+    featuredAsset?: Maybe<Asset>;
+    assets: Array<Asset>;
+    parent?: Maybe<Collection>;
+    children?: Maybe<Array<Collection>>;
+    filters: Array<ConfigurableOperation>;
+    translations: Array<CollectionTranslation>;
+    productVariants: ProductVariantList;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type CollectionProductVariantsArgs = {
+    options?: Maybe<ProductVariantListOptions>;
+};
+
+export type CollectionBreadcrumb = {
+    __typename?: 'CollectionBreadcrumb';
+    id: Scalars['ID'];
+    name: Scalars['String'];
+    slug: Scalars['String'];
+};
+
+export type CollectionFilterParameter = {
+    id?: Maybe<IdOperators>;
+    createdAt?: Maybe<DateOperators>;
+    updatedAt?: Maybe<DateOperators>;
+    languageCode?: Maybe<StringOperators>;
+    name?: Maybe<StringOperators>;
+    slug?: Maybe<StringOperators>;
+    position?: Maybe<NumberOperators>;
+    description?: Maybe<StringOperators>;
+};
+
+export type CollectionList = PaginatedList & {
+    __typename?: 'CollectionList';
+    items: Array<Collection>;
+    totalItems: Scalars['Int'];
+};
+
+export type CollectionListOptions = {
+    /** Skips the first n results, for use in pagination */
+    skip?: Maybe<Scalars['Int']>;
+    /** Takes n results, for use in pagination */
+    take?: Maybe<Scalars['Int']>;
+    /** Specifies which properties to sort the results by */
+    sort?: Maybe<CollectionSortParameter>;
+    /** Allows the results to be filtered */
+    filter?: Maybe<CollectionFilterParameter>;
+    /** Specifies whether multiple "filter" arguments should be combines with a logical AND or OR operation. Defaults to AND. */
+    filterOperator?: Maybe<LogicalOperator>;
+};
+
+/**
+ * Which Collections are present in the products returned
+ * by the search, and in what quantity.
+ */
+export type CollectionResult = {
+    __typename?: 'CollectionResult';
+    collection: Collection;
+    count: Scalars['Int'];
+};
+
+export type CollectionSortParameter = {
+    id?: Maybe<SortOrder>;
+    createdAt?: Maybe<SortOrder>;
+    updatedAt?: Maybe<SortOrder>;
+    name?: Maybe<SortOrder>;
+    slug?: Maybe<SortOrder>;
+    position?: Maybe<SortOrder>;
+    description?: Maybe<SortOrder>;
+};
+
+export type CollectionTranslation = {
+    __typename?: 'CollectionTranslation';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    slug: Scalars['String'];
+    description: Scalars['String'];
+};
+
+export type ConfigArg = {
+    __typename?: 'ConfigArg';
+    name: Scalars['String'];
+    value: Scalars['String'];
+};
+
+export type ConfigArgDefinition = {
+    __typename?: 'ConfigArgDefinition';
+    name: Scalars['String'];
+    type: Scalars['String'];
+    list: Scalars['Boolean'];
+    required: Scalars['Boolean'];
+    defaultValue?: Maybe<Scalars['JSON']>;
+    label?: Maybe<Scalars['String']>;
+    description?: Maybe<Scalars['String']>;
+    ui?: Maybe<Scalars['JSON']>;
+};
+
+export type ConfigArgInput = {
+    name: Scalars['String'];
+    /** A JSON stringified representation of the actual value */
+    value: Scalars['String'];
+};
+
+export type ConfigurableOperation = {
+    __typename?: 'ConfigurableOperation';
+    code: Scalars['String'];
+    args: Array<ConfigArg>;
+};
+
+export type ConfigurableOperationDefinition = {
+    __typename?: 'ConfigurableOperationDefinition';
+    code: Scalars['String'];
+    args: Array<ConfigArgDefinition>;
+    description: Scalars['String'];
+};
+
+export type ConfigurableOperationInput = {
+    code: Scalars['String'];
+    arguments: Array<ConfigArgInput>;
+};
+
+export type Coordinate = {
+    __typename?: 'Coordinate';
+    x: Scalars['Float'];
+    y: Scalars['Float'];
+};
+
+export type Country = Node & {
+    __typename?: 'Country';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    code: Scalars['String'];
+    name: Scalars['String'];
+    enabled: Scalars['Boolean'];
+    translations: Array<CountryTranslation>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type CountryList = PaginatedList & {
+    __typename?: 'CountryList';
+    items: Array<Country>;
+    totalItems: Scalars['Int'];
+};
+
+export type CountryTranslation = {
+    __typename?: 'CountryTranslation';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+};
+
+/** Returned if the provided coupon code is invalid */
+export type CouponCodeExpiredError = ErrorResult & {
+    __typename?: 'CouponCodeExpiredError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+    couponCode: Scalars['String'];
+};
+
+/** Returned if the provided coupon code is invalid */
+export type CouponCodeInvalidError = ErrorResult & {
+    __typename?: 'CouponCodeInvalidError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+    couponCode: Scalars['String'];
+};
+
+/** Returned if the provided coupon code is invalid */
+export type CouponCodeLimitError = ErrorResult & {
+    __typename?: 'CouponCodeLimitError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+    couponCode: Scalars['String'];
+    limit: Scalars['Int'];
+};
+
+export type CreateAddressInput = {
+    fullName?: Maybe<Scalars['String']>;
+    company?: Maybe<Scalars['String']>;
+    streetLine1: Scalars['String'];
+    streetLine2?: Maybe<Scalars['String']>;
+    city?: Maybe<Scalars['String']>;
+    province?: Maybe<Scalars['String']>;
+    postalCode?: Maybe<Scalars['String']>;
+    countryCode: Scalars['String'];
+    phoneNumber?: Maybe<Scalars['String']>;
+    defaultShippingAddress?: Maybe<Scalars['Boolean']>;
+    defaultBillingAddress?: Maybe<Scalars['Boolean']>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type CreateCustomerInput = {
+    title?: Maybe<Scalars['String']>;
+    firstName: Scalars['String'];
+    lastName: Scalars['String'];
+    phoneNumber?: Maybe<Scalars['String']>;
+    emailAddress: Scalars['String'];
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+/**
+ * @description
+ * ISO 4217 currency code
+ *
+ * @docsCategory common
+ */
+export enum CurrencyCode {
+    /** United Arab Emirates dirham */
+    AED = 'AED',
+    /** Afghan afghani */
+    AFN = 'AFN',
+    /** Albanian lek */
+    ALL = 'ALL',
+    /** Armenian dram */
+    AMD = 'AMD',
+    /** Netherlands Antillean guilder */
+    ANG = 'ANG',
+    /** Angolan kwanza */
+    AOA = 'AOA',
+    /** Argentine peso */
+    ARS = 'ARS',
+    /** Australian dollar */
+    AUD = 'AUD',
+    /** Aruban florin */
+    AWG = 'AWG',
+    /** Azerbaijani manat */
+    AZN = 'AZN',
+    /** Bosnia and Herzegovina convertible mark */
+    BAM = 'BAM',
+    /** Barbados dollar */
+    BBD = 'BBD',
+    /** Bangladeshi taka */
+    BDT = 'BDT',
+    /** Bulgarian lev */
+    BGN = 'BGN',
+    /** Bahraini dinar */
+    BHD = 'BHD',
+    /** Burundian franc */
+    BIF = 'BIF',
+    /** Bermudian dollar */
+    BMD = 'BMD',
+    /** Brunei dollar */
+    BND = 'BND',
+    /** Boliviano */
+    BOB = 'BOB',
+    /** Brazilian real */
+    BRL = 'BRL',
+    /** Bahamian dollar */
+    BSD = 'BSD',
+    /** Bhutanese ngultrum */
+    BTN = 'BTN',
+    /** Botswana pula */
+    BWP = 'BWP',
+    /** Belarusian ruble */
+    BYN = 'BYN',
+    /** Belize dollar */
+    BZD = 'BZD',
+    /** Canadian dollar */
+    CAD = 'CAD',
+    /** Congolese franc */
+    CDF = 'CDF',
+    /** Swiss franc */
+    CHF = 'CHF',
+    /** Chilean peso */
+    CLP = 'CLP',
+    /** Renminbi (Chinese) yuan */
+    CNY = 'CNY',
+    /** Colombian peso */
+    COP = 'COP',
+    /** Costa Rican colon */
+    CRC = 'CRC',
+    /** Cuban convertible peso */
+    CUC = 'CUC',
+    /** Cuban peso */
+    CUP = 'CUP',
+    /** Cape Verde escudo */
+    CVE = 'CVE',
+    /** Czech koruna */
+    CZK = 'CZK',
+    /** Djiboutian franc */
+    DJF = 'DJF',
+    /** Danish krone */
+    DKK = 'DKK',
+    /** Dominican peso */
+    DOP = 'DOP',
+    /** Algerian dinar */
+    DZD = 'DZD',
+    /** Egyptian pound */
+    EGP = 'EGP',
+    /** Eritrean nakfa */
+    ERN = 'ERN',
+    /** Ethiopian birr */
+    ETB = 'ETB',
+    /** Euro */
+    EUR = 'EUR',
+    /** Fiji dollar */
+    FJD = 'FJD',
+    /** Falkland Islands pound */
+    FKP = 'FKP',
+    /** Pound sterling */
+    GBP = 'GBP',
+    /** Georgian lari */
+    GEL = 'GEL',
+    /** Ghanaian cedi */
+    GHS = 'GHS',
+    /** Gibraltar pound */
+    GIP = 'GIP',
+    /** Gambian dalasi */
+    GMD = 'GMD',
+    /** Guinean franc */
+    GNF = 'GNF',
+    /** Guatemalan quetzal */
+    GTQ = 'GTQ',
+    /** Guyanese dollar */
+    GYD = 'GYD',
+    /** Hong Kong dollar */
+    HKD = 'HKD',
+    /** Honduran lempira */
+    HNL = 'HNL',
+    /** Croatian kuna */
+    HRK = 'HRK',
+    /** Haitian gourde */
+    HTG = 'HTG',
+    /** Hungarian forint */
+    HUF = 'HUF',
+    /** Indonesian rupiah */
+    IDR = 'IDR',
+    /** Israeli new shekel */
+    ILS = 'ILS',
+    /** Indian rupee */
+    INR = 'INR',
+    /** Iraqi dinar */
+    IQD = 'IQD',
+    /** Iranian rial */
+    IRR = 'IRR',
+    /** Icelandic króna */
+    ISK = 'ISK',
+    /** Jamaican dollar */
+    JMD = 'JMD',
+    /** Jordanian dinar */
+    JOD = 'JOD',
+    /** Japanese yen */
+    JPY = 'JPY',
+    /** Kenyan shilling */
+    KES = 'KES',
+    /** Kyrgyzstani som */
+    KGS = 'KGS',
+    /** Cambodian riel */
+    KHR = 'KHR',
+    /** Comoro franc */
+    KMF = 'KMF',
+    /** North Korean won */
+    KPW = 'KPW',
+    /** South Korean won */
+    KRW = 'KRW',
+    /** Kuwaiti dinar */
+    KWD = 'KWD',
+    /** Cayman Islands dollar */
+    KYD = 'KYD',
+    /** Kazakhstani tenge */
+    KZT = 'KZT',
+    /** Lao kip */
+    LAK = 'LAK',
+    /** Lebanese pound */
+    LBP = 'LBP',
+    /** Sri Lankan rupee */
+    LKR = 'LKR',
+    /** Liberian dollar */
+    LRD = 'LRD',
+    /** Lesotho loti */
+    LSL = 'LSL',
+    /** Libyan dinar */
+    LYD = 'LYD',
+    /** Moroccan dirham */
+    MAD = 'MAD',
+    /** Moldovan leu */
+    MDL = 'MDL',
+    /** Malagasy ariary */
+    MGA = 'MGA',
+    /** Macedonian denar */
+    MKD = 'MKD',
+    /** Myanmar kyat */
+    MMK = 'MMK',
+    /** Mongolian tögrög */
+    MNT = 'MNT',
+    /** Macanese pataca */
+    MOP = 'MOP',
+    /** Mauritanian ouguiya */
+    MRU = 'MRU',
+    /** Mauritian rupee */
+    MUR = 'MUR',
+    /** Maldivian rufiyaa */
+    MVR = 'MVR',
+    /** Malawian kwacha */
+    MWK = 'MWK',
+    /** Mexican peso */
+    MXN = 'MXN',
+    /** Malaysian ringgit */
+    MYR = 'MYR',
+    /** Mozambican metical */
+    MZN = 'MZN',
+    /** Namibian dollar */
+    NAD = 'NAD',
+    /** Nigerian naira */
+    NGN = 'NGN',
+    /** Nicaraguan córdoba */
+    NIO = 'NIO',
+    /** Norwegian krone */
+    NOK = 'NOK',
+    /** Nepalese rupee */
+    NPR = 'NPR',
+    /** New Zealand dollar */
+    NZD = 'NZD',
+    /** Omani rial */
+    OMR = 'OMR',
+    /** Panamanian balboa */
+    PAB = 'PAB',
+    /** Peruvian sol */
+    PEN = 'PEN',
+    /** Papua New Guinean kina */
+    PGK = 'PGK',
+    /** Philippine peso */
+    PHP = 'PHP',
+    /** Pakistani rupee */
+    PKR = 'PKR',
+    /** Polish złoty */
+    PLN = 'PLN',
+    /** Paraguayan guaraní */
+    PYG = 'PYG',
+    /** Qatari riyal */
+    QAR = 'QAR',
+    /** Romanian leu */
+    RON = 'RON',
+    /** Serbian dinar */
+    RSD = 'RSD',
+    /** Russian ruble */
+    RUB = 'RUB',
+    /** Rwandan franc */
+    RWF = 'RWF',
+    /** Saudi riyal */
+    SAR = 'SAR',
+    /** Solomon Islands dollar */
+    SBD = 'SBD',
+    /** Seychelles rupee */
+    SCR = 'SCR',
+    /** Sudanese pound */
+    SDG = 'SDG',
+    /** Swedish krona/kronor */
+    SEK = 'SEK',
+    /** Singapore dollar */
+    SGD = 'SGD',
+    /** Saint Helena pound */
+    SHP = 'SHP',
+    /** Sierra Leonean leone */
+    SLL = 'SLL',
+    /** Somali shilling */
+    SOS = 'SOS',
+    /** Surinamese dollar */
+    SRD = 'SRD',
+    /** South Sudanese pound */
+    SSP = 'SSP',
+    /** São Tomé and Príncipe dobra */
+    STN = 'STN',
+    /** Salvadoran colón */
+    SVC = 'SVC',
+    /** Syrian pound */
+    SYP = 'SYP',
+    /** Swazi lilangeni */
+    SZL = 'SZL',
+    /** Thai baht */
+    THB = 'THB',
+    /** Tajikistani somoni */
+    TJS = 'TJS',
+    /** Turkmenistan manat */
+    TMT = 'TMT',
+    /** Tunisian dinar */
+    TND = 'TND',
+    /** Tongan paʻanga */
+    TOP = 'TOP',
+    /** Turkish lira */
+    TRY = 'TRY',
+    /** Trinidad and Tobago dollar */
+    TTD = 'TTD',
+    /** New Taiwan dollar */
+    TWD = 'TWD',
+    /** Tanzanian shilling */
+    TZS = 'TZS',
+    /** Ukrainian hryvnia */
+    UAH = 'UAH',
+    /** Ugandan shilling */
+    UGX = 'UGX',
+    /** United States dollar */
+    USD = 'USD',
+    /** Uruguayan peso */
+    UYU = 'UYU',
+    /** Uzbekistan som */
+    UZS = 'UZS',
+    /** Venezuelan bolívar soberano */
+    VES = 'VES',
+    /** Vietnamese đồng */
+    VND = 'VND',
+    /** Vanuatu vatu */
+    VUV = 'VUV',
+    /** Samoan tala */
+    WST = 'WST',
+    /** CFA franc BEAC */
+    XAF = 'XAF',
+    /** East Caribbean dollar */
+    XCD = 'XCD',
+    /** CFA franc BCEAO */
+    XOF = 'XOF',
+    /** CFP franc (franc Pacifique) */
+    XPF = 'XPF',
+    /** Yemeni rial */
+    YER = 'YER',
+    /** South African rand */
+    ZAR = 'ZAR',
+    /** Zambian kwacha */
+    ZMW = 'ZMW',
+    /** Zimbabwean dollar */
+    ZWL = 'ZWL',
+}
+
+export type CurrentUser = {
+    __typename?: 'CurrentUser';
+    id: Scalars['ID'];
+    identifier: Scalars['String'];
+    channels: Array<CurrentUserChannel>;
+};
+
+export type CurrentUserChannel = {
+    __typename?: 'CurrentUserChannel';
+    id: Scalars['ID'];
+    token: Scalars['String'];
+    code: Scalars['String'];
+    permissions: Array<Permission>;
+};
+
+export type CustomField = {
+    name: Scalars['String'];
+    type: Scalars['String'];
+    list: Scalars['Boolean'];
+    label?: Maybe<Array<LocalizedString>>;
+    description?: Maybe<Array<LocalizedString>>;
+    readonly?: Maybe<Scalars['Boolean']>;
+    internal?: Maybe<Scalars['Boolean']>;
+    nullable?: Maybe<Scalars['Boolean']>;
+    ui?: Maybe<Scalars['JSON']>;
+};
+
+export type CustomFieldConfig =
+    | StringCustomFieldConfig
+    | LocaleStringCustomFieldConfig
+    | IntCustomFieldConfig
+    | FloatCustomFieldConfig
+    | BooleanCustomFieldConfig
+    | DateTimeCustomFieldConfig
+    | RelationCustomFieldConfig
+    | TextCustomFieldConfig;
+
+export type Customer = Node & {
+    __typename?: 'Customer';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    title?: Maybe<Scalars['String']>;
+    firstName: Scalars['String'];
+    lastName: Scalars['String'];
+    phoneNumber?: Maybe<Scalars['String']>;
+    emailAddress: Scalars['String'];
+    addresses?: Maybe<Array<Address>>;
+    orders: OrderList;
+    user?: Maybe<User>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type CustomerOrdersArgs = {
+    options?: Maybe<OrderListOptions>;
+};
+
+export type CustomerFilterParameter = {
+    id?: Maybe<IdOperators>;
+    createdAt?: Maybe<DateOperators>;
+    updatedAt?: Maybe<DateOperators>;
+    title?: Maybe<StringOperators>;
+    firstName?: Maybe<StringOperators>;
+    lastName?: Maybe<StringOperators>;
+    phoneNumber?: Maybe<StringOperators>;
+    emailAddress?: Maybe<StringOperators>;
+};
+
+export type CustomerGroup = Node & {
+    __typename?: 'CustomerGroup';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    name: Scalars['String'];
+    customers: CustomerList;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type CustomerGroupCustomersArgs = {
+    options?: Maybe<CustomerListOptions>;
+};
+
+export type CustomerList = PaginatedList & {
+    __typename?: 'CustomerList';
+    items: Array<Customer>;
+    totalItems: Scalars['Int'];
+};
+
+export type CustomerListOptions = {
+    /** Skips the first n results, for use in pagination */
+    skip?: Maybe<Scalars['Int']>;
+    /** Takes n results, for use in pagination */
+    take?: Maybe<Scalars['Int']>;
+    /** Specifies which properties to sort the results by */
+    sort?: Maybe<CustomerSortParameter>;
+    /** Allows the results to be filtered */
+    filter?: Maybe<CustomerFilterParameter>;
+    /** Specifies whether multiple "filter" arguments should be combines with a logical AND or OR operation. Defaults to AND. */
+    filterOperator?: Maybe<LogicalOperator>;
+};
+
+export type CustomerSortParameter = {
+    id?: Maybe<SortOrder>;
+    createdAt?: Maybe<SortOrder>;
+    updatedAt?: Maybe<SortOrder>;
+    title?: Maybe<SortOrder>;
+    firstName?: Maybe<SortOrder>;
+    lastName?: Maybe<SortOrder>;
+    phoneNumber?: Maybe<SortOrder>;
+    emailAddress?: Maybe<SortOrder>;
+};
+
+/** Operators for filtering on a list of Date fields */
+export type DateListOperators = {
+    inList: Scalars['DateTime'];
+};
+
+/** Operators for filtering on a DateTime field */
+export type DateOperators = {
+    eq?: Maybe<Scalars['DateTime']>;
+    before?: Maybe<Scalars['DateTime']>;
+    after?: Maybe<Scalars['DateTime']>;
+    between?: Maybe<DateRange>;
+};
+
+export type DateRange = {
+    start: Scalars['DateTime'];
+    end: Scalars['DateTime'];
+};
+
+/**
+ * Expects the same validation formats as the `<input type="datetime-local">` HTML element.
+ * See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local#Additional_attributes
+ */
+export type DateTimeCustomFieldConfig = CustomField & {
+    __typename?: 'DateTimeCustomFieldConfig';
+    name: Scalars['String'];
+    type: Scalars['String'];
+    list: Scalars['Boolean'];
+    label?: Maybe<Array<LocalizedString>>;
+    description?: Maybe<Array<LocalizedString>>;
+    readonly?: Maybe<Scalars['Boolean']>;
+    internal?: Maybe<Scalars['Boolean']>;
+    nullable?: Maybe<Scalars['Boolean']>;
+    min?: Maybe<Scalars['String']>;
+    max?: Maybe<Scalars['String']>;
+    step?: Maybe<Scalars['Int']>;
+    ui?: Maybe<Scalars['JSON']>;
+};
+
+export type DeletionResponse = {
+    __typename?: 'DeletionResponse';
+    result: DeletionResult;
+    message?: Maybe<Scalars['String']>;
+};
+
+export enum DeletionResult {
+    /** The entity was successfully deleted */
+    DELETED = 'DELETED',
+    /** Deletion did not take place, reason given in message */
+    NOT_DELETED = 'NOT_DELETED',
+}
+
+export type Discount = {
+    __typename?: 'Discount';
+    adjustmentSource: Scalars['String'];
+    type: AdjustmentType;
+    description: Scalars['String'];
+    amount: Scalars['Int'];
+    amountWithTax: Scalars['Int'];
+};
+
+/** Returned when attempting to create a Customer with an email address already registered to an existing User. */
+export type EmailAddressConflictError = ErrorResult & {
+    __typename?: 'EmailAddressConflictError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+export enum ErrorCode {
+    UNKNOWN_ERROR = 'UNKNOWN_ERROR',
+    NATIVE_AUTH_STRATEGY_ERROR = 'NATIVE_AUTH_STRATEGY_ERROR',
+    INVALID_CREDENTIALS_ERROR = 'INVALID_CREDENTIALS_ERROR',
+    ORDER_STATE_TRANSITION_ERROR = 'ORDER_STATE_TRANSITION_ERROR',
+    EMAIL_ADDRESS_CONFLICT_ERROR = 'EMAIL_ADDRESS_CONFLICT_ERROR',
+    ORDER_LIMIT_ERROR = 'ORDER_LIMIT_ERROR',
+    NEGATIVE_QUANTITY_ERROR = 'NEGATIVE_QUANTITY_ERROR',
+    INSUFFICIENT_STOCK_ERROR = 'INSUFFICIENT_STOCK_ERROR',
+    ORDER_MODIFICATION_ERROR = 'ORDER_MODIFICATION_ERROR',
+    INELIGIBLE_SHIPPING_METHOD_ERROR = 'INELIGIBLE_SHIPPING_METHOD_ERROR',
+    ORDER_PAYMENT_STATE_ERROR = 'ORDER_PAYMENT_STATE_ERROR',
+    INELIGIBLE_PAYMENT_METHOD_ERROR = 'INELIGIBLE_PAYMENT_METHOD_ERROR',
+    PAYMENT_FAILED_ERROR = 'PAYMENT_FAILED_ERROR',
+    PAYMENT_DECLINED_ERROR = 'PAYMENT_DECLINED_ERROR',
+    COUPON_CODE_INVALID_ERROR = 'COUPON_CODE_INVALID_ERROR',
+    COUPON_CODE_EXPIRED_ERROR = 'COUPON_CODE_EXPIRED_ERROR',
+    COUPON_CODE_LIMIT_ERROR = 'COUPON_CODE_LIMIT_ERROR',
+    ALREADY_LOGGED_IN_ERROR = 'ALREADY_LOGGED_IN_ERROR',
+    MISSING_PASSWORD_ERROR = 'MISSING_PASSWORD_ERROR',
+    PASSWORD_VALIDATION_ERROR = 'PASSWORD_VALIDATION_ERROR',
+    PASSWORD_ALREADY_SET_ERROR = 'PASSWORD_ALREADY_SET_ERROR',
+    VERIFICATION_TOKEN_INVALID_ERROR = 'VERIFICATION_TOKEN_INVALID_ERROR',
+    VERIFICATION_TOKEN_EXPIRED_ERROR = 'VERIFICATION_TOKEN_EXPIRED_ERROR',
+    IDENTIFIER_CHANGE_TOKEN_INVALID_ERROR = 'IDENTIFIER_CHANGE_TOKEN_INVALID_ERROR',
+    IDENTIFIER_CHANGE_TOKEN_EXPIRED_ERROR = 'IDENTIFIER_CHANGE_TOKEN_EXPIRED_ERROR',
+    PASSWORD_RESET_TOKEN_INVALID_ERROR = 'PASSWORD_RESET_TOKEN_INVALID_ERROR',
+    PASSWORD_RESET_TOKEN_EXPIRED_ERROR = 'PASSWORD_RESET_TOKEN_EXPIRED_ERROR',
+    NOT_VERIFIED_ERROR = 'NOT_VERIFIED_ERROR',
+    NO_ACTIVE_ORDER_ERROR = 'NO_ACTIVE_ORDER_ERROR',
+}
+
+export type ErrorResult = {
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+export type Facet = Node & {
+    __typename?: 'Facet';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    code: Scalars['String'];
+    values: Array<FacetValue>;
+    translations: Array<FacetTranslation>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type FacetFilterParameter = {
+    id?: Maybe<IdOperators>;
+    createdAt?: Maybe<DateOperators>;
+    updatedAt?: Maybe<DateOperators>;
+    languageCode?: Maybe<StringOperators>;
+    name?: Maybe<StringOperators>;
+    code?: Maybe<StringOperators>;
+};
+
+export type FacetList = PaginatedList & {
+    __typename?: 'FacetList';
+    items: Array<Facet>;
+    totalItems: Scalars['Int'];
+};
+
+export type FacetListOptions = {
+    /** Skips the first n results, for use in pagination */
+    skip?: Maybe<Scalars['Int']>;
+    /** Takes n results, for use in pagination */
+    take?: Maybe<Scalars['Int']>;
+    /** Specifies which properties to sort the results by */
+    sort?: Maybe<FacetSortParameter>;
+    /** Allows the results to be filtered */
+    filter?: Maybe<FacetFilterParameter>;
+    /** Specifies whether multiple "filter" arguments should be combines with a logical AND or OR operation. Defaults to AND. */
+    filterOperator?: Maybe<LogicalOperator>;
+};
+
+export type FacetSortParameter = {
+    id?: Maybe<SortOrder>;
+    createdAt?: Maybe<SortOrder>;
+    updatedAt?: Maybe<SortOrder>;
+    name?: Maybe<SortOrder>;
+    code?: Maybe<SortOrder>;
+};
+
+export type FacetTranslation = {
+    __typename?: 'FacetTranslation';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+};
+
+export type FacetValue = Node & {
+    __typename?: 'FacetValue';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    facet: Facet;
+    name: Scalars['String'];
+    code: Scalars['String'];
+    translations: Array<FacetValueTranslation>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+/**
+ * Used to construct boolean expressions for filtering search results
+ * by FacetValue ID. Examples:
+ *
+ * * ID=1 OR ID=2: `{ facetValueFilters: [{ or: [1,2] }] }`
+ * * ID=1 AND ID=2: `{ facetValueFilters: [{ and: 1 }, { and: 2 }] }`
+ * * ID=1 AND (ID=2 OR ID=3): `{ facetValueFilters: [{ and: 1 }, { or: [2,3] }] }`
+ */
+export type FacetValueFilterInput = {
+    and?: Maybe<Scalars['ID']>;
+    or?: Maybe<Array<Scalars['ID']>>;
+};
+
+/**
+ * Which FacetValues are present in the products returned
+ * by the search, and in what quantity.
+ */
+export type FacetValueResult = {
+    __typename?: 'FacetValueResult';
+    facetValue: FacetValue;
+    count: Scalars['Int'];
+};
+
+export type FacetValueTranslation = {
+    __typename?: 'FacetValueTranslation';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+};
+
+export type FloatCustomFieldConfig = CustomField & {
+    __typename?: 'FloatCustomFieldConfig';
+    name: Scalars['String'];
+    type: Scalars['String'];
+    list: Scalars['Boolean'];
+    label?: Maybe<Array<LocalizedString>>;
+    description?: Maybe<Array<LocalizedString>>;
+    readonly?: Maybe<Scalars['Boolean']>;
+    internal?: Maybe<Scalars['Boolean']>;
+    nullable?: Maybe<Scalars['Boolean']>;
+    min?: Maybe<Scalars['Float']>;
+    max?: Maybe<Scalars['Float']>;
+    step?: Maybe<Scalars['Float']>;
+    ui?: Maybe<Scalars['JSON']>;
+};
+
+export type Fulfillment = Node & {
+    __typename?: 'Fulfillment';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    orderItems: Array<OrderItem>;
+    state: Scalars['String'];
+    method: Scalars['String'];
+    trackingCode?: Maybe<Scalars['String']>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export enum GlobalFlag {
+    TRUE = 'TRUE',
+    FALSE = 'FALSE',
+    INHERIT = 'INHERIT',
+}
+
+export type HistoryEntry = Node & {
+    __typename?: 'HistoryEntry';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    type: HistoryEntryType;
+    data: Scalars['JSON'];
+};
+
+export type HistoryEntryFilterParameter = {
+    id?: Maybe<IdOperators>;
+    createdAt?: Maybe<DateOperators>;
+    updatedAt?: Maybe<DateOperators>;
+    type?: Maybe<StringOperators>;
+};
+
+export type HistoryEntryList = PaginatedList & {
+    __typename?: 'HistoryEntryList';
+    items: Array<HistoryEntry>;
+    totalItems: Scalars['Int'];
+};
+
+export type HistoryEntryListOptions = {
+    /** Skips the first n results, for use in pagination */
+    skip?: Maybe<Scalars['Int']>;
+    /** Takes n results, for use in pagination */
+    take?: Maybe<Scalars['Int']>;
+    /** Specifies which properties to sort the results by */
+    sort?: Maybe<HistoryEntrySortParameter>;
+    /** Allows the results to be filtered */
+    filter?: Maybe<HistoryEntryFilterParameter>;
+    /** Specifies whether multiple "filter" arguments should be combines with a logical AND or OR operation. Defaults to AND. */
+    filterOperator?: Maybe<LogicalOperator>;
+};
+
+export type HistoryEntrySortParameter = {
+    id?: Maybe<SortOrder>;
+    createdAt?: Maybe<SortOrder>;
+    updatedAt?: Maybe<SortOrder>;
+};
+
+export enum HistoryEntryType {
+    CUSTOMER_REGISTERED = 'CUSTOMER_REGISTERED',
+    CUSTOMER_VERIFIED = 'CUSTOMER_VERIFIED',
+    CUSTOMER_DETAIL_UPDATED = 'CUSTOMER_DETAIL_UPDATED',
+    CUSTOMER_ADDED_TO_GROUP = 'CUSTOMER_ADDED_TO_GROUP',
+    CUSTOMER_REMOVED_FROM_GROUP = 'CUSTOMER_REMOVED_FROM_GROUP',
+    CUSTOMER_ADDRESS_CREATED = 'CUSTOMER_ADDRESS_CREATED',
+    CUSTOMER_ADDRESS_UPDATED = 'CUSTOMER_ADDRESS_UPDATED',
+    CUSTOMER_ADDRESS_DELETED = 'CUSTOMER_ADDRESS_DELETED',
+    CUSTOMER_PASSWORD_UPDATED = 'CUSTOMER_PASSWORD_UPDATED',
+    CUSTOMER_PASSWORD_RESET_REQUESTED = 'CUSTOMER_PASSWORD_RESET_REQUESTED',
+    CUSTOMER_PASSWORD_RESET_VERIFIED = 'CUSTOMER_PASSWORD_RESET_VERIFIED',
+    CUSTOMER_EMAIL_UPDATE_REQUESTED = 'CUSTOMER_EMAIL_UPDATE_REQUESTED',
+    CUSTOMER_EMAIL_UPDATE_VERIFIED = 'CUSTOMER_EMAIL_UPDATE_VERIFIED',
+    CUSTOMER_NOTE = 'CUSTOMER_NOTE',
+    ORDER_STATE_TRANSITION = 'ORDER_STATE_TRANSITION',
+    ORDER_PAYMENT_TRANSITION = 'ORDER_PAYMENT_TRANSITION',
+    ORDER_FULFILLMENT = 'ORDER_FULFILLMENT',
+    ORDER_CANCELLATION = 'ORDER_CANCELLATION',
+    ORDER_REFUND_TRANSITION = 'ORDER_REFUND_TRANSITION',
+    ORDER_FULFILLMENT_TRANSITION = 'ORDER_FULFILLMENT_TRANSITION',
+    ORDER_NOTE = 'ORDER_NOTE',
+    ORDER_COUPON_APPLIED = 'ORDER_COUPON_APPLIED',
+    ORDER_COUPON_REMOVED = 'ORDER_COUPON_REMOVED',
+    ORDER_MODIFIED = 'ORDER_MODIFIED',
+}
+
+/** Operators for filtering on a list of ID fields */
+export type IdListOperators = {
+    inList: Scalars['ID'];
+};
+
+/** Operators for filtering on an ID field */
+export type IdOperators = {
+    eq?: Maybe<Scalars['String']>;
+    notEq?: Maybe<Scalars['String']>;
+    in?: Maybe<Array<Scalars['String']>>;
+    notIn?: Maybe<Array<Scalars['String']>>;
+};
+
+/**
+ * Returned if the token used to change a Customer's email address is valid, but has
+ * expired according to the `verificationTokenDuration` setting in the AuthOptions.
+ */
+export type IdentifierChangeTokenExpiredError = ErrorResult & {
+    __typename?: 'IdentifierChangeTokenExpiredError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+/**
+ * Returned if the token used to change a Customer's email address is either
+ * invalid or does not match any expected tokens.
+ */
+export type IdentifierChangeTokenInvalidError = ErrorResult & {
+    __typename?: 'IdentifierChangeTokenInvalidError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+/** Returned when attempting to add a Payment using a PaymentMethod for which the Order is not eligible. */
+export type IneligiblePaymentMethodError = ErrorResult & {
+    __typename?: 'IneligiblePaymentMethodError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+    eligibilityCheckerMessage?: Maybe<Scalars['String']>;
+};
+
+/** Returned when attempting to set a ShippingMethod for which the Order is not eligible */
+export type IneligibleShippingMethodError = ErrorResult & {
+    __typename?: 'IneligibleShippingMethodError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+/** Returned when attempting to add more items to the Order than are available */
+export type InsufficientStockError = ErrorResult & {
+    __typename?: 'InsufficientStockError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+    quantityAvailable: Scalars['Int'];
+    order: Order;
+};
+
+export type IntCustomFieldConfig = CustomField & {
+    __typename?: 'IntCustomFieldConfig';
+    name: Scalars['String'];
+    type: Scalars['String'];
+    list: Scalars['Boolean'];
+    label?: Maybe<Array<LocalizedString>>;
+    description?: Maybe<Array<LocalizedString>>;
+    readonly?: Maybe<Scalars['Boolean']>;
+    internal?: Maybe<Scalars['Boolean']>;
+    nullable?: Maybe<Scalars['Boolean']>;
+    min?: Maybe<Scalars['Int']>;
+    max?: Maybe<Scalars['Int']>;
+    step?: Maybe<Scalars['Int']>;
+    ui?: Maybe<Scalars['JSON']>;
+};
+
+/** Returned if the user authentication credentials are not valid */
+export type InvalidCredentialsError = ErrorResult & {
+    __typename?: 'InvalidCredentialsError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+    authenticationError: Scalars['String'];
+};
+
+/**
+ * @description
+ * Languages in the form of a ISO 639-1 language code with optional
+ * 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)
+ * and includes the major spoken languages of the world and any widely-used variants.
+ *
+ * @docsCategory common
+ */
+export enum LanguageCode {
+    /** Afrikaans */
+    af = 'af',
+    /** Akan */
+    ak = 'ak',
+    /** Albanian */
+    sq = 'sq',
+    /** Amharic */
+    am = 'am',
+    /** Arabic */
+    ar = 'ar',
+    /** Armenian */
+    hy = 'hy',
+    /** Assamese */
+    as = 'as',
+    /** Azerbaijani */
+    az = 'az',
+    /** Bambara */
+    bm = 'bm',
+    /** Bangla */
+    bn = 'bn',
+    /** Basque */
+    eu = 'eu',
+    /** Belarusian */
+    be = 'be',
+    /** Bosnian */
+    bs = 'bs',
+    /** Breton */
+    br = 'br',
+    /** Bulgarian */
+    bg = 'bg',
+    /** Burmese */
+    my = 'my',
+    /** Catalan */
+    ca = 'ca',
+    /** Chechen */
+    ce = 'ce',
+    /** Chinese */
+    zh = 'zh',
+    /** Simplified Chinese */
+    zh_Hans = 'zh_Hans',
+    /** Traditional Chinese */
+    zh_Hant = 'zh_Hant',
+    /** Church Slavic */
+    cu = 'cu',
+    /** Cornish */
+    kw = 'kw',
+    /** Corsican */
+    co = 'co',
+    /** Croatian */
+    hr = 'hr',
+    /** Czech */
+    cs = 'cs',
+    /** Danish */
+    da = 'da',
+    /** Dutch */
+    nl = 'nl',
+    /** Flemish */
+    nl_BE = 'nl_BE',
+    /** Dzongkha */
+    dz = 'dz',
+    /** English */
+    en = 'en',
+    /** Australian English */
+    en_AU = 'en_AU',
+    /** Canadian English */
+    en_CA = 'en_CA',
+    /** British English */
+    en_GB = 'en_GB',
+    /** American English */
+    en_US = 'en_US',
+    /** Esperanto */
+    eo = 'eo',
+    /** Estonian */
+    et = 'et',
+    /** Ewe */
+    ee = 'ee',
+    /** Faroese */
+    fo = 'fo',
+    /** Finnish */
+    fi = 'fi',
+    /** French */
+    fr = 'fr',
+    /** Canadian French */
+    fr_CA = 'fr_CA',
+    /** Swiss French */
+    fr_CH = 'fr_CH',
+    /** Fulah */
+    ff = 'ff',
+    /** Galician */
+    gl = 'gl',
+    /** Ganda */
+    lg = 'lg',
+    /** Georgian */
+    ka = 'ka',
+    /** German */
+    de = 'de',
+    /** Austrian German */
+    de_AT = 'de_AT',
+    /** Swiss High German */
+    de_CH = 'de_CH',
+    /** Greek */
+    el = 'el',
+    /** Gujarati */
+    gu = 'gu',
+    /** Haitian Creole */
+    ht = 'ht',
+    /** Hausa */
+    ha = 'ha',
+    /** Hebrew */
+    he = 'he',
+    /** Hindi */
+    hi = 'hi',
+    /** Hungarian */
+    hu = 'hu',
+    /** Icelandic */
+    is = 'is',
+    /** Igbo */
+    ig = 'ig',
+    /** Indonesian */
+    id = 'id',
+    /** Interlingua */
+    ia = 'ia',
+    /** Irish */
+    ga = 'ga',
+    /** Italian */
+    it = 'it',
+    /** Japanese */
+    ja = 'ja',
+    /** Javanese */
+    jv = 'jv',
+    /** Kalaallisut */
+    kl = 'kl',
+    /** Kannada */
+    kn = 'kn',
+    /** Kashmiri */
+    ks = 'ks',
+    /** Kazakh */
+    kk = 'kk',
+    /** Khmer */
+    km = 'km',
+    /** Kikuyu */
+    ki = 'ki',
+    /** Kinyarwanda */
+    rw = 'rw',
+    /** Korean */
+    ko = 'ko',
+    /** Kurdish */
+    ku = 'ku',
+    /** Kyrgyz */
+    ky = 'ky',
+    /** Lao */
+    lo = 'lo',
+    /** Latin */
+    la = 'la',
+    /** Latvian */
+    lv = 'lv',
+    /** Lingala */
+    ln = 'ln',
+    /** Lithuanian */
+    lt = 'lt',
+    /** Luba-Katanga */
+    lu = 'lu',
+    /** Luxembourgish */
+    lb = 'lb',
+    /** Macedonian */
+    mk = 'mk',
+    /** Malagasy */
+    mg = 'mg',
+    /** Malay */
+    ms = 'ms',
+    /** Malayalam */
+    ml = 'ml',
+    /** Maltese */
+    mt = 'mt',
+    /** Manx */
+    gv = 'gv',
+    /** Maori */
+    mi = 'mi',
+    /** Marathi */
+    mr = 'mr',
+    /** Mongolian */
+    mn = 'mn',
+    /** Nepali */
+    ne = 'ne',
+    /** North Ndebele */
+    nd = 'nd',
+    /** Northern Sami */
+    se = 'se',
+    /** Norwegian Bokmål */
+    nb = 'nb',
+    /** Norwegian Nynorsk */
+    nn = 'nn',
+    /** Nyanja */
+    ny = 'ny',
+    /** Odia */
+    or = 'or',
+    /** Oromo */
+    om = 'om',
+    /** Ossetic */
+    os = 'os',
+    /** Pashto */
+    ps = 'ps',
+    /** Persian */
+    fa = 'fa',
+    /** Dari */
+    fa_AF = 'fa_AF',
+    /** Polish */
+    pl = 'pl',
+    /** Portuguese */
+    pt = 'pt',
+    /** Brazilian Portuguese */
+    pt_BR = 'pt_BR',
+    /** European Portuguese */
+    pt_PT = 'pt_PT',
+    /** Punjabi */
+    pa = 'pa',
+    /** Quechua */
+    qu = 'qu',
+    /** Romanian */
+    ro = 'ro',
+    /** Moldavian */
+    ro_MD = 'ro_MD',
+    /** Romansh */
+    rm = 'rm',
+    /** Rundi */
+    rn = 'rn',
+    /** Russian */
+    ru = 'ru',
+    /** Samoan */
+    sm = 'sm',
+    /** Sango */
+    sg = 'sg',
+    /** Sanskrit */
+    sa = 'sa',
+    /** Scottish Gaelic */
+    gd = 'gd',
+    /** Serbian */
+    sr = 'sr',
+    /** Shona */
+    sn = 'sn',
+    /** Sichuan Yi */
+    ii = 'ii',
+    /** Sindhi */
+    sd = 'sd',
+    /** Sinhala */
+    si = 'si',
+    /** Slovak */
+    sk = 'sk',
+    /** Slovenian */
+    sl = 'sl',
+    /** Somali */
+    so = 'so',
+    /** Southern Sotho */
+    st = 'st',
+    /** Spanish */
+    es = 'es',
+    /** European Spanish */
+    es_ES = 'es_ES',
+    /** Mexican Spanish */
+    es_MX = 'es_MX',
+    /** Sundanese */
+    su = 'su',
+    /** Swahili */
+    sw = 'sw',
+    /** Congo Swahili */
+    sw_CD = 'sw_CD',
+    /** Swedish */
+    sv = 'sv',
+    /** Tajik */
+    tg = 'tg',
+    /** Tamil */
+    ta = 'ta',
+    /** Tatar */
+    tt = 'tt',
+    /** Telugu */
+    te = 'te',
+    /** Thai */
+    th = 'th',
+    /** Tibetan */
+    bo = 'bo',
+    /** Tigrinya */
+    ti = 'ti',
+    /** Tongan */
+    to = 'to',
+    /** Turkish */
+    tr = 'tr',
+    /** Turkmen */
+    tk = 'tk',
+    /** Ukrainian */
+    uk = 'uk',
+    /** Urdu */
+    ur = 'ur',
+    /** Uyghur */
+    ug = 'ug',
+    /** Uzbek */
+    uz = 'uz',
+    /** Vietnamese */
+    vi = 'vi',
+    /** Volapük */
+    vo = 'vo',
+    /** Welsh */
+    cy = 'cy',
+    /** Western Frisian */
+    fy = 'fy',
+    /** Wolof */
+    wo = 'wo',
+    /** Xhosa */
+    xh = 'xh',
+    /** Yiddish */
+    yi = 'yi',
+    /** Yoruba */
+    yo = 'yo',
+    /** Zulu */
+    zu = 'zu',
+}
+
+export type LocaleStringCustomFieldConfig = CustomField & {
+    __typename?: 'LocaleStringCustomFieldConfig';
+    name: Scalars['String'];
+    type: Scalars['String'];
+    list: Scalars['Boolean'];
+    length?: Maybe<Scalars['Int']>;
+    label?: Maybe<Array<LocalizedString>>;
+    description?: Maybe<Array<LocalizedString>>;
+    readonly?: Maybe<Scalars['Boolean']>;
+    internal?: Maybe<Scalars['Boolean']>;
+    nullable?: Maybe<Scalars['Boolean']>;
+    pattern?: Maybe<Scalars['String']>;
+    ui?: Maybe<Scalars['JSON']>;
+};
+
+export type LocalizedString = {
+    __typename?: 'LocalizedString';
+    languageCode: LanguageCode;
+    value: Scalars['String'];
+};
+
+export enum LogicalOperator {
+    AND = 'AND',
+    OR = 'OR',
+}
+
+/** Returned when attempting to register or verify a customer account without a password, when one is required. */
+export type MissingPasswordError = ErrorResult & {
+    __typename?: 'MissingPasswordError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+export type MolliePaymentIntent = {
+    __typename?: 'MolliePaymentIntent';
+    url: Scalars['String'];
+};
+
+export type MolliePaymentIntentError = ErrorResult & {
+    __typename?: 'MolliePaymentIntentError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+export type MolliePaymentIntentInput = {
+    paymentMethodCode: Scalars['String'];
+};
+
+export type MolliePaymentIntentResult = MolliePaymentIntent | MolliePaymentIntentError;
+
+export type Mutation = {
+    __typename?: 'Mutation';
+    /** Adds an item to the order. If custom fields are defined on the OrderLine entity, a third argument 'customFields' will be available. */
+    addItemToOrder: UpdateOrderItemsResult;
+    /** Add a Payment to the Order */
+    addPaymentToOrder: AddPaymentToOrderResult;
+    /** Adjusts an OrderLine. If custom fields are defined on the OrderLine entity, a third argument 'customFields' of type `OrderLineCustomFieldsInput` will be available. */
+    adjustOrderLine: UpdateOrderItemsResult;
+    /** Applies the given coupon code to the active Order */
+    applyCouponCode: ApplyCouponCodeResult;
+    /** Authenticates the user using a named authentication strategy */
+    authenticate: AuthenticationResult;
+    /** Create a new Customer Address */
+    createCustomerAddress: Address;
+    createMolliePaymentIntent: MolliePaymentIntentResult;
+    /** Delete an existing Address */
+    deleteCustomerAddress: Success;
+    /** Authenticates the user using the native authentication strategy. This mutation is an alias for `authenticate({ native: { ... }})` */
+    login: NativeAuthenticationResult;
+    /** End the current authenticated session */
+    logout: Success;
+    /** Regenerate and send a verification token for a new Customer registration. Only applicable if `authOptions.requireVerification` is set to true. */
+    refreshCustomerVerification: RefreshCustomerVerificationResult;
+    /**
+     * Register a Customer account with the given credentials. There are three possible registration flows:
+     *
+     * _If `authOptions.requireVerification` is set to `true`:_
+     *
+     * 1. **The Customer is registered _with_ a password**. A verificationToken will be created (and typically emailed to the Customer). That
+     *    verificationToken would then be passed to the `verifyCustomerAccount` mutation _without_ a password. The Customer is then
+     *    verified and authenticated in one step.
+     * 2. **The Customer is registered _without_ a password**. A verificationToken will be created (and typically emailed to the Customer). That
+     *    verificationToken would then be passed to the `verifyCustomerAccount` mutation _with_ the chosen password of the Customer. The Customer is then
+     *    verified and authenticated in one step.
+     *
+     * _If `authOptions.requireVerification` is set to `false`:_
+     *
+     * 3. The Customer _must_ be registered _with_ a password. No further action is needed - the Customer is able to authenticate immediately.
+     */
+    registerCustomerAccount: RegisterCustomerAccountResult;
+    /** Remove all OrderLine from the Order */
+    removeAllOrderLines: RemoveOrderItemsResult;
+    /** Removes the given coupon code from the active Order */
+    removeCouponCode?: Maybe<Order>;
+    /** Remove an OrderLine from the Order */
+    removeOrderLine: RemoveOrderItemsResult;
+    /** Requests a password reset email to be sent */
+    requestPasswordReset?: Maybe<RequestPasswordResetResult>;
+    /**
+     * Request to update the emailAddress of the active Customer. If `authOptions.requireVerification` is enabled
+     * (as is the default), then the `identifierChangeToken` will be assigned to the current User and
+     * a IdentifierChangeRequestEvent will be raised. This can then be used e.g. by the EmailPlugin to email
+     * that verification token to the Customer, which is then used to verify the change of email address.
+     */
+    requestUpdateCustomerEmailAddress: RequestUpdateCustomerEmailAddressResult;
+    /** Resets a Customer's password based on the provided token */
+    resetPassword: ResetPasswordResult;
+    /** Set the Customer for the Order. Required only if the Customer is not currently logged in */
+    setCustomerForOrder: SetCustomerForOrderResult;
+    /** Sets the billing address for this order */
+    setOrderBillingAddress: ActiveOrderResult;
+    /** Allows any custom fields to be set for the active order */
+    setOrderCustomFields: ActiveOrderResult;
+    /** Sets the shipping address for this order */
+    setOrderShippingAddress: ActiveOrderResult;
+    /** Sets the shipping method by id, which can be obtained with the `eligibleShippingMethods` query */
+    setOrderShippingMethod: SetOrderShippingMethodResult;
+    /** Transitions an Order to a new state. Valid next states can be found by querying `nextOrderStates` */
+    transitionOrderToState?: Maybe<TransitionOrderToStateResult>;
+    /** Update an existing Customer */
+    updateCustomer: Customer;
+    /** Update an existing Address */
+    updateCustomerAddress: Address;
+    /**
+     * Confirm the update of the emailAddress with the provided token, which has been generated by the
+     * `requestUpdateCustomerEmailAddress` mutation.
+     */
+    updateCustomerEmailAddress: UpdateCustomerEmailAddressResult;
+    /** Update the password of the active Customer */
+    updateCustomerPassword: UpdateCustomerPasswordResult;
+    /**
+     * Verify a Customer email address with the token sent to that address. Only applicable if `authOptions.requireVerification` is set to true.
+     *
+     * If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the password _must_ be
+     * provided here.
+     */
+    verifyCustomerAccount: VerifyCustomerAccountResult;
+};
+
+export type MutationAddItemToOrderArgs = {
+    productVariantId: Scalars['ID'];
+    quantity: Scalars['Int'];
+};
+
+export type MutationAddPaymentToOrderArgs = {
+    input: PaymentInput;
+};
+
+export type MutationAdjustOrderLineArgs = {
+    orderLineId: Scalars['ID'];
+    quantity: Scalars['Int'];
+};
+
+export type MutationApplyCouponCodeArgs = {
+    couponCode: Scalars['String'];
+};
+
+export type MutationAuthenticateArgs = {
+    input: AuthenticationInput;
+    rememberMe?: Maybe<Scalars['Boolean']>;
+};
+
+export type MutationCreateCustomerAddressArgs = {
+    input: CreateAddressInput;
+};
+
+export type MutationCreateMolliePaymentIntentArgs = {
+    input: MolliePaymentIntentInput;
+};
+
+export type MutationDeleteCustomerAddressArgs = {
+    id: Scalars['ID'];
+};
+
+export type MutationLoginArgs = {
+    username: Scalars['String'];
+    password: Scalars['String'];
+    rememberMe?: Maybe<Scalars['Boolean']>;
+};
+
+export type MutationRefreshCustomerVerificationArgs = {
+    emailAddress: Scalars['String'];
+};
+
+export type MutationRegisterCustomerAccountArgs = {
+    input: RegisterCustomerInput;
+};
+
+export type MutationRemoveCouponCodeArgs = {
+    couponCode: Scalars['String'];
+};
+
+export type MutationRemoveOrderLineArgs = {
+    orderLineId: Scalars['ID'];
+};
+
+export type MutationRequestPasswordResetArgs = {
+    emailAddress: Scalars['String'];
+};
+
+export type MutationRequestUpdateCustomerEmailAddressArgs = {
+    password: Scalars['String'];
+    newEmailAddress: Scalars['String'];
+};
+
+export type MutationResetPasswordArgs = {
+    token: Scalars['String'];
+    password: Scalars['String'];
+};
+
+export type MutationSetCustomerForOrderArgs = {
+    input: CreateCustomerInput;
+};
+
+export type MutationSetOrderBillingAddressArgs = {
+    input: CreateAddressInput;
+};
+
+export type MutationSetOrderCustomFieldsArgs = {
+    input: UpdateOrderInput;
+};
+
+export type MutationSetOrderShippingAddressArgs = {
+    input: CreateAddressInput;
+};
+
+export type MutationSetOrderShippingMethodArgs = {
+    shippingMethodId: Scalars['ID'];
+};
+
+export type MutationTransitionOrderToStateArgs = {
+    state: Scalars['String'];
+};
+
+export type MutationUpdateCustomerArgs = {
+    input: UpdateCustomerInput;
+};
+
+export type MutationUpdateCustomerAddressArgs = {
+    input: UpdateAddressInput;
+};
+
+export type MutationUpdateCustomerEmailAddressArgs = {
+    token: Scalars['String'];
+};
+
+export type MutationUpdateCustomerPasswordArgs = {
+    currentPassword: Scalars['String'];
+    newPassword: Scalars['String'];
+};
+
+export type MutationVerifyCustomerAccountArgs = {
+    token: Scalars['String'];
+    password?: Maybe<Scalars['String']>;
+};
+
+export type NativeAuthInput = {
+    username: Scalars['String'];
+    password: Scalars['String'];
+};
+
+/** Returned when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
+export type NativeAuthStrategyError = ErrorResult & {
+    __typename?: 'NativeAuthStrategyError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+export type NativeAuthenticationResult =
+    | CurrentUser
+    | InvalidCredentialsError
+    | NotVerifiedError
+    | NativeAuthStrategyError;
+
+/** Returned when attempting to set a negative OrderLine quantity. */
+export type NegativeQuantityError = ErrorResult & {
+    __typename?: 'NegativeQuantityError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+/**
+ * Returned when invoking a mutation which depends on there being an active Order on the
+ * current session.
+ */
+export type NoActiveOrderError = ErrorResult & {
+    __typename?: 'NoActiveOrderError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+export type Node = {
+    id: Scalars['ID'];
+};
+
+/**
+ * Returned if `authOptions.requireVerification` is set to `true` (which is the default)
+ * and an unverified user attempts to authenticate.
+ */
+export type NotVerifiedError = ErrorResult & {
+    __typename?: 'NotVerifiedError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+/** Operators for filtering on a list of Number fields */
+export type NumberListOperators = {
+    inList: Scalars['Float'];
+};
+
+/** Operators for filtering on a Int or Float field */
+export type NumberOperators = {
+    eq?: Maybe<Scalars['Float']>;
+    lt?: Maybe<Scalars['Float']>;
+    lte?: Maybe<Scalars['Float']>;
+    gt?: Maybe<Scalars['Float']>;
+    gte?: Maybe<Scalars['Float']>;
+    between?: Maybe<NumberRange>;
+};
+
+export type NumberRange = {
+    start: Scalars['Float'];
+    end: Scalars['Float'];
+};
+
+export type Order = Node & {
+    __typename?: 'Order';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    /**
+     * The date & time that the Order was placed, i.e. the Customer
+     * completed the checkout and the Order is no longer "active"
+     */
+    orderPlacedAt?: Maybe<Scalars['DateTime']>;
+    /** A unique code for the Order */
+    code: Scalars['String'];
+    state: Scalars['String'];
+    /** An order is active as long as the payment process has not been completed */
+    active: Scalars['Boolean'];
+    customer?: Maybe<Customer>;
+    shippingAddress?: Maybe<OrderAddress>;
+    billingAddress?: Maybe<OrderAddress>;
+    lines: Array<OrderLine>;
+    /**
+     * Surcharges are arbitrary modifications to the Order total which are neither
+     * ProductVariants nor discounts resulting from applied Promotions. For example,
+     * one-off discounts based on customer interaction, or surcharges based on payment
+     * methods.
+     */
+    surcharges: Array<Surcharge>;
+    discounts: Array<Discount>;
+    /** An array of all coupon codes applied to the Order */
+    couponCodes: Array<Scalars['String']>;
+    /** Promotions applied to the order. Only gets populated after the payment process has completed. */
+    promotions: Array<Promotion>;
+    payments?: Maybe<Array<Payment>>;
+    fulfillments?: Maybe<Array<Fulfillment>>;
+    totalQuantity: Scalars['Int'];
+    /**
+     * The subTotal is the total of all OrderLines in the Order. This figure also includes any Order-level
+     * discounts which have been prorated (proportionally distributed) amongst the OrderItems.
+     * To get a total of all OrderLines which does not account for prorated discounts, use the
+     * sum of `OrderLine.discountedLinePrice` values.
+     */
+    subTotal: Scalars['Int'];
+    /** Same as subTotal, but inclusive of tax */
+    subTotalWithTax: Scalars['Int'];
+    currencyCode: CurrencyCode;
+    shippingLines: Array<ShippingLine>;
+    shipping: Scalars['Int'];
+    shippingWithTax: Scalars['Int'];
+    /** Equal to subTotal plus shipping */
+    total: Scalars['Int'];
+    /** The final payable amount. Equal to subTotalWithTax plus shippingWithTax */
+    totalWithTax: Scalars['Int'];
+    /** A summary of the taxes being applied to this Order */
+    taxSummary: Array<OrderTaxSummary>;
+    history: HistoryEntryList;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type OrderHistoryArgs = {
+    options?: Maybe<HistoryEntryListOptions>;
+};
+
+export type OrderAddress = {
+    __typename?: 'OrderAddress';
+    fullName?: Maybe<Scalars['String']>;
+    company?: Maybe<Scalars['String']>;
+    streetLine1?: Maybe<Scalars['String']>;
+    streetLine2?: Maybe<Scalars['String']>;
+    city?: Maybe<Scalars['String']>;
+    province?: Maybe<Scalars['String']>;
+    postalCode?: Maybe<Scalars['String']>;
+    country?: Maybe<Scalars['String']>;
+    countryCode?: Maybe<Scalars['String']>;
+    phoneNumber?: Maybe<Scalars['String']>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type OrderFilterParameter = {
+    id?: Maybe<IdOperators>;
+    createdAt?: Maybe<DateOperators>;
+    updatedAt?: Maybe<DateOperators>;
+    orderPlacedAt?: Maybe<DateOperators>;
+    code?: Maybe<StringOperators>;
+    state?: Maybe<StringOperators>;
+    active?: Maybe<BooleanOperators>;
+    totalQuantity?: Maybe<NumberOperators>;
+    subTotal?: Maybe<NumberOperators>;
+    subTotalWithTax?: Maybe<NumberOperators>;
+    currencyCode?: Maybe<StringOperators>;
+    shipping?: Maybe<NumberOperators>;
+    shippingWithTax?: Maybe<NumberOperators>;
+    total?: Maybe<NumberOperators>;
+    totalWithTax?: Maybe<NumberOperators>;
+};
+
+export type OrderItem = Node & {
+    __typename?: 'OrderItem';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    cancelled: Scalars['Boolean'];
+    /** The price of a single unit, excluding tax and discounts */
+    unitPrice: Scalars['Int'];
+    /** The price of a single unit, including tax but excluding discounts */
+    unitPriceWithTax: Scalars['Int'];
+    /**
+     * The price of a single unit including discounts, excluding tax.
+     *
+     * If Order-level discounts have been applied, this will not be the
+     * actual taxable unit price (see `proratedUnitPrice`), but is generally the
+     * correct price to display to customers to avoid confusion
+     * about the internal handling of distributed Order-level discounts.
+     */
+    discountedUnitPrice: Scalars['Int'];
+    /** The price of a single unit including discounts and tax */
+    discountedUnitPriceWithTax: Scalars['Int'];
+    /**
+     * The actual unit price, taking into account both item discounts _and_ prorated (proportionally-distributed)
+     * Order-level discounts. This value is the true economic value of the OrderItem, and is used in tax
+     * and refund calculations.
+     */
+    proratedUnitPrice: Scalars['Int'];
+    /** The proratedUnitPrice including tax */
+    proratedUnitPriceWithTax: Scalars['Int'];
+    unitTax: Scalars['Int'];
+    taxRate: Scalars['Float'];
+    adjustments: Array<Adjustment>;
+    taxLines: Array<TaxLine>;
+    fulfillment?: Maybe<Fulfillment>;
+    refundId?: Maybe<Scalars['ID']>;
+};
+
+/** Returned when the maximum order size limit has been reached. */
+export type OrderLimitError = ErrorResult & {
+    __typename?: 'OrderLimitError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+    maxItems: Scalars['Int'];
+};
+
+export type OrderLine = Node & {
+    __typename?: 'OrderLine';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    productVariant: ProductVariant;
+    featuredAsset?: Maybe<Asset>;
+    /** The price of a single unit, excluding tax and discounts */
+    unitPrice: Scalars['Int'];
+    /** The price of a single unit, including tax but excluding discounts */
+    unitPriceWithTax: Scalars['Int'];
+    /** Non-zero if the unitPrice has changed since it was initially added to Order */
+    unitPriceChangeSinceAdded: Scalars['Int'];
+    /** Non-zero if the unitPriceWithTax has changed since it was initially added to Order */
+    unitPriceWithTaxChangeSinceAdded: Scalars['Int'];
+    /**
+     * The price of a single unit including discounts, excluding tax.
+     *
+     * If Order-level discounts have been applied, this will not be the
+     * actual taxable unit price (see `proratedUnitPrice`), but is generally the
+     * correct price to display to customers to avoid confusion
+     * about the internal handling of distributed Order-level discounts.
+     */
+    discountedUnitPrice: Scalars['Int'];
+    /** The price of a single unit including discounts and tax */
+    discountedUnitPriceWithTax: Scalars['Int'];
+    /**
+     * The actual unit price, taking into account both item discounts _and_ prorated (proportionally-distributed)
+     * Order-level discounts. This value is the true economic value of the OrderItem, and is used in tax
+     * and refund calculations.
+     */
+    proratedUnitPrice: Scalars['Int'];
+    /** The proratedUnitPrice including tax */
+    proratedUnitPriceWithTax: Scalars['Int'];
+    quantity: Scalars['Int'];
+    items: Array<OrderItem>;
+    taxRate: Scalars['Float'];
+    /** The total price of the line excluding tax and discounts. */
+    linePrice: Scalars['Int'];
+    /** The total price of the line including tax but excluding discounts. */
+    linePriceWithTax: Scalars['Int'];
+    /** The price of the line including discounts, excluding tax */
+    discountedLinePrice: Scalars['Int'];
+    /** The price of the line including discounts and tax */
+    discountedLinePriceWithTax: Scalars['Int'];
+    /**
+     * The actual line price, taking into account both item discounts _and_ prorated (proportionally-distributed)
+     * Order-level discounts. This value is the true economic value of the OrderLine, and is used in tax
+     * and refund calculations.
+     */
+    proratedLinePrice: Scalars['Int'];
+    /** The proratedLinePrice including tax */
+    proratedLinePriceWithTax: Scalars['Int'];
+    /** The total tax on this line */
+    lineTax: Scalars['Int'];
+    discounts: Array<Discount>;
+    taxLines: Array<TaxLine>;
+    order: Order;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type OrderList = PaginatedList & {
+    __typename?: 'OrderList';
+    items: Array<Order>;
+    totalItems: Scalars['Int'];
+};
+
+export type OrderListOptions = {
+    /** Skips the first n results, for use in pagination */
+    skip?: Maybe<Scalars['Int']>;
+    /** Takes n results, for use in pagination */
+    take?: Maybe<Scalars['Int']>;
+    /** Specifies which properties to sort the results by */
+    sort?: Maybe<OrderSortParameter>;
+    /** Allows the results to be filtered */
+    filter?: Maybe<OrderFilterParameter>;
+    /** Specifies whether multiple "filter" arguments should be combines with a logical AND or OR operation. Defaults to AND. */
+    filterOperator?: Maybe<LogicalOperator>;
+};
+
+/** Returned when attempting to modify the contents of an Order that is not in the `AddingItems` state. */
+export type OrderModificationError = ErrorResult & {
+    __typename?: 'OrderModificationError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+/** Returned when attempting to add a Payment to an Order that is not in the `ArrangingPayment` state. */
+export type OrderPaymentStateError = ErrorResult & {
+    __typename?: 'OrderPaymentStateError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+export type OrderSortParameter = {
+    id?: Maybe<SortOrder>;
+    createdAt?: Maybe<SortOrder>;
+    updatedAt?: Maybe<SortOrder>;
+    orderPlacedAt?: Maybe<SortOrder>;
+    code?: Maybe<SortOrder>;
+    state?: Maybe<SortOrder>;
+    totalQuantity?: Maybe<SortOrder>;
+    subTotal?: Maybe<SortOrder>;
+    subTotalWithTax?: Maybe<SortOrder>;
+    shipping?: Maybe<SortOrder>;
+    shippingWithTax?: Maybe<SortOrder>;
+    total?: Maybe<SortOrder>;
+    totalWithTax?: Maybe<SortOrder>;
+};
+
+/** Returned if there is an error in transitioning the Order state */
+export type OrderStateTransitionError = ErrorResult & {
+    __typename?: 'OrderStateTransitionError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+    transitionError: Scalars['String'];
+    fromState: Scalars['String'];
+    toState: Scalars['String'];
+};
+
+/**
+ * A summary of the taxes being applied to this order, grouped
+ * by taxRate.
+ */
+export type OrderTaxSummary = {
+    __typename?: 'OrderTaxSummary';
+    /** A description of this tax */
+    description: Scalars['String'];
+    /** The taxRate as a percentage */
+    taxRate: Scalars['Float'];
+    /** The total net price or OrderItems to which this taxRate applies */
+    taxBase: Scalars['Int'];
+    /** The total tax being applied to the Order at this taxRate */
+    taxTotal: Scalars['Int'];
+};
+
+export type PaginatedList = {
+    items: Array<Node>;
+    totalItems: Scalars['Int'];
+};
+
+/** Returned when attempting to verify a customer account with a password, when a password has already been set. */
+export type PasswordAlreadySetError = ErrorResult & {
+    __typename?: 'PasswordAlreadySetError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+/**
+ * Returned if the token used to reset a Customer's password is valid, but has
+ * expired according to the `verificationTokenDuration` setting in the AuthOptions.
+ */
+export type PasswordResetTokenExpiredError = ErrorResult & {
+    __typename?: 'PasswordResetTokenExpiredError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+/**
+ * Returned if the token used to reset a Customer's password is either
+ * invalid or does not match any expected tokens.
+ */
+export type PasswordResetTokenInvalidError = ErrorResult & {
+    __typename?: 'PasswordResetTokenInvalidError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+/** Returned when attempting to register or verify a customer account where the given password fails password validation. */
+export type PasswordValidationError = ErrorResult & {
+    __typename?: 'PasswordValidationError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+    validationErrorMessage: Scalars['String'];
+};
+
+export type Payment = Node & {
+    __typename?: 'Payment';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    method: Scalars['String'];
+    amount: Scalars['Int'];
+    state: Scalars['String'];
+    transactionId?: Maybe<Scalars['String']>;
+    errorMessage?: Maybe<Scalars['String']>;
+    refunds: Array<Refund>;
+    metadata?: Maybe<Scalars['JSON']>;
+};
+
+/** Returned when a Payment is declined by the payment provider. */
+export type PaymentDeclinedError = ErrorResult & {
+    __typename?: 'PaymentDeclinedError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+    paymentErrorMessage: Scalars['String'];
+};
+
+/** Returned when a Payment fails due to an error. */
+export type PaymentFailedError = ErrorResult & {
+    __typename?: 'PaymentFailedError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+    paymentErrorMessage: Scalars['String'];
+};
+
+/** Passed as input to the `addPaymentToOrder` mutation. */
+export type PaymentInput = {
+    /** This field should correspond to the `code` property of a PaymentMethod. */
+    method: Scalars['String'];
+    /**
+     * This field should contain arbitrary data passed to the specified PaymentMethodHandler's `createPayment()` method
+     * as the "metadata" argument. For example, it could contain an ID for the payment and other
+     * data generated by the payment provider.
+     */
+    metadata: Scalars['JSON'];
+};
+
+export type PaymentMethod = Node & {
+    __typename?: 'PaymentMethod';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    name: Scalars['String'];
+    code: Scalars['String'];
+    description: Scalars['String'];
+    enabled: Scalars['Boolean'];
+    checker?: Maybe<ConfigurableOperation>;
+    handler: ConfigurableOperation;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type PaymentMethodQuote = {
+    __typename?: 'PaymentMethodQuote';
+    id: Scalars['ID'];
+    code: Scalars['String'];
+    name: Scalars['String'];
+    description: Scalars['String'];
+    isEligible: Scalars['Boolean'];
+    eligibilityMessage?: Maybe<Scalars['String']>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+/**
+ * @description
+ * Permissions for administrators and customers. Used to control access to
+ * GraphQL resolvers via the {@link Allow} decorator.
+ *
+ * @docsCategory common
+ */
+export enum Permission {
+    /** Authenticated means simply that the user is logged in */
+    Authenticated = 'Authenticated',
+    /** SuperAdmin has unrestricted access to all operations */
+    SuperAdmin = 'SuperAdmin',
+    /** Owner means the user owns this entity, e.g. a Customer's own Order */
+    Owner = 'Owner',
+    /** Public means any unauthenticated user may perform the operation */
+    Public = 'Public',
+    /** Grants permission to update GlobalSettings */
+    UpdateGlobalSettings = 'UpdateGlobalSettings',
+    /** Grants permission to create Products, Facets, Assets, Collections */
+    CreateCatalog = 'CreateCatalog',
+    /** Grants permission to read Products, Facets, Assets, Collections */
+    ReadCatalog = 'ReadCatalog',
+    /** Grants permission to update Products, Facets, Assets, Collections */
+    UpdateCatalog = 'UpdateCatalog',
+    /** Grants permission to delete Products, Facets, Assets, Collections */
+    DeleteCatalog = 'DeleteCatalog',
+    /** Grants permission to create PaymentMethods, ShippingMethods, TaxCategories, TaxRates, Zones, Countries, System & GlobalSettings */
+    CreateSettings = 'CreateSettings',
+    /** Grants permission to read PaymentMethods, ShippingMethods, TaxCategories, TaxRates, Zones, Countries, System & GlobalSettings */
+    ReadSettings = 'ReadSettings',
+    /** Grants permission to update PaymentMethods, ShippingMethods, TaxCategories, TaxRates, Zones, Countries, System & GlobalSettings */
+    UpdateSettings = 'UpdateSettings',
+    /** Grants permission to delete PaymentMethods, ShippingMethods, TaxCategories, TaxRates, Zones, Countries, System & GlobalSettings */
+    DeleteSettings = 'DeleteSettings',
+    /** Grants permission to create Administrator */
+    CreateAdministrator = 'CreateAdministrator',
+    /** Grants permission to read Administrator */
+    ReadAdministrator = 'ReadAdministrator',
+    /** Grants permission to update Administrator */
+    UpdateAdministrator = 'UpdateAdministrator',
+    /** Grants permission to delete Administrator */
+    DeleteAdministrator = 'DeleteAdministrator',
+    /** Grants permission to create Asset */
+    CreateAsset = 'CreateAsset',
+    /** Grants permission to read Asset */
+    ReadAsset = 'ReadAsset',
+    /** Grants permission to update Asset */
+    UpdateAsset = 'UpdateAsset',
+    /** Grants permission to delete Asset */
+    DeleteAsset = 'DeleteAsset',
+    /** Grants permission to create Channel */
+    CreateChannel = 'CreateChannel',
+    /** Grants permission to read Channel */
+    ReadChannel = 'ReadChannel',
+    /** Grants permission to update Channel */
+    UpdateChannel = 'UpdateChannel',
+    /** Grants permission to delete Channel */
+    DeleteChannel = 'DeleteChannel',
+    /** Grants permission to create Collection */
+    CreateCollection = 'CreateCollection',
+    /** Grants permission to read Collection */
+    ReadCollection = 'ReadCollection',
+    /** Grants permission to update Collection */
+    UpdateCollection = 'UpdateCollection',
+    /** Grants permission to delete Collection */
+    DeleteCollection = 'DeleteCollection',
+    /** Grants permission to create Country */
+    CreateCountry = 'CreateCountry',
+    /** Grants permission to read Country */
+    ReadCountry = 'ReadCountry',
+    /** Grants permission to update Country */
+    UpdateCountry = 'UpdateCountry',
+    /** Grants permission to delete Country */
+    DeleteCountry = 'DeleteCountry',
+    /** Grants permission to create Customer */
+    CreateCustomer = 'CreateCustomer',
+    /** Grants permission to read Customer */
+    ReadCustomer = 'ReadCustomer',
+    /** Grants permission to update Customer */
+    UpdateCustomer = 'UpdateCustomer',
+    /** Grants permission to delete Customer */
+    DeleteCustomer = 'DeleteCustomer',
+    /** Grants permission to create CustomerGroup */
+    CreateCustomerGroup = 'CreateCustomerGroup',
+    /** Grants permission to read CustomerGroup */
+    ReadCustomerGroup = 'ReadCustomerGroup',
+    /** Grants permission to update CustomerGroup */
+    UpdateCustomerGroup = 'UpdateCustomerGroup',
+    /** Grants permission to delete CustomerGroup */
+    DeleteCustomerGroup = 'DeleteCustomerGroup',
+    /** Grants permission to create Facet */
+    CreateFacet = 'CreateFacet',
+    /** Grants permission to read Facet */
+    ReadFacet = 'ReadFacet',
+    /** Grants permission to update Facet */
+    UpdateFacet = 'UpdateFacet',
+    /** Grants permission to delete Facet */
+    DeleteFacet = 'DeleteFacet',
+    /** Grants permission to create Order */
+    CreateOrder = 'CreateOrder',
+    /** Grants permission to read Order */
+    ReadOrder = 'ReadOrder',
+    /** Grants permission to update Order */
+    UpdateOrder = 'UpdateOrder',
+    /** Grants permission to delete Order */
+    DeleteOrder = 'DeleteOrder',
+    /** Grants permission to create PaymentMethod */
+    CreatePaymentMethod = 'CreatePaymentMethod',
+    /** Grants permission to read PaymentMethod */
+    ReadPaymentMethod = 'ReadPaymentMethod',
+    /** Grants permission to update PaymentMethod */
+    UpdatePaymentMethod = 'UpdatePaymentMethod',
+    /** Grants permission to delete PaymentMethod */
+    DeletePaymentMethod = 'DeletePaymentMethod',
+    /** Grants permission to create Product */
+    CreateProduct = 'CreateProduct',
+    /** Grants permission to read Product */
+    ReadProduct = 'ReadProduct',
+    /** Grants permission to update Product */
+    UpdateProduct = 'UpdateProduct',
+    /** Grants permission to delete Product */
+    DeleteProduct = 'DeleteProduct',
+    /** Grants permission to create Promotion */
+    CreatePromotion = 'CreatePromotion',
+    /** Grants permission to read Promotion */
+    ReadPromotion = 'ReadPromotion',
+    /** Grants permission to update Promotion */
+    UpdatePromotion = 'UpdatePromotion',
+    /** Grants permission to delete Promotion */
+    DeletePromotion = 'DeletePromotion',
+    /** Grants permission to create ShippingMethod */
+    CreateShippingMethod = 'CreateShippingMethod',
+    /** Grants permission to read ShippingMethod */
+    ReadShippingMethod = 'ReadShippingMethod',
+    /** Grants permission to update ShippingMethod */
+    UpdateShippingMethod = 'UpdateShippingMethod',
+    /** Grants permission to delete ShippingMethod */
+    DeleteShippingMethod = 'DeleteShippingMethod',
+    /** Grants permission to create Tag */
+    CreateTag = 'CreateTag',
+    /** Grants permission to read Tag */
+    ReadTag = 'ReadTag',
+    /** Grants permission to update Tag */
+    UpdateTag = 'UpdateTag',
+    /** Grants permission to delete Tag */
+    DeleteTag = 'DeleteTag',
+    /** Grants permission to create TaxCategory */
+    CreateTaxCategory = 'CreateTaxCategory',
+    /** Grants permission to read TaxCategory */
+    ReadTaxCategory = 'ReadTaxCategory',
+    /** Grants permission to update TaxCategory */
+    UpdateTaxCategory = 'UpdateTaxCategory',
+    /** Grants permission to delete TaxCategory */
+    DeleteTaxCategory = 'DeleteTaxCategory',
+    /** Grants permission to create TaxRate */
+    CreateTaxRate = 'CreateTaxRate',
+    /** Grants permission to read TaxRate */
+    ReadTaxRate = 'ReadTaxRate',
+    /** Grants permission to update TaxRate */
+    UpdateTaxRate = 'UpdateTaxRate',
+    /** Grants permission to delete TaxRate */
+    DeleteTaxRate = 'DeleteTaxRate',
+    /** Grants permission to create System */
+    CreateSystem = 'CreateSystem',
+    /** Grants permission to read System */
+    ReadSystem = 'ReadSystem',
+    /** Grants permission to update System */
+    UpdateSystem = 'UpdateSystem',
+    /** Grants permission to delete System */
+    DeleteSystem = 'DeleteSystem',
+    /** Grants permission to create Zone */
+    CreateZone = 'CreateZone',
+    /** Grants permission to read Zone */
+    ReadZone = 'ReadZone',
+    /** Grants permission to update Zone */
+    UpdateZone = 'UpdateZone',
+    /** Grants permission to delete Zone */
+    DeleteZone = 'DeleteZone',
+}
+
+/** The price range where the result has more than one price */
+export type PriceRange = {
+    __typename?: 'PriceRange';
+    min: Scalars['Int'];
+    max: Scalars['Int'];
+};
+
+export type Product = Node & {
+    __typename?: 'Product';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    slug: Scalars['String'];
+    description: Scalars['String'];
+    featuredAsset?: Maybe<Asset>;
+    assets: Array<Asset>;
+    /** Returns all ProductVariants */
+    variants: Array<ProductVariant>;
+    /** Returns a paginated, sortable, filterable list of ProductVariants */
+    variantList: ProductVariantList;
+    optionGroups: Array<ProductOptionGroup>;
+    facetValues: Array<FacetValue>;
+    translations: Array<ProductTranslation>;
+    collections: Array<Collection>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type ProductVariantListArgs = {
+    options?: Maybe<ProductVariantListOptions>;
+};
+
+export type ProductFilterParameter = {
+    id?: Maybe<IdOperators>;
+    createdAt?: Maybe<DateOperators>;
+    updatedAt?: Maybe<DateOperators>;
+    languageCode?: Maybe<StringOperators>;
+    name?: Maybe<StringOperators>;
+    slug?: Maybe<StringOperators>;
+    description?: Maybe<StringOperators>;
+};
+
+export type ProductList = PaginatedList & {
+    __typename?: 'ProductList';
+    items: Array<Product>;
+    totalItems: Scalars['Int'];
+};
+
+export type ProductListOptions = {
+    /** Skips the first n results, for use in pagination */
+    skip?: Maybe<Scalars['Int']>;
+    /** Takes n results, for use in pagination */
+    take?: Maybe<Scalars['Int']>;
+    /** Specifies which properties to sort the results by */
+    sort?: Maybe<ProductSortParameter>;
+    /** Allows the results to be filtered */
+    filter?: Maybe<ProductFilterParameter>;
+    /** Specifies whether multiple "filter" arguments should be combines with a logical AND or OR operation. Defaults to AND. */
+    filterOperator?: Maybe<LogicalOperator>;
+};
+
+export type ProductOption = Node & {
+    __typename?: 'ProductOption';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    code: Scalars['String'];
+    name: Scalars['String'];
+    groupId: Scalars['ID'];
+    group: ProductOptionGroup;
+    translations: Array<ProductOptionTranslation>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type ProductOptionGroup = Node & {
+    __typename?: 'ProductOptionGroup';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    code: Scalars['String'];
+    name: Scalars['String'];
+    options: Array<ProductOption>;
+    translations: Array<ProductOptionGroupTranslation>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type ProductOptionGroupTranslation = {
+    __typename?: 'ProductOptionGroupTranslation';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+};
+
+export type ProductOptionTranslation = {
+    __typename?: 'ProductOptionTranslation';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+};
+
+export type ProductSortParameter = {
+    id?: Maybe<SortOrder>;
+    createdAt?: Maybe<SortOrder>;
+    updatedAt?: Maybe<SortOrder>;
+    name?: Maybe<SortOrder>;
+    slug?: Maybe<SortOrder>;
+    description?: Maybe<SortOrder>;
+};
+
+export type ProductTranslation = {
+    __typename?: 'ProductTranslation';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    slug: Scalars['String'];
+    description: Scalars['String'];
+};
+
+export type ProductVariant = Node & {
+    __typename?: 'ProductVariant';
+    id: Scalars['ID'];
+    product: Product;
+    productId: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    sku: Scalars['String'];
+    name: Scalars['String'];
+    featuredAsset?: Maybe<Asset>;
+    assets: Array<Asset>;
+    price: Scalars['Int'];
+    currencyCode: CurrencyCode;
+    priceWithTax: Scalars['Int'];
+    stockLevel: Scalars['String'];
+    taxRateApplied: TaxRate;
+    taxCategory: TaxCategory;
+    options: Array<ProductOption>;
+    facetValues: Array<FacetValue>;
+    translations: Array<ProductVariantTranslation>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type ProductVariantFilterParameter = {
+    id?: Maybe<IdOperators>;
+    productId?: Maybe<IdOperators>;
+    createdAt?: Maybe<DateOperators>;
+    updatedAt?: Maybe<DateOperators>;
+    languageCode?: Maybe<StringOperators>;
+    sku?: Maybe<StringOperators>;
+    name?: Maybe<StringOperators>;
+    price?: Maybe<NumberOperators>;
+    currencyCode?: Maybe<StringOperators>;
+    priceWithTax?: Maybe<NumberOperators>;
+    stockLevel?: Maybe<StringOperators>;
+};
+
+export type ProductVariantList = PaginatedList & {
+    __typename?: 'ProductVariantList';
+    items: Array<ProductVariant>;
+    totalItems: Scalars['Int'];
+};
+
+export type ProductVariantListOptions = {
+    /** Skips the first n results, for use in pagination */
+    skip?: Maybe<Scalars['Int']>;
+    /** Takes n results, for use in pagination */
+    take?: Maybe<Scalars['Int']>;
+    /** Specifies which properties to sort the results by */
+    sort?: Maybe<ProductVariantSortParameter>;
+    /** Allows the results to be filtered */
+    filter?: Maybe<ProductVariantFilterParameter>;
+    /** Specifies whether multiple "filter" arguments should be combines with a logical AND or OR operation. Defaults to AND. */
+    filterOperator?: Maybe<LogicalOperator>;
+};
+
+export type ProductVariantSortParameter = {
+    id?: Maybe<SortOrder>;
+    productId?: Maybe<SortOrder>;
+    createdAt?: Maybe<SortOrder>;
+    updatedAt?: Maybe<SortOrder>;
+    sku?: Maybe<SortOrder>;
+    name?: Maybe<SortOrder>;
+    price?: Maybe<SortOrder>;
+    priceWithTax?: Maybe<SortOrder>;
+    stockLevel?: Maybe<SortOrder>;
+};
+
+export type ProductVariantTranslation = {
+    __typename?: 'ProductVariantTranslation';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+};
+
+export type Promotion = Node & {
+    __typename?: 'Promotion';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    startsAt?: Maybe<Scalars['DateTime']>;
+    endsAt?: Maybe<Scalars['DateTime']>;
+    couponCode?: Maybe<Scalars['String']>;
+    perCustomerUsageLimit?: Maybe<Scalars['Int']>;
+    name: Scalars['String'];
+    enabled: Scalars['Boolean'];
+    conditions: Array<ConfigurableOperation>;
+    actions: Array<ConfigurableOperation>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type PromotionList = PaginatedList & {
+    __typename?: 'PromotionList';
+    items: Array<Promotion>;
+    totalItems: Scalars['Int'];
+};
+
+export type Query = {
+    __typename?: 'Query';
+    /** The active Channel */
+    activeChannel: Channel;
+    /** The active Customer */
+    activeCustomer?: Maybe<Customer>;
+    /**
+     * The active Order. Will be `null` until an Order is created via `addItemToOrder`. Once an Order reaches the
+     * state of `PaymentAuthorized` or `PaymentSettled`, then that Order is no longer considered "active" and this
+     * query will once again return `null`.
+     */
+    activeOrder?: Maybe<Order>;
+    /** An array of supported Countries */
+    availableCountries: Array<Country>;
+    /** A list of Collections available to the shop */
+    collections: CollectionList;
+    /** Returns a Collection either by its id or slug. If neither 'id' nor 'slug' is specified, an error will result. */
+    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>;
+    /** A list of Facets available to the shop */
+    facets: FacetList;
+    /** Returns a Facet by its id */
+    facet?: Maybe<Facet>;
+    /** Returns information about the current authenticated User */
+    me?: Maybe<CurrentUser>;
+    /** Returns the possible next states that the activeOrder can transition to */
+    nextOrderStates: Array<Scalars['String']>;
+    /**
+     * Returns an Order based on the id. Note that in the Shop API, only orders belonging to the
+     * currently-authenticated User may be queried.
+     */
+    order?: Maybe<Order>;
+    /**
+     * Returns an Order based on the order `code`. For guest Orders (i.e. Orders placed by non-authenticated Customers)
+     * this query will only return the Order within 2 hours of the Order being placed. This allows an Order confirmation
+     * screen to be shown immediately after completion of a guest checkout, yet prevents security risks of allowing
+     * general anonymous access to Order data.
+     */
+    orderByCode?: Maybe<Order>;
+    /** Get a Product either by id or slug. If neither 'id' nor 'slug' is specified, an error will result. */
+    product?: Maybe<Product>;
+    /** Get a list of Products */
+    products: ProductList;
+    /** Search Products based on the criteria set by the `SearchInput` */
+    search: SearchResponse;
+};
+
+export type QueryCollectionsArgs = {
+    options?: Maybe<CollectionListOptions>;
+};
+
+export type QueryCollectionArgs = {
+    id?: Maybe<Scalars['ID']>;
+    slug?: Maybe<Scalars['String']>;
+};
+
+export type QueryFacetsArgs = {
+    options?: Maybe<FacetListOptions>;
+};
+
+export type QueryFacetArgs = {
+    id: Scalars['ID'];
+};
+
+export type QueryOrderArgs = {
+    id: Scalars['ID'];
+};
+
+export type QueryOrderByCodeArgs = {
+    code: Scalars['String'];
+};
+
+export type QueryProductArgs = {
+    id?: Maybe<Scalars['ID']>;
+    slug?: Maybe<Scalars['String']>;
+};
+
+export type QueryProductsArgs = {
+    options?: Maybe<ProductListOptions>;
+};
+
+export type QuerySearchArgs = {
+    input: SearchInput;
+};
+
+export type RefreshCustomerVerificationResult = Success | NativeAuthStrategyError;
+
+export type Refund = Node & {
+    __typename?: 'Refund';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    items: Scalars['Int'];
+    shipping: Scalars['Int'];
+    adjustment: Scalars['Int'];
+    total: Scalars['Int'];
+    method?: Maybe<Scalars['String']>;
+    state: Scalars['String'];
+    transactionId?: Maybe<Scalars['String']>;
+    reason?: Maybe<Scalars['String']>;
+    orderItems: Array<OrderItem>;
+    paymentId: Scalars['ID'];
+    metadata?: Maybe<Scalars['JSON']>;
+};
+
+export type RegisterCustomerAccountResult =
+    | Success
+    | MissingPasswordError
+    | PasswordValidationError
+    | NativeAuthStrategyError;
+
+export type RegisterCustomerInput = {
+    emailAddress: Scalars['String'];
+    title?: Maybe<Scalars['String']>;
+    firstName?: Maybe<Scalars['String']>;
+    lastName?: Maybe<Scalars['String']>;
+    phoneNumber?: Maybe<Scalars['String']>;
+    password?: Maybe<Scalars['String']>;
+};
+
+export type RelationCustomFieldConfig = CustomField & {
+    __typename?: 'RelationCustomFieldConfig';
+    name: Scalars['String'];
+    type: Scalars['String'];
+    list: Scalars['Boolean'];
+    label?: Maybe<Array<LocalizedString>>;
+    description?: Maybe<Array<LocalizedString>>;
+    readonly?: Maybe<Scalars['Boolean']>;
+    internal?: Maybe<Scalars['Boolean']>;
+    nullable?: Maybe<Scalars['Boolean']>;
+    entity: Scalars['String'];
+    scalarFields: Array<Scalars['String']>;
+    ui?: Maybe<Scalars['JSON']>;
+};
+
+export type RemoveOrderItemsResult = Order | OrderModificationError;
+
+export type RequestPasswordResetResult = Success | NativeAuthStrategyError;
+
+export type RequestUpdateCustomerEmailAddressResult =
+    | Success
+    | InvalidCredentialsError
+    | EmailAddressConflictError
+    | NativeAuthStrategyError;
+
+export type ResetPasswordResult =
+    | CurrentUser
+    | PasswordResetTokenInvalidError
+    | PasswordResetTokenExpiredError
+    | PasswordValidationError
+    | NativeAuthStrategyError
+    | NotVerifiedError;
+
+export type Role = Node & {
+    __typename?: 'Role';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    code: Scalars['String'];
+    description: Scalars['String'];
+    permissions: Array<Permission>;
+    channels: Array<Channel>;
+};
+
+export type RoleList = PaginatedList & {
+    __typename?: 'RoleList';
+    items: Array<Role>;
+    totalItems: Scalars['Int'];
+};
+
+export type SearchInput = {
+    term?: Maybe<Scalars['String']>;
+    facetValueIds?: Maybe<Array<Scalars['ID']>>;
+    facetValueOperator?: Maybe<LogicalOperator>;
+    facetValueFilters?: Maybe<Array<FacetValueFilterInput>>;
+    collectionId?: Maybe<Scalars['ID']>;
+    collectionSlug?: Maybe<Scalars['String']>;
+    groupByProduct?: Maybe<Scalars['Boolean']>;
+    take?: Maybe<Scalars['Int']>;
+    skip?: Maybe<Scalars['Int']>;
+    sort?: Maybe<SearchResultSortParameter>;
+};
+
+export type SearchReindexResponse = {
+    __typename?: 'SearchReindexResponse';
+    success: Scalars['Boolean'];
+};
+
+export type SearchResponse = {
+    __typename?: 'SearchResponse';
+    items: Array<SearchResult>;
+    totalItems: Scalars['Int'];
+    facetValues: Array<FacetValueResult>;
+    collections: Array<CollectionResult>;
+};
+
+export type SearchResult = {
+    __typename?: 'SearchResult';
+    sku: Scalars['String'];
+    slug: Scalars['String'];
+    productId: Scalars['ID'];
+    productName: Scalars['String'];
+    productAsset?: Maybe<SearchResultAsset>;
+    productVariantId: Scalars['ID'];
+    productVariantName: Scalars['String'];
+    productVariantAsset?: Maybe<SearchResultAsset>;
+    price: SearchResultPrice;
+    priceWithTax: SearchResultPrice;
+    currencyCode: CurrencyCode;
+    description: Scalars['String'];
+    facetIds: Array<Scalars['ID']>;
+    facetValueIds: Array<Scalars['ID']>;
+    /** An array of ids of the Collections in which this result appears */
+    collectionIds: Array<Scalars['ID']>;
+    /** A relevance score for the result. Differs between database implementations */
+    score: Scalars['Float'];
+};
+
+export type SearchResultAsset = {
+    __typename?: 'SearchResultAsset';
+    id: Scalars['ID'];
+    preview: Scalars['String'];
+    focalPoint?: Maybe<Coordinate>;
+};
+
+/** The price of a search result product, either as a range or as a single price */
+export type SearchResultPrice = PriceRange | SinglePrice;
+
+export type SearchResultSortParameter = {
+    name?: Maybe<SortOrder>;
+    price?: Maybe<SortOrder>;
+};
+
+export type SetCustomerForOrderResult =
+    | Order
+    | AlreadyLoggedInError
+    | EmailAddressConflictError
+    | NoActiveOrderError;
+
+export type SetOrderShippingMethodResult =
+    | Order
+    | OrderModificationError
+    | IneligibleShippingMethodError
+    | NoActiveOrderError;
+
+export type ShippingLine = {
+    __typename?: 'ShippingLine';
+    shippingMethod: ShippingMethod;
+    price: Scalars['Int'];
+    priceWithTax: Scalars['Int'];
+    discountedPrice: Scalars['Int'];
+    discountedPriceWithTax: Scalars['Int'];
+    discounts: Array<Discount>;
+};
+
+export type ShippingMethod = Node & {
+    __typename?: 'ShippingMethod';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    code: Scalars['String'];
+    name: Scalars['String'];
+    description: Scalars['String'];
+    fulfillmentHandlerCode: Scalars['String'];
+    checker: ConfigurableOperation;
+    calculator: ConfigurableOperation;
+    translations: Array<ShippingMethodTranslation>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type ShippingMethodList = PaginatedList & {
+    __typename?: 'ShippingMethodList';
+    items: Array<ShippingMethod>;
+    totalItems: Scalars['Int'];
+};
+
+export type ShippingMethodQuote = {
+    __typename?: 'ShippingMethodQuote';
+    id: Scalars['ID'];
+    price: Scalars['Int'];
+    priceWithTax: Scalars['Int'];
+    code: Scalars['String'];
+    name: Scalars['String'];
+    description: Scalars['String'];
+    /** Any optional metadata returned by the ShippingCalculator in the ShippingCalculationResult */
+    metadata?: Maybe<Scalars['JSON']>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type ShippingMethodTranslation = {
+    __typename?: 'ShippingMethodTranslation';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    languageCode: LanguageCode;
+    name: Scalars['String'];
+    description: Scalars['String'];
+};
+
+/** The price value where the result has a single price */
+export type SinglePrice = {
+    __typename?: 'SinglePrice';
+    value: Scalars['Int'];
+};
+
+export enum SortOrder {
+    ASC = 'ASC',
+    DESC = 'DESC',
+}
+
+export type StringCustomFieldConfig = CustomField & {
+    __typename?: 'StringCustomFieldConfig';
+    name: Scalars['String'];
+    type: Scalars['String'];
+    list: Scalars['Boolean'];
+    length?: Maybe<Scalars['Int']>;
+    label?: Maybe<Array<LocalizedString>>;
+    description?: Maybe<Array<LocalizedString>>;
+    readonly?: Maybe<Scalars['Boolean']>;
+    internal?: Maybe<Scalars['Boolean']>;
+    nullable?: Maybe<Scalars['Boolean']>;
+    pattern?: Maybe<Scalars['String']>;
+    options?: Maybe<Array<StringFieldOption>>;
+    ui?: Maybe<Scalars['JSON']>;
+};
+
+export type StringFieldOption = {
+    __typename?: 'StringFieldOption';
+    value: Scalars['String'];
+    label?: Maybe<Array<LocalizedString>>;
+};
+
+/** Operators for filtering on a list of String fields */
+export type StringListOperators = {
+    inList: Scalars['String'];
+};
+
+/** Operators for filtering on a String field */
+export type StringOperators = {
+    eq?: Maybe<Scalars['String']>;
+    notEq?: Maybe<Scalars['String']>;
+    contains?: Maybe<Scalars['String']>;
+    notContains?: Maybe<Scalars['String']>;
+    in?: Maybe<Array<Scalars['String']>>;
+    notIn?: Maybe<Array<Scalars['String']>>;
+    regex?: Maybe<Scalars['String']>;
+};
+
+/** Indicates that an operation succeeded, where we do not want to return any more specific information. */
+export type Success = {
+    __typename?: 'Success';
+    success: Scalars['Boolean'];
+};
+
+export type Surcharge = Node & {
+    __typename?: 'Surcharge';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    description: Scalars['String'];
+    sku?: Maybe<Scalars['String']>;
+    taxLines: Array<TaxLine>;
+    price: Scalars['Int'];
+    priceWithTax: Scalars['Int'];
+    taxRate: Scalars['Float'];
+};
+
+export type Tag = Node & {
+    __typename?: 'Tag';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    value: Scalars['String'];
+};
+
+export type TagList = PaginatedList & {
+    __typename?: 'TagList';
+    items: Array<Tag>;
+    totalItems: Scalars['Int'];
+};
+
+export type TaxCategory = Node & {
+    __typename?: 'TaxCategory';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    name: Scalars['String'];
+    isDefault: Scalars['Boolean'];
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type TaxLine = {
+    __typename?: 'TaxLine';
+    description: Scalars['String'];
+    taxRate: Scalars['Float'];
+};
+
+export type TaxRate = Node & {
+    __typename?: 'TaxRate';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    name: Scalars['String'];
+    enabled: Scalars['Boolean'];
+    value: Scalars['Float'];
+    category: TaxCategory;
+    zone: Zone;
+    customerGroup?: Maybe<CustomerGroup>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type TaxRateList = PaginatedList & {
+    __typename?: 'TaxRateList';
+    items: Array<TaxRate>;
+    totalItems: Scalars['Int'];
+};
+
+export type TextCustomFieldConfig = CustomField & {
+    __typename?: 'TextCustomFieldConfig';
+    name: Scalars['String'];
+    type: Scalars['String'];
+    list: Scalars['Boolean'];
+    label?: Maybe<Array<LocalizedString>>;
+    description?: Maybe<Array<LocalizedString>>;
+    readonly?: Maybe<Scalars['Boolean']>;
+    internal?: Maybe<Scalars['Boolean']>;
+    nullable?: Maybe<Scalars['Boolean']>;
+    ui?: Maybe<Scalars['JSON']>;
+};
+
+export type TransitionOrderToStateResult = Order | OrderStateTransitionError;
+
+export type UpdateAddressInput = {
+    id: Scalars['ID'];
+    fullName?: Maybe<Scalars['String']>;
+    company?: Maybe<Scalars['String']>;
+    streetLine1?: Maybe<Scalars['String']>;
+    streetLine2?: Maybe<Scalars['String']>;
+    city?: Maybe<Scalars['String']>;
+    province?: Maybe<Scalars['String']>;
+    postalCode?: Maybe<Scalars['String']>;
+    countryCode?: Maybe<Scalars['String']>;
+    phoneNumber?: Maybe<Scalars['String']>;
+    defaultShippingAddress?: Maybe<Scalars['Boolean']>;
+    defaultBillingAddress?: Maybe<Scalars['Boolean']>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type UpdateCustomerEmailAddressResult =
+    | Success
+    | IdentifierChangeTokenInvalidError
+    | IdentifierChangeTokenExpiredError
+    | NativeAuthStrategyError;
+
+export type UpdateCustomerInput = {
+    title?: Maybe<Scalars['String']>;
+    firstName?: Maybe<Scalars['String']>;
+    lastName?: Maybe<Scalars['String']>;
+    phoneNumber?: Maybe<Scalars['String']>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type UpdateCustomerPasswordResult =
+    | Success
+    | InvalidCredentialsError
+    | PasswordValidationError
+    | NativeAuthStrategyError;
+
+export type UpdateOrderInput = {
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+export type UpdateOrderItemsResult =
+    | Order
+    | OrderModificationError
+    | OrderLimitError
+    | NegativeQuantityError
+    | InsufficientStockError;
+
+export type User = Node & {
+    __typename?: 'User';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    identifier: Scalars['String'];
+    verified: Scalars['Boolean'];
+    roles: Array<Role>;
+    lastLogin?: Maybe<Scalars['DateTime']>;
+    authenticationMethods: Array<AuthenticationMethod>;
+    customFields?: Maybe<Scalars['JSON']>;
+};
+
+/**
+ * Returned if the verification token (used to verify a Customer's email address) is valid, but has
+ * expired according to the `verificationTokenDuration` setting in the AuthOptions.
+ */
+export type VerificationTokenExpiredError = ErrorResult & {
+    __typename?: 'VerificationTokenExpiredError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+/**
+ * Returned if the verification token (used to verify a Customer's email address) is either
+ * invalid or does not match any expected tokens.
+ */
+export type VerificationTokenInvalidError = ErrorResult & {
+    __typename?: 'VerificationTokenInvalidError';
+    errorCode: ErrorCode;
+    message: Scalars['String'];
+};
+
+export type VerifyCustomerAccountResult =
+    | CurrentUser
+    | VerificationTokenInvalidError
+    | VerificationTokenExpiredError
+    | MissingPasswordError
+    | PasswordValidationError
+    | PasswordAlreadySetError
+    | NativeAuthStrategyError;
+
+export type Zone = Node & {
+    __typename?: 'Zone';
+    id: Scalars['ID'];
+    createdAt: Scalars['DateTime'];
+    updatedAt: Scalars['DateTime'];
+    name: Scalars['String'];
+    members: Array<Country>;
+    customFields?: Maybe<Scalars['JSON']>;
+};

+ 18 - 0
packages/payments-plugin/src/mollie/mollie-shop-schema.ts

@@ -0,0 +1,18 @@
+import { gql } from 'graphql-tag';
+
+export const shopSchema = gql`
+    type MolliePaymentIntentError implements ErrorResult {
+        errorCode: ErrorCode!
+        message: String!
+    }
+    type MolliePaymentIntent {
+        url: String!
+    }
+    union MolliePaymentIntentResult = MolliePaymentIntent | MolliePaymentIntentError
+    input MolliePaymentIntentInput {
+        paymentMethodCode: String!
+    }
+    extend type Mutation {
+        createMolliePaymentIntent(input: MolliePaymentIntentInput!): MolliePaymentIntentResult!
+    }
+`;

+ 10 - 55
packages/payments-plugin/src/mollie/mollie.controller.ts

@@ -1,26 +1,12 @@
-import createMollieClient, { PaymentStatus } from '@mollie/api-client';
 import { Body, Controller, Param, Post } from '@nestjs/common';
-import {
-    ChannelService,
-    LanguageCode,
-    Logger,
-    OrderService,
-    Payment,
-    PaymentMethodService,
-    RequestContext,
-    TransactionalConnection,
-} from '@vendure/core';
+import { Logger } from '@vendure/core';
 
 import { loggerCtx } from './constants';
+import { MollieService } from './mollie.service';
 
 @Controller('payments')
 export class MollieController {
-    constructor(
-        private orderService: OrderService,
-        private connection: TransactionalConnection,
-        private paymentMethodService: PaymentMethodService,
-        private channelService: ChannelService,
-    ) {}
+    constructor(private mollieService: MollieService) {}
 
     @Post('mollie/:channelToken/:paymentMethodId')
     async webhook(
@@ -28,45 +14,14 @@ export class MollieController {
         @Param('paymentMethodId') paymentMethodId: string,
         @Body() body: any,
     ): Promise<void> {
-        const ctx = await this.createContext(channelToken);
-        Logger.info(`Received payment for ${channelToken}`, loggerCtx);
-        const paymentMethod = await this.paymentMethodService.findOne(ctx, paymentMethodId);
-        if (!paymentMethod) {
-            // Fail silently, as we don't want to expose if a paymentMethodId exists or not
-            return Logger.error(`No paymentMethod found with id ${paymentMethodId}`, loggerCtx);
+        if (!body.id) {
+            return Logger.warn(` Ignoring incoming webhook, because it has no body.id.`, loggerCtx);
         }
-        const apiKey = paymentMethod.handler.args.find(a => a.name === 'apiKey')?.value;
-        if (!apiKey) {
-            throw Error(`No apiKey found for payment ${paymentMethod.id} for channel ${channelToken}`);
+        try {
+            await this.mollieService.settlePayment({ channelToken, paymentMethodId, paymentId: body.id });
+        } catch (error) {
+            Logger.error(`Failed to process incoming webhook: ${error?.message}`, loggerCtx, error);
+            throw error;
         }
-        const client = createMollieClient({ apiKey });
-        const molliePayment = await client.payments.get(body.id);
-        Logger.info(
-            `Received payment ${molliePayment.id} for order ${molliePayment.metadata.orderCode} with status ${molliePayment.status}`,
-            loggerCtx,
-        );
-        const dbPayment = await this.connection
-            .getRepository(Payment)
-            .findOneOrFail({ where: { transactionId: molliePayment.id } });
-        if (molliePayment.status === PaymentStatus.paid) {
-            await this.orderService.settlePayment(ctx, dbPayment.id);
-            Logger.info(`Payment for order ${molliePayment.metadata.orderCode} settled`, loggerCtx);
-        } else {
-            Logger.warn(
-                `Received payment for order ${molliePayment.metadata.orderCode} with status ${molliePayment.status}`,
-                loggerCtx,
-            );
-        }
-    }
-
-    private async createContext(channelToken: string): Promise<RequestContext> {
-        const channel = await this.channelService.getChannelFromToken(channelToken);
-        return new RequestContext({
-            apiType: 'admin',
-            isAuthorized: true,
-            authorizedAsOwnerOnly: false,
-            channel,
-            languageCode: LanguageCode.en,
-        });
     }
 }

+ 19 - 54
packages/payments-plugin/src/mollie/mollie.handler.ts

@@ -9,12 +9,12 @@ import {
     PaymentMethodService,
     SettlePaymentResult,
 } from '@vendure/core';
+import { Permission } from '@vendure/core';
 
 import { loggerCtx, PLUGIN_INIT_OPTIONS } from './constants';
-import { MolliePluginOptions } from './mollie.plugin';
+import { MollieService } from './mollie.service';
 
-let paymentMethodService: PaymentMethodService;
-let options: MolliePluginOptions;
+let mollieService: MollieService;
 export const molliePaymentHandler = new PaymentMethodHandler({
     code: 'mollie-payment-handler',
     description: [
@@ -31,69 +31,34 @@ export const molliePaymentHandler = new PaymentMethodHandler({
         redirectUrl: {
             type: 'string',
             label: [{ languageCode: LanguageCode.en, value: 'Redirect URL' }],
+            description: [
+                { languageCode: LanguageCode.en, value: 'Redirect the client to this URL after payment' },
+            ],
         },
     },
     init(injector) {
-        paymentMethodService = injector.get(PaymentMethodService);
-        options = injector.get(PLUGIN_INIT_OPTIONS);
+        mollieService = injector.get(MollieService);
     },
     createPayment: async (
         ctx,
         order,
         amount,
         args,
-        _metadata,
+        metadata,
     ): Promise<CreatePaymentResult | CreatePaymentErrorResult> => {
-        try {
-            const { apiKey } = args;
-            let { redirectUrl } = args;
-            const paymentMethods = await paymentMethodService.findAll(ctx);
-            const paymentMethod = paymentMethods.items.find(
-                pm =>
-                    pm.handler.args.find(arg => arg.value === apiKey) &&
-                    pm.handler.args.find(arg => arg.value === redirectUrl),
-            );
-            if (!paymentMethod) {
-                throw Error(`No paymentMethod found for given apiKey`); // This should never happen
-            }
-            const mollieClient = createMollieClient({ apiKey });
-            redirectUrl = redirectUrl.endsWith('/') ? redirectUrl.slice(0, -1) : redirectUrl; // remove appending slash
-            const vendureHost = options.vendureHost.endsWith('/')
-                ? options.vendureHost.slice(0, -1)
-                : options.vendureHost; // remove appending slash
-            const payment = await mollieClient.payments.create({
-                amount: {
-                    value: `${(order.totalWithTax / 100).toFixed(2)}`,
-                    currency: order.currencyCode,
-                },
-                metadata: {
-                    orderCode: order.code,
-                },
-                description: `Order ${order.code}`,
-                redirectUrl: `${redirectUrl}/${order.code}`,
-                webhookUrl: `${vendureHost}/payments/mollie/${ctx.channel.token}/${paymentMethod.id}`,
-            });
-            return {
-                amount: order.totalWithTax,
-                transactionId: payment.id,
-                state: 'Authorized' as const,
-                metadata: {
-                    public: {
-                        redirectLink: payment.getPaymentUrl(),
-                    },
-                },
-            };
-        } catch (err: any) {
-            Logger.error(err, loggerCtx);
-            return {
-                amount: order.totalWithTax,
-                state: 'Error',
-                errorMessage: err.message,
-            };
+        // Creating a payment immediately settles the payment in Mollie flow, so only Admins and internal calls should be allowed to do this
+        if (ctx.apiType !== 'admin') {
+            throw Error(`CreatePayment is not allowed for apiType '${ctx.apiType}'`);
         }
+        return {
+            amount,
+            state: 'Settled' as const,
+            transactionId: metadata.paymentId,
+            metadata, // Store all given metadata on a payment
+        };
     },
-    settlePayment: async (order, payment, args): Promise<SettlePaymentResult> => {
-        // Settlement is handled by incoming webhook in mollie.controller.ts
+    settlePayment: async (ctx, order, payment, args): Promise<SettlePaymentResult> => {
+        // this should never be called
         return { success: true };
     },
     createRefund: async (ctx, input, amount, order, payment, args): Promise<CreateRefundResult> => {

+ 22 - 20
packages/payments-plugin/src/mollie/mollie.plugin.ts

@@ -1,8 +1,12 @@
 import { PluginCommonModule, RuntimeVendureConfig, VendurePlugin } from '@vendure/core';
+import { gql } from 'graphql-tag';
 
 import { PLUGIN_INIT_OPTIONS } from './constants';
+import { shopSchema } from './mollie-shop-schema';
 import { MollieController } from './mollie.controller';
 import { molliePaymentHandler } from './mollie.handler';
+import { MollieResolver } from './mollie.resolver';
+import { MollieService } from './mollie.service';
 
 /**
  * @description
@@ -53,32 +57,26 @@ export interface MolliePluginOptions {
  *
  * ## Storefront usage
  *
- * In your storefront you add a payment to an order using the `addPaymentToOrder` mutation. In this example, our Mollie
+ * In your storefront you add a payment to an order using the `createMolliePaymentIntent` mutation. In this example, our Mollie
  * PaymentMethod was given the code "mollie-payment-method".
  *
  * ```GraphQL
- * mutation AddPaymentToOrder {
- *   addPaymentToOrder(input: {
- *     method: "mollie-payment-method"
- *     metadata: {}
+ * mutation CreateMolliePaymentIntent {
+ *   createMolliePaymentIntent(input: {
+ *     paymentMethodCode: "mollie-payment-method"
  *   }) {
- *    ...on Order {
- *      id
- *      state
- *      payments {
- *          id
- *          metadata
- *      }
- *    }
- *    ...on ErrorResult {
- *      errorCode
- *      message
- *    }
+ *          ... on MolliePaymentIntent {
+                url
+            }
+            ... on MolliePaymentIntentError {
+                errorCode
+                message
+            }
  *   }
  * }
  * ```
- * The response will have
- * a `order.payments.metadata.public.redirectLink` in it, which can be used to redirect your customer to the Mollie
+ * The response will contain
+ * a redirectUrl, which can be used to redirect your customer to the Mollie
  * platform.
  *
  * After completing payment on the Mollie platform,
@@ -100,11 +98,15 @@ export interface MolliePluginOptions {
 @VendurePlugin({
     imports: [PluginCommonModule],
     controllers: [MollieController],
-    providers: [{ provide: PLUGIN_INIT_OPTIONS, useFactory: () => MolliePlugin.options }],
+    providers: [MollieService, { provide: PLUGIN_INIT_OPTIONS, useFactory: () => MolliePlugin.options }],
     configuration: (config: RuntimeVendureConfig) => {
         config.paymentOptions.paymentMethodHandlers.push(molliePaymentHandler);
         return config;
     },
+    shopApiExtensions: {
+        schema: shopSchema,
+        resolvers: [MollieResolver],
+    },
 })
 export class MolliePlugin {
     static options: MolliePluginOptions;

+ 33 - 0
packages/payments-plugin/src/mollie/mollie.resolver.ts

@@ -0,0 +1,33 @@
+import { Args, Mutation, ResolveField, Resolver } from '@nestjs/graphql';
+import { Allow, Ctx, Permission, RequestContext } from '@vendure/core';
+
+import {
+    MolliePaymentIntent,
+    MolliePaymentIntentError,
+    MolliePaymentIntentResult,
+} from './graphql/generated-shop-types';
+import { MollieService } from './mollie.service';
+
+@Resolver()
+export class MollieResolver {
+    constructor(private mollieService: MollieService) {}
+
+    @Mutation()
+    @Allow(Permission.Owner)
+    async createMolliePaymentIntent(
+        @Ctx() ctx: RequestContext,
+        @Args('input') input: { paymentMethodCode: string },
+    ): Promise<MolliePaymentIntentResult> {
+        return this.mollieService.createPaymentIntent(ctx, input.paymentMethodCode);
+    }
+
+    @ResolveField()
+    @Resolver('MolliePaymentIntentResult')
+    __resolveType(value: MolliePaymentIntentError | MolliePaymentIntent): string {
+        if ((value as MolliePaymentIntentError).errorCode) {
+            return 'MolliePaymentIntentError';
+        } else {
+            return 'MolliePaymentIntent';
+        }
+    }
+}

+ 191 - 0
packages/payments-plugin/src/mollie/mollie.service.ts

@@ -0,0 +1,191 @@
+import createMollieClient, { PaymentStatus } from '@mollie/api-client';
+import { Inject, Injectable } from '@nestjs/common';
+import {
+    ActiveOrderService,
+    ChannelService,
+    EntityHydrator,
+    LanguageCode,
+    Logger,
+    Order,
+    OrderService,
+    PaymentMethodService,
+    RequestContext,
+} from '@vendure/core';
+import { OrderStateTransitionError } from '@vendure/core/dist/common/error/generated-graphql-shop-errors';
+
+import { loggerCtx, PLUGIN_INIT_OPTIONS } from './constants';
+import {
+    ErrorCode,
+    MolliePaymentIntentError,
+    MolliePaymentIntentResult,
+} from './graphql/generated-shop-types';
+import { MolliePluginOptions } from './mollie.plugin';
+
+interface SettlePaymentInput {
+    channelToken: string;
+    paymentMethodId: string;
+    paymentId: string;
+}
+
+class PaymentIntentError implements MolliePaymentIntentError {
+    errorCode = ErrorCode.ORDER_PAYMENT_STATE_ERROR;
+    constructor(public message: string) {}
+}
+
+@Injectable()
+export class MollieService {
+    constructor(
+        private paymentMethodService: PaymentMethodService,
+        @Inject(PLUGIN_INIT_OPTIONS) private options: MolliePluginOptions,
+        private activeOrderService: ActiveOrderService,
+        private orderService: OrderService,
+        private channelService: ChannelService,
+        private entityHydrator: EntityHydrator,
+    ) {}
+
+    /**
+     * Creates a redirectUrl to Mollie for the given paymentMethod and current activeOrder
+     */
+    async createPaymentIntent(
+        ctx: RequestContext,
+        paymentMethodCode: string,
+    ): Promise<MolliePaymentIntentResult> {
+        const [order, paymentMethods] = await Promise.all([
+            this.activeOrderService.getOrderFromContext(ctx),
+            this.paymentMethodService.findAll(ctx),
+        ]);
+        if (!order) {
+            return new PaymentIntentError('No active order found for session');
+        }
+        await this.entityHydrator.hydrate(ctx, order, { relations: ['lines', 'customer', 'shippingLines'] });
+        if (!order.lines?.length) {
+            return new PaymentIntentError('Cannot create payment intent for empty order');
+        }
+        if (!order.customer) {
+            return new PaymentIntentError('Cannot create payment intent for order without customer');
+        }
+        if (!order.shippingLines?.length) {
+            return new PaymentIntentError('Cannot create payment intent for order without shippingMethod');
+        }
+        const paymentMethod = paymentMethods.items.find(pm => pm.code === paymentMethodCode);
+        if (!paymentMethod) {
+            return new PaymentIntentError(`No paymentMethod found with code ${paymentMethodCode}`);
+        }
+        const apiKeyArg = paymentMethod.handler.args.find(arg => arg.name === 'apiKey');
+        const redirectUrlArg = paymentMethod.handler.args.find(arg => arg.name === 'redirectUrl');
+        if (!apiKeyArg || !redirectUrlArg) {
+            Logger.warn(
+                `CreatePaymentIntent failed, because no apiKey or redirect is configured for ${paymentMethod.code}`,
+                loggerCtx,
+            );
+            return new PaymentIntentError(
+                `Paymentmethod ${paymentMethod.code} has no apiKey or redirectUrl configured`,
+            );
+        }
+        const apiKey = apiKeyArg.value;
+        let redirectUrl = redirectUrlArg.value;
+        const mollieClient = createMollieClient({ apiKey });
+        redirectUrl = redirectUrl.endsWith('/') ? redirectUrl.slice(0, -1) : redirectUrl; // remove appending slash
+        const vendureHost = this.options.vendureHost.endsWith('/')
+            ? this.options.vendureHost.slice(0, -1)
+            : this.options.vendureHost; // remove appending slash
+        const payment = await mollieClient.payments.create({
+            amount: {
+                value: `${(order.totalWithTax / 100).toFixed(2)}`,
+                currency: order.currencyCode,
+            },
+            metadata: {
+                orderCode: order.code,
+            },
+            description: `Order ${order.code}`,
+            redirectUrl: `${redirectUrl}/${order.code}`,
+            webhookUrl: `${vendureHost}/payments/mollie/${ctx.channel.token}/${paymentMethod.id}`,
+        });
+        const url = payment.getCheckoutUrl();
+        if (!url) {
+            throw Error(`Unable to getCheckoutUrl() from Mollie payment`);
+        }
+        return {
+            url,
+        };
+    }
+
+    /**
+     * Makes a request to Mollie to verify the given payment by id
+     */
+    async settlePayment({ channelToken, paymentMethodId, paymentId }: SettlePaymentInput): Promise<void> {
+        const ctx = await this.createContext(channelToken);
+        Logger.info(`Received payment for ${channelToken}`, loggerCtx);
+        const paymentMethod = await this.paymentMethodService.findOne(ctx, paymentMethodId);
+        if (!paymentMethod) {
+            // Fail silently, as we don't want to expose if a paymentMethodId exists or not
+            return Logger.warn(`No paymentMethod found with id ${paymentMethodId}`, loggerCtx);
+        }
+        const apiKey = paymentMethod.handler.args.find(a => a.name === 'apiKey')?.value;
+        if (!apiKey) {
+            throw Error(`No apiKey found for payment ${paymentMethod.id} for channel ${channelToken}`);
+        }
+        const client = createMollieClient({ apiKey });
+        const molliePayment = await client.payments.get(paymentId);
+        const orderCode = molliePayment.metadata.orderCode;
+        if (molliePayment.status !== PaymentStatus.paid) {
+            return Logger.warn(
+                `Received payment for ${channelToken} for order ${orderCode} with status ${molliePayment.status}`,
+                loggerCtx,
+            );
+        }
+        if (!orderCode) {
+            throw Error(
+                `Molliepayment does not have metadata.orderCode, unable to settle payment ${molliePayment.id}!`,
+            );
+        }
+        Logger.info(
+            `Received payment ${molliePayment.id} for order ${orderCode} with status ${molliePayment.status}`,
+            loggerCtx,
+        );
+        const order = await this.orderService.findOneByCode(ctx, orderCode);
+        if (!order) {
+            throw Error(`Unable to find order ${orderCode}, unable to settle payment ${molliePayment.id}!`);
+        }
+        if (order.state !== 'ArrangingPayment') {
+            const transitionToStateResult = await this.orderService.transitionToState(
+                ctx,
+                order.id,
+                'ArrangingPayment',
+            );
+            if (transitionToStateResult instanceof OrderStateTransitionError) {
+                throw Error(
+                    `Error transitioning order ${order.code} from ${transitionToStateResult.fromState} to ${transitionToStateResult.toState}: ${transitionToStateResult.message}`,
+                );
+            }
+        }
+        const addPaymentToOrderResult = await this.orderService.addPaymentToOrder(ctx, order.id, {
+            method: paymentMethod.code,
+            metadata: {
+                paymentId: molliePayment.id,
+                mode: molliePayment.mode,
+                method: molliePayment.method,
+                profileId: molliePayment.profileId,
+                settlementAmount: molliePayment.settlementAmount,
+                customerId: molliePayment.customerId,
+                authorizedAt: molliePayment.authorizedAt,
+                paidAt: molliePayment.paidAt,
+            },
+        });
+        if (!(addPaymentToOrderResult instanceof Order)) {
+            throw Error(`Error adding payment to order ${orderCode}: ${addPaymentToOrderResult.message}`);
+        }
+        Logger.info(`Payment for order ${molliePayment.metadata.orderCode} settled`, loggerCtx);
+    }
+
+    private async createContext(channelToken: string): Promise<RequestContext> {
+        const channel = await this.channelService.getChannelFromToken(channelToken);
+        return new RequestContext({
+            apiType: 'admin',
+            isAuthorized: true,
+            authorizedAsOwnerOnly: false,
+            channel,
+            languageCode: LanguageCode.en,
+        });
+    }
+}

+ 4 - 1
packages/payments-plugin/src/stripe/stripe.handler.ts

@@ -28,9 +28,12 @@ export const stripePaymentMethodHandler = new PaymentMethodHandler({
         stripeService = injector.get(StripeService);
     },
 
-    async createPayment(_, __, amount, ___, metadata): Promise<CreatePaymentResult> {
+    async createPayment(ctx, _, amount, ___, metadata): Promise<CreatePaymentResult> {
         // Payment is already settled in Stripe by the time the webhook in stripe.controller.ts
         // adds the payment to the order
+        if (ctx.apiType !== 'admin') {
+            throw Error(`CreatePayment is not allowed for apiType '${ctx.apiType}'`);
+        }
         return {
             amount,
             state: 'Settled' as const,

+ 46 - 9
packages/ui-devkit/src/compiler/compile.ts

@@ -9,7 +9,12 @@ import * as path from 'path';
 import { DEFAULT_BASE_HREF, MODULES_OUTPUT_DIR } from './constants';
 import { copyGlobalStyleFile, setupScaffold } from './scaffold';
 import { getAllTranslationFiles, mergeExtensionTranslations } from './translations';
-import { Extension, StaticAssetDefinition, UiExtensionCompilerOptions } from './types';
+import {
+    Extension,
+    StaticAssetDefinition,
+    UiExtensionCompilerOptions,
+    UiExtensionCompilerProcessArgument,
+} from './types';
 import {
     copyStaticAsset,
     copyUiDevkit,
@@ -31,7 +36,8 @@ import {
 export function compileUiExtensions(
     options: UiExtensionCompilerOptions,
 ): AdminUiAppConfig | AdminUiAppDevModeConfig {
-    const { outputPath, baseHref, devMode, watchPort, extensions, command } = options;
+    const { outputPath, baseHref, devMode, watchPort, extensions, command, additionalProcessArguments } =
+        options;
     const usingYarn = options.command && options.command === 'npm' ? false : shouldUseYarn();
     if (devMode) {
         return runWatchMode(
@@ -40,9 +46,16 @@ export function compileUiExtensions(
             watchPort || 4200,
             extensions,
             usingYarn,
+            additionalProcessArguments,
         );
     } else {
-        return runCompileMode(outputPath, baseHref || DEFAULT_BASE_HREF, extensions, usingYarn);
+        return runCompileMode(
+            outputPath,
+            baseHref || DEFAULT_BASE_HREF,
+            extensions,
+            usingYarn,
+            additionalProcessArguments,
+        );
     }
 }
 
@@ -51,6 +64,7 @@ function runCompileMode(
     baseHref: string,
     extensions: Extension[],
     usingYarn: boolean,
+    args?: UiExtensionCompilerProcessArgument[],
 ): AdminUiAppConfig {
     const cmd = usingYarn ? 'yarn' : 'npm';
     const distPath = path.join(outputPath, 'dist');
@@ -58,7 +72,13 @@ function runCompileMode(
     const compile = () =>
         new Promise<void>(async (resolve, reject) => {
             await setupScaffold(outputPath, extensions);
-            const commandArgs = ['run', 'build', `--outputPath=${distPath}`, `--base-href=${baseHref}`];
+            const commandArgs = [
+                'run',
+                'build',
+                `--outputPath=${distPath}`,
+                `--base-href=${baseHref}`,
+                ...buildProcessArguments(args),
+            ];
             if (!usingYarn) {
                 // npm requires `--` before any command line args being passed to a script
                 commandArgs.splice(2, 0, '--');
@@ -91,6 +111,7 @@ function runWatchMode(
     port: number,
     extensions: Extension[],
     usingYarn: boolean,
+    args?: UiExtensionCompilerProcessArgument[],
 ): AdminUiAppDevModeConfig {
     const cmd = usingYarn ? 'yarn' : 'npm';
     const devkitPath = require.resolve('@vendure/ui-devkit');
@@ -107,11 +128,15 @@ function runWatchMode(
             const globalStylesExtensions = extensions.filter(isGlobalStylesExtension);
             const staticAssetExtensions = extensions.filter(isStaticAssetExtension);
             const allTranslationFiles = getAllTranslationFiles(extensions.filter(isTranslationExtension));
-            buildProcess = spawn(cmd, ['run', 'start', `--port=${port}`, `--base-href=${baseHref}`], {
-                cwd: outputPath,
-                shell: true,
-                stdio: 'inherit',
-            });
+            buildProcess = spawn(
+                cmd,
+                ['run', 'start', `--port=${port}`, `--base-href=${baseHref}`, ...buildProcessArguments(args)],
+                {
+                    cwd: outputPath,
+                    shell: true,
+                    stdio: 'inherit',
+                },
+            );
 
             buildProcess.on('close', code => {
                 if (code !== 0) {
@@ -238,6 +263,18 @@ function runWatchMode(
     return { sourcePath: outputPath, port, compile, route: baseHrefToRoute(baseHref) };
 }
 
+function buildProcessArguments(args?: UiExtensionCompilerProcessArgument[]): string[] {
+    return (args ?? []).map(arg => {
+        if (Array.isArray(arg)) {
+            const [key, value] = arg;
+
+            return `${key}=${value}`;
+        }
+
+        return arg;
+    });
+}
+
 function baseHrefToRoute(baseHref: string): string {
     return baseHref.replace(/^\//, '').replace(/\/$/, '');
 }

+ 23 - 0
packages/ui-devkit/src/compiler/types.ts

@@ -173,6 +173,14 @@ export interface AdminUiExtensionLazyModule {
     ngModuleName: string;
 }
 
+/**
+ * @description
+ * Argument to configure process (watch or compile)
+ *
+ * @docsCategory UiDevkit
+ */
+export type UiExtensionCompilerProcessArgument = string | [string, any];
+
 /**
  * @description
  * Options to configure how the Admin UI should be compiled.
@@ -218,6 +226,7 @@ export interface UiExtensionCompilerOptions {
      * @default 4200 | undefined
      */
     watchPort?: number;
+
     /**
      * @description
      * Internally, the Angular CLI will be invoked as an npm script. By default, the compiler will use Yarn
@@ -227,6 +236,20 @@ export interface UiExtensionCompilerOptions {
      * @since 1.5.0
      */
     command?: 'yarn' | 'npm';
+
+    /**
+     * @description
+     * Additional command-line arguments which will get passed to the [ng build](https://angular.io/cli/build)
+     * command (or [ng serve](https://angular.io/cli/serve) if `devMode = true`).
+     *
+     * @example
+     * ['--disable-host-check'] // to disable host check
+     *
+     * @default undefined
+     *
+     * @since 1.5.0
+     */
+    additionalProcessArguments?: UiExtensionCompilerProcessArgument[];
 }
 
 export type Translations = {

+ 6 - 0
scripts/codegen/generate-graphql-types.ts

@@ -181,6 +181,12 @@ Promise.all([
                         plugins: clientPlugins,
                         config: e2eConfig,
                     },
+                [path.join(__dirname, '../../packages/payments-plugin/src/mollie/graphql/generated-shop-types.ts')]:
+                    {
+                        schema: [SHOP_SCHEMA_OUTPUT_FILE, path.join(__dirname, '../../packages/payments-plugin/src/mollie/mollie-shop-schema.ts')],
+                        plugins: clientPlugins,
+                        config,
+                    },
             },
         });
     })

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott