diff --git a/lib/bigcommerce/api/cart.ts b/lib/bigcommerce/api/cart.ts
index ebf5c50b1..e21d3c436 100644
--- a/lib/bigcommerce/api/cart.ts
+++ b/lib/bigcommerce/api/cart.ts
@@ -15,43 +15,87 @@ const cartApi: BigcommerceApiHandler = async (req, res, config) => {
const { cookies } = req
const cartId = cookies[config.cartCookie]
- // Return current cart info
- if (req.method === 'GET') {
- let result: { data?: Cart } = {}
+ try {
+ // Return current cart info
+ if (req.method === 'GET') {
+ let result: { data?: Cart } = {}
- try {
- result = await config.storeApiFetch(
- `/v3/carts/${cartId}?include=redirect_urls`
- )
- } catch (error) {
- if (error instanceof BigcommerceApiError && error.status === 404) {
- // Remove the cookie if it exists but the cart wasn't found
- res.setHeader('Set-Cookie', getCartCookie(name))
- } else {
- throw error
+ try {
+ result = await config.storeApiFetch(
+ `/v3/carts/${cartId}?include=redirect_urls`
+ )
+ } catch (error) {
+ if (error instanceof BigcommerceApiError && error.status === 404) {
+ // Remove the cookie if it exists but the cart wasn't found
+ res.setHeader('Set-Cookie', getCartCookie(name))
+ } else {
+ throw error
+ }
}
+
+ return res.status(200).json({ cart: result.data ?? null })
}
- return res.status(200).json({ cart: result.data ?? null })
+ // Create or add a product to the cart
+ if (req.method === 'POST') {
+ const { product } = req.body
+
+ if (!product) {
+ return res.status(400).json({
+ errors: [{ message: 'Missing product' }],
+ })
+ }
+
+ const options = {
+ method: 'POST',
+ body: JSON.stringify({
+ line_items: [parseProduct(product)],
+ }),
+ }
+ const { data } = cartId
+ ? await config.storeApiFetch(`/v3/carts/${cartId}/items`, options)
+ : await config.storeApiFetch('/v3/carts', options)
+
+ // Create or update the cart cookie
+ res.setHeader(
+ 'Set-Cookie',
+ getCartCookie(name, data.id, config.cartCookieMaxAge)
+ )
+
+ // There's no need to send any additional data here, the UI can use this response to display a
+ // "success" for the operation and revalidate the GET request for this same endpoint right after.
+ return res.status(200).json({ done: true })
+ }
+ } catch (error) {
+ const message =
+ error instanceof BigcommerceApiError
+ ? 'An unexpected error ocurred with the Bigcommerce API'
+ : 'An unexpected error ocurred'
+
+ res.status(500).json({ errors: [{ message }] })
}
}
-const ONE_DAY = 60 * 60 * 24
-const MAX_AGE = ONE_DAY * 30
-
-function getCartCookie(name: string, cartId?: string) {
- const options: CookieSerializeOptions = cartId
- ? {
- maxAge: MAX_AGE,
- expires: new Date(Date.now() + MAX_AGE * 1000),
- httpOnly: true,
- secure: process.env.NODE_ENV === 'production',
- path: '/',
- sameSite: 'lax',
- }
- : { maxAge: -1, path: '/' } // Removes the cookie
+function getCartCookie(name: string, cartId?: string, maxAge?: number) {
+ const options: CookieSerializeOptions =
+ cartId && maxAge
+ ? {
+ maxAge,
+ expires: new Date(Date.now() + maxAge * 1000),
+ httpOnly: true,
+ secure: process.env.NODE_ENV === 'production',
+ path: '/',
+ sameSite: 'lax',
+ }
+ : { maxAge: -1, path: '/' } // Removes the cookie
return serialize(name, cartId || '', options)
}
+const parseProduct = (product: any) => ({
+ quantity: product.quantity,
+ product_id: product.productId,
+ variant_id: product.variantId,
+})
+
export default createApiHandler(cartApi)
diff --git a/lib/bigcommerce/api/index.ts b/lib/bigcommerce/api/index.ts
index 752ef038d..69e26f523 100644
--- a/lib/bigcommerce/api/index.ts
+++ b/lib/bigcommerce/api/index.ts
@@ -103,10 +103,12 @@ export class Config {
}
}
+const ONE_DAY = 60 * 60 * 24
const config = new Config({
commerceUrl: API_URL,
apiToken: API_TOKEN,
cartCookie: process.env.BIGCOMMERCE_CART_COOKIE ?? 'bc_cartId',
+ cartCookieMaxAge: ONE_DAY * 30,
fetch: fetchGraphqlApi,
// REST API only
storeApiUrl: STORE_API_URL,
diff --git a/lib/bigcommerce/api/utils/errors.ts b/lib/bigcommerce/api/utils/errors.ts
index 70f97953e..1cdd72214 100644
--- a/lib/bigcommerce/api/utils/errors.ts
+++ b/lib/bigcommerce/api/utils/errors.ts
@@ -1,16 +1,5 @@
// Used for GraphQL errors
-export class BigcommerceError extends Error {
- status?: number
-
- constructor(msg: string, res?: Response) {
- super(msg)
- this.name = 'BigcommerceError'
-
- if (res) {
- this.status = res.status
- }
- }
-}
+export class BigcommerceGraphQLError extends Error {}
export class BigcommerceApiError extends Error {
status: number
diff --git a/lib/bigcommerce/cart.tsx b/lib/bigcommerce/cart.tsx
index d57736e91..1b5f6b47e 100644
--- a/lib/bigcommerce/cart.tsx
+++ b/lib/bigcommerce/cart.tsx
@@ -7,9 +7,14 @@ import {
export type Cart = any
export const CartProvider: FC = ({ children }) => {
- return {children}
+ return {children}
}
export function useCart() {
- return useCommerceCart()
+ const cart = useCommerceCart()
+
+ // TODO: Do something to make this prop work
+ cart.isEmpty = true
+
+ return cart
}
diff --git a/lib/commerce/api/index.ts b/lib/commerce/api/index.ts
index dcf95498f..6c4bf0544 100644
--- a/lib/commerce/api/index.ts
+++ b/lib/commerce/api/index.ts
@@ -2,6 +2,7 @@ export interface CommerceAPIConfig {
commerceUrl: string
apiToken: string
cartCookie: string
+ cartCookieMaxAge: number
fetch(
query: string,
queryData?: CommerceAPIFetchOptions
diff --git a/lib/commerce/cart.tsx b/lib/commerce/cart.tsx
index 8c1335780..c11ecd017 100644
--- a/lib/commerce/cart.tsx
+++ b/lib/commerce/cart.tsx
@@ -21,11 +21,9 @@ const CartProvider: FC = ({ children, query, url }) => {
const { fetcher } = useCommerce()
const cartId = getCartCookie()
const response = useSWR(() => (cartId ? [url, query] : null), fetcher)
- // TODO: Do something to make this prop work
- const isEmpty = true
return (
-
+
{children}
)
diff --git a/pages/api/bigcommerce/cart.ts b/pages/api/bigcommerce/cart.ts
new file mode 100644
index 000000000..a294b05c4
--- /dev/null
+++ b/pages/api/bigcommerce/cart.ts
@@ -0,0 +1,3 @@
+import cartApi from '@lib/bigcommerce/api/cart'
+
+export default cartApi()