@akinon/pz-flow-payment 1.108.0-rc.87 → 1.108.0-rc.9

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 CHANGED
@@ -1,13 +1,48 @@
1
1
  # @akinon/pz-flow-payment
2
2
 
3
- ## 1.108.0-rc.87
3
+ ## 1.108.0-rc.9
4
+
5
+ ## 1.108.0-rc.8
6
+
7
+ ### Minor Changes
8
+
9
+ - f85464e9: ZERO-3640: Fix API call to use hyphenated key for payment ID in FlowPayment component
10
+
11
+ ## 1.108.0-rc.7
12
+
13
+ ## 1.108.0-rc.6
14
+
15
+ ## 1.108.0-rc.5
4
16
 
5
17
  ### Minor Changes
6
18
 
7
- - 1b9e9bee: BRDG-14604: Refactor FlowPayment component to utilize RTK Query mutations for payment options and wallet selection, enhancing session management and initialization logic
8
- - d8be48fb: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
9
- - 8b1d24eb: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
10
- - 33d4d0c9: ZERO-3615: remove custom not found
19
+ - 31a2d35: ZERO-3640: Refactor checkout API call to include useFormData option; update FlowPayment component for improved error handling and code readability
20
+
21
+ ## 1.108.0-rc.4
22
+
23
+ ### Minor Changes
24
+
25
+ - d8883ce: ZERO-3640: Refactor wallet completion handling to accept additional parameters; update related API calls
26
+
27
+ ## 1.108.0-rc.3
28
+
29
+ ### Minor Changes
30
+
31
+ - e00b4a8: ZERO-3640: Refactor wallet selection initialization and error handling; streamline payment session management
32
+
33
+ ## 1.108.0-rc.2
34
+
35
+ ## 1.108.0-rc.1
36
+
37
+ ### Minor Changes
38
+
39
+ - 21d88c1: ZERO-3640: Remove redundant API calls
40
+
41
+ ## 1.108.0-rc.0
42
+
43
+ ### Minor Changes
44
+
45
+ - ce2e9e20: ZERO-3640: Add error handling and WalletCompletePage request to FlowPayment
11
46
 
12
47
  ## 1.107.0
13
48
 
@@ -48,29 +83,6 @@
48
83
  ### Minor Changes
49
84
 
50
85
  - 69e4cc5: BRDG-14604: Refactor FlowPayment component to optimize flow initialization and cleanup logic
51
- - d8be48fb: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
52
- - 8b1d24eb: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
53
-
54
- ## 1.96.0-rc.60
55
-
56
- ## 1.96.0-rc.59
57
-
58
- ## 1.96.0-rc.58
59
-
60
- ## 1.96.0-rc.57
61
-
62
- ## 1.96.0-rc.56
63
-
64
- ### Minor Changes
65
-
66
- - 69e4cc5: BRDG-14604: Refactor FlowPayment component to optimize flow initialization and cleanup logic
67
-
68
- ## 1.96.0-rc.55
69
-
70
- ### Minor Changes
71
-
72
- - d8be48fb: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
73
- - 8b1d24eb: ZERO-3422: Update fetch method to use dynamic request method in wallet complete redirection middleware
74
86
 
75
87
  ## 1.95.0
76
88
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@akinon/pz-flow-payment",
3
3
  "license": "MIT",
4
- "version": "1.108.0-rc.87",
4
+ "version": "1.108.0-rc.9",
5
5
  "main": "src/index.tsx",
