diff --git a/components/cart/CartSidebarView/CartSidebarView.tsx b/components/cart/CartSidebarView/CartSidebarView.tsx index 394f89414..c25bd7c95 100644 --- a/components/cart/CartSidebarView/CartSidebarView.tsx +++ b/components/cart/CartSidebarView/CartSidebarView.tsx @@ -122,7 +122,7 @@ const CartSidebarView: FC = () => { {total} - diff --git a/framework/bigcommerce/README.md b/framework/bigcommerce/README.md index 9792bcdb0..86fbac91d 100644 --- a/framework/bigcommerce/README.md +++ b/framework/bigcommerce/README.md @@ -329,7 +329,6 @@ const SearchPage = ({ searchString, category, brand, sortStr }) => { const { data } = useSearch({ search: searchString || '', categoryId: category?.entityId, - categorySlug: category?.slug, brandId: brand?.entityId, sort: sortStr || '', }) diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts index 0b35324d8..79b38747c 100644 --- a/framework/shopify/api/checkout/index.ts +++ b/framework/shopify/api/checkout/index.ts @@ -1,5 +1,23 @@ -const getCheckout = async (req: any, res: any, config: any): Promise => { - res.end() +import isAllowedMethod from '../utils/is-allowed-method' +import createApiHandler, { + ShopifyApiHandler, +} from '../utils/create-api-handler' + +import { SHOPIFY_CHECKOUT_URL_COOKIE } from '@framework/const' + +const METHODS = ['GET'] + +const checkoutApi: ShopifyApiHandler = async (req, res) => { + if (!isAllowedMethod(req, res, METHODS)) return + + const { cookies } = req + const checkoutUrl = cookies[SHOPIFY_CHECKOUT_URL_COOKIE] + + if (checkoutUrl) { + res.redirect(checkoutUrl) + } else { + res.redirect('/cart') + } } -export default getCheckout +export default createApiHandler(checkoutApi, {}, {}) diff --git a/framework/shopify/api/utils/create-api-handler.ts b/framework/shopify/api/utils/create-api-handler.ts new file mode 100644 index 000000000..8820aeabc --- /dev/null +++ b/framework/shopify/api/utils/create-api-handler.ts @@ -0,0 +1,58 @@ +import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next' +import { ShopifyConfig, getConfig } from '..' + +export type ShopifyApiHandler< + T = any, + H extends ShopifyHandlers = {}, + Options extends {} = {} +> = ( + req: NextApiRequest, + res: NextApiResponse>, + config: ShopifyConfig, + handlers: H, + // Custom configs that may be used by a particular handler + options: Options +) => void | Promise + +export type ShopifyHandler = (options: { + req: NextApiRequest + res: NextApiResponse> + config: ShopifyConfig + body: Body +}) => void | Promise + +export type ShopifyHandlers = { + [k: string]: ShopifyHandler +} + +export type ShopifyApiResponse = { + data: T | null + errors?: { message: string; code?: string }[] +} + +export default function createApiHandler< + T = any, + H extends ShopifyHandlers = {}, + Options extends {} = {} +>( + handler: ShopifyApiHandler, + handlers: H, + defaultOptions: Options +) { + return function getApiHandler({ + config, + operations, + options, + }: { + config?: ShopifyConfig + operations?: Partial + options?: Options extends {} ? Partial : never + } = {}): NextApiHandler { + const ops = { ...operations, ...handlers } + const opts = { ...defaultOptions, ...options } + + return function apiHandler(req, res) { + return handler(req, res, getConfig(config), ops, opts) + } + } +} diff --git a/framework/shopify/api/utils/is-allowed-method.ts b/framework/shopify/api/utils/is-allowed-method.ts new file mode 100644 index 000000000..78bbba568 --- /dev/null +++ b/framework/shopify/api/utils/is-allowed-method.ts @@ -0,0 +1,28 @@ +import type { NextApiRequest, NextApiResponse } from 'next' + +export default function isAllowedMethod( + req: NextApiRequest, + res: NextApiResponse, + allowedMethods: string[] +) { + const methods = allowedMethods.includes('OPTIONS') + ? allowedMethods + : [...allowedMethods, 'OPTIONS'] + + if (!req.method || !methods.includes(req.method)) { + res.status(405) + res.setHeader('Allow', methods.join(', ')) + res.end() + return false + } + + if (req.method === 'OPTIONS') { + res.status(200) + res.setHeader('Allow', methods.join(', ')) + res.setHeader('Content-Length', '0') + res.end() + return false + } + + return true +} diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/cart/utils/checkout-create.ts index 50cbd1299..24d080551 100644 --- a/framework/shopify/cart/utils/checkout-create.ts +++ b/framework/shopify/cart/utils/checkout-create.ts @@ -1,8 +1,11 @@ -import { SHOPIFY_CHECKOUT_COOKIE } from '@framework' +import { + SHOPIFY_CHECKOUT_ID_COOKIE, + SHOPIFY_CHECKOUT_URL_COOKIE, +} from '@framework/const' import checkoutCreateMutation from '@framework/utils/mutations/checkout-create' import Cookies from 'js-cookie' -export const createCheckout = async (fetch: any) => { +export const checkoutCreate = async (fetch: any) => { const data = await fetch({ query: checkoutCreateMutation, }) @@ -11,10 +14,11 @@ export const createCheckout = async (fetch: any) => { const checkoutId = checkout?.id if (checkoutId) { - Cookies.set(SHOPIFY_CHECKOUT_COOKIE, checkoutId) + Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId) + Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl) } return checkout } -export default createCheckout +export default checkoutCreate diff --git a/framework/shopify/const.ts b/framework/shopify/const.ts new file mode 100644 index 000000000..0c957632c --- /dev/null +++ b/framework/shopify/const.ts @@ -0,0 +1,2 @@ +export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId' +export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl' diff --git a/framework/shopify/index.tsx b/framework/shopify/index.tsx index 3d9db989f..f6b8a8af6 100644 --- a/framework/shopify/index.tsx +++ b/framework/shopify/index.tsx @@ -8,8 +8,7 @@ import { } from '@commerce' import { CommerceError, FetcherError } from '@commerce/utils/errors' - -export const SHOPIFY_CHECKOUT_COOKIE = 'shopify_checkoutId' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const' async function getText(res: Response) { try { @@ -28,9 +27,11 @@ async function getError(res: Response) { return new FetcherError({ message: await getText(res), status: res.status }) } -export const shopifyConfig: CommerceConfig = { +export type ShopifyConfig = Partial + +export const shopifyConfig: ShopifyConfig = { locale: 'en-us', - cartCookie: SHOPIFY_CHECKOUT_COOKIE, + cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, async fetcher({ method = 'POST', variables, query }) { const res = await fetch( `https://${process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN}/api/2021-01/graphql.json`, @@ -60,8 +61,6 @@ export const shopifyConfig: CommerceConfig = { }, } -export type ShopifyConfig = Partial - export type ShopifyProps = { children?: ReactNode locale: string diff --git a/framework/shopify/lib/normalize.ts b/framework/shopify/lib/normalize.ts index 537e36f69..ff2de330c 100644 --- a/framework/shopify/lib/normalize.ts +++ b/framework/shopify/lib/normalize.ts @@ -97,7 +97,7 @@ export function normalizeCart(data: Checkout): Cart { } function normalizeLineItem({ - node: { id, title, variant, quantity, ...item }, + node: { id, title, variant, quantity }, }: CheckoutLineItemEdge): LineItem { return { id, diff --git a/framework/shopify/utils/queries/get-checkout-query.ts b/framework/shopify/utils/queries/get-checkout-query.ts index 5d0b60545..b582ca3bb 100644 --- a/framework/shopify/utils/queries/get-checkout-query.ts +++ b/framework/shopify/utils/queries/get-checkout-query.ts @@ -5,6 +5,9 @@ export const checkoutDetailsFragment = /* GraphQL */ ` totalTax totalPrice currencyCode + completedAt + createdAt + taxesIncluded lineItems(first: 250) { pageInfo { hasNextPage diff --git a/pages/api/bigcommerce/customers/index.ts b/pages/api/bigcommerce/customers/index.ts index 7b55d3aa8..911a4521a 100644 --- a/pages/api/bigcommerce/customers/index.ts +++ b/pages/api/bigcommerce/customers/index.ts @@ -1,3 +1,3 @@ -import customersApi from '@framework/api/customers' +import customersApi from '@framework/api/customer' export default customersApi()