Skip to content

Commit

Permalink
Shopify: create checkout on add to cart (vercel#432)
Browse files Browse the repository at this point in the history
* Create checkout on add to cart

* Checkout changes

* Revert files

* Fix checkout
  • Loading branch information
cond0r authored Sep 24, 2021
1 parent 8e7b942 commit f9644fe
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 73 deletions.
2 changes: 1 addition & 1 deletion framework/bigcommerce/cart/use-add-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ export const handler: MutationHook<AddItemHook> = {
[fetch, mutate]
)
},
}
}
61 changes: 35 additions & 26 deletions framework/shopify/cart/use-add-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
checkoutLineItemAddMutation,
getCheckoutId,
checkoutToCart,
checkoutCreate,
} from '../utils'
import { Mutation, MutationCheckoutLineItemsAddArgs } from '../schema'

Expand All @@ -28,34 +29,42 @@ export const handler: MutationHook<AddItemHook> = {
})
}

const { checkoutLineItemsAdd } = await fetch<
Mutation,
MutationCheckoutLineItemsAddArgs
>({
...options,
variables: {
checkoutId: getCheckoutId(),
lineItems: [
{
variantId: item.variantId,
quantity: item.quantity ?? 1,
},
],
const lineItems = [
{
variantId: item.variantId,
quantity: item.quantity ?? 1,
},
})
]

return checkoutToCart(checkoutLineItemsAdd)
},
useHook: ({ fetch }) => () => {
const { mutate } = useCart()
let checkoutId = getCheckoutId()

return useCallback(
async function addItem(input) {
const data = await fetch({ input })
await mutate(data, false)
return data
},
[fetch, mutate]
)
if (!checkoutId) {
return checkoutToCart(await checkoutCreate(fetch, lineItems))
} else {
const { checkoutLineItemsAdd } = await fetch<
Mutation,
MutationCheckoutLineItemsAddArgs
>({
...options,
variables: {
checkoutId,
lineItems,
},
})
return checkoutToCart(checkoutLineItemsAdd)
}
},
useHook:
({ fetch }) =>
() => {
const { mutate } = useCart()
return useCallback(
async function addItem(input) {
const data = await fetch({ input })
await mutate(data, false)
return data
},
[fetch, mutate]
)
},
}
71 changes: 37 additions & 34 deletions framework/shopify/cart/use-cart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,59 @@ import { useMemo } from 'react'
import useCommerceCart, { UseCart } from '@commerce/cart/use-cart'

import { SWRHook } from '@commerce/utils/types'
import { checkoutCreate, checkoutToCart } from '../utils'
import { checkoutToCart } from '../utils'
import getCheckoutQuery from '../utils/queries/get-checkout-query'
import { GetCartHook } from '../types/cart'
import Cookies from 'js-cookie'

import {
GetCheckoutQuery,
GetCheckoutQueryVariables,
CheckoutDetailsFragment,
} from '../schema'
SHOPIFY_CHECKOUT_ID_COOKIE,
SHOPIFY_CHECKOUT_URL_COOKIE,
} from '../const'

export default useCommerceCart as UseCart<typeof handler>

export const handler: SWRHook<GetCartHook> = {
fetchOptions: {
query: getCheckoutQuery,
},
async fetcher({ input: { cartId: checkoutId }, options, fetch }) {
let checkout

if (checkoutId) {
const data = await fetch({
async fetcher({ input: { cartId }, options, fetch }) {
if (cartId) {
const { node: checkout } = await fetch({
...options,
variables: {
checkoutId: checkoutId,
checkoutId: cartId,
},
})
checkout = data.node
}

if (checkout?.completedAt || !checkoutId) {
checkout = await checkoutCreate(fetch)
if (checkout?.completedAt) {
Cookies.remove(SHOPIFY_CHECKOUT_ID_COOKIE)
Cookies.remove(SHOPIFY_CHECKOUT_URL_COOKIE)
return null
} else {
return checkoutToCart({
checkout,
})
}
}

return checkoutToCart({ checkout })
return null
},
useHook: ({ useData }) => (input) => {
const response = useData({
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
})
return useMemo(
() =>
Object.create(response, {
isEmpty: {
get() {
return (response.data?.lineItems.length ?? 0) <= 0
useHook:
({ useData }) =>
(input) => {
const response = useData({
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
})
return useMemo(
() =>
Object.create(response, {
isEmpty: {
get() {
return (response.data?.lineItems.length ?? 0) <= 0
},
enumerable: true,
},
enumerable: true,
},
}),
[response]
)
},
}),
[response]
)
},
}
3 changes: 2 additions & 1 deletion framework/shopify/commerce.config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"provider": "shopify",
"features": {
"wishlist": false
"wishlist": false,
"customerAuth": true
}
}
28 changes: 20 additions & 8 deletions framework/shopify/utils/checkout-create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,39 @@ import {
} from '../const'

import checkoutCreateMutation from './mutations/checkout-create'
import { CheckoutCreatePayload } from '../schema'
import {
CheckoutCreatePayload,
CheckoutLineItemInput,
Mutation,
MutationCheckoutCreateArgs,
} from '../schema'
import { FetcherOptions } from '@commerce/utils/types'

export const checkoutCreate = async (
fetch: any
fetch: <T = any, B = Body>(options: FetcherOptions<B>) => Promise<T>,
lineItems: CheckoutLineItemInput[]
): Promise<CheckoutCreatePayload> => {
const data = await fetch({
const { checkoutCreate } = await fetch<Mutation, MutationCheckoutCreateArgs>({
query: checkoutCreateMutation,
variables: {
input: { lineItems },
},
})

const checkout = data.checkoutCreate?.checkout
const checkoutId = checkout?.id
const checkout = checkoutCreate?.checkout

if (checkoutId) {
if (checkout) {
const checkoutId = checkout?.id
const options = {
expires: SHOPIFY_COOKIE_EXPIRE,
}
Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId, options)
Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout.webUrl, options)
if (checkout?.webUrl) {
Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout.webUrl, options)
}
}

return checkout
return checkoutCreate!
}

export default checkoutCreate
5 changes: 2 additions & 3 deletions framework/shopify/utils/checkout-to-cart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,15 @@ export type CheckoutPayload =
| CheckoutQuery

const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => {
const checkout = checkoutPayload?.checkout
throwUserErrors(checkoutPayload?.checkoutUserErrors)

if (!checkout) {
if (!checkoutPayload?.checkout) {
throw new CommerceError({
message: 'Missing checkout object from response',
})
}

return normalizeCart(checkout)
return normalizeCart(checkoutPayload?.checkout)
}

export default checkoutToCart

0 comments on commit f9644fe

Please sign in to comment.