| @@ -18,11 +18,26 @@ | ||
| 18 | 18 | ? window.srfmGetPaymentKey( form, this.blockId ) |
| 19 | 19 | : this.blockId; |
| 20 | 20 | this.paymentInput = paymentBlock.querySelector( '.srfm-payment-input' ); |
| 21 | 21 | this.form = form; |
| 22 | + // AbortController scopes the document-level srfm_payment_type_changed | |
| 23 | + // listener so it is removable on form re-initialization. Pattern mirrors | |
| 24 | + // PAYMENT_UTILITY.listenAmountChanges() in stripe-payment.js. | |
| 25 | + this.abortController = new AbortController(); | |
| 22 | 26 | this.init(); |
| 23 | 27 | } |
| 24 | 28 | |
| 29 | + /** | |
| 30 | + * Tear down listeners owned by this instance. Called by | |
| 31 | + * initializePaymentManagers before replacing this manager so document-level | |
| 32 | + * listeners don't accumulate across form re-initializations. | |
| 33 | + */ | |
| 34 | + destroy() { | |
| 35 | + if ( this.abortController ) { | |
| 36 | + this.abortController.abort(); | |
| 37 | + } | |
| 38 | + } | |
| 39 | + | |
| 25 | 40 | init() { |
| 26 | 41 | // Set initial active method |
| 27 | 42 | this.updateActiveMethod(); |
| 28 | 43 | |
| @@ -64,8 +79,32 @@ | ||
| 64 | 79 | header.addEventListener( 'click', () => { |
| 65 | 80 | this.handleAccordionHeaderClick( header ); |
| 66 | 81 | } ); |
| 67 | 82 | } ); |
| 83 | + | |
| 84 | + // BOTH MODE: forward the payment-type change so gateways can reinit if needed. | |
| 85 | + // stripe-payment.js dispatches `srfm_payment_type_changed` on the document. | |
| 86 | + // We keep a per-instance listener so multiple payment blocks on a page don't | |
| 87 | + // step on each other. Scoped to abortController so it can be torn down on | |
| 88 | + // form re-initialization (see destroy()). | |
| 89 | + document.addEventListener( | |
| 90 | + 'srfm_payment_type_changed', | |
| 91 | + ( event ) => { | |
| 92 | + if ( event?.detail?.blockId !== this.blockId ) { | |
| 93 | + return; | |
| 94 | + } | |
| 95 | + if ( event?.detail?.form !== this.form ) { | |
| 96 | + return; | |
| 97 | + } | |
| 98 | + // Re-broadcast a payment-method event so accordion gateways re-render | |
| 99 | + // for the new mode (Stripe is already handled by reinitForBlock). | |
| 100 | + this.dispatchPaymentMethodEvent( | |
| 101 | + this.getSelectedMethod(), | |
| 102 | + this.form | |
| 103 | + ); | |
| 104 | + }, | |
| 105 | + { signal: this.abortController.signal } | |
| 106 | + ); | |
| 68 | 107 | } |
| 69 | 108 | |
| 70 | 109 | /** |
| 71 | 110 | * Handle accordion header click |
| @@ -314,8 +353,12 @@ | ||
| 314 | 353 | // Store payment manager instance |
| 315 | 354 | if ( ! window.srfmPaymentManagers ) { |
| 316 | 355 | window.srfmPaymentManagers = {}; |
| 317 | 356 | } |
| 357 | + | |
| 358 | + // Tear down the previous manager (if any) so its document-level | |
| 359 | + // srfm_payment_type_changed listener is removed before we replace it. | |
| 360 | + window.srfmPaymentManagers[ compositeKey ]?.destroy?.(); | |
| 318 | 361 | |
| 319 | 362 | window.srfmPaymentManagers[ compositeKey ] = new PaymentManager( |
| 320 | 363 | paymentBlock, |
| 321 | 364 | form |