4
0
forked from crowetic/commerce

shopify checkout redirect & api handler

This commit is contained in:
cond0r 2021-02-04 17:18:33 +02:00
parent 2acc21164b
commit b0d8ae565d
11 changed files with 128 additions and 17 deletions

View File

@ -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>

View File

@ -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 || '',
}) })

View File

@ -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, {}, {})

View 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)
}
}
}

View 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
}

View File

@ -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

View File

@ -0,0 +1,2 @@
export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId'
export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl'

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -1,3 +1,3 @@
import customersApi from '@framework/api/customers' import customersApi from '@framework/api/customer'
export default customersApi() export default customersApi()