@akinon/pz-flow-payment 1.107.0-rc.86 → 1.108.0-rc.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/CHANGELOG.md +4 -28
- package/package.json +1 -1
- package/src/views/index.tsx +143 -113
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# @akinon/pz-flow-payment
|
|
2
2
|
|
|
3
|
-
## 1.
|
|
3
|
+
## 1.108.0-rc.0
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- 33d4d0c: ZERO-3615: remove custom not found
|
|
7
|
+
- ce2e9e20: ZERO-3640: Add error handling and WalletCompletePage request to FlowPayment
|
|
8
|
+
|
|
9
|
+
## 1.107.0
|
|
11
10
|
|
|
12
11
|
## 1.106.0
|
|
13
12
|
|
|
@@ -46,29 +45,6 @@
|
|
|
46
45
|
### Minor Changes
|
|
47
46
|
|
|
48
47
|
- 69e4cc5: BRDG-14604: Refactor FlowPayment component to optimize flow initialization and cleanup logic
|
|
49
|
-
- d8be48fb: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
|
|
50
|
-
- 8b1d24eb: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
|
|
51
|
-
|
|
52
|
-
## 1.96.0-rc.60
|
|
53
|
-
|
|
54
|
-
## 1.96.0-rc.59
|
|
55
|
-
|
|
56
|
-
## 1.96.0-rc.58
|
|
57
|
-
|
|
58
|
-
## 1.96.0-rc.57
|
|
59
|
-
|
|
60
|
-
## 1.96.0-rc.56
|
|
61
|
-
|
|
62
|
-
### Minor Changes
|
|
63
|
-
|
|
64
|
-
- 69e4cc5: BRDG-14604: Refactor FlowPayment component to optimize flow initialization and cleanup logic
|
|
65
|
-
|
|
66
|
-
## 1.96.0-rc.55
|
|
67
|
-
|
|
68
|
-
### Minor Changes
|
|
69
|
-
|
|
70
|
-
- d8be48fb: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
|
|
71
|
-
- 8b1d24eb: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
|
|
72
48
|
|
|
73
49
|
## 1.95.0
|
|
74
50
|
|
package/package.json
CHANGED
package/src/views/index.tsx
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { checkoutApi } from '@akinon/next/data/client/checkout';
|
|
2
2
|
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
useSetWalletSelectionPageMutation,
|
|
6
|
-
useSetWalletPaymentPageMutation
|
|
7
|
-
} from '@akinon/next/data/client/checkout';
|
|
3
|
+
import { getCookie } from '@akinon/next/utils';
|
|
4
|
+
import { getUrlPathWithLocale } from '@akinon/next/utils/localization';
|
|
8
5
|
import {
|
|
9
6
|
ComponentOptions,
|
|
10
7
|
CustomTranslations,
|
|
@@ -13,7 +10,23 @@ import {
|
|
|
13
10
|
PaymentSessionResponse
|
|
14
11
|
} from '@checkout.com/checkout-web-components';
|
|
15
12
|
import { RootState } from '@theme/redux/store';
|
|
16
|
-
import { useEffect, useRef, useMemo } from 'react';
|
|
13
|
+
import { useEffect, useRef, useMemo, useState } from 'react';
|
|
14
|
+
|
|
15
|
+
const formatErrorMessage = (errors: any): string => {
|
|
16
|
+
if (typeof errors === 'string') {
|
|
17
|
+
return errors;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (Array.isArray(errors)) {
|
|
21
|
+
return errors.join(', ');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (typeof errors === 'object' && errors !== null) {
|
|
25
|
+
return Object.values(errors).join(', ');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return 'An error occurred during payment processing';
|
|
29
|
+
};
|
|
17
30
|
|
|
18
31
|
export default function FlowPayment({
|
|
19
32
|
options
|
|
@@ -65,11 +78,9 @@ export default function FlowPayment({
|
|
|
65
78
|
const lastAmountRef = useRef<string | null>(null);
|
|
66
79
|
const isInitializingRef = useRef<boolean>(false);
|
|
67
80
|
const walletPaymentInitialized = useRef<boolean>(false);
|
|
81
|
+
const [errors, setErrors] = useState<any>(null);
|
|
68
82
|
|
|
69
|
-
|
|
70
|
-
const [setPaymentOption] = useSetPaymentOptionMutation();
|
|
71
|
-
const [setWalletSelectionPage] = useSetWalletSelectionPageMutation();
|
|
72
|
-
const [setWalletPaymentPage] = useSetWalletPaymentPageMutation();
|
|
83
|
+
const environment = options?.environment || 'sandbox';
|
|
73
84
|
|
|
74
85
|
// Memoize the amount to track actual changes
|
|
75
86
|
const currentAmount = useMemo(() => {
|
|
@@ -77,100 +88,104 @@ export default function FlowPayment({
|
|
|
77
88
|
}, [preOrder.total_amount]);
|
|
78
89
|
|
|
79
90
|
const initWalletSelection = async () => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
payment_option: preOrder.payment_option?.pk
|
|
86
|
-
})
|
|
87
|
-
).unwrap();
|
|
91
|
+
const walletSelectionPageResponse = await dispatch(
|
|
92
|
+
checkoutApi.endpoints.setWalletSelectionPage.initiate({
|
|
93
|
+
payment_option: preOrder.payment_option?.pk
|
|
94
|
+
})
|
|
95
|
+
).unwrap();
|
|
88
96
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
97
|
+
const walletPaymentPageContext =
|
|
98
|
+
walletSelectionPageResponse.context_list.find(
|
|
99
|
+
(c) => c.page_name === 'WalletPaymentPage'
|
|
100
|
+
);
|
|
93
101
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
102
|
+
if (!walletPaymentPageContext) {
|
|
103
|
+
return;
|
|
97
104
|
}
|
|
98
|
-
};
|
|
99
105
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}) => {
|
|
105
|
-
// Prevent multiple simultaneous initializations
|
|
106
|
-
if (isInitializingRef.current) {
|
|
107
|
-
return;
|
|
106
|
+
// FIX: Only call setWalletPaymentPage if not already initialized
|
|
107
|
+
if (!walletPaymentInitialized.current) {
|
|
108
|
+
walletPaymentInitialized.current = true;
|
|
109
|
+
dispatch(checkoutApi.endpoints.setWalletPaymentPage.initiate({}));
|
|
108
110
|
}
|
|
111
|
+
};
|
|
109
112
|
|
|
110
|
-
|
|
113
|
+
const handlePaymentSuccess = async (paymentData: any) => {
|
|
114
|
+
setErrors(null);
|
|
111
115
|
|
|
112
116
|
try {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// Check if WalletSelectionPage is available in the context list
|
|
120
|
-
const walletSelectionPageContext =
|
|
121
|
-
paymentOptionResponse.context_list?.find(
|
|
122
|
-
(c) => c.page_name === 'WalletSelectionPage'
|
|
123
|
-
);
|
|
117
|
+
const walletPaymentPageResponse = await dispatch(
|
|
118
|
+
checkoutApi.endpoints.setWalletPaymentPage.initiate({
|
|
119
|
+
payment_token: JSON.stringify(paymentData)
|
|
120
|
+
})
|
|
121
|
+
).unwrap();
|
|
124
122
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
);
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
123
|
+
if (walletPaymentPageResponse.errors) {
|
|
124
|
+
setErrors(walletPaymentPageResponse.errors);
|
|
125
|
+
return;
|
|
131
126
|
}
|
|
132
127
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
128
|
+
if (
|
|
129
|
+
walletPaymentPageResponse.context_list.find(
|
|
130
|
+
(c) => c.page_name === 'WalletCompletePage'
|
|
131
|
+
)
|
|
132
|
+
) {
|
|
133
|
+
const paymentCompleteResponse = await dispatch(
|
|
134
|
+
checkoutApi.endpoints.setWalletCompletePage.initiate(true)
|
|
135
|
+
).unwrap();
|
|
137
136
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
if (paymentCompleteResponse.errors) {
|
|
138
|
+
setErrors(paymentCompleteResponse.errors);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
142
141
|
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
if (
|
|
143
|
+
paymentCompleteResponse.context_list.find(
|
|
144
|
+
(c) => c.page_name === 'ThankYouPage'
|
|
145
|
+
)
|
|
146
|
+
) {
|
|
147
|
+
const redirectUrl = paymentCompleteResponse.context_list.find(
|
|
148
|
+
(c) => c.page_name === 'ThankYouPage'
|
|
149
|
+
)?.page_context.context_data.redirect_url;
|
|
150
|
+
|
|
151
|
+
const redirectUrlWithLocale = `${
|
|
152
|
+
window.location.origin
|
|
153
|
+
}${getUrlPathWithLocale(
|
|
154
|
+
redirectUrl,
|
|
155
|
+
getCookie('pz-locale') || 'en'
|
|
156
|
+
)}`;
|
|
157
|
+
|
|
158
|
+
window.location.href = redirectUrlWithLocale;
|
|
159
|
+
}
|
|
145
160
|
}
|
|
146
|
-
|
|
147
|
-
// Step 3: Set wallet payment page to get updated payment session
|
|
148
|
-
const walletPaymentResponse = await setWalletPaymentPage({}).unwrap();
|
|
149
|
-
|
|
150
|
-
// Step 4: Initialize flow component with fresh payment session
|
|
151
|
-
await initFlowPaymentWebComponents({
|
|
152
|
-
publicKey,
|
|
153
|
-
paymentSession:
|
|
154
|
-
walletPaymentResponse?.pre_order?.context_extras ||
|
|
155
|
-
preOrder.context_extras
|
|
156
|
-
});
|
|
157
161
|
} catch (error) {
|
|
158
|
-
console.error(
|
|
159
|
-
|
|
160
|
-
error
|
|
161
|
-
);
|
|
162
|
-
} finally {
|
|
163
|
-
isInitializingRef.current = false;
|
|
162
|
+
console.error('Error processing payment completion:', error);
|
|
163
|
+
setErrors(error);
|
|
164
164
|
}
|
|
165
165
|
};
|
|
166
166
|
|
|
167
|
+
const handlePaymentError = async (error: any) => {
|
|
168
|
+
console.error('Payment error occurred:', error);
|
|
169
|
+
setErrors(error);
|
|
170
|
+
};
|
|
171
|
+
|
|
167
172
|
const initFlowPaymentWebComponents = async ({
|
|
168
|
-
publicKey
|
|
169
|
-
paymentSession
|
|
173
|
+
publicKey
|
|
170
174
|
}: {
|
|
171
175
|
publicKey: string;
|
|
172
|
-
paymentSession?: any;
|
|
173
176
|
}) => {
|
|
177
|
+
// Prevent multiple simultaneous initializations
|
|
178
|
+
if (isInitializingRef.current) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Check if amount has actually changed
|
|
183
|
+
if (lastAmountRef.current === currentAmount && flowComponentRef.current) {
|
|
184
|
+
return; // Don't recreate component if amount hasn't changed
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
isInitializingRef.current = true;
|
|
188
|
+
|
|
174
189
|
try {
|
|
175
190
|
// Destroy existing flow component if it exists
|
|
176
191
|
if (flowComponentRef.current) {
|
|
@@ -188,7 +203,27 @@ export default function FlowPayment({
|
|
|
188
203
|
container.innerHTML = '';
|
|
189
204
|
}
|
|
190
205
|
|
|
191
|
-
//
|
|
206
|
+
// FIX: Use existing payment session instead of making another API call
|
|
207
|
+
let paymentSession = preOrder.context_extras;
|
|
208
|
+
|
|
209
|
+
// Only get fresh payment data if we don't have a valid session
|
|
210
|
+
if (!paymentSession && lastAmountRef.current !== currentAmount) {
|
|
211
|
+
// Check if another call is already in progress
|
|
212
|
+
if (!walletPaymentInitialized.current) {
|
|
213
|
+
walletPaymentInitialized.current = true;
|
|
214
|
+
const walletPaymentResponse = await dispatch(
|
|
215
|
+
checkoutApi.endpoints.setWalletPaymentPage.initiate({})
|
|
216
|
+
).unwrap();
|
|
217
|
+
paymentSession =
|
|
218
|
+
walletPaymentResponse?.pre_order?.context_extras ||
|
|
219
|
+
preOrder.context_extras;
|
|
220
|
+
} else {
|
|
221
|
+
// Use existing context_extras if another call was already made
|
|
222
|
+
paymentSession = preOrder.context_extras;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Update the last amount reference after getting fresh data
|
|
192
227
|
lastAmountRef.current = currentAmount;
|
|
193
228
|
|
|
194
229
|
const checkout = await loadCheckoutWebComponents({
|
|
@@ -196,15 +231,21 @@ export default function FlowPayment({
|
|
|
196
231
|
environment: options?.environment || 'production',
|
|
197
232
|
locale: options?.locale || 'en',
|
|
198
233
|
translations: options?.translations,
|
|
199
|
-
paymentSession:
|
|
200
|
-
paymentSession || (preOrder.context_extras as PaymentSessionResponse),
|
|
234
|
+
paymentSession: paymentSession as PaymentSessionResponse,
|
|
201
235
|
appearance: options?.appearance,
|
|
202
236
|
componentOptions: options?.componentOptions
|
|
203
237
|
? { flow: options.componentOptions }
|
|
204
238
|
: undefined
|
|
205
239
|
});
|
|
206
240
|
|
|
207
|
-
const flowComponent = checkout.create('flow'
|
|
241
|
+
const flowComponent = checkout.create('flow', {
|
|
242
|
+
onPaymentCompleted: async (_self, paymentResponse) => {
|
|
243
|
+
handlePaymentSuccess(paymentResponse);
|
|
244
|
+
},
|
|
245
|
+
onError: async (_self, error) => {
|
|
246
|
+
handlePaymentError(error);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
208
249
|
flowComponentRef.current = flowComponent;
|
|
209
250
|
|
|
210
251
|
if (container) {
|
|
@@ -212,6 +253,8 @@ export default function FlowPayment({
|
|
|
212
253
|
}
|
|
213
254
|
} catch (error) {
|
|
214
255
|
console.error('Error initializing flow payment components:', error);
|
|
256
|
+
} finally {
|
|
257
|
+
isInitializingRef.current = false;
|
|
215
258
|
}
|
|
216
259
|
};
|
|
217
260
|
|
|
@@ -219,7 +262,6 @@ export default function FlowPayment({
|
|
|
219
262
|
initWalletSelection();
|
|
220
263
|
}, []);
|
|
221
264
|
|
|
222
|
-
// Initial flow component initialization
|
|
223
265
|
useEffect(() => {
|
|
224
266
|
if (
|
|
225
267
|
!preOrder.wallet_method ||
|
|
@@ -230,37 +272,16 @@ export default function FlowPayment({
|
|
|
230
272
|
return;
|
|
231
273
|
}
|
|
232
274
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
publicKey: walletPaymentData.data.public_key
|
|
237
|
-
});
|
|
238
|
-
}
|
|
275
|
+
initFlowPaymentWebComponents({
|
|
276
|
+
publicKey: walletPaymentData.data.public_key
|
|
277
|
+
});
|
|
239
278
|
}, [
|
|
240
279
|
preOrder.wallet_method,
|
|
241
280
|
preOrder.token,
|
|
281
|
+
currentAmount, // Use memoized amount instead of preOrder.total_amount
|
|
242
282
|
walletPaymentData?.data?.public_key
|
|
243
283
|
]);
|
|
244
284
|
|
|
245
|
-
// Handle amount changes by refreshing payment session
|
|
246
|
-
useEffect(() => {
|
|
247
|
-
if (
|
|
248
|
-
!preOrder.wallet_method ||
|
|
249
|
-
!preOrder.token ||
|
|
250
|
-
!walletPaymentData?.data?.public_key ||
|
|
251
|
-
preOrder.wallet_method !== 'checkout_flow'
|
|
252
|
-
) {
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Only refresh if component exists and amount has changed
|
|
257
|
-
if (flowComponentRef.current && lastAmountRef.current !== currentAmount) {
|
|
258
|
-
refreshPaymentSessionAndInitFlow({
|
|
259
|
-
publicKey: walletPaymentData.data.public_key
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
}, [currentAmount]);
|
|
263
|
-
|
|
264
285
|
// Cleanup function when component unmounts
|
|
265
286
|
useEffect(() => {
|
|
266
287
|
return () => {
|
|
@@ -276,5 +297,14 @@ export default function FlowPayment({
|
|
|
276
297
|
};
|
|
277
298
|
}, []);
|
|
278
299
|
|
|
279
|
-
return
|
|
300
|
+
return (
|
|
301
|
+
<div className="flow-payment-container">
|
|
302
|
+
<div id="flow-container"></div>
|
|
303
|
+
{errors && (
|
|
304
|
+
<div className="text-error-100 text-xs mt-5">
|
|
305
|
+
{formatErrorMessage(errors)}
|
|
306
|
+
</div>
|
|
307
|
+
)}
|
|
308
|
+
</div>
|
|
309
|
+
);
|
|
280
310
|
}
|