/
BraintreeForm.jsx
144 lines (130 loc) · 4.72 KB
/
BraintreeForm.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// @flow
import React, { useState, useEffect, useRef } from 'react'
import {
BraintreeUserFields,
BraintreeCardFields,
BraintreeBillingAddressFields,
BraintreeSubmitFields,
createHostedFieldsInstance,
create3DSecureInstance,
validationConstraints,
hostedFieldOptions,
veryfyCard
} from 'PaymentGateways'
import hostedFields from 'braintree-web/hosted-fields'
import threeDSecure from 'braintree-web/three-d-secure'
import { CSRFToken } from 'utilities/CSRFToken'
import validate from 'validate.js'
import './BraintreeCustomerForm.scss'
import type { Node } from 'react'
import type { BraintreeFormProps, BillingAddressData } from 'PaymentGateways'
const BraintreeForm = ({
braintreeClient,
billingAddress,
threeDSecureEnabled,
formActionPath,
countriesList,
selectedCountryCode
}: BraintreeFormProps): Node => {
const formRef = useRef<HTMLFormElement | null>(null)
// eslint-disable-next-line flowtype/no-weak-types
const [hostedFieldsInstance, setHostedFieldsInstance] = useState<any>(null)
const [braintreeNonceValue, setBraintreeNonceValue] = useState('')
const [billingAddressData, setBillingAddressData] = useState<BillingAddressData>(billingAddress)
const [isCardValid, setIsCardValid] = useState(false)
const [formErrors, setFormErrors] = useState(validate(formRef, validationConstraints))
const [cardError, setCardError] = useState(null)
const [isLoading, setIsLoading] = useState(false)
const isFormValid = isCardValid && !formErrors && !isLoading
useEffect(() => {
const getHostedFieldsInstance = async () => {
const HFInstance = await createHostedFieldsInstance(hostedFields, braintreeClient, hostedFieldOptions, setIsCardValid, setCardError)
setHostedFieldsInstance(HFInstance)
}
getHostedFieldsInstance()
}, [])
useEffect(() => {
if (braintreeNonceValue && formRef.current) {
formRef.current.submit()
}
}, [braintreeNonceValue])
const get3DSecureNonce = async (payload) => {
const threeDSecureInstance = await create3DSecureInstance(threeDSecure, braintreeClient)
const response = await veryfyCard(threeDSecureInstance, payload, billingAddressData)
const error = response.name === 'BraintreeError'
? response.code === 'THREEDS_LOOKUP_VALIDATION_ERROR' ? response.details.originalError.details.originalError.error.message : response.message
: null
const nonce = response.nonce || null
return {
error,
nonce
}
}
const clearHostedFields = () => {
hostedFieldsInstance.clear('number')
hostedFieldsInstance.clear('cvv')
hostedFieldsInstance.clear('expirationDate')
}
const validateForm = (event: SyntheticEvent<HTMLFormElement>) => {
const validationErrors = validate(event.currentTarget, validationConstraints)
setFormErrors(validationErrors)
}
const handleCardError = (error: string) => {
setCardError(`Credit card errors found: ${error}. Please correct your CC data.`)
clearHostedFields()
setIsLoading(false)
}
const onSubmit = async (event: SyntheticEvent<HTMLInputElement>) => {
event.preventDefault()
if (hostedFieldsInstance) {
setIsLoading(true)
const payload = await hostedFieldsInstance.tokenize()
.then(payload => payload)
.catch(error => console.error(error))
const response3Dsecure = threeDSecureEnabled ? await get3DSecureNonce(payload) : null
if (response3Dsecure && response3Dsecure.error) {
handleCardError(response3Dsecure.error)
return
}
const nonce = response3Dsecure ? response3Dsecure.nonce : payload.nonce
setBraintreeNonceValue(nonce)
setIsLoading(false)
}
}
return (
<form
id="customer_form"
className="form-horizontal customer"
action={formActionPath}
ref={formRef}
onChange={validateForm}
>
<input name="utf8" type="hidden" value="✓"/>
<CSRFToken/>
<fieldset>
<p className="required-fields">All fields marked with an asterisk ( * ) are mandatory</p>
<BraintreeUserFields/>
</fieldset>
<fieldset>
<BraintreeCardFields />
{cardError && <p className="alert alert-danger">{cardError}</p>}
</fieldset>
<fieldset>
<BraintreeBillingAddressFields
billingAddressData={billingAddressData}
setBillingAddressData={setBillingAddressData}
selectedCountryCode={selectedCountryCode}
countriesList={JSON.parse(countriesList)}
/>
</fieldset>
<fieldset>
<BraintreeSubmitFields
onSubmitForm={onSubmit}
isFormValid={isFormValid}
/>
</fieldset>
<input type="hidden" name="braintree[nonce]" id="braintree_nonce" value={braintreeNonceValue} />
</form>
)
}
export { BraintreeForm }