Add custome checkout function

Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
This commit is contained in:
Chloe 2022-05-08 13:04:25 +07:00
parent 1090294ec1
commit 94a145b604
41 changed files with 860 additions and 59 deletions

View File

@ -1,4 +1,6 @@
COMMERCE_PROVIDER=@vercel/commerce-opencommerce COMMERCE_PROVIDER=@vercel/commerce-opencommerce
OPENCOMMERCE_STOREFRONT_API_URL= OPENCOMMERCE_STOREFRONT_API_URL=
OPENCOMMERCE_PRIMARY_SHOP_ID= OPENCOMMERCE_PRIMARY_SHOP_ID=
OPENCOMMERCE_IMAGE_DOMAIN=
OPENCOMMERCE_STRIPE_API_KEY=

1
packages/opencommerce/global.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare module '@components/checkout/context'

View File

@ -50,7 +50,8 @@
"dependencies": { "dependencies": {
"@vercel/commerce": "^0.0.1", "@vercel/commerce": "^0.0.1",
"@vercel/fetch": "^6.1.1", "@vercel/fetch": "^6.1.1",
"graphql": "^16.3.0" "graphql": "^16.3.0",
"stripe": "^8.220.0"
}, },
"peerDependencies": { "peerDependencies": {
"next": "^12", "next": "^12",

View File

@ -2,7 +2,6 @@ import { normalizeCart } from '../../../utils/normalize'
import getCartCookie from '../../utils/get-cart-cookie' import getCartCookie from '../../utils/get-cart-cookie'
import updateCartItemsQuantityMutation from '../../mutations/update-cart-item-quantity' import updateCartItemsQuantityMutation from '../../mutations/update-cart-item-quantity'
import type { CartEndpoint } from '.' import type { CartEndpoint } from '.'
import { UpdateCartItemsQuantityPayload } from '../../../../schema'
const updateItem: CartEndpoint['handlers']['updateItem'] = async ({ const updateItem: CartEndpoint['handlers']['updateItem'] = async ({
res, res,

View File

@ -1 +1,23 @@
export default function noopApi(...args: any[]): void {} import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import checkoutEndpoint from '@vercel/commerce/api/endpoints/checkout'
import type { CheckoutSchema } from '../../../types/checkout'
import type { OpenCommerceAPI } from '../..'
import submitCheckout from './submit-checkout'
import getCheckout from './get-checkout'
export type CheckoutAPI = GetAPISchema<OpenCommerceAPI, CheckoutSchema>
export type CheckoutEndpoint = CheckoutAPI['endpoint']
export const handlers: CheckoutEndpoint['handlers'] = {
submitCheckout,
getCheckout,
}
const checkoutApi = createEndpoint<CheckoutAPI>({
handler: checkoutEndpoint,
handlers,
})
export default checkoutApi

View File

@ -0,0 +1,96 @@
import Stripe from 'stripe'
import type { CardFields } from '../../../types/customer/card'
import { LineItem } from '../../../types/cart'
import placeOrder from '../../mutations/place-order'
import setEmailOnAnonymousCart from '../../mutations/set-email-on-anonymous-cart'
import getCartCookie from '../../utils/get-cart-cookie'
import type { CheckoutEndpoint } from '.'
const stripe = new Stripe(process.env.OPENCOMMERCE_STRIPE_API_KEY as string, {
apiVersion: '2020-08-27',
})
const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({
res,
body: { item, cartId },
config: { fetch, shopId, anonymousCartTokenCookie, cartCookie },
req: { cookies },
}) => {
await fetch(setEmailOnAnonymousCart, {
variables: {
input: {
cartId,
cartToken: cookies[anonymousCartTokenCookie],
email: 'opencommerce@test.com',
},
},
})
const card = item.card as CardFields
const pm = await stripe.paymentMethods.create({
type: 'card',
card: {
number: card.cardNumber,
exp_month: Number(card.cardExpireDate.split('/')[0]),
exp_year: Number(card.cardExpireDate.split('/')[1]),
cvc: card.cardCvc,
},
} as Stripe.PaymentMethodCreateParams)
const result = await stripe.paymentIntents.create({
confirm: true,
amount: Math.round(item.checkout.cart.checkout.summary.total.amount * 100),
currency: item.checkout.cart.currency.code,
capture_method: 'manual',
metadata: {
integration_check: 'accept_a_payment',
},
payment_method: pm.id,
})
if (result.status === 'succeeded' || result.status === 'requires_capture') {
const { data } = await fetch(placeOrder, {
variables: {
input: {
payments: {
data: { stripePaymentIntentId: result.id },
amount: item.checkout.cart.checkout.summary.total.amount,
method: 'stripe_payment_intent',
},
order: {
cartId,
currencyCode: item.checkout.cart.currency.code,
email: 'opencommerce@test.com',
shopId,
fulfillmentGroups: {
shopId,
data: item.checkout.cart.checkout.fulfillmentGroups[0].data,
items: item.checkout.cart.lineItems.map((item: LineItem) => ({
price: item.variant.price,
quantity: item.quantity,
productConfiguration: {
productId: item.productId,
productVariantId: item.variantId,
},
})),
type: item.checkout.cart.checkout.fulfillmentGroups[0].type,
selectedFulfillmentMethodId:
item.checkout.cart.checkout.fulfillmentGroups[0]
.selectedFulfillmentOption.fulfillmentMethod._id,
},
},
},
},
})
res.setHeader('Set-Cookie', [
getCartCookie(cartCookie),
getCartCookie(anonymousCartTokenCookie),
])
}
res.status(200).json({ data: null, errors: [] })
}
export default submitCheckout

View File

@ -0,0 +1,73 @@
import setShippingAddressOnCartMutation from '../../../mutations/add-shipping-address'
import type { CustomerAddressEndpoint } from '.'
import updateFulfillmentOptions from '../../../mutations/update-fulfillment-options'
import selectFulfillmentOptions from '../../../mutations/select-fulfillment-options'
const addItem: CustomerAddressEndpoint['handlers']['addItem'] = async ({
res,
body: { item, cartId },
config: { fetch, anonymousCartTokenCookie },
req: { cookies },
}) => {
// Return an error if no cart is present
if (!cartId) {
return res.status(400).json({
data: null,
errors: [{ message: 'Cookie not found' }],
})
}
// Register address
const {
data: { setShippingAddressOnCart },
} = await fetch(setShippingAddressOnCartMutation, {
variables: {
input: {
address: {
address1: item.streetNumber || 'NextJS storefront',
country: item.country,
fullName: `${item.firstName || 'Test'} ${
item.lastName || 'Account'
}}`,
city: item.city || 'LA',
phone: '0123456789',
postal: item.zipCode || '1234567',
region: item.city || 'LA',
},
cartId,
cartToken: cookies[anonymousCartTokenCookie],
},
},
})
const {
data: { updateFulfillmentOptionsForGroup },
} = await fetch(updateFulfillmentOptions, {
variables: {
input: {
cartId,
fulfillmentGroupId:
setShippingAddressOnCart.cart.checkout.fulfillmentGroups[0]._id,
},
},
})
await fetch(selectFulfillmentOptions, {
variables: {
input: {
cartId,
cartToken: cookies[anonymousCartTokenCookie],
fulfillmentGroupId:
updateFulfillmentOptionsForGroup.cart.checkout.fulfillmentGroups[0]
._id,
fulfillmentMethodId:
updateFulfillmentOptionsForGroup.cart.checkout.fulfillmentGroups[0]
.availableFulfillmentOptions[0].fulfillmentMethod._id,
},
},
})
return res.status(200).json({ data: null, errors: [] })
}
export default addItem

View File

@ -0,0 +1,9 @@
import type { CustomerAddressEndpoint } from '.'
const getCards: CustomerAddressEndpoint['handlers']['getAddresses'] = async ({
res,
}) => {
return res.status(200).json({ data: null, errors: [] })
}
export default getCards

View File

@ -0,0 +1,30 @@
import type { CustomerAddressSchema } from '../../../../types/customer/address'
import type { OpenCommerceAPI } from '../../..'
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import customerAddressEndpoint from '@vercel/commerce/api/endpoints/customer/address'
import getAddresses from './get-addresses'
import addItem from './add-item'
import updateItem from './update-item'
import removeItem from './remove-item'
export type CustomerAddressAPI = GetAPISchema<
OpenCommerceAPI,
CustomerAddressSchema
>
export type CustomerAddressEndpoint = CustomerAddressAPI['endpoint']
export const handlers: CustomerAddressEndpoint['handlers'] = {
getAddresses,
addItem,
updateItem,
removeItem,
}
const customerAddressApi = createEndpoint<CustomerAddressAPI>({
handler: customerAddressEndpoint,
handlers,
})
export default customerAddressApi

View File

@ -0,0 +1,9 @@
import type { CustomerAddressEndpoint } from '.'
const removeItem: CustomerAddressEndpoint['handlers']['removeItem'] = async ({
res,
}) => {
return res.status(200).json({ data: null, errors: [] })
}
export default removeItem

View File

@ -0,0 +1,9 @@
import type { CustomerAddressEndpoint } from '.'
const updateItem: CustomerAddressEndpoint['handlers']['updateItem'] = async ({
res,
}) => {
return res.status(200).json({ data: null, errors: [] })
}
export default updateItem

View File

@ -1 +0,0 @@
export default function noopApi(...args: any[]): void {}

View File

@ -0,0 +1,43 @@
import type { CustomerCardEndpoint } from '.'
import createPaymentIntent from '../../../mutations/create-payment-intent'
const addItem: CustomerCardEndpoint['handlers']['addItem'] = async ({
res,
body: { item, cartId },
config,
req: { cookies },
}) => {
// Return an error if no item is present
if (!item) {
return res.status(400).json({
data: null,
errors: [{ message: 'Missing item' }],
})
}
// Return an error if no cart is present
if (!cartId) {
return res.status(400).json({
data: null,
errors: [{ message: 'Cookie not found' }],
})
}
const {
data: { createStripePaymentIntent },
} = await config.fetch(createPaymentIntent, {
variables: {
input: {
cartId,
shopId: config.shopId,
cartToken: cookies[config.anonymousCartTokenCookie],
},
},
})
return res.status(200).json({
data: createStripePaymentIntent.paymentIntentClientSecret,
})
}
export default addItem

View File

@ -0,0 +1,9 @@
import type { CustomerCardEndpoint } from '.'
const getCards: CustomerCardEndpoint['handlers']['getCards'] = async ({
res,
}) => {
return res.status(200).json({ data: null, errors: [] })
}
export default getCards

View File

@ -0,0 +1,26 @@
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import customerCardEndpoint from '@vercel/commerce/api/endpoints/customer/card'
import { CustomerCardSchema } from '../../../../types/customer/card'
import { OpenCommerceAPI } from '../../..'
import getCards from './get-cards'
import addItem from './add-item'
// import updateItem from './update-item'
// import removeItem from './remove-item'
export type CustomerCardAPI = GetAPISchema<OpenCommerceAPI, CustomerCardSchema>
export type CustomerCardEndpoint = CustomerCardAPI['endpoint']
export const handlers: CustomerCardEndpoint['handlers'] = {
getCards,
addItem,
updateItem: () => {},
removeItem: () => {},
}
const customerCardApi = createEndpoint<CustomerCardAPI>({
handler: customerCardEndpoint,
handlers,
})
export default customerCardApi

View File

@ -0,0 +1,13 @@
import { cartPayloadFragment } from '../queries/get-cart-query'
const setShippingAddressOnCartMutation = `
mutation setShippingAddressOnCartMutation($input: SetShippingAddressOnCartInput!) {
setShippingAddressOnCart(input: $input) {
cart {
${cartPayloadFragment}
}
}
}
`
export default setShippingAddressOnCartMutation

View File

@ -0,0 +1,211 @@
const orderCommon = `
_id
account {
_id
}
cartId
createdAt
displayStatus(language: $language)
email
fulfillmentGroups {
_id
data {
... on ShippingOrderFulfillmentGroupData {
shippingAddress {
_id
address1
address2
city
company
country
fullName
isCommercial
isShippingDefault
phone
postal
region
}
}
}
items {
nodes {
_id
addedAt
createdAt
imageURLs {
large
medium
original
small
thumbnail
}
isTaxable
optionTitle
parcel {
containers
distanceUnit
height
length
massUnit
weight
width
}
price {
amount
currency {
code
}
displayAmount
}
productConfiguration {
productId
productVariantId
}
productSlug
productType
productVendor
productTags {
nodes {
name
}
}
quantity
shop {
_id
}
subtotal {
amount
currency {
code
}
displayAmount
}
taxCode
title
updatedAt
variantTitle
}
}
selectedFulfillmentOption {
fulfillmentMethod {
_id
carrier
displayName
fulfillmentTypes
group
name
}
handlingPrice {
amount
currency {
code
}
displayAmount
}
price {
amount
currency {
code
}
displayAmount
}
}
shop {
_id
}
summary {
fulfillmentTotal {
amount
displayAmount
}
itemTotal {
amount
displayAmount
}
surchargeTotal {
amount
displayAmount
}
taxTotal {
amount
displayAmount
}
total {
amount
displayAmount
}
}
tracking
type
}
payments {
_id
amount {
displayAmount
}
billingAddress {
address1
address2
city
company
country
fullName
isCommercial
phone
postal
region
}
displayName
method {
name
}
}
referenceId
shop {
_id
currency {
code
}
}
status
summary {
fulfillmentTotal {
amount
displayAmount
}
itemTotal {
amount
displayAmount
}
surchargeTotal {
amount
displayAmount
}
taxTotal {
amount
displayAmount
}
total {
amount
displayAmount
}
}
totalItemQuantity
updatedAt
`
const placeOrder = /* GraphQL */ `
mutation placeOrderMutation(
$input: PlaceOrderInput!
$language: String! = "en"
) {
placeOrder(input: $input) {
orders {
${orderCommon}
}
token
}
}
`
export default placeOrder

View File

@ -0,0 +1,14 @@
import { cartPayloadFragment } from '../queries/get-cart-query'
const selectFulfillmentOptions = /* GraphQL */ `
mutation setFulfillmentOptionCartMutation(
$input: SelectFulfillmentOptionForGroupInput!
) {
selectFulfillmentOptionForGroup(input: $input) {
cart {
${cartPayloadFragment}
}
}
}
`
export default selectFulfillmentOptions

View File

@ -0,0 +1,15 @@
import { cartPayloadFragment } from '../queries/get-cart-query'
const setEmailOnAnonymousCart = /* GraphQL */ `
mutation setEmailOnAnonymousCartMutation(
$input: SetEmailOnAnonymousCartInput!
) {
setEmailOnAnonymousCart(input: $input) {
cart {
${cartPayloadFragment}
}
}
}
`
export default setEmailOnAnonymousCart

View File

@ -0,0 +1,15 @@
import { cartPayloadFragment } from '../queries/get-cart-query'
const updateFulfillmentOptions = /* GraphQL */ `
mutation updateFulfillmentOptionsForGroup(
$input: UpdateFulfillmentOptionsForGroupInput!
) {
updateFulfillmentOptionsForGroup(input: $input) {
cart {
${cartPayloadFragment}
}
}
}
`
export default updateFulfillmentOptions

View File

@ -0,0 +1,2 @@
export { default as useCheckout } from './use-checkout'
export { default as useSubmitCheckout } from './use-submit-checkout'

View File

@ -1,16 +1,53 @@
import type { GetCheckoutHook } from '@vercel/commerce/types/checkout'
import { useMemo } from 'react'
import { SWRHook } from '@vercel/commerce/utils/types' import { SWRHook } from '@vercel/commerce/utils/types'
import useCheckout, { import useCheckout, {
UseCheckout, UseCheckout,
} from '@vercel/commerce/checkout/use-checkout' } from '@vercel/commerce/checkout/use-checkout'
import useSubmitCheckout from './use-submit-checkout'
import { useCheckoutContext } from '@components/checkout/context'
export default useCheckout as UseCheckout<typeof handler> export default useCheckout as UseCheckout<typeof handler>
export const handler: SWRHook<any> = { export const handler: SWRHook<GetCheckoutHook> = {
fetchOptions: { fetchOptions: {
query: '', query: '',
method: '',
}, },
async fetcher({ input, options, fetch }) {}, useHook: () =>
useHook: function useHook() {
({ useData }) => const { cardFields, addressFields } = useCheckoutContext()
async (input) => ({}),
// Basic validation - check that at least one field has a value.
const hasEnteredCard = Object.values(cardFields).some(
(fieldValue) => !!fieldValue
)
const hasEnteredAddress = Object.values(addressFields).some(
(fieldValue) => !!fieldValue
)
const response = useMemo(
() => ({
data: {
hasPayment: hasEnteredCard,
hasShipping: hasEnteredAddress,
},
}),
[hasEnteredCard, hasEnteredAddress]
)
return useMemo(
() =>
Object.create(response, {
submit: {
get() {
return useSubmitCheckout
},
enumerable: true,
},
}),
[response, useSubmitCheckout]
)
},
} }

View File

@ -0,0 +1,32 @@
import { useCallback } from 'react'
import type { SubmitCheckoutHook } from '@vercel/commerce/types/checkout'
import type { MutationHook } from '@vercel/commerce/utils/types'
import useSubmitCheckout, {
UseSubmitCheckout,
} from '@vercel/commerce/checkout/use-submit-checkout'
export default useSubmitCheckout as UseSubmitCheckout<typeof handler>
export const handler: MutationHook<SubmitCheckoutHook> = {
fetchOptions: {
url: '/api/checkout',
method: 'POST',
},
async fetcher({ input: item, options, fetch }) {
const data = await fetch({
...options,
body: { item },
})
return data
},
useHook: ({ fetch }) =>
function useHook() {
return useCallback(async function onSubmitCheckout(input) {
const data = await fetch({
input,
})
return data
}, [])
},
}

View File

@ -1,4 +1,6 @@
{ {
"provider": "opencommerce", "provider": "opencommerce",
"features": {} "features": {
"customCheckout": true
}
} }

View File

@ -0,0 +1 @@
export { default as useAddItem } from './use-add-item'

View File

@ -1,17 +1,36 @@
import type { AddItemHook } from '@vercel/commerce/types/customer/address'
import type { MutationHook } from '@vercel/commerce/utils/types'
import { useCallback } from 'react'
import useAddItem, { import useAddItem, {
UseAddItem, UseAddItem,
} from '@vercel/commerce/customer/address/use-add-item' } from '@vercel/commerce/customer/address/use-add-item'
import { MutationHook } from '@vercel/commerce/utils/types' import { useCheckoutContext } from '@components/checkout/context'
export default useAddItem as UseAddItem<typeof handler> export default useAddItem as UseAddItem<typeof handler>
export const handler: MutationHook<any> = { export const handler: MutationHook<AddItemHook> = {
fetchOptions: { fetchOptions: {
query: '', url: '/api/customer/address',
method: 'POST',
}, },
async fetcher({ input, options, fetch }) {}, async fetcher({ input: item, options, fetch }) {
useHook: const data = await fetch({
({ fetch }) => ...options,
() => body: { item },
async () => ({}), })
return data
},
useHook: ({ fetch }) =>
function useHook() {
const { setAddressFields } = useCheckoutContext()
return useCallback(
async function addItem(input) {
await fetch({ input })
setAddressFields(input)
return undefined
},
[setAddressFields]
)
},
} }

View File

@ -0,0 +1,2 @@
export { default as useAddItem } from './use-add-item'
export { default as useCards } from './use-cards'

View File

@ -1,17 +1,27 @@
import type { AddItemHook } from '@vercel/commerce/types/customer/card'
import type { MutationHook } from '@vercel/commerce/utils/types'
import { useCallback } from 'react'
import useAddItem, { import useAddItem, {
UseAddItem, UseAddItem,
} from '@vercel/commerce/customer/card/use-add-item' } from '@vercel/commerce/customer/card/use-add-item'
import { MutationHook } from '@vercel/commerce/utils/types' import { useCheckoutContext } from '@components/checkout/context'
export default useAddItem as UseAddItem<typeof handler> export default useAddItem as UseAddItem<typeof handler>
export const handler: MutationHook<any> = { export const handler: MutationHook<AddItemHook> = {
fetchOptions: { fetchOptions: {
query: '', url: '',
method: '',
}, },
async fetcher({ input, options, fetch }) {}, useHook: () =>
useHook: function useHook() {
({ fetch }) => const { setCardFields } = useCheckoutContext()
() => return useCallback(
async () => ({}), async function addItem(input) {
setCardFields(input)
return undefined
},
[setCardFields]
)
},
} }

View File

@ -0,0 +1,32 @@
import { useMemo } from 'react'
import type { GetCardsHook } from '@vercel/commerce/types/customer/card'
import { SWRHook } from '@vercel/commerce/utils/types'
import useCard, { UseCards } from '@vercel/commerce/customer/card/use-cards'
export default useCard as UseCards<typeof handler>
export const handler: SWRHook<GetCardsHook> = {
fetchOptions: {
url: '/api/customer/card',
method: 'GET',
},
useHook: ({ useData }) =>
function useHook(input) {
const response = useData({
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
})
return useMemo(
() =>
Object.create(response, {
isEmpty: {
get() {
return (response.data?.length ?? 0) <= 0
},
enumerable: true,
},
}),
[response]
)
},
}

View File

@ -8,15 +8,27 @@ import { handler as useSearch } from './product/use-search'
import { handler as useLogin } from './auth/use-login' import { handler as useLogin } from './auth/use-login'
import { handler as useLogout } from './auth/use-logout' import { handler as useLogout } from './auth/use-logout'
import { handler as useSignup } from './auth/use-signup' import { handler as useSignup } from './auth/use-signup'
import { handler as useCheckout } from './checkout/use-checkout'
import { handler as useSubmitCheckout } from './checkout/use-submit-checkout'
import { handler as useAddCardItem } from './customer/card/use-add-item'
import { handler as useCards } from './customer/card/use-cards'
import { handler as useAddAddressItem } from './customer/address/use-add-item'
export const openCommerceProvider = { export const openCommerceProvider = {
locale: 'en-us', locale: 'en-us',
cartCookie: 'opencommerce_cartId', cartCookie: 'opencommerce_cartId',
fetcher, fetcher,
cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
customer: { useCustomer }, customer: {
useCustomer,
card: { useCards, useAddItem: useAddCardItem },
address: {
useAddItem: useAddAddressItem,
},
},
products: { useSearch }, products: { useSearch },
auth: { useLogin, useLogout, useSignup }, auth: { useLogin, useLogout, useSignup },
checkout: { useCheckout, useSubmitCheckout },
} }
export type OpenCommerceProvider = typeof openCommerceProvider export type OpenCommerceProvider = typeof openCommerceProvider

View File

@ -1,11 +1,11 @@
import * as Core from '@vercel/commerce/types/cart' import * as Core from '@vercel/commerce/types/cart'
import { Checkout } from '../../schema'
import { ProductVariant } from './product' import { ProductVariant } from './product'
export * from '@vercel/commerce/types/cart' export * from '@vercel/commerce/types/cart'
export type Cart = Core.Cart & { export type Cart = Core.Cart & {
lineItems: Core.LineItem[] checkout?: Checkout
id: string
} }
export type CartItemBody = Core.CartItemBody & { export type CartItemBody = Core.CartItemBody & {

View File

@ -0,0 +1 @@
export * from '@vercel/commerce/types/checkout'

View File

@ -0,0 +1 @@
export * from '@vercel/commerce/types/customer/address'

View File

@ -0,0 +1 @@
export * from '@vercel/commerce/types/customer/card'

View File

@ -249,6 +249,7 @@ export function normalizeCart(cart: OCCart): Cart {
totalPrice: cart.checkout?.summary?.total?.amount ?? 0, totalPrice: cart.checkout?.summary?.total?.amount ?? 0,
discounts: [], discounts: [],
taxesIncluded: !!cart.checkout?.summary?.taxTotal?.amount, taxesIncluded: !!cart.checkout?.summary?.taxTotal?.amount,
checkout: cart.checkout ? cart.checkout : undefined,
} }
} }

View File

@ -4,7 +4,11 @@
"module": "esnext", "module": "esnext",
"outDir": "dist", "outDir": "dist",
"baseUrl": "src", "baseUrl": "src",
"lib": ["dom", "dom.iterable", "esnext"], "lib": [
"dom",
"dom.iterable",
"esnext"
],
"declaration": true, "declaration": true,
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
@ -16,6 +20,12 @@
"incremental": true, "incremental": true,
"jsx": "react-jsx" "jsx": "react-jsx"
}, },
"include": ["src"], "include": [
"exclude": ["node_modules", "dist"] "src",
} "global.d.ts"
],
"exclude": [
"node_modules",
"dist"
]
}

View File

@ -53,6 +53,7 @@ NEXT_PUBLIC_COMMERCEJS_DEPLOYMENT_URL=
OPENCOMMERCE_STOREFRONT_API_URL= OPENCOMMERCE_STOREFRONT_API_URL=
OPENCOMMERCE_PRIMARY_SHOP_ID= OPENCOMMERCE_PRIMARY_SHOP_ID=
OPENCOMMERCE_IMAGE_DOMAIN= OPENCOMMERCE_IMAGE_DOMAIN=
OPENCOMMERCE_STRIPE_API_KEY=
SFCC_CLIENT_ID= SFCC_CLIENT_ID=
SFCC_CLIENT_SECRET= SFCC_CLIENT_SECRET=

View File

@ -7,6 +7,7 @@ import SidebarLayout from '@components/common/SidebarLayout'
import useCart from '@framework/cart/use-cart' import useCart from '@framework/cart/use-cart'
import usePrice from '@framework/product/use-price' import usePrice from '@framework/product/use-price'
import useCheckout from '@framework/checkout/use-checkout' import useCheckout from '@framework/checkout/use-checkout'
import useSubmitCheckout from '@framework/checkout/use-submit-checkout'
import ShippingWidget from '../ShippingWidget' import ShippingWidget from '../ShippingWidget'
import PaymentWidget from '../PaymentWidget' import PaymentWidget from '../PaymentWidget'
import s from './CheckoutSidebarView.module.css' import s from './CheckoutSidebarView.module.css'
@ -16,15 +17,21 @@ const CheckoutSidebarView: FC = () => {
const [loadingSubmit, setLoadingSubmit] = useState(false) const [loadingSubmit, setLoadingSubmit] = useState(false)
const { setSidebarView, closeSidebar } = useUI() const { setSidebarView, closeSidebar } = useUI()
const { data: cartData, mutate: refreshCart } = useCart() const { data: cartData, mutate: refreshCart } = useCart()
const { data: checkoutData, submit: onCheckout } = useCheckout() const { data: checkoutData } = useCheckout()
const { clearCheckoutFields } = useCheckoutContext() const onCheckout = useSubmitCheckout()
const { clearCheckoutFields, cardFields, addressFields } =
useCheckoutContext()
async function handleSubmit(event: React.ChangeEvent<HTMLFormElement>) { async function handleSubmit(event: React.ChangeEvent<HTMLFormElement>) {
try { try {
setLoadingSubmit(true) setLoadingSubmit(true)
event.preventDefault() event.preventDefault()
await onCheckout({
await onCheckout() card: cardFields,
address: addressFields,
checkout: { cart: cartData },
})
clearCheckoutFields() clearCheckoutFields()
setLoadingSubmit(false) setLoadingSubmit(false)
refreshCart() refreshCart()

View File

@ -42,7 +42,6 @@ const PaymentMethodView: FC = () => {
city: event.target.city.value, city: event.target.city.value,
country: event.target.country.value, country: event.target.country.value,
}) })
setSidebarView('CHECKOUT_VIEW') setSidebarView('CHECKOUT_VIEW')
} }

View File

@ -552,6 +552,52 @@
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
"@graphql-codegen/cli@2.6.2":
version "2.6.2"
resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-2.6.2.tgz#a9aa4656141ee0998cae8c7ad7d0bf9ca8e0c9ae"
integrity sha512-UO75msoVgvLEvfjCezM09cQQqp32+mR8Ma1ACsBpr7nroFvHbgcu2ulx1cMovg4sxDBCsvd9Eq/xOOMpARUxtw==
dependencies:
"@graphql-codegen/core" "2.5.1"
"@graphql-codegen/plugin-helpers" "^2.4.1"
"@graphql-tools/apollo-engine-loader" "^7.0.5"
"@graphql-tools/code-file-loader" "^7.0.6"
"@graphql-tools/git-loader" "^7.0.5"
"@graphql-tools/github-loader" "^7.0.5"
"@graphql-tools/graphql-file-loader" "^7.0.5"
"@graphql-tools/json-file-loader" "^7.1.2"
"@graphql-tools/load" "^7.3.0"
"@graphql-tools/prisma-loader" "^7.0.6"
"@graphql-tools/url-loader" "^7.0.11"
"@graphql-tools/utils" "^8.1.1"
ansi-escapes "^4.3.1"
chalk "^4.1.0"
change-case-all "1.0.14"
chokidar "^3.5.2"
common-tags "^1.8.0"
cosmiconfig "^7.0.0"
debounce "^1.2.0"
dependency-graph "^0.11.0"
detect-indent "^6.0.0"
glob "^7.1.6"
globby "^11.0.4"
graphql-config "^4.1.0"
inquirer "^8.0.0"
is-glob "^4.0.1"
json-to-pretty-yaml "^1.2.2"
latest-version "5.1.0"
listr "^0.14.3"
listr-update-renderer "^0.5.0"
log-symbols "^4.0.0"
minimatch "^4.0.0"
mkdirp "^1.0.4"
string-env-interpolation "^1.0.1"
ts-log "^2.2.3"
tslib "~2.3.0"
valid-url "^1.0.9"
wrap-ansi "^7.0.0"
yaml "^1.10.0"
yargs "^17.0.0"
"@graphql-codegen/cli@^2.3.1": "@graphql-codegen/cli@^2.3.1":
version "2.4.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-2.4.0.tgz#7df3ee2bdd5b88a5904ee6f52eafeb370ef70e51" resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-2.4.0.tgz#7df3ee2bdd5b88a5904ee6f52eafeb370ef70e51"
@ -618,14 +664,6 @@
"@graphql-tools/utils" "^8.1.1" "@graphql-tools/utils" "^8.1.1"
tslib "~2.3.0" tslib "~2.3.0"
"@graphql-codegen/introspection@2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@graphql-codegen/introspection/-/introspection-2.1.1.tgz#5f3aac47ef46ed817baf969e78dd2dd6d307b18a"
integrity sha512-O9zsy0IoFYDo37pBVF4pSvRMDx/AKdgOxyko4R/O+0DHEw9Nya/pQ3dbn+LDLj2n6X+xOXUBUfFvqhODTqU28w==
dependencies:
"@graphql-codegen/plugin-helpers" "^2.3.2"
tslib "~2.3.0"
"@graphql-codegen/plugin-helpers@^2.3.2": "@graphql-codegen/plugin-helpers@^2.3.2":
version "2.3.2" version "2.3.2"
resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-2.3.2.tgz#3f9ba625791901d19be733db1dfc9a3dbd0dac44" resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-2.3.2.tgz#3f9ba625791901d19be733db1dfc9a3dbd0dac44"
@ -681,17 +719,6 @@
auto-bind "~4.0.0" auto-bind "~4.0.0"
tslib "~2.3.0" tslib "~2.3.0"
"@graphql-codegen/typescript-react-apollo@3.2.11":
version "3.2.11"
resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-3.2.11.tgz#dc13abc1ec24aa78f7f0774c1f52da5d982dd1fc"
integrity sha512-Bfo7/OprnWk/srhA/3I0cKySg/SyVPX3ZoxzTk6ChYVBsy69jKDkdPWwlmE7Fjfv7+5G+xXb99OoqUUgBLma3w==
dependencies:
"@graphql-codegen/plugin-helpers" "^2.4.0"
"@graphql-codegen/visitor-plugin-common" "2.7.4"
auto-bind "~4.0.0"
change-case-all "1.0.14"
tslib "~2.3.0"
"@graphql-codegen/typescript@2.4.8", "@graphql-codegen/typescript@^2.4.8": "@graphql-codegen/typescript@2.4.8", "@graphql-codegen/typescript@^2.4.8":
version "2.4.8" version "2.4.8"
resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-2.4.8.tgz#e8110baba9713cde72d57a5c95aa27400363ed9a" resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-2.4.8.tgz#e8110baba9713cde72d57a5c95aa27400363ed9a"
@ -6256,7 +6283,7 @@ qs@6.7.0:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
qs@^6.10.2, qs@^6.6.0: qs@^6.10.2, qs@^6.10.3, qs@^6.6.0:
version "6.10.3" version "6.10.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
@ -7131,6 +7158,14 @@ stripe@^8.197.0:
"@types/node" ">=8.1.0" "@types/node" ">=8.1.0"
qs "^6.6.0" qs "^6.6.0"
stripe@^8.220.0:
version "8.220.0"
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.220.0.tgz#90bdedcb7d83e64f22c47b47f5072d837ef56357"
integrity sha512-hE3NapEqNCiiQD1lMQPccKgJsu2aANR+oDudUHcuvRnNUJ3GrbntwACs7Op45PvHpJ/RY4l46XDwTMgdWJAm3w==
dependencies:
"@types/node" ">=8.1.0"
qs "^6.10.3"
styled-jsx@5.0.0-beta.6: styled-jsx@5.0.0-beta.6:
version "5.0.0-beta.6" version "5.0.0-beta.6"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.0-beta.6.tgz#666552f8831a06f80c9084a47afc4b32b0c9f461" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.0-beta.6.tgz#666552f8831a06f80c9084a47afc4b32b0c9f461"