@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.
- package/components/add-to-cart-widget.tsx +3 -3
- package/components/buy-item/buy-item-button.tsx +28 -0
- package/components/buy-item/buy-item-card.tsx +82 -0
- package/components/buy-item/buy-item-popup.tsx +38 -0
- package/components/{select-category-and-item-widget.tsx → buy-item/select-category-and-item-widget.tsx} +4 -4
- package/components/buy-item/select-category-item-card.tsx +111 -0
- package/components/{cart-line-item.tsx → cart-panel/cart-line-item.tsx} +4 -3
- package/components/{cart.tsx → cart-panel/index.tsx} +24 -17
- package/components/category-item-ios-wheel-selector.tsx +60 -0
- package/components/category-item-radio-selector.tsx +55 -0
- package/components/{checkout → checkout-panel}/confirm-order.tsx +3 -2
- package/components/{checkout → checkout-panel}/index.tsx +26 -69
- package/components/checkout-panel/payment/contact-info.tsx +59 -0
- package/components/checkout-panel/payment/index.tsx +118 -0
- package/components/checkout-panel/payment/pay-with-bank-transfer.tsx +106 -0
- package/components/checkout-panel/payment/pay-with-card.tsx +198 -0
- package/components/{checkout → checkout-panel/payment}/pay-with-crypto.tsx +52 -30
- package/components/checkout-panel/payment/payment-methods/amex.tsx +32 -0
- package/components/checkout-panel/payment/payment-methods/diners-club.tsx +13 -0
- package/components/checkout-panel/payment/payment-methods/discover.tsx +25 -0
- package/components/checkout-panel/payment/payment-methods/index.tsx +24 -0
- package/components/checkout-panel/payment/payment-methods/jcb.tsx +26 -0
- package/components/checkout-panel/payment/payment-methods/mastercard.tsx +27 -0
- package/components/checkout-panel/payment/payment-methods/visa.tsx +25 -0
- package/components/{checkout → checkout-panel}/shipping-info.tsx +4 -10
- package/components/{facet-toggles-widget → facet-values-widget}/facet-image.tsx +7 -3
- package/components/{facet-toggles-widget → facet-values-widget}/index.tsx +7 -5
- package/components/index.ts +11 -9
- package/components/{select-item-in-category-view.tsx → select-category-item-panel.tsx} +72 -76
- package/index.ts +2 -2
- package/package.json +11 -4
- package/service/context.tsx +4 -4
- package/service/impls/standalone/index.ts +4 -4
- package/service/impls/standalone/orders/index.ts +42 -20
- package/service/impls/standalone/standalone-service.ts +128 -44
- package/types/category.ts +3 -2
- package/types/checkout.ts +6 -0
- package/types/commerce-service.ts +20 -7
- package/types/facet.ts +1 -3
- package/types/index.ts +4 -4
- package/types/item-selector.ts +14 -0
- package/util/index.ts +31 -1
- package/util/square-payment.ts +43 -0
- package/util/use-sync-sku-param-w-current-item.ts +4 -1
- package/blocks/components/commerce-cat-and-item-block.tsx +0 -13
- package/blocks/def/commerce-cat-and-item-block.ts +0 -9
- package/components/checkout/contact-info.tsx +0 -83
- package/components/checkout/pay-by-bank-transfer.tsx +0 -76
- package/components/facets-widget.tsx +0 -55
- package/components/product-selection-mobile-picker.tsx +0 -78
- package/components/product-selection-radio-group.tsx +0 -38
- /package/components/{checkout → checkout-panel}/countries.ts +0 -0
- /package/components/{checkout → checkout-panel}/icons/btc.tsx +0 -0
- /package/components/{checkout → checkout-panel}/icons/eth.tsx +0 -0
- /package/components/{checkout → checkout-panel}/icons/usdt.tsx +0 -0
- /package/components/{checkout → checkout-panel}/thank-you.tsx +0 -0
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import {
|
|
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
|
|
19
|
-
import
|
|
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
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
<
|
|
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 =
|
|
75
|
-
<
|
|
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
|
|
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-
|
|
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
|
-
|
|
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
|
-
<
|
|
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'>
|
|
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'>
|
|
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'>
|
|
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
|
|
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
|