@hanzo/commerce 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/components/add-to-cart-widget.tsx +3 -3
  2. package/components/buy-item/buy-item-button.tsx +28 -0
  3. package/components/buy-item/buy-item-card.tsx +82 -0
  4. package/components/buy-item/buy-item-popup.tsx +38 -0
  5. package/components/{select-category-and-item-widget.tsx → buy-item/select-category-and-item-widget.tsx} +4 -4
  6. package/components/buy-item/select-category-item-card.tsx +111 -0
  7. package/components/{cart-line-item.tsx → cart-panel/cart-line-item.tsx} +4 -3
  8. package/components/{cart.tsx → cart-panel/index.tsx} +24 -17
  9. package/components/category-item-ios-wheel-selector.tsx +60 -0
  10. package/components/category-item-radio-selector.tsx +55 -0
  11. package/components/{checkout → checkout-panel}/confirm-order.tsx +3 -2
  12. package/components/{checkout → checkout-panel}/index.tsx +26 -69
  13. package/components/checkout-panel/payment/contact-info.tsx +59 -0
  14. package/components/checkout-panel/payment/index.tsx +118 -0
  15. package/components/checkout-panel/payment/pay-with-bank-transfer.tsx +106 -0
  16. package/components/checkout-panel/payment/pay-with-card.tsx +198 -0
  17. package/components/{checkout → checkout-panel/payment}/pay-with-crypto.tsx +52 -30
  18. package/components/checkout-panel/payment/payment-methods/amex.tsx +32 -0
  19. package/components/checkout-panel/payment/payment-methods/diners-club.tsx +13 -0
  20. package/components/checkout-panel/payment/payment-methods/discover.tsx +25 -0
  21. package/components/checkout-panel/payment/payment-methods/index.tsx +24 -0
  22. package/components/checkout-panel/payment/payment-methods/jcb.tsx +26 -0
  23. package/components/checkout-panel/payment/payment-methods/mastercard.tsx +27 -0
  24. package/components/checkout-panel/payment/payment-methods/visa.tsx +25 -0
  25. package/components/{checkout → checkout-panel}/shipping-info.tsx +4 -10
  26. package/components/{facet-toggles-widget → facet-values-widget}/facet-image.tsx +7 -3
  27. package/components/{facet-toggles-widget → facet-values-widget}/index.tsx +7 -5
  28. package/components/index.ts +11 -9
  29. package/components/{select-item-in-category-view.tsx → select-category-item-panel.tsx} +72 -76
  30. package/index.ts +2 -2
  31. package/package.json +11 -4
  32. package/service/context.tsx +4 -4
  33. package/service/impls/standalone/index.ts +4 -4
  34. package/service/impls/standalone/orders/index.ts +42 -20
  35. package/service/impls/standalone/standalone-service.ts +128 -44
  36. package/types/category.ts +3 -2
  37. package/types/checkout.ts +6 -0
  38. package/types/commerce-service.ts +20 -7
  39. package/types/facet.ts +1 -3
  40. package/types/index.ts +4 -4
  41. package/types/item-selector.ts +14 -0
  42. package/util/index.ts +31 -1
  43. package/util/square-payment.ts +43 -0
  44. package/util/use-sync-sku-param-w-current-item.ts +4 -1
  45. package/blocks/components/commerce-cat-and-item-block.tsx +0 -13
  46. package/blocks/def/commerce-cat-and-item-block.ts +0 -9
  47. package/components/checkout/contact-info.tsx +0 -83
  48. package/components/checkout/pay-by-bank-transfer.tsx +0 -76
  49. package/components/facets-widget.tsx +0 -55
  50. package/components/product-selection-mobile-picker.tsx +0 -78
  51. package/components/product-selection-radio-group.tsx +0 -38
  52. /package/components/{checkout → checkout-panel}/countries.ts +0 -0
  53. /package/components/{checkout → checkout-panel}/icons/btc.tsx +0 -0
  54. /package/components/{checkout → checkout-panel}/icons/eth.tsx +0 -0
  55. /package/components/{checkout → checkout-panel}/icons/usdt.tsx +0 -0
  56. /package/components/{checkout → checkout-panel}/thank-you.tsx +0 -0
@@ -1,13 +1,9 @@
1
1
  'use client'
2
2
 
3
- import { useEffect, useState } from 'react'
3
+ import { useState } from 'react'
4
4
  import { ChevronLeft } from 'lucide-react'
