@@ -40,10 +40,12 @@ export const createAppleGoogleLabel = ( moneiData ) => {
4040 * @return {* } JSX Element
4141 */
4242export const MoneiAppleGoogleContent = ( props ) => {
43- const { useEffect, useRef, useState, createPortal } = wp . element ;
43+ const { useEffect, useRef, useState, createPortal, useMemo, useCallback } =
44+ wp . element ;
4445 const { useSelect } = wp . data ;
4546 const { onPaymentSetup, onCheckoutSuccess } = props . eventRegistration ;
46- const { activePaymentMethod } = props ;
47+ const { activePaymentMethod, emitResponse } = props ;
48+ const { responseTypes, noticeContexts } = emitResponse ;
4749 const moneiData =
4850 props . moneiData ||
4951 // eslint-disable-next-line no-undef
@@ -63,94 +65,25 @@ export const MoneiAppleGoogleContent = ( props ) => {
6365 activePaymentMethod ===
6466 ( props . paymentMethodId || 'monei_apple_google' ) ;
6567
66- const buttonManager = useButtonStateManager ( {
67- isActive,
68- emitResponse : props . emitResponse ,
69- tokenFieldName : 'monei_payment_request_token' ,
70- errorMessage : moneiData . tokenErrorString ,
71- } ) ;
72-
73- /**
74- * Initialize MONEI Payment Request
75- */
76- const initPaymentRequest = ( ) => {
77- // eslint-disable-next-line no-undef
78- if ( typeof monei === 'undefined' || ! monei . PaymentRequest ) {
79- console . error ( 'MONEI SDK is not available' ) ;
80- return ;
81- }
82-
83- const currentTotal = cartTotals ?. total_price
84- ? parseInt ( cartTotals . total_price )
85- : Math . round ( moneiData . total * 100 ) ;
86-
87- lastAmountRef . current = currentTotal ;
88-
89- // Clean up existing instance
90- if ( paymentRequestRef . current ?. close ) {
91- try {
92- paymentRequestRef . current . close ( ) ;
93- } catch ( e ) {
94- // Silent fail
95- }
96- }
97-
98- const container = document . getElementById (
99- 'payment-request-container'
100- ) ;
101- if ( ! container ) {
102- console . error ( 'Payment request container not found' ) ;
103- return ;
104- }
105-
106- // Clear container
107- container . innerHTML = '' ;
108-
109- // eslint-disable-next-line no-undef
110- const paymentRequest = monei . PaymentRequest ( {
111- accountId : moneiData . accountId ,
112- sessionId : moneiData . sessionId ,
113- language : moneiData . language ,
114- amount : currentTotal ,
115- currency : moneiData . currency ,
116- style : moneiData . paymentRequestStyle || { } ,
117- onSubmit ( result ) {
118- if ( result . token ) {
119- setError ( '' ) ;
120- buttonManager . enableCheckout ( result . token ) ;
121- }
122- } ,
123- onError ( error ) {
124- const errorMessage =
125- error . message ||
126- `${ error . status || 'Error' } ${
127- error . statusCode ? `(${ error . statusCode } )` : ''
128- } `;
129- setError ( errorMessage ) ;
130- console . error ( 'Payment Request error:' , error ) ;
131- } ,
132- } ) ;
68+ // Memoize buttonManager config to ensure stability
69+ const buttonManagerConfig = useMemo (
70+ ( ) => ( {
71+ isActive,
72+ emitResponse,
73+ tokenFieldName : 'monei_payment_request_token' ,
74+ errorMessage : moneiData . tokenErrorString ,
75+ } ) ,
76+ [ isActive , emitResponse , moneiData . tokenErrorString ]
77+ ) ;
13378
134- paymentRequest . render ( container ) ;
135- paymentRequestRef . current = paymentRequest ;
136- } ;
79+ const buttonManager = useButtonStateManager ( buttonManagerConfig ) ;
13780
13881 /**
139- * Update the amount in the existing Payment Request instance
82+ * Create or re-render MONEI Payment Request with specified amount
83+ * @param {number } amount - Payment amount in cents
14084 */
141- const updatePaymentRequestAmount = ( ) => {
142- const currentTotal = cartTotals ?. total_price
143- ? parseInt ( cartTotals . total_price )
144- : Math . round ( moneiData . total * 100 ) ;
145-
146- // Only update if amount actually changed
147- if ( currentTotal === lastAmountRef . current ) {
148- return ;
149- }
150-
151- lastAmountRef . current = currentTotal ;
152-
153- if ( paymentRequestRef . current ) {
85+ const createOrRenderPaymentRequest = useCallback (
86+ ( amount ) => {
15487 // Clean up existing instance
15588 if ( paymentRequestRef . current ?. close ) {
15689 try {
@@ -164,6 +97,7 @@ export const MoneiAppleGoogleContent = ( props ) => {
16497 'payment-request-container'
16598 ) ;
16699 if ( ! container ) {
100+ console . error ( 'Payment request container not found' ) ;
167101 return ;
168102 }
169103
@@ -175,7 +109,7 @@ export const MoneiAppleGoogleContent = ( props ) => {
175109 accountId : moneiData . accountId ,
176110 sessionId : moneiData . sessionId ,
177111 language : moneiData . language ,
178- amount : currentTotal ,
112+ amount,
179113 currency : moneiData . currency ,
180114 style : moneiData . paymentRequestStyle || { } ,
181115 onSubmit ( result ) {
@@ -197,8 +131,46 @@ export const MoneiAppleGoogleContent = ( props ) => {
197131
198132 paymentRequest . render ( container ) ;
199133 paymentRequestRef . current = paymentRequest ;
134+ } ,
135+ [ moneiData , setError , buttonManager ]
136+ ) ;
137+
138+ /**
139+ * Initialize MONEI Payment Request
140+ */
141+ const initPaymentRequest = useCallback ( ( ) => {
142+ // eslint-disable-next-line no-undef
143+ if ( typeof monei === 'undefined' || ! monei . PaymentRequest ) {
144+ console . error ( 'MONEI SDK is not available' ) ;
145+ return ;
146+ }
147+
148+ const currentTotal = cartTotals ?. total_price
149+ ? parseInt ( cartTotals . total_price )
150+ : Math . round ( moneiData . total * 100 ) ;
151+
152+ lastAmountRef . current = currentTotal ;
153+
154+ createOrRenderPaymentRequest ( currentTotal ) ;
155+ } , [ cartTotals , moneiData . total , createOrRenderPaymentRequest ] ) ;
156+
157+ /**
158+ * Update the amount in the existing Payment Request instance
159+ */
160+ const updatePaymentRequestAmount = useCallback ( ( ) => {
161+ const currentTotal = cartTotals ?. total_price
162+ ? parseInt ( cartTotals . total_price )
163+ : Math . round ( moneiData . total * 100 ) ;
164+
165+ // Only update if amount actually changed
166+ if ( currentTotal === lastAmountRef . current ) {
167+ return ;
200168 }
201- } ;
169+
170+ lastAmountRef . current = currentTotal ;
171+
172+ createOrRenderPaymentRequest ( currentTotal ) ;
173+ } , [ cartTotals , moneiData . total , createOrRenderPaymentRequest ] ) ;
202174
203175 // Initialize on mount
204176 useEffect ( ( ) => {
@@ -213,7 +185,7 @@ export const MoneiAppleGoogleContent = ( props ) => {
213185 } else if ( ! monei || ! monei . PaymentRequest ) {
214186 console . error ( 'MONEI SDK is not available' ) ;
215187 }
216- } , [ ] ) ;
188+ } , [ initPaymentRequest ] ) ;
217189
218190 // Update amount when cart totals change
219191 useEffect ( ( ) => {
@@ -224,7 +196,7 @@ export const MoneiAppleGoogleContent = ( props ) => {
224196 ) {
225197 updatePaymentRequestAmount ( ) ;
226198 }
227- } , [ cartTotals ] ) ;
199+ } , [ cartTotals , updatePaymentRequestAmount ] ) ;
228200
229201 // Cleanup on unmount
230202 useEffect ( ( ) => {
@@ -246,7 +218,7 @@ export const MoneiAppleGoogleContent = ( props ) => {
246218 } ) ;
247219
248220 return ( ) => unsubscribe ( ) ;
249- } , [ onPaymentSetup ] ) ;
221+ } , [ onPaymentSetup , buttonManager ] ) ;
250222
251223 // Setup checkout success hook
252224 useEffect ( ( ) => {
@@ -257,7 +229,7 @@ export const MoneiAppleGoogleContent = ( props ) => {
257229 // If no paymentId, backend handles everything (redirect flow)
258230 if ( ! paymentDetails ?. paymentId ) {
259231 return {
260- type : props . emitResponse . responseTypes . SUCCESS ,
232+ type : responseTypes . SUCCESS ,
261233 } ;
262234 }
263235
@@ -275,15 +247,15 @@ export const MoneiAppleGoogleContent = ( props ) => {
275247
276248 if ( result . nextAction && result . nextAction . mustRedirect ) {
277249 return {
278- type : props . emitResponse . responseTypes . SUCCESS ,
250+ type : responseTypes . SUCCESS ,
279251 redirectUrl : result . nextAction . redirectUrl ,
280252 } ;
281253 }
282254 if ( result . status === 'FAILED' ) {
283255 const failUrl = new URL ( paymentDetails . failUrl ) ;
284256 failUrl . searchParams . set ( 'status' , 'FAILED' ) ;
285257 return {
286- type : props . emitResponse . responseTypes . SUCCESS ,
258+ type : responseTypes . SUCCESS ,
287259 redirectUrl : failUrl . toString ( ) ,
288260 } ;
289261 } else {
@@ -295,7 +267,7 @@ export const MoneiAppleGoogleContent = ( props ) => {
295267 url . searchParams . set ( 'status' , result . status ) ;
296268
297269 return {
298- type : props . emitResponse . responseTypes . SUCCESS ,
270+ type : responseTypes . SUCCESS ,
299271 redirectUrl : url . toString ( ) ,
300272 } ;
301273 }
@@ -306,17 +278,16 @@ export const MoneiAppleGoogleContent = ( props ) => {
306278 ) ;
307279 setIsConfirming ( false ) ;
308280 return {
309- type : props . emitResponse . responseTypes . ERROR ,
281+ type : responseTypes . ERROR ,
310282 message : error . message || 'Payment confirmation failed' ,
311- messageContext :
312- props . emitResponse . noticeContexts . PAYMENTS ,
283+ messageContext : noticeContexts . PAYMENTS ,
313284 } ;
314285 }
315286 }
316287 ) ;
317288
318289 return ( ) => unsubscribe ( ) ;
319- } , [ onCheckoutSuccess ] ) ;
290+ } , [ onCheckoutSuccess , responseTypes , noticeContexts ] ) ;
320291
321292 return (
322293 < fieldset className = "monei-fieldset monei-payment-request-fieldset" >
0 commit comments