@riosst100/pwa-marketplace 2.5.1 → 2.5.2
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/package.json +1 -1
- package/src/components/LiveChat/liveChat.js +1 -1
- package/src/components/ProductItem/index.js +1 -1
- package/src/components/RFQ/modalRfq.js +1 -1
- package/src/components/RFQPage/quoteDetail.js +1 -1
- package/src/components/RMAPage/RMACreate.js +1 -1
- package/src/components/RMAPage/RMADetail.js +1 -1
- package/src/components/Xendit/summary.js +71 -0
- package/src/components/Xendit/summary.module.css +23 -0
- package/src/components/Xendit/xendit.js +348 -0
- package/src/components/Xendit/xendit.module.css +58 -0
- package/src/intercept.js +14 -0
- package/src/overwrites/venia-ui/lib/components/CheckoutPage/OrderSummary/orderSummary.js +1 -1
- package/src/overwrites/venia-ui/lib/components/CheckoutPage/PaymentInformation/paymentMethods.js +9 -0
- package/src/talons/Xendit/useXendit.js +574 -0
- package/src/talons/Xendit/xendit.js +165 -0
package/package.json
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FormattedMessage } from 'react-intl';
|
|
3
|
+
import { shape, string, func } from 'prop-types';
|
|
4
|
+
|
|
5
|
+
import { useSummary } from '@magento/peregrine/lib/talons/CheckoutPage/PaymentInformation/useSummary';
|
|
6
|
+
import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
7
|
+
|
|
8
|
+
import defaultClasses from './summary.module.css';
|
|
9
|
+
import LoadingIndicator from '@magento/venia-ui/lib/components/LoadingIndicator';
|
|
10
|
+
import summaryPayments from '@magento/venia-ui/lib/components/CheckoutPage/PaymentInformation/summaryPaymentCollection';
|
|
11
|
+
|
|
12
|
+
const Summary = props => {
|
|
13
|
+
const { classes: propClasses, onEdit } = props;
|
|
14
|
+
const classes = useStyle(defaultClasses, propClasses);
|
|
15
|
+
|
|
16
|
+
const talonProps = useSummary();
|
|
17
|
+
|
|
18
|
+
const { isLoading, selectedPaymentMethod } = talonProps;
|
|
19
|
+
|
|
20
|
+
if (isLoading && !selectedPaymentMethod) {
|
|
21
|
+
return (
|
|
22
|
+
<LoadingIndicator classes={{ root: classes.loading }}>
|
|
23
|
+
<FormattedMessage
|
|
24
|
+
id={'checkoutPage.loadingPaymentInformation'}
|
|
25
|
+
defaultMessage={'Fetching Payment Information'}
|
|
26
|
+
/>
|
|
27
|
+
</LoadingIndicator>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const hasCustomSummaryComp = Object.keys(summaryPayments).includes(
|
|
32
|
+
selectedPaymentMethod.code
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (hasCustomSummaryComp) {
|
|
36
|
+
const SummaryPaymentMethodComponent =
|
|
37
|
+
summaryPayments[selectedPaymentMethod.code];
|
|
38
|
+
return <SummaryPaymentMethodComponent onEdit={onEdit} />;
|
|
39
|
+
} else {
|
|
40
|
+
return (
|
|
41
|
+
<div className={classes.root}>
|
|
42
|
+
<div className={classes.heading_container}>
|
|
43
|
+
<h5 className={classes.heading}>
|
|
44
|
+
<FormattedMessage
|
|
45
|
+
id={'checkoutPage.paymentInformation'}
|
|
46
|
+
defaultMessage={'Payment Information'}
|
|
47
|
+
/>
|
|
48
|
+
</h5>
|
|
49
|
+
</div>
|
|
50
|
+
<div className={classes.card_details_container}>
|
|
51
|
+
<span className={classes.payment_details}>
|
|
52
|
+
{selectedPaymentMethod.title}
|
|
53
|
+
</span>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default Summary;
|
|
61
|
+
|
|
62
|
+
Summary.propTypes = {
|
|
63
|
+
classes: shape({
|
|
64
|
+
root: string,
|
|
65
|
+
heading_container: string,
|
|
66
|
+
heading: string,
|
|
67
|
+
card_details_container: string,
|
|
68
|
+
payment_details: string
|
|
69
|
+
}),
|
|
70
|
+
onEdit: func.isRequired
|
|
71
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
composes: gap-xs from global;
|
|
3
|
+
composes: grid from global;
|
|
4
|
+
composes: p-md from global;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.heading_container {
|
|
8
|
+
composes: grid from global;
|
|
9
|
+
composes: grid-cols-1 from global;
|
|
10
|
+
composes: grid-flow-col from global;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.heading {
|
|
14
|
+
composes: font-semibold from global;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.card_details_container {
|
|
18
|
+
composes: gap-2xs from global;
|
|
19
|
+
composes: grid from global;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.payment_details {
|
|
23
|
+
}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import React, { useMemo, useCallback } from 'react';
|
|
2
|
+
import { useIntl } from 'react-intl';
|
|
3
|
+
import { bool, func, shape, string } from 'prop-types';
|
|
4
|
+
import { useXendit } from '@riosst100/src/talons/Xendit/useXendit';
|
|
5
|
+
|
|
6
|
+
import { isRequired } from '@magento/venia-ui/lib/util/formValidators';
|
|
7
|
+
import Country from '@magento/venia-ui/lib/components/Country';
|
|
8
|
+
import Region from '@magento/venia-ui/lib/components/Region';
|
|
9
|
+
import Postcode from '@magento/venia-ui/lib/components/Postcode';
|
|
10
|
+
import Checkbox from '@magento/venia-ui/lib/components/Checkbox';
|
|
11
|
+
import Field from '@magento/venia-ui/lib/components/Field';
|
|
12
|
+
import TextInput from '@magento/venia-ui/lib/components/TextInput';
|
|
13
|
+
import BrainTreeDropin from './brainTreeDropIn';
|
|
14
|
+
import LoadingIndicator from '@magento/venia-ui/lib/components/LoadingIndicator';
|
|
15
|
+
import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
16
|
+
|
|
17
|
+
import defaultClasses from './xendit.module.css';
|
|
18
|
+
import FormError from '@magento/venia-ui/lib/components/FormError';
|
|
19
|
+
import GoogleReCaptcha from '@magento/venia-ui/lib/components/GoogleReCaptcha';
|
|
20
|
+
|
|
21
|
+
const STEP_DESCRIPTIONS = [
|
|
22
|
+
{ defaultMessage: 'Loading Payment', id: 'checkoutPage.step0' },
|
|
23
|
+
{
|
|
24
|
+
defaultMessage: 'Checking Credit Card Information',
|
|
25
|
+
id: 'checkoutPage.step1'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
defaultMessage: 'Checking Credit Card Information',
|
|
29
|
+
id: 'checkoutPage.step2'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
defaultMessage: 'Checking Credit Card Information',
|
|
33
|
+
id: 'checkoutPage.step3'
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
defaultMessage: 'Saved Credit Card Information Successfully',
|
|
37
|
+
id: 'checkoutPage.step4'
|
|
38
|
+
}
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The initial view for the Braintree payment method.
|
|
43
|
+
*/
|
|
44
|
+
const Xendit = props => {
|
|
45
|
+
const {
|
|
46
|
+
classes: propClasses,
|
|
47
|
+
onPaymentSuccess: onSuccess,
|
|
48
|
+
onPaymentReady: onReady,
|
|
49
|
+
onPaymentError: onError,
|
|
50
|
+
resetShouldSubmit,
|
|
51
|
+
shouldSubmit
|
|
52
|
+
} = props;
|
|
53
|
+
const { formatMessage } = useIntl();
|
|
54
|
+
|
|
55
|
+
const classes = useStyle(defaultClasses, propClasses);
|
|
56
|
+
|
|
57
|
+
const talonProps = useXendit({
|
|
58
|
+
onSuccess,
|
|
59
|
+
onReady,
|
|
60
|
+
onError,
|
|
61
|
+
shouldSubmit,
|
|
62
|
+
resetShouldSubmit
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const {
|
|
66
|
+
errors,
|
|
67
|
+
shouldRequestPaymentNonce,
|
|
68
|
+
onPaymentError,
|
|
69
|
+
onPaymentSuccess,
|
|
70
|
+
onPaymentReady,
|
|
71
|
+
isBillingAddressSame,
|
|
72
|
+
isLoading,
|
|
73
|
+
/**
|
|
74
|
+
* `stepNumber` depicts the state of the process flow in credit card
|
|
75
|
+
* payment flow.
|
|
76
|
+
*
|
|
77
|
+
* `0` No call made yet
|
|
78
|
+
* `1` Billing address mutation intiated
|
|
79
|
+
* `2` Braintree nonce requsted
|
|
80
|
+
* `3` Payment information mutation intiated
|
|
81
|
+
* `4` All mutations done
|
|
82
|
+
*/
|
|
83
|
+
stepNumber,
|
|
84
|
+
initialValues,
|
|
85
|
+
shippingAddressCountry,
|
|
86
|
+
shouldTeardownDropin,
|
|
87
|
+
resetShouldTeardownDropin,
|
|
88
|
+
recaptchaWidgetProps
|
|
89
|
+
} = talonProps;
|
|
90
|
+
|
|
91
|
+
const creditCardComponentClassName = isLoading
|
|
92
|
+
? classes.credit_card_root_hidden
|
|
93
|
+
: classes.credit_card_root;
|
|
94
|
+
|
|
95
|
+
const billingAddressFieldsClassName = isBillingAddressSame
|
|
96
|
+
? classes.billing_address_fields_root_hidden
|
|
97
|
+
: classes.billing_address_fields_root;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Instead of defining classes={root: classes.FIELD_NAME}
|
|
101
|
+
* we are using useMemo to only do it once (hopefully).
|
|
102
|
+
*/
|
|
103
|
+
const fieldClasses = useMemo(() => {
|
|
104
|
+
return [
|
|
105
|
+
'first_name',
|
|
106
|
+
'last_name',
|
|
107
|
+
'country',
|
|
108
|
+
'street1',
|
|
109
|
+
'street2',
|
|
110
|
+
'city',
|
|
111
|
+
'region',
|
|
112
|
+
'postal_code',
|
|
113
|
+
'phone_number'
|
|
114
|
+
].reduce((acc, fieldName) => {
|
|
115
|
+
acc[fieldName] = { root: classes[fieldName] };
|
|
116
|
+
|
|
117
|
+
return acc;
|
|
118
|
+
}, {});
|
|
119
|
+
}, [classes]);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* These 2 functions are wrappers around the `isRequired` function
|
|
123
|
+
* of `formValidators`. They perform validations only if the
|
|
124
|
+
* billing address is different from shipping address.
|
|
125
|
+
*
|
|
126
|
+
* We write this function in `venia-ui` and not in the `peregrine` talon
|
|
127
|
+
* because it references `isRequired` which is a `venia-ui` function.
|
|
128
|
+
*/
|
|
129
|
+
const isFieldRequired = useCallback((value, { isBillingAddressSame }) => {
|
|
130
|
+
if (isBillingAddressSame) {
|
|
131
|
+
/**
|
|
132
|
+
* Informed validator functions return `undefined` if
|
|
133
|
+
* validation is `true`
|
|
134
|
+
*/
|
|
135
|
+
return undefined;
|
|
136
|
+
} else {
|
|
137
|
+
return isRequired(value);
|
|
138
|
+
}
|
|
139
|
+
}, []);
|
|
140
|
+
|
|
141
|
+
const stepTitle = STEP_DESCRIPTIONS[stepNumber].defaultMessage
|
|
142
|
+
? formatMessage({
|
|
143
|
+
id: STEP_DESCRIPTIONS[stepNumber].id,
|
|
144
|
+
defaultMessage: STEP_DESCRIPTIONS[stepNumber].defaultMessage
|
|
145
|
+
})
|
|
146
|
+
: formatMessage({
|
|
147
|
+
id: 'checkoutPage.loadingPayment',
|
|
148
|
+
defaultMessage: 'Loading Payment'
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const loadingIndicator = isLoading ? (
|
|
152
|
+
<LoadingIndicator>{stepTitle}</LoadingIndicator>
|
|
153
|
+
) : null;
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<div className={classes.root} data-cy="Xendit-root">
|
|
157
|
+
<div className={creditCardComponentClassName}>
|
|
158
|
+
<FormError
|
|
159
|
+
allowErrorMessages
|
|
160
|
+
classes={{ root: classes.formErrorContainer }}
|
|
161
|
+
errors={Array.from(errors.values())}
|
|
162
|
+
/>
|
|
163
|
+
<div className={classes.dropin_root}>
|
|
164
|
+
<BrainTreeDropin
|
|
165
|
+
onError={onPaymentError}
|
|
166
|
+
onReady={onPaymentReady}
|
|
167
|
+
onSuccess={onPaymentSuccess}
|
|
168
|
+
shouldRequestPaymentNonce={shouldRequestPaymentNonce}
|
|
169
|
+
shouldTeardownDropin={shouldTeardownDropin}
|
|
170
|
+
resetShouldTeardownDropin={resetShouldTeardownDropin}
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
<div
|
|
174
|
+
data-cy="Xendit-AddressCheck-root"
|
|
175
|
+
className={classes.address_check}
|
|
176
|
+
>
|
|
177
|
+
<Checkbox
|
|
178
|
+
data-cy="PaymentInformation-billingAddressSame"
|
|
179
|
+
field="isBillingAddressSame"
|
|
180
|
+
label={formatMessage({
|
|
181
|
+
id: 'checkoutPage.billingAddressSame',
|
|
182
|
+
defaultMessage:
|
|
183
|
+
'Billing address same as shipping address'
|
|
184
|
+
})}
|
|
185
|
+
initialValue={initialValues.isBillingAddressSame}
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
188
|
+
<div
|
|
189
|
+
data-cy="Xendit-billingAddressFields"
|
|
190
|
+
className={billingAddressFieldsClassName}
|
|
191
|
+
>
|
|
192
|
+
<Field
|
|
193
|
+
id="firstName"
|
|
194
|
+
classes={fieldClasses.first_name}
|
|
195
|
+
label={formatMessage({
|
|
196
|
+
id: 'global.firstName',
|
|
197
|
+
defaultMessage: 'First Name'
|
|
198
|
+
})}
|
|
199
|
+
>
|
|
200
|
+
<TextInput
|
|
201
|
+
data-cy="Xendit-billingAddress-firstname"
|
|
202
|
+
id="firstName"
|
|
203
|
+
field="firstName"
|
|
204
|
+
validate={isFieldRequired}
|
|
205
|
+
initialValue={initialValues.firstName}
|
|
206
|
+
/>
|
|
207
|
+
</Field>
|
|
208
|
+
<Field
|
|
209
|
+
id="lastName"
|
|
210
|
+
classes={fieldClasses.last_name}
|
|
211
|
+
label={formatMessage({
|
|
212
|
+
id: 'global.lastName',
|
|
213
|
+
defaultMessage: 'Last Name'
|
|
214
|
+
})}
|
|
215
|
+
>
|
|
216
|
+
<TextInput
|
|
217
|
+
data-cy="Xendit-billingAddress-lastname"
|
|
218
|
+
id="lastName"
|
|
219
|
+
field="lastName"
|
|
220
|
+
validate={isFieldRequired}
|
|
221
|
+
initialValue={initialValues.lastName}
|
|
222
|
+
/>
|
|
223
|
+
</Field>
|
|
224
|
+
<Country
|
|
225
|
+
data-cy="Xendit-billingAddress-country"
|
|
226
|
+
classes={fieldClasses.country}
|
|
227
|
+
validate={isFieldRequired}
|
|
228
|
+
initialValue={
|
|
229
|
+
/**
|
|
230
|
+
* If there is no initial value to start with
|
|
231
|
+
* use the country from shipping address.
|
|
232
|
+
*/
|
|
233
|
+
initialValues.country || shippingAddressCountry
|
|
234
|
+
}
|
|
235
|
+
/>
|
|
236
|
+
<Field
|
|
237
|
+
id="street1"
|
|
238
|
+
classes={fieldClasses.street1}
|
|
239
|
+
label={formatMessage({
|
|
240
|
+
id: 'global.streetAddress',
|
|
241
|
+
defaultMessage: 'Street Address'
|
|
242
|
+
})}
|
|
243
|
+
>
|
|
244
|
+
<TextInput
|
|
245
|
+
data-cy="Xendit-billingAddress-street1"
|
|
246
|
+
id="street1"
|
|
247
|
+
field="street1"
|
|
248
|
+
validate={isFieldRequired}
|
|
249
|
+
initialValue={initialValues.street1}
|
|
250
|
+
/>
|
|
251
|
+
</Field>
|
|
252
|
+
<Field
|
|
253
|
+
id="street2"
|
|
254
|
+
classes={fieldClasses.street2}
|
|
255
|
+
label={formatMessage({
|
|
256
|
+
id: 'global.streetAddress2',
|
|
257
|
+
defaultMessage: 'Street Address 2'
|
|
258
|
+
})}
|
|
259
|
+
optional={true}
|
|
260
|
+
>
|
|
261
|
+
<TextInput
|
|
262
|
+
data-cy="Xendit-billingAddress-street2"
|
|
263
|
+
id="street2"
|
|
264
|
+
field="street2"
|
|
265
|
+
initialValue={initialValues.street2}
|
|
266
|
+
/>
|
|
267
|
+
</Field>
|
|
268
|
+
<Field
|
|
269
|
+
id="city"
|
|
270
|
+
classes={fieldClasses.city}
|
|
271
|
+
label={formatMessage({
|
|
272
|
+
id: 'global.city',
|
|
273
|
+
defaultMessage: 'City'
|
|
274
|
+
})}
|
|
275
|
+
>
|
|
276
|
+
<TextInput
|
|
277
|
+
data-cy="Xendit-billingAddress-city"
|
|
278
|
+
id="city"
|
|
279
|
+
field="city"
|
|
280
|
+
validate={isFieldRequired}
|
|
281
|
+
initialValue={initialValues.city}
|
|
282
|
+
/>
|
|
283
|
+
</Field>
|
|
284
|
+
<Region
|
|
285
|
+
data-cy="Xendit-billingAddress-region"
|
|
286
|
+
classes={fieldClasses.region}
|
|
287
|
+
initialValue={initialValues.region}
|
|
288
|
+
validate={isFieldRequired}
|
|
289
|
+
fieldInput={'region[label]'}
|
|
290
|
+
fieldSelect={'region[region_id]'}
|
|
291
|
+
optionValueKey={'id'}
|
|
292
|
+
/>
|
|
293
|
+
<Postcode
|
|
294
|
+
data-cy="Xendit-billingAddress-postcode"
|
|
295
|
+
classes={fieldClasses.postal_code}
|
|
296
|
+
validate={isFieldRequired}
|
|
297
|
+
initialValue={initialValues.postcode}
|
|
298
|
+
/>
|
|
299
|
+
<Field
|
|
300
|
+
id="phoneNumber"
|
|
301
|
+
classes={fieldClasses.phone_number}
|
|
302
|
+
label={formatMessage({
|
|
303
|
+
id: 'global.phoneNumber',
|
|
304
|
+
defaultMessage: 'Phone Number'
|
|
305
|
+
})}
|
|
306
|
+
>
|
|
307
|
+
<TextInput
|
|
308
|
+
data-cy="Xendit-billingAddress-phoneNumber"
|
|
309
|
+
id="phoneNumber"
|
|
310
|
+
field="phoneNumber"
|
|
311
|
+
validate={isFieldRequired}
|
|
312
|
+
initialValue={initialValues.phoneNumber}
|
|
313
|
+
/>
|
|
314
|
+
</Field>
|
|
315
|
+
</div>
|
|
316
|
+
<GoogleReCaptcha {...recaptchaWidgetProps} />
|
|
317
|
+
</div>
|
|
318
|
+
{loadingIndicator}
|
|
319
|
+
</div>
|
|
320
|
+
);
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
export default Xendit;
|
|
324
|
+
|
|
325
|
+
Xendit.propTypes = {
|
|
326
|
+
classes: shape({
|
|
327
|
+
root: string,
|
|
328
|
+
dropin_root: string,
|
|
329
|
+
billing_address_fields_root: string,
|
|
330
|
+
first_name: string,
|
|
331
|
+
last_name: string,
|
|
332
|
+
city: string,
|
|
333
|
+
region: string,
|
|
334
|
+
postal_code: string,
|
|
335
|
+
phone_number: string,
|
|
336
|
+
country: string,
|
|
337
|
+
street1: string,
|
|
338
|
+
street2: string,
|
|
339
|
+
address_check: string,
|
|
340
|
+
credit_card_root: string,
|
|
341
|
+
credit_card_root_hidden: string
|
|
342
|
+
}),
|
|
343
|
+
shouldSubmit: bool.isRequired,
|
|
344
|
+
onPaymentSuccess: func,
|
|
345
|
+
onPaymentReady: func,
|
|
346
|
+
onPaymentError: func,
|
|
347
|
+
resetShouldSubmit: func.isRequired
|
|
348
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
}
|
|
3
|
+
|
|
4
|
+
.credit_card_root {
|
|
5
|
+
composes: visible from global;
|
|
6
|
+
composes: opacity-100 from global;
|
|
7
|
+
transition-delay: 64ms;
|
|
8
|
+
transition-duration: 384ms;
|
|
9
|
+
transition-property: opacity, visbility;
|
|
10
|
+
transition-timing-function: var(--venia-global-anim-standard);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.credit_card_root_hidden {
|
|
14
|
+
composes: h-0 from global;
|
|
15
|
+
composes: invisible from global;
|
|
16
|
+
composes: opacity-0 from global;
|
|
17
|
+
composes: overflow-hidden from global;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.dropin_root {
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.billing_address_fields_root {
|
|
24
|
+
composes: gap-x-xs from global;
|
|
25
|
+
composes: gap-y-sm from global;
|
|
26
|
+
composes: grid from global;
|
|
27
|
+
composes: px-0 from global;
|
|
28
|
+
composes: py-xs from global;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.billing_address_fields_root_hidden {
|
|
32
|
+
composes: h-0 from global;
|
|
33
|
+
composes: invisible from global;
|
|
34
|
+
composes: opacity-0 from global;
|
|
35
|
+
composes: overflow-hidden from global;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.formErrorContainer {
|
|
39
|
+
composes: pt-sm from global;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.first_name,
|
|
43
|
+
.last_name {
|
|
44
|
+
composes: col-end-span2 from global;
|
|
45
|
+
|
|
46
|
+
composes: lg_col-end-span1 from global;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.country,
|
|
50
|
+
.street1,
|
|
51
|
+
.street2,
|
|
52
|
+
.address_check,
|
|
53
|
+
.city,
|
|
54
|
+
.region,
|
|
55
|
+
.postal_code,
|
|
56
|
+
.phone_number {
|
|
57
|
+
composes: col-end-span2 from global;
|
|
58
|
+
}
|
package/src/intercept.js
CHANGED
|
@@ -220,4 +220,18 @@ module.exports = targets => {
|
|
|
220
220
|
routesArray.push(...routes);
|
|
221
221
|
return routesArray;
|
|
222
222
|
});
|
|
223
|
+
|
|
224
|
+
targets.of('@magento/venia-ui').checkoutPagePaymentTypes.tap(checkoutPagePaymentTypes =>
|
|
225
|
+
checkoutPagePaymentTypes.add({
|
|
226
|
+
paymentCode: 'xendit',
|
|
227
|
+
importPath: '@riosst100/pwa-marketplace/src/components/Xendit/xendit.js'
|
|
228
|
+
})
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
// targets.of('@magento/venia-ui').summaryPagePaymentTypes.tap(summaryPagePaymentTypes =>
|
|
232
|
+
// summaryPagePaymentTypes.add({
|
|
233
|
+
// paymentCode: 'xendit',
|
|
234
|
+
// importPath: '@riosst100/pwa-marketplace/src/components/Xendit/summary.js'
|
|
235
|
+
// })
|
|
236
|
+
// );
|
|
223
237
|
};
|
package/src/overwrites/venia-ui/lib/components/CheckoutPage/PaymentInformation/paymentMethods.js
CHANGED
|
@@ -37,6 +37,12 @@ const PaymentMethods = props => {
|
|
|
37
37
|
return null;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
console.log('payments')
|
|
41
|
+
console.log(payments)
|
|
42
|
+
|
|
43
|
+
console.log('availablePaymentMethods')
|
|
44
|
+
console.log(availablePaymentMethods)
|
|
45
|
+
|
|
40
46
|
const radios = availablePaymentMethods
|
|
41
47
|
.map(({ code, title }) => {
|
|
42
48
|
// If we don't have an implementation for a method type, ignore it.
|
|
@@ -74,6 +80,9 @@ const PaymentMethods = props => {
|
|
|
74
80
|
})
|
|
75
81
|
.filter(paymentMethod => !!paymentMethod);
|
|
76
82
|
|
|
83
|
+
console.log('radios != [] harusny')
|
|
84
|
+
console.log(radios)
|
|
85
|
+
|
|
77
86
|
const noPaymentMethodMessage = !radios.length ? (
|
|
78
87
|
<div className={classes.payment_errors}>
|
|
79
88
|
<span>
|
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState, useMemo } from 'react';
|
|
2
|
+
import { useFormState, useFormApi } from 'informed';
|
|
3
|
+
import { useQuery, useApolloClient, useMutation } from '@apollo/client';
|
|
4
|
+
import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
|
|
5
|
+
|
|
6
|
+
import { useCartContext } from '@magento/peregrine/lib/context/cart';
|
|
7
|
+
|
|
8
|
+
import DEFAULT_OPERATIONS from './xendit.gql';
|
|
9
|
+
import { useGoogleReCaptcha } from '@magento/peregrine/lib/hooks/useGoogleReCaptcha';
|
|
10
|
+
|
|
11
|
+
const getRegion = region => {
|
|
12
|
+
return region.region_id || region.label || region.code;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Maps address response data from GET_BILLING_ADDRESS and GET_SHIPPING_ADDRESS
|
|
17
|
+
* queries to input names in the billing address form.
|
|
18
|
+
* {@link creditCard.gql.js}.
|
|
19
|
+
*
|
|
20
|
+
* @param {ShippingCartAddress|BillingCartAddress} rawAddressData query data
|
|
21
|
+
*/
|
|
22
|
+
export const mapAddressData = rawAddressData => {
|
|
23
|
+
if (rawAddressData) {
|
|
24
|
+
const {
|
|
25
|
+
firstName,
|
|
26
|
+
lastName,
|
|
27
|
+
city,
|
|
28
|
+
postcode,
|
|
29
|
+
phoneNumber,
|
|
30
|
+
street,
|
|
31
|
+
country,
|
|
32
|
+
region
|
|
33
|
+
} = rawAddressData;
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
firstName,
|
|
37
|
+
lastName,
|
|
38
|
+
city,
|
|
39
|
+
postcode,
|
|
40
|
+
phoneNumber,
|
|
41
|
+
street1: street[0],
|
|
42
|
+
street2: street[1] || '',
|
|
43
|
+
country: country.code,
|
|
44
|
+
region: getRegion(region)
|
|
45
|
+
};
|
|
46
|
+
} else {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Talon to handle Credit Card payment method.
|
|
53
|
+
*
|
|
54
|
+
* @param {Boolean} props.shouldSubmit boolean value which represents if a payment nonce request has been submitted
|
|
55
|
+
* @param {Function} props.onSuccess callback to invoke when the a payment nonce has been generated
|
|
56
|
+
* @param {Function} props.onReady callback to invoke when the braintree dropin component is ready
|
|
57
|
+
* @param {Function} props.onError callback to invoke when the braintree dropin component throws an error
|
|
58
|
+
* @param {Function} props.resetShouldSubmit callback to reset the shouldSubmit flag
|
|
59
|
+
* @param {DocumentNode} props.operations.getBillingAddressQuery query to fetch billing address from cache
|
|
60
|
+
* @param {DocumentNode} props.operations.getIsBillingAddressSameQuery query to fetch is billing address same checkbox value from cache
|
|
61
|
+
* @param {DocumentNode} props.operations.getPaymentNonceQuery query to fetch payment nonce saved in cache
|
|
62
|
+
* @param {DocumentNode} props.operations.setBillingAddressMutation mutation to update billing address on the cart
|
|
63
|
+
* @param {DocumentNode} props.operations.setCreditCardDetailsOnCartMutation mutation to update payment method and payment nonce on the cart
|
|
64
|
+
*
|
|
65
|
+
* @returns {
|
|
66
|
+
* errors: Map<String, Error>,
|
|
67
|
+
* shouldRequestPaymentNonce: Boolean,
|
|
68
|
+
* onPaymentError: Function,
|
|
69
|
+
* onPaymentSuccess: Function,
|
|
70
|
+
* onPaymentReady: Function,
|
|
71
|
+
* isBillingAddressSame: Boolean,
|
|
72
|
+
* isLoading: Boolean,
|
|
73
|
+
* stepNumber: Number,
|
|
74
|
+
* initialValues: {
|
|
75
|
+
* firstName: String,
|
|
76
|
+
* lastName: String,
|
|
77
|
+
* city: String,
|
|
78
|
+
* postcode: String,
|
|
79
|
+
* phoneNumber: String,
|
|
80
|
+
* street1: String,
|
|
81
|
+
* street2: String,
|
|
82
|
+
* country: String,
|
|
83
|
+
* state: String,
|
|
84
|
+
* isBillingAddressSame: Boolean
|
|
85
|
+
* },
|
|
86
|
+
* shippingAddressCountry: String,
|
|
87
|
+
* shouldTeardownDropin: Boolean,
|
|
88
|
+
* resetShouldTeardownDropin: Function
|
|
89
|
+
* }
|
|
90
|
+
*/
|
|
91
|
+
export const useXendit = props => {
|
|
92
|
+
const {
|
|
93
|
+
onSuccess,
|
|
94
|
+
onReady,
|
|
95
|
+
onError,
|
|
96
|
+
shouldSubmit,
|
|
97
|
+
resetShouldSubmit
|
|
98
|
+
} = props;
|
|
99
|
+
|
|
100
|
+
const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
|
|
101
|
+
|
|
102
|
+
const {
|
|
103
|
+
getBillingAddressQuery,
|
|
104
|
+
getIsBillingAddressSameQuery,
|
|
105
|
+
getPaymentNonceQuery,
|
|
106
|
+
getShippingAddressQuery,
|
|
107
|
+
setBillingAddressMutation,
|
|
108
|
+
setCreditCardDetailsOnCartMutation
|
|
109
|
+
} = operations;
|
|
110
|
+
|
|
111
|
+
const {
|
|
112
|
+
recaptchaLoading,
|
|
113
|
+
generateReCaptchaData,
|
|
114
|
+
recaptchaWidgetProps
|
|
115
|
+
} = useGoogleReCaptcha({
|
|
116
|
+
currentForm: 'BRAINTREE',
|
|
117
|
+
formAction: 'braintree'
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Definitions
|
|
122
|
+
*/
|
|
123
|
+
|
|
124
|
+
const [isDropinLoading, setDropinLoading] = useState(true);
|
|
125
|
+
const [shouldRequestPaymentNonce, setShouldRequestPaymentNonce] = useState(
|
|
126
|
+
false
|
|
127
|
+
);
|
|
128
|
+
const [shouldTeardownDropin, setShouldTeardownDropin] = useState(false);
|
|
129
|
+
/**
|
|
130
|
+
* `stepNumber` depicts the state of the process flow in credit card
|
|
131
|
+
* payment flow.
|
|
132
|
+
*
|
|
133
|
+
* `0` No call made yet
|
|
134
|
+
* `1` Billing address mutation initiated
|
|
135
|
+
* `2` Braintree nonce requested
|
|
136
|
+
* `3` Payment information mutation initiated
|
|
137
|
+
* `4` All mutations done
|
|
138
|
+
*/
|
|
139
|
+
const [stepNumber, setStepNumber] = useState(0);
|
|
140
|
+
|
|
141
|
+
const client = useApolloClient();
|
|
142
|
+
const formState = useFormState();
|
|
143
|
+
const { validate: validateBillingAddressForm } = useFormApi();
|
|
144
|
+
const [{ cartId }] = useCartContext();
|
|
145
|
+
|
|
146
|
+
const isLoading =
|
|
147
|
+
isDropinLoading ||
|
|
148
|
+
recaptchaLoading ||
|
|
149
|
+
(stepNumber >= 1 && stepNumber <= 3);
|
|
150
|
+
|
|
151
|
+
const { data: billingAddressData } = useQuery(getBillingAddressQuery, {
|
|
152
|
+
skip: !cartId,
|
|
153
|
+
variables: { cartId }
|
|
154
|
+
});
|
|
155
|
+
const { data: shippingAddressData } = useQuery(getShippingAddressQuery, {
|
|
156
|
+
skip: !cartId,
|
|
157
|
+
variables: { cartId }
|
|
158
|
+
});
|
|
159
|
+
const { data: isBillingAddressSameData } = useQuery(
|
|
160
|
+
getIsBillingAddressSameQuery,
|
|
161
|
+
{ skip: !cartId, variables: { cartId } }
|
|
162
|
+
);
|
|
163
|
+
const [
|
|
164
|
+
updateBillingAddress,
|
|
165
|
+
{
|
|
166
|
+
error: billingAddressMutationError,
|
|
167
|
+
called: billingAddressMutationCalled,
|
|
168
|
+
loading: billingAddressMutationLoading
|
|
169
|
+
}
|
|
170
|
+
] = useMutation(setBillingAddressMutation);
|
|
171
|
+
|
|
172
|
+
const [
|
|
173
|
+
updateCCDetails,
|
|
174
|
+
{
|
|
175
|
+
error: ccMutationError,
|
|
176
|
+
called: ccMutationCalled,
|
|
177
|
+
loading: ccMutationLoading
|
|
178
|
+
}
|
|
179
|
+
] = useMutation(setCreditCardDetailsOnCartMutation);
|
|
180
|
+
|
|
181
|
+
const shippingAddressCountry = shippingAddressData
|
|
182
|
+
? shippingAddressData.cart.shippingAddresses[0].country.code
|
|
183
|
+
: DEFAULT_COUNTRY_CODE;
|
|
184
|
+
const isBillingAddressSame = formState.values.isBillingAddressSame;
|
|
185
|
+
|
|
186
|
+
const initialValues = useMemo(() => {
|
|
187
|
+
const isBillingAddressSame = isBillingAddressSameData
|
|
188
|
+
? isBillingAddressSameData.cart.isBillingAddressSame
|
|
189
|
+
: true;
|
|
190
|
+
|
|
191
|
+
let billingAddress = {};
|
|
192
|
+
/**
|
|
193
|
+
* If billing address is same as shipping address, do
|
|
194
|
+
* not auto fill the fields.
|
|
195
|
+
*/
|
|
196
|
+
if (billingAddressData && !isBillingAddressSame) {
|
|
197
|
+
if (billingAddressData.cart.billingAddress) {
|
|
198
|
+
const {
|
|
199
|
+
// eslint-disable-next-line no-unused-vars
|
|
200
|
+
__typename,
|
|
201
|
+
...rawBillingAddress
|
|
202
|
+
} = billingAddressData.cart.billingAddress;
|
|
203
|
+
billingAddress = mapAddressData(rawBillingAddress);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return { isBillingAddressSame, ...billingAddress };
|
|
208
|
+
}, [isBillingAddressSameData, billingAddressData]);
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Helpers
|
|
212
|
+
*/
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* This function sets the boolean isBillingAddressSame
|
|
216
|
+
* in cache for future use. We use cache because there
|
|
217
|
+
* is no way to save this on the cart in remote.
|
|
218
|
+
*/
|
|
219
|
+
const setIsBillingAddressSameInCache = useCallback(() => {
|
|
220
|
+
client.writeQuery({
|
|
221
|
+
query: getIsBillingAddressSameQuery,
|
|
222
|
+
data: {
|
|
223
|
+
cart: {
|
|
224
|
+
__typename: 'Cart',
|
|
225
|
+
id: cartId,
|
|
226
|
+
isBillingAddressSame
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}, [client, cartId, getIsBillingAddressSameQuery, isBillingAddressSame]);
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* This function sets the billing address on the cart using the
|
|
234
|
+
* shipping address.
|
|
235
|
+
*/
|
|
236
|
+
const setShippingAddressAsBillingAddress = useCallback(() => {
|
|
237
|
+
var shippingAddress = shippingAddressData
|
|
238
|
+
? mapAddressData(shippingAddressData.cart.shippingAddresses[0])
|
|
239
|
+
: {};
|
|
240
|
+
|
|
241
|
+
shippingAddress.region =
|
|
242
|
+
shippingAddress.region == null ? '' : shippingAddress.region;
|
|
243
|
+
|
|
244
|
+
updateBillingAddress({
|
|
245
|
+
variables: {
|
|
246
|
+
cartId,
|
|
247
|
+
...shippingAddress,
|
|
248
|
+
sameAsShipping: true
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
}, [updateBillingAddress, shippingAddressData, cartId]);
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* This function sets the billing address on the cart using the
|
|
255
|
+
* information from the form.
|
|
256
|
+
*/
|
|
257
|
+
const setBillingAddress = useCallback(() => {
|
|
258
|
+
const {
|
|
259
|
+
firstName,
|
|
260
|
+
lastName,
|
|
261
|
+
country,
|
|
262
|
+
street1,
|
|
263
|
+
street2,
|
|
264
|
+
city,
|
|
265
|
+
region,
|
|
266
|
+
postcode,
|
|
267
|
+
phoneNumber
|
|
268
|
+
} = formState.values;
|
|
269
|
+
|
|
270
|
+
updateBillingAddress({
|
|
271
|
+
variables: {
|
|
272
|
+
cartId,
|
|
273
|
+
firstName,
|
|
274
|
+
lastName,
|
|
275
|
+
country,
|
|
276
|
+
street1,
|
|
277
|
+
street2: street2 || '',
|
|
278
|
+
city,
|
|
279
|
+
region: getRegion(region),
|
|
280
|
+
postcode,
|
|
281
|
+
phoneNumber,
|
|
282
|
+
sameAsShipping: false
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}, [formState.values, updateBillingAddress, cartId]);
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* This function sets the payment nonce details in the cache.
|
|
289
|
+
* We use cache because there is no way to save this information
|
|
290
|
+
* on the cart in the remote.
|
|
291
|
+
*
|
|
292
|
+
* We do not save the nonce code because it is a PII.
|
|
293
|
+
*/
|
|
294
|
+
const setPaymentDetailsInCache = useCallback(
|
|
295
|
+
braintreeNonce => {
|
|
296
|
+
/**
|
|
297
|
+
* We dont save the nonce code due to PII,
|
|
298
|
+
* we only save the subset of details.
|
|
299
|
+
*/
|
|
300
|
+
const { details, description, type } = braintreeNonce;
|
|
301
|
+
client.writeQuery({
|
|
302
|
+
query: getPaymentNonceQuery,
|
|
303
|
+
data: {
|
|
304
|
+
cart: {
|
|
305
|
+
__typename: 'Cart',
|
|
306
|
+
id: cartId,
|
|
307
|
+
paymentNonce: {
|
|
308
|
+
details,
|
|
309
|
+
description,
|
|
310
|
+
type
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
},
|
|
316
|
+
[cartId, client, getPaymentNonceQuery]
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* This function saves the nonce code from braintree
|
|
321
|
+
* on the cart along with the payment method used in
|
|
322
|
+
* this case `braintree`.
|
|
323
|
+
*/
|
|
324
|
+
const updateCCDetailsOnCart = useCallback(
|
|
325
|
+
async braintreeNonce => {
|
|
326
|
+
try {
|
|
327
|
+
const { nonce } = braintreeNonce;
|
|
328
|
+
const reCaptchaData = await generateReCaptchaData();
|
|
329
|
+
|
|
330
|
+
await updateCCDetails({
|
|
331
|
+
variables: {
|
|
332
|
+
cartId,
|
|
333
|
+
paymentMethod: 'braintree',
|
|
334
|
+
paymentNonce: nonce
|
|
335
|
+
},
|
|
336
|
+
...reCaptchaData
|
|
337
|
+
});
|
|
338
|
+
} catch (error) {
|
|
339
|
+
// Error is logged by apollo link - no need to double log.
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
[updateCCDetails, cartId, generateReCaptchaData]
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Function to be called by the braintree dropin when the
|
|
347
|
+
* nonce generation is successful.
|
|
348
|
+
*/
|
|
349
|
+
const onPaymentSuccess = useCallback(
|
|
350
|
+
braintreeNonce => {
|
|
351
|
+
setPaymentDetailsInCache(braintreeNonce);
|
|
352
|
+
/**
|
|
353
|
+
* Updating payment braintreeNonce and selected payment method on cart.
|
|
354
|
+
*/
|
|
355
|
+
updateCCDetailsOnCart(braintreeNonce);
|
|
356
|
+
setStepNumber(3);
|
|
357
|
+
},
|
|
358
|
+
[setPaymentDetailsInCache, updateCCDetailsOnCart]
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Function to be called by the braintree dropin when the
|
|
363
|
+
* nonce generation is not successful.
|
|
364
|
+
*/
|
|
365
|
+
const onPaymentError = useCallback(
|
|
366
|
+
error => {
|
|
367
|
+
setStepNumber(0);
|
|
368
|
+
setShouldRequestPaymentNonce(false);
|
|
369
|
+
resetShouldSubmit();
|
|
370
|
+
if (onError) {
|
|
371
|
+
onError(error);
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
[onError, resetShouldSubmit]
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Function to be called by the braintree dropin when the
|
|
379
|
+
* credit card component has loaded successfully.
|
|
380
|
+
*/
|
|
381
|
+
const onPaymentReady = useCallback(() => {
|
|
382
|
+
setDropinLoading(false);
|
|
383
|
+
setStepNumber(0);
|
|
384
|
+
if (onReady) {
|
|
385
|
+
onReady();
|
|
386
|
+
}
|
|
387
|
+
}, [onReady]);
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Function to be called by braintree dropin when the payment
|
|
391
|
+
* teardown is done successfully before re creating the new dropin.
|
|
392
|
+
*/
|
|
393
|
+
const resetShouldTeardownDropin = useCallback(() => {
|
|
394
|
+
setShouldTeardownDropin(false);
|
|
395
|
+
}, []);
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Effects
|
|
399
|
+
*/
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Step 1 effect
|
|
403
|
+
*
|
|
404
|
+
* User has clicked the update button
|
|
405
|
+
*/
|
|
406
|
+
useEffect(() => {
|
|
407
|
+
try {
|
|
408
|
+
if (shouldSubmit) {
|
|
409
|
+
/**
|
|
410
|
+
* Validate billing address fields and only process with
|
|
411
|
+
* submit if there are no errors.
|
|
412
|
+
*
|
|
413
|
+
* We do this because the user can click Review Order button
|
|
414
|
+
* without fillig in all fields and the form submission
|
|
415
|
+
* happens manually. The informed Form component validates
|
|
416
|
+
* on submission but that only happens when we use the onSubmit
|
|
417
|
+
* prop. In this case we are using manually submission because
|
|
418
|
+
* of the nature of the credit card submission process.
|
|
419
|
+
*/
|
|
420
|
+
validateBillingAddressForm();
|
|
421
|
+
|
|
422
|
+
const hasErrors = Object.keys(formState.errors).length;
|
|
423
|
+
|
|
424
|
+
if (!hasErrors) {
|
|
425
|
+
setStepNumber(1);
|
|
426
|
+
if (isBillingAddressSame) {
|
|
427
|
+
setShippingAddressAsBillingAddress();
|
|
428
|
+
} else {
|
|
429
|
+
setBillingAddress();
|
|
430
|
+
}
|
|
431
|
+
setIsBillingAddressSameInCache();
|
|
432
|
+
} else {
|
|
433
|
+
throw new Error('Errors in the billing address form');
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
} catch (err) {
|
|
437
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
438
|
+
console.error(err);
|
|
439
|
+
}
|
|
440
|
+
setStepNumber(0);
|
|
441
|
+
resetShouldSubmit();
|
|
442
|
+
setShouldRequestPaymentNonce(false);
|
|
443
|
+
}
|
|
444
|
+
}, [
|
|
445
|
+
shouldSubmit,
|
|
446
|
+
isBillingAddressSame,
|
|
447
|
+
setShippingAddressAsBillingAddress,
|
|
448
|
+
setBillingAddress,
|
|
449
|
+
setIsBillingAddressSameInCache,
|
|
450
|
+
resetShouldSubmit,
|
|
451
|
+
validateBillingAddressForm,
|
|
452
|
+
formState.errors
|
|
453
|
+
]);
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Step 2 effect
|
|
457
|
+
*
|
|
458
|
+
* Billing address mutation has completed
|
|
459
|
+
*/
|
|
460
|
+
useEffect(() => {
|
|
461
|
+
try {
|
|
462
|
+
const billingAddressMutationCompleted =
|
|
463
|
+
billingAddressMutationCalled && !billingAddressMutationLoading;
|
|
464
|
+
|
|
465
|
+
if (
|
|
466
|
+
billingAddressMutationCompleted &&
|
|
467
|
+
!billingAddressMutationError
|
|
468
|
+
) {
|
|
469
|
+
/**
|
|
470
|
+
* Billing address save mutation is successful
|
|
471
|
+
* we can initiate the braintree nonce request
|
|
472
|
+
*/
|
|
473
|
+
setStepNumber(2);
|
|
474
|
+
setShouldRequestPaymentNonce(true);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (
|
|
478
|
+
billingAddressMutationCompleted &&
|
|
479
|
+
billingAddressMutationError
|
|
480
|
+
) {
|
|
481
|
+
/**
|
|
482
|
+
* Billing address save mutation is not successful.
|
|
483
|
+
* Reset update button clicked flag.
|
|
484
|
+
*/
|
|
485
|
+
throw new Error('Billing address mutation failed');
|
|
486
|
+
}
|
|
487
|
+
} catch (err) {
|
|
488
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
489
|
+
console.error(err);
|
|
490
|
+
}
|
|
491
|
+
setStepNumber(0);
|
|
492
|
+
resetShouldSubmit();
|
|
493
|
+
setShouldRequestPaymentNonce(false);
|
|
494
|
+
}
|
|
495
|
+
}, [
|
|
496
|
+
billingAddressMutationError,
|
|
497
|
+
billingAddressMutationCalled,
|
|
498
|
+
billingAddressMutationLoading,
|
|
499
|
+
resetShouldSubmit
|
|
500
|
+
]);
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Step 3 effect
|
|
504
|
+
*
|
|
505
|
+
* Credit card save mutation has completed
|
|
506
|
+
*/
|
|
507
|
+
useEffect(() => {
|
|
508
|
+
/**
|
|
509
|
+
* Saved billing address, payment method and payment nonce on cart.
|
|
510
|
+
*
|
|
511
|
+
* Time to call onSuccess.
|
|
512
|
+
*/
|
|
513
|
+
|
|
514
|
+
try {
|
|
515
|
+
const ccMutationCompleted = ccMutationCalled && !ccMutationLoading;
|
|
516
|
+
|
|
517
|
+
if (ccMutationCompleted && !ccMutationError) {
|
|
518
|
+
if (onSuccess) {
|
|
519
|
+
onSuccess();
|
|
520
|
+
}
|
|
521
|
+
resetShouldSubmit();
|
|
522
|
+
setStepNumber(4);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (ccMutationCompleted && ccMutationError) {
|
|
526
|
+
/**
|
|
527
|
+
* If credit card mutation failed, reset update button clicked so the
|
|
528
|
+
* user can click again and set `stepNumber` to 0.
|
|
529
|
+
*/
|
|
530
|
+
throw new Error('Credit card nonce save mutation failed.');
|
|
531
|
+
}
|
|
532
|
+
} catch (err) {
|
|
533
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
534
|
+
console.error(err);
|
|
535
|
+
}
|
|
536
|
+
setStepNumber(0);
|
|
537
|
+
resetShouldSubmit();
|
|
538
|
+
setShouldRequestPaymentNonce(false);
|
|
539
|
+
setShouldTeardownDropin(true);
|
|
540
|
+
}
|
|
541
|
+
}, [
|
|
542
|
+
ccMutationCalled,
|
|
543
|
+
ccMutationLoading,
|
|
544
|
+
onSuccess,
|
|
545
|
+
setShouldRequestPaymentNonce,
|
|
546
|
+
resetShouldSubmit,
|
|
547
|
+
ccMutationError
|
|
548
|
+
]);
|
|
549
|
+
|
|
550
|
+
const errors = useMemo(
|
|
551
|
+
() =>
|
|
552
|
+
new Map([
|
|
553
|
+
['setBillingAddressMutation', billingAddressMutationError],
|
|
554
|
+
['setCreditCardDetailsOnCartMutation', ccMutationError]
|
|
555
|
+
]),
|
|
556
|
+
[billingAddressMutationError, ccMutationError]
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
return {
|
|
560
|
+
errors,
|
|
561
|
+
onPaymentError,
|
|
562
|
+
onPaymentSuccess,
|
|
563
|
+
onPaymentReady,
|
|
564
|
+
isBillingAddressSame,
|
|
565
|
+
isLoading,
|
|
566
|
+
shouldRequestPaymentNonce,
|
|
567
|
+
stepNumber,
|
|
568
|
+
initialValues,
|
|
569
|
+
shippingAddressCountry,
|
|
570
|
+
shouldTeardownDropin,
|
|
571
|
+
resetShouldTeardownDropin,
|
|
572
|
+
recaptchaWidgetProps
|
|
573
|
+
};
|
|
574
|
+
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { gql } from '@apollo/client';
|
|
2
|
+
|
|
3
|
+
import { PriceSummaryFragment } from '@magento/peregrine/lib/talons/CartPage/PriceSummary/priceSummaryFragments.gql';
|
|
4
|
+
import { AvailablePaymentMethodsFragment } from '@magento/peregrine/lib/talons/CheckoutPage/PaymentInformation/paymentInformation.gql';
|
|
5
|
+
|
|
6
|
+
export const GET_IS_BILLING_ADDRESS_SAME = gql`
|
|
7
|
+
query getIsBillingAddressSame($cartId: String!) {
|
|
8
|
+
cart(cart_id: $cartId) @client {
|
|
9
|
+
id
|
|
10
|
+
isBillingAddressSame
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export const GET_PAYMENT_NONCE = gql`
|
|
16
|
+
query getPaymentNonce($cartId: String!) {
|
|
17
|
+
cart(cart_id: $cartId) @client {
|
|
18
|
+
id
|
|
19
|
+
paymentNonce
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
export const GET_BILLING_ADDRESS = gql`
|
|
25
|
+
query getBillingAddress($cartId: String!) {
|
|
26
|
+
cart(cart_id: $cartId) {
|
|
27
|
+
id
|
|
28
|
+
billingAddress: billing_address {
|
|
29
|
+
firstName: firstname
|
|
30
|
+
lastName: lastname
|
|
31
|
+
country {
|
|
32
|
+
code
|
|
33
|
+
}
|
|
34
|
+
street
|
|
35
|
+
city
|
|
36
|
+
region {
|
|
37
|
+
code
|
|
38
|
+
label
|
|
39
|
+
region_id
|
|
40
|
+
}
|
|
41
|
+
postcode
|
|
42
|
+
phoneNumber: telephone
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
export const GET_SHIPPING_ADDRESS = gql`
|
|
49
|
+
query getSelectedShippingAddress($cartId: String!) {
|
|
50
|
+
cart(cart_id: $cartId) {
|
|
51
|
+
id
|
|
52
|
+
shippingAddresses: shipping_addresses {
|
|
53
|
+
firstName: firstname
|
|
54
|
+
lastName: lastname
|
|
55
|
+
country {
|
|
56
|
+
code
|
|
57
|
+
}
|
|
58
|
+
street
|
|
59
|
+
city
|
|
60
|
+
region {
|
|
61
|
+
code
|
|
62
|
+
label
|
|
63
|
+
region_id
|
|
64
|
+
}
|
|
65
|
+
postcode
|
|
66
|
+
phoneNumber: telephone
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
export const SET_BILLING_ADDRESS = gql`
|
|
73
|
+
mutation setBillingAddress(
|
|
74
|
+
$cartId: String!
|
|
75
|
+
$firstName: String!
|
|
76
|
+
$lastName: String!
|
|
77
|
+
$street1: String!
|
|
78
|
+
$street2: String
|
|
79
|
+
$city: String!
|
|
80
|
+
$region: String!
|
|
81
|
+
$postcode: String!
|
|
82
|
+
$country: String!
|
|
83
|
+
$phoneNumber: String!
|
|
84
|
+
) {
|
|
85
|
+
setBillingAddressOnCart(
|
|
86
|
+
input: {
|
|
87
|
+
cart_id: $cartId
|
|
88
|
+
billing_address: {
|
|
89
|
+
address: {
|
|
90
|
+
firstname: $firstName
|
|
91
|
+
lastname: $lastName
|
|
92
|
+
street: [$street1, $street2]
|
|
93
|
+
city: $city
|
|
94
|
+
region: $region
|
|
95
|
+
postcode: $postcode
|
|
96
|
+
country_code: $country
|
|
97
|
+
telephone: $phoneNumber
|
|
98
|
+
save_in_address_book: false
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
) {
|
|
103
|
+
cart {
|
|
104
|
+
id
|
|
105
|
+
billing_address {
|
|
106
|
+
firstname
|
|
107
|
+
lastname
|
|
108
|
+
country {
|
|
109
|
+
code
|
|
110
|
+
}
|
|
111
|
+
street
|
|
112
|
+
city
|
|
113
|
+
region {
|
|
114
|
+
code
|
|
115
|
+
label
|
|
116
|
+
region_id
|
|
117
|
+
}
|
|
118
|
+
postcode
|
|
119
|
+
telephone
|
|
120
|
+
}
|
|
121
|
+
...PriceSummaryFragment
|
|
122
|
+
...AvailablePaymentMethodsFragment
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
${PriceSummaryFragment}
|
|
127
|
+
${AvailablePaymentMethodsFragment}
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
export const SET_CC_DETAILS_ON_CART = gql`
|
|
131
|
+
mutation setSelectedPaymentMethod(
|
|
132
|
+
$cartId: String!
|
|
133
|
+
$paymentNonce: String!
|
|
134
|
+
) {
|
|
135
|
+
setPaymentMethodOnCart(
|
|
136
|
+
input: {
|
|
137
|
+
cart_id: $cartId
|
|
138
|
+
payment_method: {
|
|
139
|
+
code: "braintree"
|
|
140
|
+
braintree: {
|
|
141
|
+
payment_method_nonce: $paymentNonce
|
|
142
|
+
is_active_payment_token_enabler: false
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
) {
|
|
147
|
+
cart {
|
|
148
|
+
id
|
|
149
|
+
selected_payment_method {
|
|
150
|
+
code
|
|
151
|
+
title
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
`;
|
|
157
|
+
|
|
158
|
+
export default {
|
|
159
|
+
getBillingAddressQuery: GET_BILLING_ADDRESS,
|
|
160
|
+
getIsBillingAddressSameQuery: GET_IS_BILLING_ADDRESS_SAME,
|
|
161
|
+
getPaymentNonceQuery: GET_PAYMENT_NONCE,
|
|
162
|
+
getShippingAddressQuery: GET_SHIPPING_ADDRESS,
|
|
163
|
+
setBillingAddressMutation: SET_BILLING_ADDRESS,
|
|
164
|
+
setCreditCardDetailsOnCartMutation: SET_CC_DETAILS_ON_CART
|
|
165
|
+
};
|