PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 3.3.0
ShopBuilder – WooCommerce Builder For Elementor v3.3.0
3.4.2 3.4.1 3.4.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 All 63 releases
shopbuilder / app / Controllers / PluginsSupport / PayPalPPCPSupport.php

PayPalPPCPSupport.php in ShopBuilder – WooCommerce Builder For Elementor 3.3.0, at app/Controllers/PluginsSupport/PayPalPPCPSupport.php

138 lines 3.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WooCommerce PayPal Payments (PPCP) compatibility support.
4 *
5 * Prevents checkout issues caused by dynamic script injection in the
6 * payment fragment and ensures Advanced Card Processing fields
7 * re-initialize properly after AJAX checkout updates.
8 *
9 * @package RadiusTheme\SB
10 */
11
12 namespace RadiusTheme\SB\Controllers\PluginsSupport;
13
14 use RadiusTheme\SB\Helpers\BuilderFns;
15 use RadiusTheme\SB\Traits\SingletonTrait;
16
17 // Do not allow directly accessing this file.
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit( 'This script cannot be accessed directly.' );
20 }
21
22 /**
23 * WooCommerce PayPal Payments compatibility class.
24 *
25 * PayPal PPCP injects dynamic script tags (SDK configuration, nonces,
26 * client tokens) into the .woocommerce-checkout-payment fragment on
27 * every update_order_review AJAX response. This causes the fragment
28 * HTML to change each request, triggering WooCommerce to replace the
29 * DOM and potentially creating infinite update loops or causing
30 * Advanced Card Processing hosted fields to lose their state.
31 *
32 * This class strips those dynamic scripts from the fragment and moves
33 * them to a separate fragment key so the payment HTML stays stable
34 * between requests. It also fires a custom event so PPCP card fields
35 * can re-initialize after DOM replacement.
36 */
37 class PayPalPPCPSupport {
38
39 /**
40 * SingletonTrait.
41 */
42 use SingletonTrait;
43
44 /**
45 * Constructor.
46 */
47 private function __construct() {
48 add_filter(
49 'woocommerce_update_order_review_fragments',
50 [ $this, 'stabilize_payment_fragment' ],
51 999
52 );
53
54 add_action( 'wp_footer', [ $this, 'render_ppcp_reinit_script' ] );
55 }
56
57 /**
58 * Stabilize the payment method fragment.
59 *
60 * Extracts inline and external script tags from the payment fragment
61 * so its HTML stays identical between AJAX requests. The scripts are
62 * moved to a separate fragment that JS can execute after DOM
63 * replacement without triggering another update cycle.
64 *
65 * @param array $fragments Checkout AJAX fragments.
66 *
67 * @return array Modified fragments.
68 */
69 public function stabilize_payment_fragment( $fragments ) {
70 $key = '.woocommerce-checkout-payment';
71
72 if ( empty( $fragments[ $key ] ) ) {
73 return $fragments;
74 }
75
76 $html = $fragments[ $key ];
77
78 // Extract all <script> tags from the payment fragment.
79 $scripts = '';
80
81 $html = preg_replace_callback(
82 '#<script\b[^>]*>.*?</script>#is',
83 static function ( $matches ) use ( &$scripts ) {
84 $scripts .= $matches[0];
85 return '';
86 },
87 $html
88 );
89
90 // Set the cleaned (stable) HTML back as the fragment.
91 $fragments[ $key ] = $html;
92
93 // Pass extracted scripts in a separate fragment for JS execution.
94 if ( ! empty( $scripts ) ) {
95 $fragments['.rtsb-checkout-payment-scripts'] = '<div class="rtsb-checkout-payment-scripts" style="display:none;">' . $scripts . '</div>';
96 }
97
98 return $fragments;
99 }
100
101 /**
102 * Render inline script to re-initialize PPCP card fields after checkout update.
103 *
104 * PayPal Advanced Card Processing renders hosted card field iframes
105 * that are destroyed when WooCommerce replaces the payment fragment.
106 * This script triggers PPCP's native re-render mechanism after each
107 * updated_checkout event.
108 *
109 * @return void
110 */
111 public function render_ppcp_reinit_script() {
112 if ( ! BuilderFns::is_checkout() ) {
113 return;
114 }
115 ?>
116 <script id="rtsb-ppcp-reinit">
117 (function($) {
118 if (typeof $ === 'undefined') return;
119
120 $(document.body).on('updated_checkout', function() {
121 // Trigger PPCP payment method change to force card fields re-render.
122 var $selected = $('input[name="payment_method"]:checked');
123 if ($selected.length && $selected.val().indexOf('ppcp') !== -1) {
124 $selected.trigger('click');
125 }
126
127 // Fire custom event for PPCP SDK re-initialization.
128 $(document.body).trigger('rtsb_ppcp_checkout_updated');
129
130 // PPCP listens for payment_method_selected to render buttons/fields.
131 $(document.body).trigger('payment_method_selected');
132 });
133 })(jQuery);
134 </script>
135 <?php
136 }
137 }
138