@hanzo/commerce 2.0.0 → 2.1.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/checkout/index.tsx +17 -65
- package/components/checkout/payment/contact-info.tsx +59 -0
- package/components/checkout/payment/index.tsx +117 -0
- package/components/checkout/payment/pay-with-bank-transfer.tsx +106 -0
- package/components/checkout/payment/pay-with-card.tsx +193 -0
- package/components/checkout/{pay-with-crypto.tsx → payment/pay-with-crypto.tsx} +51 -28
- package/components/checkout/payment/payment-methods/amex.tsx +32 -0
- package/components/checkout/payment/payment-methods/diners-club.tsx +13 -0
- package/components/checkout/payment/payment-methods/discover.tsx +25 -0
- package/components/checkout/payment/payment-methods/index.tsx +24 -0
- package/components/checkout/payment/payment-methods/jcb.tsx +26 -0
- package/components/checkout/payment/payment-methods/mastercard.tsx +27 -0
- package/components/checkout/payment/payment-methods/visa.tsx +25 -0
- package/components/checkout/shipping-info.tsx +4 -10
- package/package.json +5 -3
- package/service/impls/standalone/orders/index.ts +42 -20
- package/service/impls/standalone/standalone-service.ts +11 -6
- package/types/checkout.ts +6 -0
- package/types/commerce-service.ts +3 -3
- package/types/index.ts +1 -3
- package/util/index.ts +2 -1
- package/util/square-payment.ts +43 -0
- package/components/checkout/contact-info.tsx +0 -83
- package/components/checkout/pay-by-bank-transfer.tsx +0 -76
|
@@ -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,85 +11,41 @@ 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
14
|
import { Cart } from '..'
|
|
22
|
-
import
|
|
23
|
-
|
|
24
|
-
const contactFormSchema = z.object({
|
|
25
|
-
name: z.string().min(2, 'Name must be at least 2 characters.'),
|
|
26
|
-
email: z.string().email(),
|
|
27
|
-
})
|
|
15
|
+
import Payment from './payment'
|
|
28
16
|
|
|
29
17
|
const Checkout: React.FC<{toggleCheckout: () => void}> = observer(({toggleCheckout}) => {
|
|
30
18
|
const auth = useAuth()
|
|
31
|
-
const cmmc = useCommerce()
|
|
32
19
|
|
|
33
20
|
const [currentStep, setCurrentStep] = useState(1)
|
|
34
|
-
const [paymentMethod, setPaymentMethod] = useState<'crypto' | 'bank' | undefined>()
|
|
35
21
|
const [orderId, setOrderId] = useState<string>()
|
|
36
22
|
|
|
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
23
|
const step1 = auth.loggedIn ? (
|
|
69
|
-
<
|
|
24
|
+
<Payment
|
|
25
|
+
orderId={orderId}
|
|
26
|
+
setOrderId={setOrderId}
|
|
27
|
+
setCurrentStep={setCurrentStep}
|
|
28
|
+
/>
|
|
70
29
|
) : (
|
|
71
30
|
<LoginComponent hideHeader className='max-w-[20rem] mx-auto'/>
|
|
72
31
|
)
|
|
73
32
|
|
|
74
|
-
const step2 =
|
|
75
|
-
<
|
|
76
|
-
) : (
|
|
77
|
-
<PayByBankTransfer setCurrentStep={setCurrentStep}/>
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
const step3 = (
|
|
81
|
-
<ShippingInfo orderId={orderId} paymentMethod={paymentMethod} setCurrentStep={setCurrentStep}/>
|
|
33
|
+
const step2 = (
|
|
34
|
+
<ShippingInfo orderId={orderId} setCurrentStep={setCurrentStep}/>
|
|
82
35
|
)
|
|
83
36
|
|
|
84
|
-
const
|
|
37
|
+
const step3 = <ThankYou/>
|
|
85
38
|
|
|
86
|
-
const steps = [step1, step2, step3
|
|
39
|
+
const steps = [step1, step2, step3]
|
|
87
40
|
|
|
88
41
|
return (
|
|
89
|
-
<div className="fixed top-0 left-0 !max-w-none w-full h-full min-h-screen bg-background z-
|
|
42
|
+
<div className="fixed top-0 left-0 !max-w-none w-full h-full min-h-screen bg-background z-50">
|
|
90
43
|
<Toaster/>
|
|
91
44
|
<Main className='flex flex-col gap-1 h-full'>
|
|
92
|
-
<div className='flex justify-between'>
|
|
45
|
+
<div className='flex justify-between items-center'>
|
|
93
46
|
<Button
|
|
94
47
|
variant='ghost'
|
|
95
48
|
size='icon'
|
|
96
|
-
className='sm:ml-2 sm:mt-2'
|
|
97
49
|
onClick={() => {
|
|
98
50
|
setCurrentStep(0)
|
|
99
51
|
toggleCheckout()
|
|
@@ -109,18 +61,18 @@ const Checkout: React.FC<{toggleCheckout: () => void}> = observer(({toggleChecko
|
|
|
109
61
|
<Cart hideCheckout className='fixed justify-center border-none mt-10 w-1/3 max-w-[40rem]'/>
|
|
110
62
|
</div>
|
|
111
63
|
|
|
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'>
|
|
64
|
+
<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
65
|
<div className='flex gap-2 mx-auto items-center text-xxs sm:text-base'>
|
|
114
66
|
<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'>
|
|
67
|
+
<div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Payment</div>
|
|
116
68
|
</div>
|
|
117
69
|
<Separator className='w-[4rem] sm:w-[6rem]'/>
|
|
118
70
|
<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'>
|
|
71
|
+
<div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Delivery</div>
|
|
120
72
|
</div>
|
|
121
73
|
<Separator className='w-[4rem] sm:w-[6rem]'/>
|
|
122
74
|
<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'>
|
|
75
|
+
<div className='relative text-foreground top-4 h-0 whitespace-nowrap'>Done!</div>
|
|
124
76
|
</div>
|
|
125
77
|
</div>
|
|
126
78
|
{steps[currentStep - 1]}
|
|
@@ -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,117 @@
|
|
|
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, type TransactionStatus } from '../../..'
|
|
16
|
+
|
|
17
|
+
const contactFormSchema = z.object({
|
|
18
|
+
name: z.string().min(1, 'Enter your full name.'),
|
|
19
|
+
email: z.string().email(),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const Payment: React.FC<{
|
|
23
|
+
setCurrentStep: (currentStep: number) => void
|
|
24
|
+
orderId?: string
|
|
25
|
+
setOrderId: (orderId?: string) => void
|
|
26
|
+
}> = observer(({
|
|
27
|
+
setCurrentStep,
|
|
28
|
+
orderId,
|
|
29
|
+
setOrderId
|
|
30
|
+
}) => {
|
|
31
|
+
const cmmc = useCommerce()
|
|
32
|
+
const auth = useAuth()
|
|
33
|
+
const [transactionStatus, setTransactionStatus] = useState<TransactionStatus>('unpaid')
|
|
34
|
+
|
|
35
|
+
const contactForm = useForm<z.infer<typeof contactFormSchema>>({
|
|
36
|
+
resolver: zodResolver(contactFormSchema),
|
|
37
|
+
defaultValues: {
|
|
38
|
+
name: auth.user?.displayName ?? '',
|
|
39
|
+
email: auth.user?.email ?? '',
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (auth.loggedIn) {
|
|
45
|
+
contactForm.setValue('name', auth.user?.displayName ?? '')
|
|
46
|
+
contactForm.setValue('email', auth.user?.email ?? '')
|
|
47
|
+
}
|
|
48
|
+
}, [auth.loggedIn])
|
|
49
|
+
|
|
50
|
+
const storePaymentInfo = async (paymentInfo: any) => {
|
|
51
|
+
if (auth.user) {
|
|
52
|
+
const {name, email} = contactForm.getValues()
|
|
53
|
+
let id
|
|
54
|
+
if (!orderId) {
|
|
55
|
+
id = await cmmc.createOrder(email ? email : auth.user.email, name)
|
|
56
|
+
setOrderId(id)
|
|
57
|
+
}
|
|
58
|
+
if (id) {
|
|
59
|
+
await cmmc.updateOrderPaymentInfo(id, paymentInfo)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Tabs defaultValue='card' className='w-full mx-auto max-w-[50rem]'>
|
|
66
|
+
<TabsList className='grid w-full grid-cols-3 mx-auto bg-level-2 h-auto'>
|
|
67
|
+
<TabsTrigger
|
|
68
|
+
value='card'
|
|
69
|
+
className='font-nav whitespace-normal h-full text-sm sm:text-base'
|
|
70
|
+
disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
|
|
71
|
+
>
|
|
72
|
+
PAY WITH CARD
|
|
73
|
+
</TabsTrigger>
|
|
74
|
+
<TabsTrigger
|
|
75
|
+
value='crypto'
|
|
76
|
+
className='font-nav whitespace-normal h-full text-sm sm:text-base'
|
|
77
|
+
disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
|
|
78
|
+
>
|
|
79
|
+
PAY WITH CRYPTO
|
|
80
|
+
</TabsTrigger>
|
|
81
|
+
<TabsTrigger
|
|
82
|
+
value='bank'
|
|
83
|
+
className='font-nav whitespace-normal h-full text-sm sm:text-base'
|
|
84
|
+
disabled={transactionStatus === 'paid' || transactionStatus === 'confirmed'}
|
|
85
|
+
>
|
|
86
|
+
BANK TRANSFER
|
|
87
|
+
</TabsTrigger>
|
|
88
|
+
</TabsList>
|
|
89
|
+
<TabsContent value='card'>
|
|
90
|
+
<PayWithCard
|
|
91
|
+
setCurrentStep={setCurrentStep}
|
|
92
|
+
transactionStatus={transactionStatus}
|
|
93
|
+
setTransactionStatus={setTransactionStatus}
|
|
94
|
+
storePaymentInfo={storePaymentInfo}
|
|
95
|
+
contactForm={contactForm}
|
|
96
|
+
/>
|
|
97
|
+
</TabsContent>
|
|
98
|
+
<TabsContent value='crypto'>
|
|
99
|
+
<PayWithCrypto
|
|
100
|
+
setCurrentStep={setCurrentStep}
|
|
101
|
+
transactionStatus={transactionStatus}
|
|
102
|
+
setTransactionStatus={setTransactionStatus}
|
|
103
|
+
storePaymentInfo={storePaymentInfo}
|
|
104
|
+
/>
|
|
105
|
+
</TabsContent>
|
|
106
|
+
<TabsContent value='bank'>
|
|
107
|
+
<PayWithBankTransfer
|
|
108
|
+
setCurrentStep={setCurrentStep}
|
|
109
|
+
storePaymentInfo={storePaymentInfo}
|
|
110
|
+
contactForm={contactForm}
|
|
111
|
+
/>
|
|
112
|
+
</TabsContent>
|
|
113
|
+
</Tabs>
|
|
114
|
+
)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
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,193 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
import { ApplePay, GooglePay, CreditCard, PaymentForm } from 'react-square-web-payments-sdk'
|
|
5
|
+
import type { UseFormReturn } from 'react-hook-form'
|
|
6
|
+
import { ApplyTypography, Button } from '@hanzo/ui/primitives'
|
|
7
|
+
import { useCommerce, type TransactionStatus } from '../../..'
|
|
8
|
+
import PaymentMethods from './payment-methods'
|
|
9
|
+
import { processSquareCardPayment } from '../../../util'
|
|
10
|
+
import ContactInfo from './contact-info'
|
|
11
|
+
|
|
12
|
+
const PayWithCard: React.FC<{
|
|
13
|
+
setCurrentStep: (currentStep: number) => void
|
|
14
|
+
transactionStatus: TransactionStatus
|
|
15
|
+
setTransactionStatus: (status: TransactionStatus) => void
|
|
16
|
+
storePaymentInfo: (paymentInfo: any) => Promise<void>
|
|
17
|
+
contactForm: UseFormReturn<{
|
|
18
|
+
name: string
|
|
19
|
+
email: string
|
|
20
|
+
}, any, {
|
|
21
|
+
name: string
|
|
22
|
+
email: string
|
|
23
|
+
}>
|
|
24
|
+
}> = ({
|
|
25
|
+
setCurrentStep,
|
|
26
|
+
transactionStatus,
|
|
27
|
+
setTransactionStatus,
|
|
28
|
+
storePaymentInfo,
|
|
29
|
+
contactForm,
|
|
30
|
+
}) => {
|
|
31
|
+
const cmmc = useCommerce()
|
|
32
|
+
|
|
33
|
+
const cardTokenizeResponseReceived = async (
|
|
34
|
+
token: any,
|
|
35
|
+
verifiedBuyer: any
|
|
36
|
+
) => {
|
|
37
|
+
contactForm.handleSubmit(async () => {
|
|
38
|
+
setTransactionStatus('paid')
|
|
39
|
+
const res = await processSquareCardPayment(token.token, cmmc.cartTotal, verifiedBuyer.token)
|
|
40
|
+
if (res) {
|
|
41
|
+
console.log(token)
|
|
42
|
+
await storePaymentInfo({paymentMethod: token.details.method ?? null, processed: res})
|
|
43
|
+
setTransactionStatus('confirmed')
|
|
44
|
+
} else {
|
|
45
|
+
setTransactionStatus('error')
|
|
46
|
+
}
|
|
47
|
+
})()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const createVerificationDetails = () => {
|
|
51
|
+
const {name, email} = contactForm.getValues()
|
|
52
|
+
return {
|
|
53
|
+
amount: cmmc.cartTotal.toFixed(2),
|
|
54
|
+
billingContact: {
|
|
55
|
+
givenName: name,
|
|
56
|
+
email,
|
|
57
|
+
},
|
|
58
|
+
currencyCode: 'USD',
|
|
59
|
+
intent: 'CHARGE',
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const createPaymentRequest= () => ({
|
|
64
|
+
countryCode: "US",
|
|
65
|
+
currencyCode: "USD",
|
|
66
|
+
lineItems: cmmc.cartItems.map(item => ({
|
|
67
|
+
amount: item.price.toFixed(2),
|
|
68
|
+
label: item.title,
|
|
69
|
+
id: item.sku,
|
|
70
|
+
})),
|
|
71
|
+
requestBillingContact: false,
|
|
72
|
+
requestShippingContact: false,
|
|
73
|
+
total: {
|
|
74
|
+
amount: cmmc.cartTotal.toFixed(2),
|
|
75
|
+
label: "Total",
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<PaymentForm
|
|
81
|
+
/**
|
|
82
|
+
* Identifies the calling form with a verified application ID generated from
|
|
83
|
+
* the Square Application Dashboard.
|
|
84
|
+
*/
|
|
85
|
+
applicationId={process.env.NEXT_PUBLIC_SQUARE_APPLICATION_ID}
|
|
86
|
+
/**
|
|
87
|
+
* Invoked when payment form receives the result of a tokenize generation
|
|
88
|
+
* request. The result will be a valid credit card or wallet token, or an error.
|
|
89
|
+
*/
|
|
90
|
+
cardTokenizeResponseReceived={cardTokenizeResponseReceived}
|
|
91
|
+
/**
|
|
92
|
+
* This function enable the Strong Customer Authentication (SCA) flow
|
|
93
|
+
*
|
|
94
|
+
* We strongly recommend use this function to verify the buyer and reduce
|
|
95
|
+
* the chance of fraudulent transactions.
|
|
96
|
+
*/
|
|
97
|
+
createVerificationDetails={createVerificationDetails}
|
|
98
|
+
/**
|
|
99
|
+
* This function is required for digital wallets (Apple Pay, Google Pay)
|
|
100
|
+
*/
|
|
101
|
+
createPaymentRequest={createPaymentRequest}
|
|
102
|
+
/**
|
|
103
|
+
* Identifies the location of the merchant that is taking the payment.
|
|
104
|
+
* Obtained from the Square Application Dashboard - Locations tab.
|
|
105
|
+
*/
|
|
106
|
+
locationId={process.env.NEXT_PUBLIC_SQUARE_LOCATION_ID}
|
|
107
|
+
>
|
|
108
|
+
<div className='flex flex-col gap-2 mt-6'>
|
|
109
|
+
{transactionStatus === 'confirmed' ? (
|
|
110
|
+
<ApplyTypography className='flex flex-col gap-4'>
|
|
111
|
+
<h5 className='mx-auto font-nav'>Payment confirmed!</h5>
|
|
112
|
+
<p className='mx-auto'>Thank you for your purchase.</p>
|
|
113
|
+
<Button onClick={() => setCurrentStep(2)}>Continue</Button>
|
|
114
|
+
</ApplyTypography>
|
|
115
|
+
) : transactionStatus === 'paid' ? (
|
|
116
|
+
<ApplyTypography className='flex flex-col gap-4'>
|
|
117
|
+
<h5 className='mx-auto font-nav'>Processing your payment...</h5>
|
|
118
|
+
</ApplyTypography>
|
|
119
|
+
) : (
|
|
120
|
+
<>
|
|
121
|
+
<GooglePay/>
|
|
122
|
+
<ApplePay/>
|
|
123
|
+
|
|
124
|
+
<p className='flex gap-2 whitespace-nowrap items-center my-6 sm:my-10 text-sm text-foreground/60'>
|
|
125
|
+
<hr className='bg-foreground/60 w-full border'/> or pay with card <hr className='bg-foreground/60 w-full border'/>
|
|
126
|
+
</p>
|
|
127
|
+
|
|
128
|
+
<ContactInfo form={contactForm}/>
|
|
129
|
+
|
|
130
|
+
<PaymentMethods/>
|
|
131
|
+
|
|
132
|
+
{/* Imitates hanzo/ui Button and Input styles, I was unable to render the
|
|
133
|
+
hanzo/ui button outright and keeping the submit form functionality*/}
|
|
134
|
+
<CreditCard
|
|
135
|
+
style={{
|
|
136
|
+
'.input-container': {
|
|
137
|
+
borderColor: '#2D2D2D',
|
|
138
|
+
borderRadius: '6px',
|
|
139
|
+
},
|
|
140
|
+
'.input-container.is-focus': {
|
|
141
|
+
borderColor: '#ffffff',
|
|
142
|
+
},
|
|
143
|
+
'.input-container.is-error': {
|
|
144
|
+
borderColor: '#ff1600',
|
|
145
|
+
},
|
|
146
|
+
'.message-text': {
|
|
147
|
+
color: '#999999',
|
|
148
|
+
},
|
|
149
|
+
'.message-icon': {
|
|
150
|
+
color: '#999999',
|
|
151
|
+
},
|
|
152
|
+
'.message-text.is-error': {
|
|
153
|
+
color: '#ff1600',
|
|
154
|
+
},
|
|
155
|
+
'.message-icon.is-error': {
|
|
156
|
+
color: '#ff1600',
|
|
157
|
+
},
|
|
158
|
+
input: {
|
|
159
|
+
backgroundColor: '#000000',
|
|
160
|
+
color: '#FFFFFF',
|
|
161
|
+
},
|
|
162
|
+
'input::placeholder': {
|
|
163
|
+
color: '#999999',
|
|
164
|
+
},
|
|
165
|
+
'input.is-error': {
|
|
166
|
+
color: '#ff1600',
|
|
167
|
+
},
|
|
168
|
+
}}
|
|
169
|
+
buttonProps={{
|
|
170
|
+
className: 'font-nav h-10',
|
|
171
|
+
css: {
|
|
172
|
+
backgroundColor: '#fff',
|
|
173
|
+
color: '#000',
|
|
174
|
+
padding: 0,
|
|
175
|
+
'&:hover': {
|
|
176
|
+
backgroundColor: '#ffffffd9',
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
}}
|
|
180
|
+
/>
|
|
181
|
+
{transactionStatus === 'error' && (
|
|
182
|
+
<ApplyTypography>
|
|
183
|
+
<p className='mx-auto text-destructive'>There was an error processing your payment.</p>
|
|
184
|
+
</ApplyTypography>
|
|
185
|
+
)}
|
|
186
|
+
</>
|
|
187
|
+
)}
|
|
188
|
+
</div>
|
|
189
|
+
</PaymentForm>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export default PayWithCard
|