Skip to content

Commit

Permalink
feat: added ep payment gateway setup command
Browse files Browse the repository at this point in the history
  • Loading branch information
field123 committed Sep 26, 2023
1 parent 1ae214b commit 8493655
Show file tree
Hide file tree
Showing 11 changed files with 452 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
import yargs from "yargs"
import {
EPPaymentsCommandArguments,
EPPaymentsCommandData,
EPPaymentsCommandError,
} from "./ep-payments-integration.types"
import { CommandContext, CommandHandlerFunction } from "../../../types/command"
import { handleErrors } from "../../../util/error-handler"
import { trackCommandHandler } from "../../../util/track-command-handler"
import {
createActiveStoreMiddleware,
createAuthenticationCheckerMiddleware,
} from "../../generate/generate-command"
import { PaymentsCommandArguments } from "../payments.types"
import * as ansiColors from "ansi-colors"
import inquirer from "inquirer"
import { isTTY } from "../../../util/is-tty"
import boxen from "boxen"
import ora from "ora"
import { logging } from "@angular-devkit/core"
import { setupEPPaymentsPaymentGateway } from "./util/setup-epcc-ep-payment"
import {
EPPaymentsSetup,
epPaymentsSetupSchema,
} from "./util/setup-ep-payments-schema"

export function createEPPaymentsCommand(
ctx: CommandContext,
): yargs.CommandModule<PaymentsCommandArguments, EPPaymentsCommandArguments> {
return {
command: "ep-payments",
describe:
"setup EP Payment gateway for your Elastic Path powered storefront",
builder: async (yargs) => {
return yargs
.middleware(createAuthenticationCheckerMiddleware(ctx))
.middleware(createActiveStoreMiddleware(ctx))
.option("account-id", {
type: "string",
description: "EP Payments account ID",
})
.option("publishable-key", {
type: "string",
description: "EP Payments publishable key",
})
.fail(false)
.help()
},
handler: handleErrors(
trackCommandHandler(ctx, createEPPaymentsCommandHandler),
),
}
}

export function createEPPaymentsCommandHandler(
ctx: CommandContext,
): CommandHandlerFunction<
EPPaymentsCommandData,
EPPaymentsCommandError,
EPPaymentsCommandArguments
> {
return async function epPaymentsCommandHandler(args) {
const spinner = ora()

try {
if (!ctx.epClient) {
spinner.fail(`Failed to setup EP Payments.`)
return {
success: false,
error: {
code: "missing_ep_client",
message: "Failed to setup EP Payments - missing EP client",
},
}
}

const options = await resolveOptions(args, ctx.logger, ansiColors)

spinner.start(`Setting up EP Payments...`)
const result = await setupEPPaymentsPaymentGateway(
{
epPaymentsStripeAccountId: options.accountId,
epPaymentsStripePublishableKey: options.publishableKey,
},
ctx.epClient,
ctx.logger,
)

if (!result.success) {
spinner.fail(`Failed to setup EP Payments.`)
return {
success: false,
error: {
code: "ep_payments_setup_failed",
message: "Failed to setup EP Payments",
},
}
}

spinner.succeed(`EP Payments setup successfully.`)
return {
success: true,
data: {},
}
} catch (e) {
spinner.fail(`Failed to setup Algolia integration`)
return {
success: false,
error: {
code: "ALGOLIA_INTEGRATION_SETUP_FAILED",
message: "Failed to setup Algolia integration",
},
}
}
}
}

async function resolveOptions(
args: EPPaymentsCommandArguments,
logger: logging.Logger,
colors: typeof ansiColors,
): Promise<EPPaymentsSetup> {
if (args.interactive && isTTY()) {
return epPaymentsOptionsPrompts(args, logger, colors)
}

const parsed = epPaymentsSetupSchema.safeParse({
accountId: args.accountId,
publishableKey: args.publishableKey,
})

if (!parsed.success) {
throw new Error(`Invalid arguments: ${JSON.stringify(parsed.error)}`)
}

return parsed.data
}

