From 9cc8efc5099492d2475dc54cdeb2903bbf874d9f Mon Sep 17 00:00:00 2001 From: Robert Field Date: Tue, 14 Nov 2023 16:52:57 +0000 Subject: [PATCH 1/6] chore: updated typos --- packages/composable-cli/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/composable-cli/README.md b/packages/composable-cli/README.md index 0d01b8f4..da01c217 100644 --- a/packages/composable-cli/README.md +++ b/packages/composable-cli/README.md @@ -10,7 +10,7 @@ This package contains the executable for running [Elastic Path Commerce Cloud](h ## Generating a storefront -### Login to Elasticpath +### Login to Elastic Path ```bash composable-cli login @@ -22,7 +22,7 @@ composable-cli login composable-cli generate d2c my-storefront ``` -Select your Elasticpath store from the list of stores. +Select your Elastic Path store from the list of stores. ### Getting help @@ -38,9 +38,9 @@ composable-cli --help composable-cli int algolia ``` -## Payments Setup +## Elastic Path Payments Setup -### Configuring Ep Payments +### Configuring Elastic Path Payments ```bash composable-cli p ep-payments From d0397d19cac556e8d3e14acf5a71cd47a8a8cfee Mon Sep 17 00:00:00 2001 From: Robert Field Date: Tue, 14 Nov 2023 16:54:11 +0000 Subject: [PATCH 2/6] build: fixed includes --- packages/react-shopper-hooks/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-shopper-hooks/tsconfig.json b/packages/react-shopper-hooks/tsconfig.json index 85232eae..34a14ce6 100644 --- a/packages/react-shopper-hooks/tsconfig.json +++ b/packages/react-shopper-hooks/tsconfig.json @@ -25,5 +25,5 @@ }, "lib": ["dom", "dom.iterable", "esnext"], }, - "include": ["lib", "example"] + "include": ["lib/**/*", "example/**/*"] } \ No newline at end of file From 54e5668d1ac450b07887155e452046cda7762215 Mon Sep 17 00:00:00 2001 From: Robert Field Date: Tue, 14 Nov 2023 17:02:19 +0000 Subject: [PATCH 3/6] feat: added manual gateway to d2c schematics for mocking out a checkout flow --- packages/d2c-schematics/collection.json | 5 + .../enter-payment-information.ts.template | 10 ++ .../files/e2e/util/gateway-check.ts.template | 13 ++ .../payments/CheckoutForm.tsx.template | 137 ++++++++++++++++++ .../manual-payment-gateway/index.ts | 35 +++++ .../manual-payment-gateway/schema.json | 50 +++++++ packages/d2c-schematics/tsup.config.ts | 5 + 7 files changed, 255 insertions(+) create mode 100644 packages/d2c-schematics/manual-payment-gateway/files/e2e/util/enter-payment-information.ts.template create mode 100644 packages/d2c-schematics/manual-payment-gateway/files/e2e/util/gateway-check.ts.template create mode 100644 packages/d2c-schematics/manual-payment-gateway/files/src/components/checkout/payments/CheckoutForm.tsx.template create mode 100644 packages/d2c-schematics/manual-payment-gateway/index.ts create mode 100644 packages/d2c-schematics/manual-payment-gateway/schema.json diff --git a/packages/d2c-schematics/collection.json b/packages/d2c-schematics/collection.json index f0080a1b..2cde2635 100644 --- a/packages/d2c-schematics/collection.json +++ b/packages/d2c-schematics/collection.json @@ -97,6 +97,11 @@ "factory": "./ep-payments-payment-gateway", "schema": "./ep-payments-payment-gateway/schema.json", "description": "Create components for EP Payments payment gateway." + }, + "manual-payment-gateway": { + "factory": "./manual-payment-gateway", + "schema": "./manual-payment-gateway/schema.json", + "description": "Create components for EP Payments payment gateway." } } } diff --git a/packages/d2c-schematics/manual-payment-gateway/files/e2e/util/enter-payment-information.ts.template b/packages/d2c-schematics/manual-payment-gateway/files/e2e/util/enter-payment-information.ts.template new file mode 100644 index 00000000..738196b7 --- /dev/null +++ b/packages/d2c-schematics/manual-payment-gateway/files/e2e/util/enter-payment-information.ts.template @@ -0,0 +1,10 @@ +import { Page } from "@playwright/test"; +import { fillAllFormFields, FormInput } from "./fill-form-field"; + +export async function enterPaymentInformation(page: Page, values: FormInput) { + const paymentIframe = await page + .locator('[id="payment-element"]') + .frameLocator("iframe"); + + await fillAllFormFields(paymentIframe, values); +} diff --git a/packages/d2c-schematics/manual-payment-gateway/files/e2e/util/gateway-check.ts.template b/packages/d2c-schematics/manual-payment-gateway/files/e2e/util/gateway-check.ts.template new file mode 100644 index 00000000..b1ecf29c --- /dev/null +++ b/packages/d2c-schematics/manual-payment-gateway/files/e2e/util/gateway-check.ts.template @@ -0,0 +1,13 @@ +import type { Moltin as EPCCClient } from "@moltin/sdk"; + +export async function gatewayCheck(client: EPCCClient): Promise { + try { + const gateways = await client.Gateways.All(); + const epPaymentGateway = gateways.data.find( + (gateway) => gateway.slug === "elastic_path_payments_stripe", + )?.enabled; + return !!epPaymentGateway; + } catch (err) { + return false; + } +} diff --git a/packages/d2c-schematics/manual-payment-gateway/files/src/components/checkout/payments/CheckoutForm.tsx.template b/packages/d2c-schematics/manual-payment-gateway/files/src/components/checkout/payments/CheckoutForm.tsx.template new file mode 100644 index 00000000..0878af72 --- /dev/null +++ b/packages/d2c-schematics/manual-payment-gateway/files/src/components/checkout/payments/CheckoutForm.tsx.template @@ -0,0 +1,137 @@ +import { Form, Formik } from "formik"; +import { + CheckoutForm as CheckoutFormType, + checkoutFormSchema, +} from "../form-schema/checkout-form-schema"; +import { PaymentRequestBody } from "@moltin/sdk"; +import ShippingForm from "../ShippingForm"; +import CustomFormControl from "../CustomFormControl"; +import BillingForm from "../BillingForm"; +import { useCart, useStore } from "@elasticpath/react-shopper-hooks"; +import { CheckoutFormComponent } from "../types/checkout-form"; +import { toFormikValidationSchema } from "zod-formik-adapter"; + +const initialValues: Partial = { + personal: { + email: "", + }, + sameAsShipping: true, + shippingAddress: { + first_name: "", + last_name: "", + line_1: "", + country: "", + region: "", + postcode: "", + }, +}; + +const CheckoutForm: CheckoutFormComponent = ({ + showCompletedOrder, +}): JSX.Element => { + const { client } = useStore(); + const { checkout } = useCart(); + + return ( + <> + { + const { + personal, + shippingAddress, + billingAddress, + sameAsShipping, + } = validatedValues; + + const payment: PaymentRequestBody = { + gateway: "manual", + method: "purchase", + }; + + const orderResponse = await checkout( + personal.email, + shippingAddress, + payment, + sameAsShipping, + billingAddress ?? undefined, + ); + + showCompletedOrder(orderResponse, validatedValues); + }} + > + {({ handleChange, values, isSubmitting }) => ( +
+
+
+

+ {" "} + Contact Information +

+ +
+
+

Shipping Address

+ + +
+ {!values.sameAsShipping && } + + +
+
+ )} +
+ + ); +}; + +export default CheckoutForm; diff --git a/packages/d2c-schematics/manual-payment-gateway/index.ts b/packages/d2c-schematics/manual-payment-gateway/index.ts new file mode 100644 index 00000000..feec072d --- /dev/null +++ b/packages/d2c-schematics/manual-payment-gateway/index.ts @@ -0,0 +1,35 @@ +import { + MergeStrategy, + Rule, + Tree, + apply, + applyTemplates, + chain, + mergeWith, + move, + strings, + url, + noop, + filter, +} from "@angular-devkit/schematics" +import { Schema as ManualPaymentGatewayOptions } from "./schema" + +export default function (options: ManualPaymentGatewayOptions): Rule { + return async (_host: Tree) => { + return chain([ + mergeWith( + apply(url("./files"), [ + options.skipTests + ? filter((path) => !path.endsWith(".spec.ts.template")) + : noop(), + applyTemplates({ + utils: strings, + ...options, + }), + move(options.path || ""), + ]), + MergeStrategy.Overwrite, + ), + ]) + } +} diff --git a/packages/d2c-schematics/manual-payment-gateway/schema.json b/packages/d2c-schematics/manual-payment-gateway/schema.json new file mode 100644 index 00000000..390cddf7 --- /dev/null +++ b/packages/d2c-schematics/manual-payment-gateway/schema.json @@ -0,0 +1,50 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "ManualPaymentGatewayCheckout", + "title": "Manual Payment Gateway Options Schema", + "type": "object", + "description": "Generates checkout page and supporting functionality in the workspace.", + "additionalProperties": true, + "properties": { + "skipTests": { + "description": "Do not create \"spec.ts\" test files for the application.", + "type": "boolean", + "default": false, + "alias": "S" + }, + "skipConfig": { + "description": "Do not configure Checkout in Elastic Path Commerce Cloud.", + "type": "boolean", + "default": false + }, + "directory": { + "type": "string", + "description": "The directory name to create the workspace in." + }, + "path": { + "type": "string", + "format": "path", + "$default": { + "$source": "workingDirectory" + }, + "description": "The path at which to create the component file, relative to the current workspace. Default is a folder with the same name as the component in the project root.", + "visible": false + }, + "epccClientId": { + "description": "The client id value for an Elastic Path Commerce Cloud store.", + "type": "string", + "visible": "false" + }, + "epccClientSecret": { + "description": "The client secret value for an Elastic Path Commerce Cloud store.", + "type": "string", + "visible": "false" + }, + "epccEndpointUrl": { + "description": "The geographical endpoint url for an Elastic Path Commerce Cloud store.", + "type": "string", + "visible": "false" + } + }, + "required": ["epccClientId", "epccClientSecret", "epccEndpointUrl", "directory"] +} diff --git a/packages/d2c-schematics/tsup.config.ts b/packages/d2c-schematics/tsup.config.ts index 474575a3..6aa46fe4 100644 --- a/packages/d2c-schematics/tsup.config.ts +++ b/packages/d2c-schematics/tsup.config.ts @@ -29,6 +29,7 @@ export default defineConfig(() => { "checkout/index": "./checkout/index.ts", "ep-payments-payment-gateway/index": "./ep-payments-payment-gateway/index.ts", + "manual-payment-gateway/index": "./manual-payment-gateway/index.ts", }, format: ["cjs"], clean: false, @@ -127,6 +128,10 @@ export default defineConfig(() => { from: ["./ep-payments-payment-gateway/**/*"], to: ["./dist/ep-payments-payment-gateway"], }, + { + from: ["./manual-payment-gateway/**/*"], + to: ["./dist/manual-payment-gateway"], + }, ], }), ], From caae49a1dcdb89df36b03ebfc22cc56f95bda9fd Mon Sep 17 00:00:00 2001 From: Robert Field Date: Tue, 14 Nov 2023 17:21:22 +0000 Subject: [PATCH 4/6] feat: support manual gateway is cli as basic quick start option --- .../src/commands/generate/d2c/d2c-command.tsx | 8 ++++---- packages/d2c-schematics/checkout/index.ts | 6 ++++-- packages/d2c-schematics/checkout/schema.json | 2 +- packages/d2c-schematics/d2c/schema.json | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/composable-cli/src/commands/generate/d2c/d2c-command.tsx b/packages/composable-cli/src/commands/generate/d2c/d2c-command.tsx index 56a25974..b83f71ca 100644 --- a/packages/composable-cli/src/commands/generate/d2c/d2c-command.tsx +++ b/packages/composable-cli/src/commands/generate/d2c/d2c-command.tsx @@ -629,7 +629,7 @@ type PaymentTypeOptions = epPaymentsStripeAccountId: string epPaymentsStripePublishableKey: string } - | { paymentGatewayType: "None" } + | { paymentGatewayType: "Manual" } type PlpTypeOptions = | { @@ -678,8 +678,8 @@ async function schematicOptionPrompts(): Promise<{ value: "EP Payments", }, { - name: "None", - value: "None", + name: "Basic (quick start)", + value: "Manual", }, ], }, @@ -688,7 +688,7 @@ async function schematicOptionPrompts(): Promise<{ const paymentGateway = paymentGatewayType === "EP Payments" ? await epPaymentsSchematicPrompts() - : { paymentGatewayType: "None" as const } + : { paymentGatewayType: "Manual" as const } return { plp, diff --git a/packages/d2c-schematics/checkout/index.ts b/packages/d2c-schematics/checkout/index.ts index 475cda39..3fc84692 100644 --- a/packages/d2c-schematics/checkout/index.ts +++ b/packages/d2c-schematics/checkout/index.ts @@ -38,18 +38,20 @@ export default function (options: CheckoutOptions): Rule { }), move(options.path || ""), ]), - MergeStrategy.Overwrite + MergeStrategy.Overwrite, ), ]) } } function resolveGatewaySchematic( - gateway: CheckoutOptions["paymentGatewayType"] + gateway: CheckoutOptions["paymentGatewayType"], ): string { switch (gateway) { case "EP Payments": return "ep-payments-payment-gateway" + case "Manual": + return "manual-payment-gateway" case "None": return "none" default: diff --git a/packages/d2c-schematics/checkout/schema.json b/packages/d2c-schematics/checkout/schema.json index fc20395b..d6896c77 100644 --- a/packages/d2c-schematics/checkout/schema.json +++ b/packages/d2c-schematics/checkout/schema.json @@ -33,7 +33,7 @@ "paymentGatewayType": { "description": "The type of payment gateway to use for your checkout experience.", "type": "string", - "enum": ["EP Payments", "None"], + "enum": ["EP Payments", "Manual", "None"], "x-prompt": "Which payment gateway do you want to use?" }, "epccClientId": { diff --git a/packages/d2c-schematics/d2c/schema.json b/packages/d2c-schematics/d2c/schema.json index d8a95766..926f1fa0 100644 --- a/packages/d2c-schematics/d2c/schema.json +++ b/packages/d2c-schematics/d2c/schema.json @@ -84,7 +84,7 @@ "plpType": { "description": "The type of product list page to use.", "type": "string", - "enum": ["Algolia", "None"] + "enum": ["Algolia", "Manual", "None"] } }, "required": ["name", "epccClientId", "epccClientSecret", "epccEndpointUrl"] From b9565b8e6d6534caa78112a5b1754eaad99c726a Mon Sep 17 00:00:00 2001 From: Robert Field Date: Tue, 14 Nov 2023 18:06:55 +0000 Subject: [PATCH 5/6] feat: manual payment gateway support --- .../src/commands/generate/d2c/d2c-command.tsx | 42 +++++ .../commands/generate/generate-command.tsx | 2 + .../ep-payments/ep-payments-command.tsx | 6 +- .../payments/manual/manual-command.tsx | 159 ++++++++++++++++++ .../manual/manual-integration.types.ts | 20 +++ .../manual/util/ep-payments-schema.ts | 8 + .../manual/util/setup-ep-payments-schema.ts | 14 ++ .../manual/util/setup-epcc-manual-gateway.ts | 51 ++++++ .../payments/manual/util/update-gateway.ts | 37 ++++ .../commands/payments/payments-command.tsx | 2 + .../src/commands/store/store-command.tsx | 2 +- 11 files changed, 337 insertions(+), 6 deletions(-) create mode 100644 packages/composable-cli/src/commands/payments/manual/manual-command.tsx create mode 100644 packages/composable-cli/src/commands/payments/manual/manual-integration.types.ts create mode 100644 packages/composable-cli/src/commands/payments/manual/util/ep-payments-schema.ts create mode 100644 packages/composable-cli/src/commands/payments/manual/util/setup-ep-payments-schema.ts create mode 100644 packages/composable-cli/src/commands/payments/manual/util/setup-epcc-manual-gateway.ts create mode 100644 packages/composable-cli/src/commands/payments/manual/util/update-gateway.ts diff --git a/packages/composable-cli/src/commands/generate/d2c/d2c-command.tsx b/packages/composable-cli/src/commands/generate/d2c/d2c-command.tsx index b83f71ca..f931bf45 100644 --- a/packages/composable-cli/src/commands/generate/d2c/d2c-command.tsx +++ b/packages/composable-cli/src/commands/generate/d2c/d2c-command.tsx @@ -55,6 +55,10 @@ import { paramCase } from "change-case" import { retrieveComposableRcFile } from "../../../lib/config-middleware" import findUp from "find-up" import path from "path" +import { + createManualPaymentCommandHandler, + isManualGatewayAlreadyExistsError, +} from "../../payments/manual/manual-command" export function createD2CCommand( ctx: CommandContext, @@ -537,6 +541,44 @@ export function createD2CCommandHandler( } } + if (gatheredOptions.paymentGatewayType === "Manual") { + logger.info( + boxen( + `${colors.bold.green( + "Basic checkout needs to be configured", + )}\nTo get your checkout working you need to configure basic checkout which is powered by manual gateway.`, + { + padding: 1, + margin: 1, + }, + ), + ) + + const { configureManualGateway } = await inquirer.prompt([ + { + type: "confirm", + name: "configureManualGateway", + message: "Do you want to configure Basic checkout?", + }, + ]) + + if (configureManualGateway) { + const result = await createManualPaymentCommandHandler(updatedCtx)({ + ...args, + }) + + if ( + !result.success && + isManualGatewayAlreadyExistsError(result.error) + ) { + notes.push({ + title: "Basic checkout setup", + description: "The Manual payment gateway was already setup.", + }) + } + } + } + if (gatheredOptions.paymentGatewayType === "EP Payments") { logger.info( boxen( diff --git a/packages/composable-cli/src/commands/generate/generate-command.tsx b/packages/composable-cli/src/commands/generate/generate-command.tsx index b85f1bff..4c47b6fb 100644 --- a/packages/composable-cli/src/commands/generate/generate-command.tsx +++ b/packages/composable-cli/src/commands/generate/generate-command.tsx @@ -121,6 +121,8 @@ export function createActiveStoreMiddleware( } if (hasActiveStore(store) || !isTTY()) { + const activeStore = ctx.store.get("store") as Record + ctx.logger.info(`Using store: ${activeStore?.name} - ${activeStore?.id}`) return } diff --git a/packages/composable-cli/src/commands/payments/ep-payments/ep-payments-command.tsx b/packages/composable-cli/src/commands/payments/ep-payments/ep-payments-command.tsx index bbb420df..1bf4148a 100644 --- a/packages/composable-cli/src/commands/payments/ep-payments/ep-payments-command.tsx +++ b/packages/composable-cli/src/commands/payments/ep-payments/ep-payments-command.tsx @@ -7,10 +7,7 @@ import { } from "./ep-payments-integration.types" import { CommandContext, CommandHandlerFunction } from "../../../types/command" import { trackCommandHandler } from "../../../util/track-command-handler" -import { - createActiveStoreMiddleware, - createAuthenticationCheckerMiddleware, -} from "../../generate/generate-command" +import { createAuthenticationCheckerMiddleware } from "../../generate/generate-command" import { PaymentsCommandArguments } from "../payments.types" import * as ansiColors from "ansi-colors" import inquirer from "inquirer" @@ -38,7 +35,6 @@ export function createEPPaymentsCommand( builder: async (yargs) => { return yargs .middleware(createAuthenticationCheckerMiddleware(ctx)) - .middleware(createActiveStoreMiddleware(ctx)) .option("account-id", { type: "string", description: "EP Payments account ID", diff --git a/packages/composable-cli/src/commands/payments/manual/manual-command.tsx b/packages/composable-cli/src/commands/payments/manual/manual-command.tsx new file mode 100644 index 00000000..3bf298da --- /dev/null +++ b/packages/composable-cli/src/commands/payments/manual/manual-command.tsx @@ -0,0 +1,159 @@ +import yargs from "yargs" + +import { CommandContext, CommandHandlerFunction } from "../../../types/command" +import { trackCommandHandler } from "../../../util/track-command-handler" +import { createAuthenticationCheckerMiddleware } from "../../generate/generate-command" +import { PaymentsCommandArguments } from "../payments.types" +import inquirer from "inquirer" +import { isTTY } from "../../../util/is-tty" +import boxen from "boxen" +import ora from "ora" +import { setupManualPaymentGateway } from "./util/setup-epcc-manual-gateway" +import { EPPaymentsForce } from "./util/setup-ep-payments-schema" +import { processUnknownError } from "../../../util/process-unknown-error" +import { checkGateway } from "@elasticpath/composable-common" +import { + ManualCommandArguments, + ManualCommandData, + ManualCommandError, + ManualCommandErrorAlreadyExists, +} from "./manual-integration.types" + +export function createManualPaymentCommand( + ctx: CommandContext, +): yargs.CommandModule { + return { + command: "manual", + describe: + "setup EP Payment gateway for your Elastic Path powered storefront", + builder: async (yargs) => { + return yargs + .middleware(createAuthenticationCheckerMiddleware(ctx)) + .option("force", { + type: "boolean", + description: "Force setup of Manual even if already enabled", + }) + .fail(false) + .help() + }, + handler: ctx.handleErrors( + trackCommandHandler(ctx, createManualPaymentCommandHandler), + ), + } +} + +export function createManualPaymentCommandHandler( + ctx: CommandContext, +): CommandHandlerFunction< + ManualCommandData, + ManualCommandError, + ManualCommandArguments +> { + return async function manualPaymentCommandHandler(args) { + const spinner = ora() + + const { epClient, logger } = ctx + + try { + if (!epClient) { + spinner.fail(`Failed to setup Manual Payment.`) + return { + success: false, + error: { + code: "missing_ep_client", + message: "Failed to setup Manual Payment - missing EP client", + }, + } + } + + spinner.start(`Checking if Manual gateway already exists...`) + // check if Manual gateway is already setup + if (!args.force) { + const checkGatewayResult = await checkGateway(epClient, "manual") + spinner.stop() + + if (checkGatewayResult.success) { + const forceResult = await resolveForceOptions(args) + + if (!forceResult.force) { + spinner.fail( + `Manual gateway already exists and you didn't want to force an update.`, + ) + + logger.warn( + boxen("`Manual was already setup.", { + padding: 1, + borderColor: "yellow", + }), + ) + return { + success: false, + error: { + code: "manual_already_setup", + message: "Manual was already setup", + }, + } + } + } + } + + spinner.start(`Setting up Manual gateway...`) + const result = await setupManualPaymentGateway(epClient, logger) + + if (!result.success) { + spinner.fail(`Failed to setup Manual Gateway.`) + return { + success: false, + error: { + code: "manual_gateway_setup_failed", + message: "Failed to setup Manual", + }, + } + } + + spinner.succeed(`Manual setup successfully.`) + + return { + success: true, + data: {}, + } + } catch (e) { + spinner.fail(`Failed to setup Manual gateway.`) + logger.error(processUnknownError(e)) + return { + success: false, + error: { + code: "FAILED_TO_SETUP_MANUAL_GATEWAY", + message: "Failed to setup Manual gateway", + }, + } + } + } +} + +export function isManualGatewayAlreadyExistsError( + error: ManualCommandError, +): error is ManualCommandErrorAlreadyExists { + return error.code === "manual_already_setup" +} + +async function resolveForceOptions( + args: ManualCommandArguments, +): Promise { + if (args.interactive && isTTY()) { + const { force } = await inquirer.prompt([ + { + type: "confirm", + name: "force", + message: "Manual is already enabled would you like update anyway?", + }, + ]) + return { + force, + } + } + + throw new Error( + `Invalid arguments: Manual is already enabled and missing force argument`, + ) +} diff --git a/packages/composable-cli/src/commands/payments/manual/manual-integration.types.ts b/packages/composable-cli/src/commands/payments/manual/manual-integration.types.ts new file mode 100644 index 00000000..483666c6 --- /dev/null +++ b/packages/composable-cli/src/commands/payments/manual/manual-integration.types.ts @@ -0,0 +1,20 @@ +import { PaymentsCommandArguments } from "../payments.types" + +export type ManualCommandData = {} + +export type ManualCommandError = + | { + code: string + message: string + } + | ManualCommandErrorAlreadyExists + +export type ManualCommandErrorAlreadyExists = { + code: "manual_already_setup" + message: string + accountId: string +} + +export type ManualCommandArguments = { + force?: boolean +} & PaymentsCommandArguments diff --git a/packages/composable-cli/src/commands/payments/manual/util/ep-payments-schema.ts b/packages/composable-cli/src/commands/payments/manual/util/ep-payments-schema.ts new file mode 100644 index 00000000..4203b6f6 --- /dev/null +++ b/packages/composable-cli/src/commands/payments/manual/util/ep-payments-schema.ts @@ -0,0 +1,8 @@ +import { z } from "zod" + +export const manualGatewaySettingsSchema = z.object({ + epPaymentsStripeAccountId: z.string().min(1), + epPaymentsStripePublishableKey: z.string().min(1), +}) + +export type ManualGatewaySettings = z.TypeOf diff --git a/packages/composable-cli/src/commands/payments/manual/util/setup-ep-payments-schema.ts b/packages/composable-cli/src/commands/payments/manual/util/setup-ep-payments-schema.ts new file mode 100644 index 00000000..4975a82a --- /dev/null +++ b/packages/composable-cli/src/commands/payments/manual/util/setup-ep-payments-schema.ts @@ -0,0 +1,14 @@ +import { z } from "zod" + +export const epPaymentsSetupSchema = z.object({ + accountId: z.string(), + publishableKey: z.string(), +}) + +export type EPPaymentsSetup = z.TypeOf + +export const epPaymentsForceSchema = z.object({ + force: z.boolean(), +}) + +export type EPPaymentsForce = z.TypeOf diff --git a/packages/composable-cli/src/commands/payments/manual/util/setup-epcc-manual-gateway.ts b/packages/composable-cli/src/commands/payments/manual/util/setup-epcc-manual-gateway.ts new file mode 100644 index 00000000..bf5b60e7 --- /dev/null +++ b/packages/composable-cli/src/commands/payments/manual/util/setup-epcc-manual-gateway.ts @@ -0,0 +1,51 @@ +import { logging } from "@angular-devkit/core" +import type { Gateway, Moltin } from "@moltin/sdk" +import { OperationResult } from "@elasticpath/composable-common" +import { updateManualGateway } from "./update-gateway" +import { processUnknownError } from "../../../../util/process-unknown-error" + +export async function setupManualPaymentGateway( + epccClient: Moltin, + logger: logging.LoggerApi, +): Promise< + OperationResult< + Gateway, + { + code: "manual_gateway_update_failed" | "unknown" + message: string + } + > +> { + try { + /** + * Update manual gateway to be enabled + */ + const updateResult = await updateManualGateway(epccClient) + + if (!updateResult.success) { + logger.debug(`Failed to update ep payment gateway.`) + return { + success: false, + error: { + code: "manual_gateway_update_failed", + message: `Failed to update ep payment gateway. ${processUnknownError( + updateResult, + )}`, + }, + } + } + + return updateResult + } catch (err: unknown) { + const errorStr = processUnknownError(err) + logger.error(errorStr) + + return { + success: false, + error: { + code: "unknown", + message: errorStr, + }, + } + } +} diff --git a/packages/composable-cli/src/commands/payments/manual/util/update-gateway.ts b/packages/composable-cli/src/commands/payments/manual/util/update-gateway.ts new file mode 100644 index 00000000..6ad9a4b2 --- /dev/null +++ b/packages/composable-cli/src/commands/payments/manual/util/update-gateway.ts @@ -0,0 +1,37 @@ +import type { Gateway, Moltin as EpccClient } from "@moltin/sdk" +import { OperationResult } from "@elasticpath/composable-common" + +const errMsg = "Failed to enable manual gateway." + +export async function updateManualGateway( + client: EpccClient, +): Promise> { + try { + const updatedGateway = await client.Gateways.Update("manual", { + enabled: true, + }) + + if (updatedGateway.data) { + return { + success: true, + data: updatedGateway.data, + } + } + + return { + success: false, + error: new Error(`${errMsg} ${JSON.stringify(updatedGateway)}`), + } + } catch (err: unknown) { + return { + success: false, + error: new Error( + `${errMsg} An unknown error occurred ${ + err instanceof Error + ? `${err.name} - ${err.message}` + : "Failed to render error." + }`, + ), + } + } +} diff --git a/packages/composable-cli/src/commands/payments/payments-command.tsx b/packages/composable-cli/src/commands/payments/payments-command.tsx index 0b29ff83..adcd5346 100644 --- a/packages/composable-cli/src/commands/payments/payments-command.tsx +++ b/packages/composable-cli/src/commands/payments/payments-command.tsx @@ -14,6 +14,7 @@ import { } from "./payments.types" import { createActiveStoreMiddleware } from "../generate/generate-command" import { createComposableProjectMiddleware } from "../../lib/composable-project-middleware" +import { createManualPaymentCommand } from "./manual/manual-command" export function createPaymentsCommand( ctx: CommandContext, @@ -27,6 +28,7 @@ export function createPaymentsCommand( .middleware(createComposableProjectMiddleware(ctx)) .middleware(createActiveStoreMiddleware(ctx)) .command(createEPPaymentsCommand(ctx)) + .command(createManualPaymentCommand(ctx)) .help() .demandCommand(1) .strict() diff --git a/packages/composable-cli/src/commands/store/store-command.tsx b/packages/composable-cli/src/commands/store/store-command.tsx index 9e31fbf2..44e902f6 100644 --- a/packages/composable-cli/src/commands/store/store-command.tsx +++ b/packages/composable-cli/src/commands/store/store-command.tsx @@ -42,8 +42,8 @@ export function createStoreCommand( .middleware(createAuthenticationMiddleware(ctx)) .command(createSetStoreCommand(ctx)) .fail(false) - .help("h") .demandCommand(1) + .help() .strict() }, handler: ctx.handleErrors( From 508ede14f8e01b61823465f01d18cec81b6c2a30 Mon Sep 17 00:00:00 2001 From: Robert Field Date: Tue, 14 Nov 2023 18:07:44 +0000 Subject: [PATCH 6/6] chore: changeset --- .changeset/cyan-coins-sneeze.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .changeset/cyan-coins-sneeze.md diff --git a/.changeset/cyan-coins-sneeze.md b/.changeset/cyan-coins-sneeze.md new file mode 100644 index 00000000..50948750 --- /dev/null +++ b/.changeset/cyan-coins-sneeze.md @@ -0,0 +1,10 @@ +--- +"@elasticpath/react-shopper-hooks": minor +"composable-cli": minor +"@elasticpath/d2c-schematics": minor +"@elasticpath/composable-common": minor +"@elasticpath/composable-integration-hub-deployer": minor +"@elasticpath/shopper-common": minor +--- + +manual payment gateway support for d2c starter kit