forked from crowetic/commerce
shopify checkout redirect & api handler
This commit is contained in:
parent
2acc21164b
commit
b0d8ae565d
@ -122,7 +122,7 @@ const CartSidebarView: FC = () => {
|
|||||||
<span>{total}</span>
|
<span>{total}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button href={data.webUrl} Component="a" width="100%">
|
<Button href="/checkout" Component="a" width="100%">
|
||||||
Proceed to Checkout
|
Proceed to Checkout
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -329,7 +329,6 @@ const SearchPage = ({ searchString, category, brand, sortStr }) => {
|
|||||||
const { data } = useSearch({
|
const { data } = useSearch({
|
||||||
search: searchString || '',
|
search: searchString || '',
|
||||||
categoryId: category?.entityId,
|
categoryId: category?.entityId,
|
||||||
categorySlug: category?.slug,
|
|
||||||
brandId: brand?.entityId,
|
brandId: brand?.entityId,
|
||||||
sort: sortStr || '',
|
sort: sortStr || '',
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,23 @@
|
|||||||
const getCheckout = async (req: any, res: any, config: any): Promise<any> => {
|
import isAllowedMethod from '../utils/is-allowed-method'
|
||||||
res.end()
|
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 checkoutCreateMutation from '@framework/utils/mutations/checkout-create'
|
||||||
import Cookies from 'js-cookie'
|
import Cookies from 'js-cookie'
|
||||||
|
|
||||||
export const createCheckout = async (fetch: any) => {
|
export const checkoutCreate = async (fetch: any) => {
|
||||||
const data = await fetch({
|
const data = await fetch({
|
||||||
query: checkoutCreateMutation,
|
query: checkoutCreateMutation,
|
||||||
})
|
})
|
||||||
@ -11,10 +14,11 @@ export const createCheckout = async (fetch: any) => {
|
|||||||
const checkoutId = checkout?.id
|
const checkoutId = checkout?.id
|
||||||
|
|
||||||
if (checkoutId) {
|
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
|
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'
|
} from '@commerce'
|
||||||
|
|
||||||
import { CommerceError, FetcherError } from '@commerce/utils/errors'
|
import { CommerceError, FetcherError } from '@commerce/utils/errors'
|
||||||
|
import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const'
|
||||||
export const SHOPIFY_CHECKOUT_COOKIE = 'shopify_checkoutId'
|
|
||||||
|
|
||||||
async function getText(res: Response) {
|
async function getText(res: Response) {
|
||||||
try {
|
try {
|
||||||
@ -28,9 +27,11 @@ async function getError(res: Response) {
|
|||||||
return new FetcherError({ message: await getText(res), status: res.status })
|
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',
|
locale: 'en-us',
|
||||||
cartCookie: SHOPIFY_CHECKOUT_COOKIE,
|
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
|
||||||
async fetcher({ method = 'POST', variables, query }) {
|
async fetcher({ method = 'POST', variables, query }) {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`https://${process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN}/api/2021-01/graphql.json`,
|
`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 = {
|
export type ShopifyProps = {
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
locale: string
|
locale: string
|
||||||
|
@ -97,7 +97,7 @@ export function normalizeCart(data: Checkout): Cart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeLineItem({
|
function normalizeLineItem({
|
||||||
node: { id, title, variant, quantity, ...item },
|
node: { id, title, variant, quantity },
|
||||||
}: CheckoutLineItemEdge): LineItem {
|
}: CheckoutLineItemEdge): LineItem {
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
|
@ -5,6 +5,9 @@ export const checkoutDetailsFragment = /* GraphQL */ `
|
|||||||
totalTax
|
totalTax
|
||||||
totalPrice
|
totalPrice
|
||||||
currencyCode
|
currencyCode
|
||||||
|
completedAt
|
||||||
|
createdAt
|
||||||
|
taxesIncluded
|
||||||
lineItems(first: 250) {
|
lineItems(first: 250) {
|
||||||
pageInfo {
|
pageInfo {
|
||||||
hasNextPage
|
hasNextPage
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
import customersApi from '@framework/api/customers'
|
import customersApi from '@framework/api/customer'
|
||||||
|
|
||||||
export default customersApi()
|
export default customersApi()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user