6
6
  "dependencies": {
7
7
  "@checkout.com/checkout-web-components": "0.7.0-beta"
@@ -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
- useSetPaymentOptionMutation,
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
@@ -64,12 +77,9 @@ export default function FlowPayment({
64
77
  const flowComponentRef = useRef<any>(null);
65
78
  const lastAmountRef = useRef<string | null>(null);
66
79
  const isInitializingRef = useRef<boolean>(false);
67
- const walletPaymentInitialized = useRef<boolean>(false);
68
-
69
- // RTK Query mutations
70
- const [setPaymentOption] = useSetPaymentOptionMutation();
71
- const [setWalletSelectionPage] = useSetWalletSelectionPageMutation();
72
- const [setWalletPaymentPage] = useSetWalletPaymentPageMutation();
80
+ const [errors, setErrors] = useState<any>(null);
81
+ const [walletSelectionReady, setWalletSelectionReady] =
82
+ useState<boolean>(false);
73
83
 
74
84
  // Memoize the amount to track actual changes
75
85
  const currentAmount = useMemo(() => {
@@ -77,100 +87,104 @@ export default function FlowPayment({
77
87
  }, [preOrder.total_amount]);
78
88
 
79
89
  const initWalletSelection = async () => {
80
- if (!walletPaymentInitialized.current) {
81
- walletPaymentInitialized.current = true;
82
-
83
- const walletSelectionPageResponse = await dispatch(
84
- checkoutApi.endpoints.setWalletSelectionPage.initiate({
85
- payment_option: preOrder.payment_option?.pk
86
- })
87
- ).unwrap();
90
+ const walletSelectionPageResponse = await dispatch(
91
+ checkoutApi.endpoints.setWalletSelectionPage.initiate({
92
+ payment_option: preOrder.payment_option?.pk
93
+ })
94
+ ).unwrap();
88
95
 
89
- const walletPaymentPageContext =
90
- walletSelectionPageResponse.context_list.find(
91
- (c) => c.page_name === 'WalletPaymentPage'
92
- );
96
+ const walletPaymentPageContext =
97
+ walletSelectionPageResponse.context_list.find(
98
+ (c) => c.page_name === 'WalletPaymentPage'
99
+ );
93
100
 
94
- if (walletPaymentPageContext) {
95
- dispatch(checkoutApi.endpoints.setWalletPaymentPage.initiate({}));
96
- }
101
+ if (!walletPaymentPageContext) {
102
+ setErrors(
103
+ 'Payment method not available. Please try a different payment option.'
104
+ );
105
+ setWalletSelectionReady(false);
106
+ return;
97
107
  }
98
- };
99
108
 
100
- const refreshPaymentSessionAndInitFlow = async ({
101
- publicKey
102
- }: {
103
- publicKey: string;
104
- }) => {
105
- // Prevent multiple simultaneous initializations
106
- if (isInitializingRef.current) {
109
+ // Initialize wallet payment page to get payment session data
110
+ const walletPaymentPageResponse = await dispatch(
111
+ checkoutApi.endpoints.setWalletPaymentPage.initiate({
112
+ payment_method: 'checkout_flow',
113
+ wallet_method: preOrder.wallet_method
114
+ })
115
+ ).unwrap();
116
+
117
+ if (walletPaymentPageResponse.errors) {
118
+ setErrors(walletPaymentPageResponse.errors);
119
+ setWalletSelectionReady(false);
107
120
  return;
108
121
  }
109
122
 
110
- isInitializingRef.current = true;
123
+ setWalletSelectionReady(true);
124
+ };
111
125
 
112
- try {
113
- // Step 1: Re-set the current payment option to refresh the session with new amount
114
- if (preOrder.payment_option?.pk) {
115
- const paymentOptionResponse = await setPaymentOption(
116
- preOrder.payment_option.pk
117
- ).unwrap();
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
- );
124
-
125
- if (!walletSelectionPageContext) {
126
- console.warn(
127
- 'WalletSelectionPage not found in payment option response context list'
128
- );
129
- return;
130
- }
131
- }
126
+ const handlePaymentSuccess = async (paymentData: any) => {
127
+ setErrors(null);
132
128
 
133
- // Step 2: Set wallet selection page
134
- const walletSelectionResponse = await setWalletSelectionPage({
135
- payment_option: preOrder.payment_option?.pk
136
- }).unwrap();
129
+ try {
130
+ const ckoPaymentId = paymentData?.id;
131
+ const isSuccess = paymentData?.status === 'Approved';
137
132
 
138
- const walletPaymentPageContext =
139
- walletSelectionResponse.context_list?.find(
140
- (c) => c.page_name === 'WalletPaymentPage'
141
- );
133
+ const paymentCompleteResponse = await dispatch(
134
+ checkoutApi.endpoints.setWalletCompletePage.initiate({
135
+ success: isSuccess,
136
+ 'cko-payment-id': ckoPaymentId
137
+ })
138
+ ).unwrap();
142
139
 
143
- if (!walletPaymentPageContext) {
140
+ if (paymentCompleteResponse.errors) {
141
+ setErrors(paymentCompleteResponse.errors);
144
142
  return;
145
143
  }
146
144
 
147
- // Step 3: Set wallet payment page to get updated payment session
148
- const walletPaymentResponse = await setWalletPaymentPage({}).unwrap();
145
+ if (
146
+ paymentCompleteResponse.context_list.find(
147
+ (c) => c.page_name === 'ThankYouPage'
148
+ )
149
+ ) {
150
+ const redirectUrl = paymentCompleteResponse.context_list.find(
151
+ (c) => c.page_name === 'ThankYouPage'
152
+ )?.page_context.context_data.redirect_url;
149
153
 
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
- });
154
+ const redirectUrlWithLocale = `${
155
+ window.location.origin
156
+ }${getUrlPathWithLocale(redirectUrl, getCookie('pz-locale') || 'en')}`;
157
+
158
+ window.location.href = redirectUrlWithLocale;
159
+ }
157
160
  } catch (error) {
158
- console.error(
159
- 'Error refreshing payment session and initializing flow:',
160
- error
161
- );
162
- } finally {
163
- isInitializingRef.current = false;
161
+ console.error('Error processing payment completion:', error);
162
+ setErrors(error);
164
163
  }
165
164
  };
166
165
 
166
+ const handlePaymentError = async (error: any) => {
167
+ console.error('Payment error occurred:', error);
168
+ setErrors(error);
169
+ };
170
+
167
171
  const initFlowPaymentWebComponents = async ({
168
- publicKey,
169
- paymentSession
172
+ publicKey
170
173
  }: {
171
174
  publicKey: string;
172
- paymentSession?: any;
173
175
  }) => {
176
+ // Prevent multiple simultaneous initializations
177
+ if (isInitializingRef.current) {
178
+ return;
179
+ }
180
+
181
+ // Check if amount has actually changed
182
+ if (lastAmountRef.current === currentAmount && flowComponentRef.current) {
183
+ return; // Don't recreate component if amount hasn't changed
184
+ }
185
+
186
+ isInitializingRef.current = true;
187
+
174
188
  try {
175
189
  // Destroy existing flow component if it exists
176
190
  if (flowComponentRef.current) {
@@ -188,7 +202,18 @@ export default function FlowPayment({
188
202
  container.innerHTML = '';
189
203
  }
190
204
 
191
- // Update the last amount reference
205
+ // FIX: Use existing payment session from preOrder.context_extras
206
+ let paymentSession = preOrder.context_extras;
207
+
208
+ // If we don't have payment session data, we need to wait for it
209
+ if (!paymentSession) {
210
+ console.warn(
211
+ 'FlowPayment: Payment session not available yet, component will retry when data is ready'
212
+ );
213
+ return;
214
+ }
215
+
216
+ // Update the last amount reference after getting fresh data
192
217
  lastAmountRef.current = currentAmount;
193
218
 
194
219
  const checkout = await loadCheckoutWebComponents({
@@ -196,15 +221,21 @@ export default function FlowPayment({
196
221
  environment: options?.environment || 'production',
197
222
  locale: options?.locale || 'en',
198
223
  translations: options?.translations,
199
- paymentSession:
200
- paymentSession || (preOrder.context_extras as PaymentSessionResponse),
224
+ paymentSession: paymentSession as PaymentSessionResponse,
201
225
  appearance: options?.appearance,
202
226
  componentOptions: options?.componentOptions
203
227
  ? { flow: options.componentOptions }
204
228
  : undefined
205
229
  });
206
230
 
207
- const flowComponent = checkout.create('flow');
231
+ const flowComponent = checkout.create('flow', {
232
+ onPaymentCompleted: async (_self, paymentResponse) => {
233
+ handlePaymentSuccess(paymentResponse);
234
+ },
235
+ onError: async (_self, error) => {
236
+ handlePaymentError(error);
237
+ }
238
+ });
208
239
  flowComponentRef.current = flowComponent;
209
240
 
210
241
  if (container) {
@@ -212,6 +243,8 @@ export default function FlowPayment({
212
243
  }
213
244
  } catch (error) {
214
245
  console.error('Error initializing flow payment components:', error);
246
+ } finally {
247
+ isInitializingRef.current = false;
215
248
  }
216
249
  };
217
250
 
@@ -219,48 +252,29 @@ export default function FlowPayment({
219
252
  initWalletSelection();
220
253
  }, []);
221
254
 
222
- // Initial flow component initialization
223
255
  useEffect(() => {
224
256
  if (
225
257
  !preOrder.wallet_method ||
226
- !preOrder.token ||
227
258
  !walletPaymentData?.data?.public_key ||
228
- preOrder.wallet_method !== 'checkout_flow'
259
+ preOrder.wallet_method !== 'checkout_flow' ||
260
+ !walletSelectionReady ||
261
+ !preOrder.context_extras
229
262
  ) {
230
263
  return;
231
264
  }
232
265
 
233
- // Only initialize if component doesn't exist yet (initial mount)
234
- if (!flowComponentRef.current) {
235
- initFlowPaymentWebComponents({
236
- publicKey: walletPaymentData.data.public_key
237
- });
238
- }
266
+ initFlowPaymentWebComponents({
267
+ publicKey: walletPaymentData.data.public_key
268
+ });
239
269
  }, [
240
270
  preOrder.wallet_method,
241
271
  preOrder.token,
242
- walletPaymentData?.data?.public_key
272
+ currentAmount, // Use memoized amount instead of preOrder.total_amount
273
+ walletPaymentData?.data?.public_key,
274
+ walletSelectionReady,
275
+ preOrder.context_extras // Add payment session dependency
243
276
  ]);
244
277
 
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
278
  // Cleanup function when component unmounts
265
279
  useEffect(() => {
266
280
  return () => {
@@ -271,10 +285,17 @@ export default function FlowPayment({
271
285
  console.warn('Error destroying flow component on unmount:', error);
272
286
  }
273
287
  }
274
- // Reset initialization flag on unmount
275
- walletPaymentInitialized.current = false;
276
288
  };
277
289
  }, []);
278
290
 
279
- return <div id="flow-container" className="flow-payment-container"></div>;
291
+ return (
292
+ <div className="flow-payment-container">
293
+ <div id="flow-container"></div>
294
+ {errors && (
295
+ <div className="text-error-100 text-xs mt-5">
296
+ {formatErrorMessage(errors)}
297
+ </div>
298
+ )}
299
+ </div>
300
+ );
280
301
  }