diff --git a/framework/bigcommerce/api/index.ts b/framework/bigcommerce/api/index.ts index 609ffcfca..90e04a6a4 100644 --- a/framework/bigcommerce/api/index.ts +++ b/framework/bigcommerce/api/index.ts @@ -13,6 +13,7 @@ import type { CartAPI } from './cart' import type { CustomerAPI } from './customer' import type { LoginAPI } from './login' import type { LogoutAPI } from './logout' +import type { SignupAPI } from './signup' import login from './operations/login' export interface BigcommerceConfig extends CommerceAPIConfig { @@ -114,7 +115,7 @@ export const provider = { export type Provider = typeof provider -export type APIs = CartAPI | CustomerAPI | LoginAPI | LogoutAPI +export type APIs = CartAPI | CustomerAPI | LoginAPI | LogoutAPI | SignupAPI export type BigcommerceAPI

= CommerceAPI

diff --git a/framework/bigcommerce/api/signup/index.ts b/framework/bigcommerce/api/signup/index.ts new file mode 100644 index 000000000..9e656990e --- /dev/null +++ b/framework/bigcommerce/api/signup/index.ts @@ -0,0 +1,10 @@ +import type { GetAPISchema } from '@commerce/api' +import type { SignupSchema } from '../../types/signup' +import type { BigcommerceAPI } from '..' +import signup from './signup' + +export type SignupAPI = GetAPISchema + +export type SignupEndpoint = SignupAPI['endpoint'] + +export const operations = { signup } diff --git a/framework/bigcommerce/api/signup/signup.ts b/framework/bigcommerce/api/signup/signup.ts new file mode 100644 index 000000000..df4f6ec57 --- /dev/null +++ b/framework/bigcommerce/api/signup/signup.ts @@ -0,0 +1,62 @@ +import { BigcommerceApiError } from '../utils/errors' +import type { SignupEndpoint } from '.' + +const signup: SignupEndpoint['operations']['signup'] = async ({ + res, + body: { firstName, lastName, email, password }, + config, + commerce, +}) => { + // TODO: Add proper validations with something like Ajv + if (!(firstName && lastName && email && password)) { + return res.status(400).json({ + data: null, + errors: [{ message: 'Invalid request' }], + }) + } + // TODO: validate the password and email + // Passwords must be at least 7 characters and contain both alphabetic + // and numeric characters. + + try { + await config.storeApiFetch('/v3/customers', { + method: 'POST', + body: JSON.stringify([ + { + first_name: firstName, + last_name: lastName, + email, + authentication: { + new_password: password, + }, + }, + ]), + }) + } catch (error) { + if (error instanceof BigcommerceApiError && error.status === 422) { + const hasEmailError = '0.email' in error.data?.errors + + // If there's an error with the email, it most likely means it's duplicated + if (hasEmailError) { + return res.status(400).json({ + data: null, + errors: [ + { + message: 'The email is already in use', + code: 'duplicated_email', + }, + ], + }) + } + } + + throw error + } + + // Login the customer right after creating it + await commerce.login({ variables: { email, password }, res, config }) + + res.status(200).json({ data: null }) +} + +export default signup diff --git a/framework/bigcommerce/types/signup.ts b/framework/bigcommerce/types/signup.ts new file mode 100644 index 000000000..58543c6f6 --- /dev/null +++ b/framework/bigcommerce/types/signup.ts @@ -0,0 +1 @@ +export * from '@commerce/types/signup' diff --git a/framework/commerce/api/endpoints/signup.ts b/framework/commerce/api/endpoints/signup.ts new file mode 100644 index 000000000..d18542289 --- /dev/null +++ b/framework/commerce/api/endpoints/signup.ts @@ -0,0 +1,38 @@ +import type { SignupSchema } from '../../types/signup' +import { CommerceAPIError } from '../utils/errors' +import isAllowedOperation from '../utils/is-allowed-operation' +import type { GetAPISchema } from '..' + +const signupEndpoint: GetAPISchema< + any, + SignupSchema +>['endpoint']['handler'] = async (ctx) => { + const { req, res, operations, config } = ctx + + if ( + !isAllowedOperation(req, res, { + POST: operations['signup'], + }) + ) { + return + } + + const { cookies } = req + const cartId = cookies[config.cartCookie] + + try { + const body = { ...req.body, cartId } + return await operations['signup']({ ...ctx, body }) + } catch (error) { + console.error(error) + + const message = + error instanceof CommerceAPIError + ? 'An unexpected error ocurred with the Commerce API' + : 'An unexpected error ocurred' + + res.status(500).json({ data: null, errors: [{ message }] }) + } +} + +export default signupEndpoint diff --git a/framework/commerce/api/index.ts b/framework/commerce/api/index.ts index f55bf9ce2..3d1521fbd 100644 --- a/framework/commerce/api/index.ts +++ b/framework/commerce/api/index.ts @@ -5,6 +5,7 @@ import type { CartSchema } from '../types/cart' import type { CustomerSchema } from '../types/customer' import type { LoginSchema } from '../types/login' import type { LogoutSchema } from '../types/logout' +import type { SignupSchema } from '../types/signup' import { defaultOperations, OPERATIONS, @@ -17,6 +18,7 @@ export type APISchemas = | CustomerSchema | LoginSchema | LogoutSchema + | SignupSchema export type GetAPISchema< C extends CommerceAPI, diff --git a/framework/commerce/types/signup.ts b/framework/commerce/types/signup.ts new file mode 100644 index 000000000..ae25da0f0 --- /dev/null +++ b/framework/commerce/types/signup.ts @@ -0,0 +1,22 @@ +export type SignupBody = { + firstName: string + lastName: string + email: string + password: string +} + +export type SignupTypes = { + body: SignupBody +} + +export type SignupSchema = { + endpoint: { + options: {} + operations: { + signup: { + data: null + body: T['body'] + } + } + } +} diff --git a/pages/api/signup.ts b/pages/api/signup.ts new file mode 100644 index 000000000..bc24f407a --- /dev/null +++ b/pages/api/signup.ts @@ -0,0 +1,8 @@ +import signup from '@commerce/api/endpoints/signup' +import { SignupAPI, operations } from '@framework/api/signup' +import commerce from '@lib/api/commerce' + +export default commerce.endpoint({ + handler: signup as SignupAPI['endpoint']['handler'], + operations, +})