async function epPaymentsOptionsPrompts(
args: EPPaymentsCommandArguments,
logger: logging.Logger,
_colors: typeof ansiColors,
): Promise<EPPaymentsSetup> {
const { accountId: argsAccountId, publishableKey: argsPublishableKey } = args

if (!argsAccountId && !argsPublishableKey) {
logger.info(
boxen(
`If you don't have an EP payments account setup reach out to us \nhttps://www.elasticpath.com/products/payments`,
{
padding: 1,
margin: 1,
},
),
)
}

let gatheredOptions = {}

if (!argsAccountId) {
const { algoliaApplicationId } = await inquirer.prompt([
{
type: "string",
name: "accountId",
message: "What is your EP Payments account ID?",
},
])

gatheredOptions = {
...gatheredOptions,
appId: algoliaApplicationId,
}
} else {
gatheredOptions = {
...gatheredOptions,
accountId: argsAccountId,
}
}

if (!argsPublishableKey) {
const { algoliaAdminApiKey } = await inquirer.prompt([
{
type: "password",
name: "algoliaAdminApiKey",
message: "What is your Algolia Admin API Key?",
mask: "*",
},
])

gatheredOptions = {
...gatheredOptions,
adminApiKey: algoliaAdminApiKey,
}
} else {
gatheredOptions = {
...gatheredOptions,
publishableKey: argsPublishableKey,
}
}

return gatheredOptions as any
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { PaymentsCommandArguments } from "../payments.types"

export type EPPaymentsCommandData = {
indexName?: string
}

export type EPPaymentsCommandError = {
code: string
message: string
}

export type EPPaymentsCommandArguments = {
accountId?: string
publishableKey?: string
} & PaymentsCommandArguments
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { z } from "zod"

export const epPaymentGatewaySettingsSchema = z.object({
epPaymentsStripeAccountId: z.string().min(1),
epPaymentsStripePublishableKey: z.string().min(1),
})

export type EpPaymentGatewaySettings = z.TypeOf<
typeof epPaymentGatewaySettingsSchema
>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { z } from "zod"

export const epPaymentsSetupSchema = z.object({
accountId: z.string(),
publishableKey: z.string(),
})

export type EPPaymentsSetup = z.TypeOf<typeof epPaymentsSetupSchema>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { logging } from "@angular-devkit/core"
import type { Gateway, Moltin } from "@moltin/sdk"
import { checkGateway, OperationResult } from "@elasticpath/composable-common"
import { updateEpPaymentGateway } from "./update-gateway"
import { EpPaymentGatewaySettings } from "./ep-payments-schema"

export async function setupEPPaymentsPaymentGateway(
sourceInput: EpPaymentGatewaySettings,
epccClient: Moltin,
logger: logging.LoggerApi,
): Promise<OperationResult<Gateway>> {
try {
const { epPaymentsStripeAccountId } = sourceInput

/**
* Check if EP payments is enabled and do nothing if it is
*/
const checkGatewayResult = await checkGateway(
epccClient,
"elastic_path_payments_stripe",
)

if (checkGatewayResult.success) {
logger.debug(`EP Payment gateway is already enabled`)
return checkGatewayResult
}

/**
* Update ep payment gateway to be enabled with test mode on
*/
const updateResult = await updateEpPaymentGateway(
epccClient,
epPaymentsStripeAccountId,
)

if (!updateResult.success) {
logger.debug(`Failed to update ep payment gateway.`)
return updateResult
}

return updateResult
} catch (err: unknown) {
const errorStr = `An unknown error occurred: ${
err instanceof Error
? `${err.name} = ${err.message}`
: JSON.stringify(err)
}`

logger.error(errorStr)

return {
success: false,
error: new Error(errorStr),
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { Gateway, Moltin as EpccClient } from "@moltin/sdk"
import { OperationResult } from "@elasticpath/composable-common"

const errMsg = "Failed to enable elastic_path_payments_stripe gateway."

export async function updateEpPaymentGateway(
client: EpccClient,
accountId: string,
): Promise<OperationResult<Gateway>> {
try {
const updatedGateway = await client.Gateways.Update(
"elastic_path_payments_stripe",
{ enabled: true, stripe_account: accountId, test: 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."
}`,
),
}
}
}
56 changes: 56 additions & 0 deletions packages/composable-cli/src/commands/payments/payments-command.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import yargs from "yargs"
import {
CommandContext,
CommandHandlerFunction,
RootCommandArguments,
} from "../../types/command"
import { handleErrors } from "../../util/error-handler"

import { trackCommandHandler } from "../../util/track-command-handler"
import { createEPPaymentsCommand } from "./ep-payments/ep-payments-command"
import {
PaymentsCommandArguments,
PaymentsCommandData,
PaymentsCommandError,
} from "./payments.types"
import { createActiveStoreMiddleware } from "../generate/generate-command"

export function createPaymentsCommand(
ctx: CommandContext,
): yargs.CommandModule<RootCommandArguments, PaymentsCommandArguments> {
return {
command: "payments",
aliases: ["p"],
describe: "setup Elastic Path payment gateways for your storefront",
builder: (yargs) => {
return yargs
.middleware(createActiveStoreMiddleware(ctx))
.command(createEPPaymentsCommand(ctx))
.help()
.demandCommand(1)
.strict()
},
handler: handleErrors(
trackCommandHandler(ctx, createPaymentsCommandHandler),
),
}
}

export function createPaymentsCommandHandler(
_ctx: CommandContext,
): CommandHandlerFunction<
PaymentsCommandData,
PaymentsCommandError,
PaymentsCommandArguments
> {
return async function integrationCommandHandler(_args) {
return {
success: false,
error: {
code: "missing-positional-argument",
message:
'missing positional argument did you mean to run "ep-payments" command?',
},
}
}
}
Loading

0 comments on commit 8493655

Please sign in to comment.