@akinon/pz-flow-payment 1.99.0-rc.70 → 1.99.0-snapshot-ZERO-3640-20250919140314

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