mirror of
https://github.com/vercel/commerce.git
synced 2025-03-14 22:42:33 +00:00
shopify checkout redirect & api handler
This commit is contained in:
parent
2acc21164b
commit
b0d8ae565d
@ -122,7 +122,7 @@ const CartSidebarView: FC = () => {
|
||||
<span>{total}</span>
|
||||
</div>
|
||||
</div>
|
||||
<Button href={data.webUrl} Component="a" width="100%">
|
||||
<Button href="/checkout" Component="a" width="100%">
|
||||
Proceed to Checkout
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -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 || '',
|
||||
})
|
||||
|
@ -1,5 +1,23 @@
|
||||
const getCheckout = async (req: any, res: any, config: any): Promise<any> => {
|
||||
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<any> = 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, {}, {})
|
||||
|
58
framework/shopify/api/utils/create-api-handler.ts
Normal file
58
framework/shopify/api/utils/create-api-handler.ts
Normal file
@ -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<ShopifyApiResponse<T>>,
|
||||
config: ShopifyConfig,
|
||||
handlers: H,
|
||||
// Custom configs that may be used by a particular handler
|
||||
options: Options
|
||||
) => void | Promise<void>
|
||||
|
||||
export type ShopifyHandler<T = any, Body = null> = (options: {
|
||||
req: NextApiRequest
|
||||
res: NextApiResponse<ShopifyApiResponse<T>>
|
||||
config: ShopifyConfig
|
||||
body: Body
|
||||
}) => void | Promise<void>
|
||||
|
||||
export type ShopifyHandlers<T = any> = {
|
||||
[k: string]: ShopifyHandler<T, any>
|
||||
}
|
||||
|
||||
export type ShopifyApiResponse<T> = {
|
||||
data: T | null
|
||||
errors?: { message: string; code?: string }[]
|
||||
}
|
||||
|
||||
export default function createApiHandler<
|
||||
T = any,
|
||||
H extends ShopifyHandlers = {},
|
||||
Options extends {} = {}
|
||||
>(
|
||||
handler: ShopifyApiHandler<T, H, Options>,
|
||||
handlers: H,
|
||||
defaultOptions: Options
|
||||
) {
|
||||
return function getApiHandler({
|
||||
config,
|
||||
operations,
|
||||
options,
|
||||
}: {
|
||||
config?: ShopifyConfig
|
||||
operations?: Partial<H>
|
||||
options?: Options extends {} ? Partial<Options> : never
|
||||
} = {}): NextApiHandler {
|
||||
const ops = { ...operations, ...handlers }
|
||||
const opts = { ...defaultOptions, ...options }
|
||||
|
||||
return function apiHandler(req, res) {
|
||||
return handler(req, res, getConfig(config), ops, opts)
|
||||
}
|
||||
}
|
||||
}
|
28
framework/shopify/api/utils/is-allowed-method.ts
Normal file
28
framework/shopify/api/utils/is-allowed-method.ts
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
|
2
framework/shopify/const.ts
Normal file
2
framework/shopify/const.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId'
|
||||
export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl'
|
@ -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<CommerceConfig>
|
||||
|
||||
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<CommerceConfig>
|
||||
|
||||
export type ShopifyProps = {
|
||||
children?: ReactNode
|
||||
locale: string
|
||||
|
@ -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,
|
||||
|
@ -5,6 +5,9 @@ export const checkoutDetailsFragment = /* GraphQL */ `
|
||||
totalTax
|
||||
totalPrice
|
||||
currencyCode
|
||||
completedAt
|
||||
createdAt
|
||||
taxesIncluded
|
||||
lineItems(first: 250) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
|
@ -1,3 +1,3 @@
|
||||
import customersApi from '@framework/api/customers'
|
||||
import customersApi from '@framework/api/customer'
|
||||
|
||||
export default customersApi()
|
||||
|
Loading…
x
Reference in New Issue
Block a user