5
5
  import { observer } from 'mobx-react-lite'
6
6
 
7
- import { zodResolver } from '@hookform/resolvers/zod'
8
- import * as z from 'zod'
9
- import { useForm } from 'react-hook-form'
10
-
11
7
  import { Button, Main, Separator, Toaster } from '@hanzo/ui/primitives'
12
8
  import { cn } from '@hanzo/ui/util'
13
9
  import { useAuth } from '@hanzo/auth/service'
@@ -15,88 +11,49 @@ import { LoginComponent, AuthWidget } from '@hanzo/auth/components'
15
11
 
16
12
  import ShippingInfo from './shipping-info'
17
13
  import ThankYou from './thank-you'
18
- import PayWithCrypto from './pay-with-crypto'
19
- import PayByBankTransfer from './pay-by-bank-transfer'
20
- import ContactInfo from './contact-info'
21
- import { Cart } from '..'
22
- import { useCommerce } from '../../service/context'
14
+ import CartPanel from '../cart-panel'
15
+ import Payment from './payment'
23
16
 
24
- const contactFormSchema = z.object({
25
- name: z.string().min(2, 'Name must be at least 2 characters.'),
26
- email: z.string().email(),
27
- })
17
+ const CheckoutPanel: React.FC<{
18
+ close: () => void
19
+ }> = observer(({
20
+ close
21
+ }) => {
28
22
 
29
- const Checkout: React.FC<{toggleCheckout: () => void}> = observer(({toggleCheckout}) => {
30
23
  const auth = useAuth()
31
- const cmmc = useCommerce()
32
24
 
33
25
  const [currentStep, setCurrentStep] = useState(1)
34
- const [paymentMethod, setPaymentMethod] = useState<'crypto' | 'bank' | undefined>()
35
26
  const [orderId, setOrderId] = useState<string>()
36
27
 
37
- const contactForm = useForm<z.infer<typeof contactFormSchema>>({
38
- resolver: zodResolver(contactFormSchema),
39
- defaultValues: {
40
- name: auth.user?.displayName ?? '',
41
- email: auth.user?.email ?? '',
42
- },
43
- })
44
-
45
- useEffect(() => {
46
- if (auth.loggedIn) {
47
- contactForm.setValue('name', auth.user?.displayName ?? '')
48
- contactForm.setValue('email', auth.user?.email ?? '')
49
- }
50
- }, [auth.loggedIn])
51
-
52
- const selectPaymentMethod = async (method: 'crypto' | 'bank') => {
53
- contactForm.handleSubmit(async () => {
54
- if (auth.user) {
55
- if (!!orderId) {
56
- await cmmc.updateOrder(orderId, auth.user.email, method)
57
- } else {
58
- const id = await cmmc.createOrder(auth.user.email, method)
59
- setOrderId(id)
60
- }
61
- }
62
- setPaymentMethod(method)
63
- setCurrentStep(2)
64
- })()
65
- }
66
-
67
-
68
28
  const step1 = auth.loggedIn ? (
69
- <ContactInfo form={contactForm} selectPaymentMethod={selectPaymentMethod}/>
29
+ <Payment
30
+ orderId={orderId}
31
+ setOrderId={setOrderId}
32
+ setCurrentStep={setCurrentStep}
33
+ />
70
34
  ) : (
71
35
  <LoginComponent hideHeader className='max-w-[20rem] mx-auto'/>
72
36
  )
73
37
 
74
- const step2 = paymentMethod === 'crypto' ? (
75
- <PayWithCrypto setCurrentStep={setCurrentStep}/>
76
- ) : (
77
- <PayByBankTransfer setCurrentStep={setCurrentStep}/>
38
+ const step2 = (
39
+ <ShippingInfo orderId={orderId} setCurrentStep={setCurrentStep}/>
78
40
  )
79
41
 
80
- const step3 = (
81
- <ShippingInfo orderId={orderId} paymentMethod={paymentMethod} setCurrentStep={setCurrentStep}/>
82
- )
83
-
84
- const step4 = <ThankYou/>
42
+ const step3 = <ThankYou/>
85
43
 
86
- const steps = [step1, step2, step3, step4]
44
+ const steps = [step1, step2, step3]
87
45
 
88
46
  return (
89
- <div className="fixed top-0 left-0 !max-w-none w-full h-full min-h-screen bg-background z-[51]">
47
+ <div className="fixed top-0 left-0 !max-w-none w-full h-full min-h-screen bg-background z-50">
90
48
  <Toaster/>
91
49
  <Main className='flex flex-col gap-1 h-full'>
92
- <div className='flex justify-between'>
50
+ <div className='flex justify-between items-center'>
93
51
  <Button
94
52
  variant='ghost'
95
53
  size='icon'
96
- className='sm:ml-2 sm:mt-2'
97
54
  onClick={() => {
98
55
  setCurrentStep(0)
99
- toggleCheckout()
56
+ close()
100
57
  }}
101
58
  >
102
59
  <ChevronLeft/>
@@ -106,21 +63,21 @@ const Checkout: React.FC<{toggleCheckout: () => void}> = observer(({toggleChecko
106
63
 
107
64
  <div className='grid grid-cols-5 justify-center gap-8 h-full'>
108
65
  <div className='col-span-2 hidden md:flex'>
109
- <Cart hideCheckout className='fixed justify-center border-none mt-10 w-1/3 max-w-[40rem]'/>
66
+ <CartPanel noCheckout className='fixed justify-center border-none mt-10 w-1/3 max-w-[40rem]'/>
110
67
  </div>
111
68
 
112
- <div className='flex flex-col gap-8 sm:gap-14 col-span-5 md:col-span-3 max-w-[30rem] w-full mx-auto overflow-y-auto h-[calc(100%-40px)] sm:h-[calc(100%-48px)] py-4'>
69
+ <div className='flex flex-col gap-8 sm:gap-14 col-span-5 md:col-span-3 max-w-[30rem] w-full mx-auto overflow-y-auto h-[calc(100%-40px)] sm:h-[calc(100%-48px)] py-4 px-1'>
113
70
  <div className='flex gap-2 mx-auto items-center text-xxs sm:text-base'>
114
71
  <div className={cn('w-6 h-6 rounded-full border border-foreground flex flex-col justify-center items-center', currentStep === 1 ? 'bg-foreground text-muted-4' : '')}>
115
- <div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Contact</div>
72
+ <div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Payment</div>
116
73
  </div>
117
74
  <Separator className='w-[4rem] sm:w-[6rem]'/>
118
75
  <div className={cn('w-6 h-6 rounded-full border border-foreground flex flex-col justify-center items-center', currentStep === 2 ? 'bg-foreground text-muted-4' : '')}>
119
- <div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Payment</div>
76
+ <div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Delivery</div>
120
77
  </div>
121
78
  <Separator className='w-[4rem] sm:w-[6rem]'/>
122
79
  <div className={cn('w-6 h-6 rounded-full border border-foreground flex flex-col justify-center items-center', currentStep === 3 ? 'bg-foreground text-muted-4' : '')}>
123
- <div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Delivery</div>
80
+ <div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Done!</div>
124
81
  </div>
125
82
  </div>
126
83
  {steps[currentStep - 1]}
@@ -131,4 +88,4 @@ const Checkout: React.FC<{toggleCheckout: () => void}> = observer(({toggleChecko
131
88
  )
132
89
  })
133
90
 
134
- export default Checkout
91
+ export default CheckoutPanel
@@ -0,0 +1,59 @@
1
+ import {
2
+ Form,
3
+ FormControl,
4
+ FormField,
5
+ FormItem,
6
+ FormLabel,
7
+ FormMessage,
8
+ } from '@hanzo/ui/primitives/form'
9
+ import { Input } from '@hanzo/ui/primitives'
10
+ import type { UseFormReturn } from 'react-hook-form'
11
+
12
+ const ContactInfo: React.FC<{
13
+ form: UseFormReturn<{
14
+ name: string
15
+ email: string
16
+ }, any, {
17
+ name: string
18
+ email: string
19
+ }>,
20
+ }> = ({
21
+ form,
22
+ }) => {
23
+ return (
24
+ <Form {...form}>
25
+ <form className='text-left'>
26
+ <div className='flex flex-col sm:flex-row gap-2'>
27
+ <FormField
28
+ control={form.control}
29
+ name='name'
30
+ render={({ field }) => (
31
+ <FormItem className='space-y-1 w-full'>
32
+ <FormLabel>Full name</FormLabel>
33
+ <FormControl>
34
+ <Input {...field} />
35
+ </FormControl>
36
+ <FormMessage />
37
+ </FormItem>
38
+ )}
39
+ />
40
+ <FormField
41
+ control={form.control}
42
+ name='email'
43
+ render={({ field }) => (
44
+ <FormItem className='space-y-1 w-full'>
45
+ <FormLabel>Email</FormLabel>
46
+ <FormControl>
47
+ <Input {...field} />
48
+ </FormControl>
49
+ <FormMessage />
50
+ </FormItem>
51
+ )}
52
+ />
53
+ </div>
54
+ </form>
55
+ </Form>
56
+ )
57
+ }
58
+
59
+ export default ContactInfo
@@ -0,0 +1,118 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useState } from 'react'
4
+ import { observer } from 'mobx-react-lite'
5
+
6
+ import { zodResolver } from '@hookform/resolvers/zod'
7
+ import * as z from 'zod'
8
+ import { useForm } from 'react-hook-form'
9
+
10
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@hanzo/ui/primitives'
11
+ import { useAuth } from '@hanzo/auth/service'
12
+ import PayWithCrypto from './pay-with-crypto'
13
+ import PayWithBankTransfer from './pay-with-bank-transfer'
14
+ import PayWithCard from './pay-with-card'
15
+ import { useCommerce } from '../../../service/context'
16
+ import type { TransactionStatus } from '../../../types'
17
+
18
+ const contactFormSchema = z.object({
19
+ name: z.string().min(1, 'Enter your full name.'),
20
+ email: z.string().email(),
21
+ })
22
+
23
+ const Payment: React.FC<{
24
+ setCurrentStep: (currentStep: number) => void
25
+ orderId?: string
26
+ setOrderId: (orderId?: string) => void
27
+ }> = observer(({
28
+ setCurrentStep,
29
+ orderId,
30
+ setOrderId
31
+ }) => {
32
+ const cmmc = useCommerce()
33
+ const auth = useAuth()
34
+ const [transactionStatus, setTransactionStatus] = useState<TransactionStatus>('unpaid')
35
+
36
+ const contactForm = useForm<z.infer<typeof contactFormSchema>>({
37
+ resolver: zodResolver(contactFormSchema),
38
+ defaultValues: {
39
+ name: auth.user?.displayName ?? '',
40
+ email: auth.user?.email ?? '',
41
+ },
42
+ })
43
+
44
+ useEffect(() => {
45
+ if (auth.loggedIn) {
46
+ contactForm.setValue('name', auth.user?.displayName ?? '')
47
+ contactForm.setValue('email', auth.user?.email ?? '')
48
+ }
49
+ }, [auth.loggedIn])
50
+
51
+ const storePaymentInfo = async (paymentInfo: any) => {
52
+ if (auth.user) {
53
+ const {name, email} = contactForm.getValues()
54
+ let id
55
+ if (!orderId) {
56
+ id = await cmmc.createOrder(email ? email : auth.user.email, name)
57
+ setOrderId(id)
58
+ }
59
+ if (id) {
60
+ await cmmc.updateOrderPaymentInfo(id, paymentInfo)
61
+ }
62
+ }
63
+ }
64
+
65
+ return (
66
+ <Tabs defaultValue='card' className='w-full mx-auto max-w-[50rem]'>
67
+ <TabsList className='grid w-full grid-cols-3 mx-auto bg-level-2 h-auto'>
68
+ <TabsTrigger
69
+ value='card'
70
+ className='font-nav whitespace-normal h-full text-sm sm:text-base'
71
+ disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
72
+ >
73
+ PAY WITH CARD
74
+ </TabsTrigger>
75
+ <TabsTrigger
76
+ value='crypto'
77
+ className='font-nav whitespace-normal h-full text-sm sm:text-base'
78
+ disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
79
+ >
80
+ PAY WITH CRYPTO
81
+ </TabsTrigger>
82
+ <TabsTrigger
83
+ value='bank'
84
+ className='font-nav whitespace-normal h-full text-sm sm:text-base'
85
+ disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
86
+ >
87
+ BANK TRANSFER
88
+ </TabsTrigger>
89
+ </TabsList>
90
+ <TabsContent value='card'>
91
+ <PayWithCard
92
+ setCurrentStep={setCurrentStep}
93
+ transactionStatus={transactionStatus}
94
+ setTransactionStatus={setTransactionStatus}
95
+ storePaymentInfo={storePaymentInfo}
96
+ contactForm={contactForm}
97
+ />
98
+ </TabsContent>
99
+ <TabsContent value='crypto'>
100
+ <PayWithCrypto
101
+ setCurrentStep={setCurrentStep}
102
+ transactionStatus={transactionStatus}
103
+ setTransactionStatus={setTransactionStatus}
104
+ storePaymentInfo={storePaymentInfo}
105
+ />
106
+ </TabsContent>
107
+ <TabsContent value='bank'>
108
+ <PayWithBankTransfer
109
+ setCurrentStep={setCurrentStep}
110
+ storePaymentInfo={storePaymentInfo}
111
+ contactForm={contactForm}
112
+ />
113
+ </TabsContent>
114
+ </Tabs>
115
+ )
116
+ })
117
+
118
+ export default Payment
@@ -0,0 +1,106 @@
1
+ 'use client'
2
+
3
+ import type { ReactNode } from 'react'
4
+ import { Copy } from 'lucide-react'
5
+ import type { UseFormReturn } from 'react-hook-form'
6
+
7
+ import { Button, Tabs, TabsContent, TabsList, TabsTrigger, toast } from '@hanzo/ui/primitives'
8
+ import ContactInfo from './contact-info'
9
+
10
+ const InfoField: React.FC<{
11
+ label: string,
12
+ value: ReactNode,
13
+ copyValue: string
14
+ }> = ({
15
+ label,
16
+ value,
17
+ copyValue
18
+ }) => {
19
+ const copyToClipboard = (label: string, text: string) => {
20
+ navigator.clipboard.writeText(text)
21
+ toast({title: `${label} copied to clipboard`})
22
+ }
23
+
24
+ return (
25
+ <div className='flex flex-col gap-1'>
26
+ <p className='text-xs'>{label}</p>
27
+ <div className='flex items-center justify-between text-lg border rounded-lg py-2 px-4'>
28
+ {value}
29
+ <Button variant='ghost' size='icon' onClick={() => copyToClipboard(label, copyValue)}>
30
+ <Copy className='h-4 w-4'/>
31
+ </Button>
32
+ </div>
33
+ </div>
34
+ )
35
+ }
36
+
37
+ const PayWithBankTransfer: React.FC<{
38
+ setCurrentStep: (step: number) => void
39
+ storePaymentInfo: (paymentInfo: any) => Promise<void>
40
+ contactForm: UseFormReturn<{
41
+ name: string
42
+ email: string
43
+ }, any, {
44
+ name: string
45
+ email: string
46
+ }>
47
+ }> = ({
48
+ setCurrentStep,
49
+ storePaymentInfo,
50
+ contactForm
51
+ }) => {
52
+ const payByBankTransfer = async () => {
53
+ contactForm.handleSubmit(async () => {
54
+ await storePaymentInfo({paymentMethod: 'bank-transfer'})
55
+ setCurrentStep(2)
56
+ })()
57
+ }
58
+
59
+ return (
60
+ <div className='flex flex-col gap-6 mt-6'>
61
+ <ContactInfo form={contactForm}/>
62
+ <Tabs defaultValue="usd" className='w-full mx-auto max-w-[50rem]'>
63
+ <TabsList className="grid w-full grid-cols-2 max-w-[15rem] mx-auto bg-level-2">
64
+ <TabsTrigger value="usd">USD</TabsTrigger>
65
+ <TabsTrigger value="eur">EUR/GBP</TabsTrigger>
66
+ </TabsList>
67
+ <TabsContent value="usd">
68
+ <div className='flex flex-col gap-4 w-full'>
69
+ <InfoField
70
+ label='Beneficiary Bank'
71
+ value={<div>Bank of America<br/>NA 222 Broadway<br/>New York, New York. 10038</div>}
72
+ copyValue='Bank of America, NA 222 Broadway, New York, New York. 10038'
73
+ />
74
+ <InfoField
75
+ label='Beneficiary'
76
+ value={<div>Hanzo, Inc<br/>4811 Mastin Street<br/>Merriam, KS 66203</div>}
77
+ copyValue='Hanzo, Inc, 4811 Mastin Street, Merriam, KS 66203'
78
+ />
79
+ <InfoField label='Routing Number - ACH' value='113000023 / 111000025' copyValue='113000023 / 111000025'/>
80
+ <InfoField label='Routing Number - Wire' value='026009593' copyValue='026009593'/>
81
+ <InfoField label='Routing Number - SWIFT' value='BOFAUS3N' copyValue='BOFAUS3N'/>
82
+ <InfoField label='Reference' value='Lux' copyValue='Lux'/>
83
+ </div>
84
+ </TabsContent>
85
+ <TabsContent value="eur">
86
+ <div className='flex flex-col gap-4 w-full'>
87
+ <InfoField
88
+ label='Beneficiary Bank'
89
+ value={<div>Clear Junction Limited<br/>4th floor<br/>Imperial House</div>}
90
+ copyValue='Clear Junction Limited, 4th floor, Imperial House'
91
+ />
92
+ <InfoField label='Account Number (GBP)' value='33781813' copyValue='33781813'/>
93
+ <InfoField label='Account Name' value='AiDLT Global Ltd.' copyValue='AiDLT Global Ltd.'/>
94
+ <InfoField label='SWIFT/BIC' value='CLJUGB21' copyValue='CLJUGB21'/>
95
+ <InfoField label='Sort Code (GBP)' value='041307' copyValue='041307'/>
96
+ <InfoField label='IBAN' value='GB75 CLJU 0413 0733 78181 3' copyValue='GB75 CLJU 0413 0733 78181 3'/>
97
+ <InfoField label='Reference' value='Lux' copyValue='Lux'/>
98
+ </div>
99
+ </TabsContent>
100
+ </Tabs>
101
+ <Button onClick={payByBankTransfer} className='mx-auto w-full mt-4'>Continue</Button>
102
+ </div>
103
+ )
104
+ }
105
+
106
+ export default PayWithBankTransfer
@@ -0,0 +1,198 @@
1
+ 'use client'
2
+ import React from 'react'
3
+
4
+ import type { UseFormReturn } from 'react-hook-form'
5
+ // @ts-ignore
6
+ import { ApplePay, GooglePay, CreditCard, PaymentForm } from 'react-square-web-payments-sdk'
7
+
8
+ import { ApplyTypography, Button } from '@hanzo/ui/primitives'
9
+
10
+ import { processSquareCardPayment } from '../../../util'
11
+ import { useCommerce } from '../../../service/context'
12
+ import type { TransactionStatus } from '../../../types'
13
+
14
+ import PaymentMethods from './payment-methods'
15
+ import ContactInfo from './contact-info'
16
+
17
+ const PayWithCard: React.FC<{
18
+ setCurrentStep: (currentStep: number) => void
19
+ transactionStatus: TransactionStatus
20
+ setTransactionStatus: (status: TransactionStatus) => void
21
+ storePaymentInfo: (paymentInfo: any) => Promise<void>
22
+ contactForm: UseFormReturn<{
23
+ name: string
24
+ email: string
25
+ }, any, {
26
+ name: string
27
+ email: string
28
+ }>
29
+ }> = ({
30
+ setCurrentStep,
31
+ transactionStatus,
32
+ setTransactionStatus,
33
+ storePaymentInfo,
34
+ contactForm,
35
+ }) => {
36
+ const cmmc = useCommerce()
37
+
38
+ const cardTokenizeResponseReceived = async (
39
+ token: any,
40
+ verifiedBuyer: any
41
+ ) => {
42
+ contactForm.handleSubmit(async () => {
43
+ setTransactionStatus('paid')
44
+ const res = await processSquareCardPayment(token.token, cmmc.cartTotal, verifiedBuyer.token)
45
+ if (res) {
46
+ console.log(token)
47
+ await storePaymentInfo({paymentMethod: token.details.method ?? null, processed: res})
48
+ setTransactionStatus('confirmed')
49
+ } else {
50
+ setTransactionStatus('error')
51
+ }
52
+ })()
53
+ }
54
+
55
+ const createVerificationDetails = () => {
56
+ const {name, email} = contactForm.getValues()
57
+ return {
58
+ amount: cmmc.cartTotal.toFixed(2),
59
+ billingContact: {
60
+ givenName: name,
61
+ email,
62
+ },
63
+ currencyCode: 'USD',
64
+ intent: 'CHARGE',
65
+ }
66
+ }
67
+
68
+ const createPaymentRequest= () => ({
69
+ countryCode: "US",
70
+ currencyCode: "USD",
71
+ lineItems: cmmc.cartItems.map(item => ({
72
+ amount: item.price.toFixed(2),
73
+ label: item.title,
74
+ id: item.sku,
75
+ })),
76
+ requestBillingContact: false,
77
+ requestShippingContact: false,
78
+ total: {
79
+ amount: cmmc.cartTotal.toFixed(2),
80
+ label: "Total",
81
+ },
82
+ })
83
+
84
+ return (
85
+ <PaymentForm
86
+ /**
87
+ * Identifies the calling form with a verified application ID generated from
88
+ * the Square Application Dashboard.
89
+ */
90
+ applicationId={process.env.NEXT_PUBLIC_SQUARE_APPLICATION_ID}
91
+ /**
92
+ * Invoked when payment form receives the result of a tokenize generation
93
+ * request. The result will be a valid credit card or wallet token, or an error.
94
+ */
95
+ cardTokenizeResponseReceived={cardTokenizeResponseReceived}
96
+ /**
97
+ * This function enable the Strong Customer Authentication (SCA) flow
98
+ *
99
+ * We strongly recommend use this function to verify the buyer and reduce
100
+ * the chance of fraudulent transactions.
101
+ */
102
+ createVerificationDetails={createVerificationDetails}
103
+ /**
104
+ * This function is required for digital wallets (Apple Pay, Google Pay)
105
+ */
106
+ createPaymentRequest={createPaymentRequest}
107
+ /**
108
+ * Identifies the location of the merchant that is taking the payment.
109
+ * Obtained from the Square Application Dashboard - Locations tab.
110
+ */
111
+ locationId={process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID}
112
+ >
113
+ <div className='flex flex-col gap-2 mt-6'>
114
+ {transactionStatus === 'confirmed' ? (
115
+ <ApplyTypography className='flex flex-col gap-4'>
116
+ <h5 className='mx-auto font-nav'>Payment confirmed!</h5>
117
+ <p className='mx-auto'>Thank you for your purchase.</p>
118
+ <Button onClick={() => setCurrentStep(2)}>Continue</Button>
119
+ </ApplyTypography>
120
+ ) : transactionStatus === 'paid' ? (
121
+ <ApplyTypography className='flex flex-col gap-4'>
122
+ <h5 className='mx-auto font-nav'>Processing your payment...</h5>
123
+ </ApplyTypography>
124
+ ) : (
125
+ <>
126
+ <GooglePay/>
127
+ <ApplePay/>
128
+
129
+ <p className='flex gap-2 whitespace-nowrap items-center my-6 sm:my-10 text-sm text-foreground/60'>
130
+ <hr className='bg-foreground/60 w-full border'/> or pay with card <hr className='bg-foreground/60 w-full border'/>
131
+ </p>
132
+
133
+ <ContactInfo form={contactForm}/>
134
+
135
+ <PaymentMethods/>
136
+
137
+ {/* Imitates hanzo/ui Button and Input styles, I was unable to render the
138
+ hanzo/ui button outright and keeping the submit form functionality*/}
139
+ <CreditCard
140
+ style={{
141
+ '.input-container': {
142
+ borderColor: '#2D2D2D',
143
+ borderRadius: '6px',
144
+ },
145
+ '.input-container.is-focus': {
146
+ borderColor: '#ffffff',
147
+ },
148
+ '.input-container.is-error': {
149
+ borderColor: '#ff1600',
150
+ },
151
+ '.message-text': {
152
+ color: '#999999',
153
+ },
154
+ '.message-icon': {
155
+ color: '#999999',
156
+ },
157
+ '.message-text.is-error': {
158
+ color: '#ff1600',
159
+ },
160
+ '.message-icon.is-error': {
161
+ color: '#ff1600',
162
+ },
163
+ input: {
164
+ backgroundColor: '#000000',
165
+ color: '#FFFFFF',
166
+ },
167
+ 'input::placeholder': {
168
+ color: '#999999',
169
+ },
170
+ 'input.is-error': {
171
+ color: '#ff1600',
172
+ },
173
+ }}
174
+ buttonProps={{
175
+ className: 'font-nav h-10',
176
+ css: {
177
+ backgroundColor: '#fff',
178
+ color: '#000',
179
+ padding: 0,
180
+ '&:hover': {
181
+ backgroundColor: '#ffffffd9',
182
+ },
183
+ },
184
+ }}
185
+ />
186
+ {transactionStatus === 'error' && (
187
+ <ApplyTypography>
188
+ <p className='mx-auto text-destructive'>There was an error processing your payment.</p>
189
+ </ApplyTypography>
190
+ )}
191
+ </>
192
+ )}
193
+ </div>
194
+ </PaymentForm>
195
+ )
196
+ }
197
+
198
+ export default PayWithCard