PluginProbe
PayPlug for WooCommerce (Official) / 3.1.0
PayPlug for WooCommerce (Official) v3.1.0
3.1.0 3.0.0 2.18.0 1.0.17 1.0.18 1.0.19 1.0.20 1.0.21 1.0.22 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.1.0 1.10.0 1.10.1 1.2.1 1.2.10 1.2.11 1.2.2 1.2.3 1.2.4 1.2.5 All 102 releases
← All changes | src/Gateway/PPRO/Scalapay.php +126 -1 2.18.0 → 3.1.0 View file →
@@ -12,10 +12,20 @@
12 12 class Scalapay extends PayplugGenericGateway
13 13 {
14 14 protected $allowed_country_codes = [];
15 15 protected $enable_refund = true;
16 - const ENABLE_ON_TEST_MODE = false;
16 + public const ENABLE_ON_TEST_MODE = false;
17 17
18 + /**
19 + * Last-resort fallback bounds, in cents. Used only when neither the account's live API
20 + * amounts nor the stored scalapay.default_amounts are available - e.g. an options array
21 + * that predates that key, since Configuration loads the raw stored options without
22 + * merging the schema defaults. Mirrors Configuration::$expected_fields'
23 + * payment_methods.configuration.scalapay.default_amounts.
24 + */
25 + public const DEFAULT_MIN_AMOUNT = 500;
26 + public const DEFAULT_MAX_AMOUNT = 400000;
27 +
18 28 public function __construct()
19 29 {
20 30 parent::__construct();
21 31
@@ -36,8 +46,123 @@
36 46 $this->enabled = 'no';
37 47 }
38 48
39 49 add_action('woocommerce_order_item_add_action_buttons', [$this, 'refund_not_available']);
50 + add_action('woocommerce_after_checkout_validation', [$this, 'validate_checkout'], 10);
51 + }
52 +
53 + /**
54 + * Server-side guard mirroring check_gateway(): a stale/bypassed client could still submit
55 + * an order outside the merchant's configured (or account-authorized) amount range.
56 + *
57 + * @throws \Exception
58 + */
59 + public function validate_checkout(): void
60 + {
61 + $posted_data = $this->get_post_data();
62 +
63 + if (($posted_data['payment_method'] ?? '') !== $this->id) {
64 + return;
65 + }
66 +
67 + [$min_cents, $max_cents] = self::effective_bounds(
68 + $this->settings['payment_methods']['configuration']['scalapay'] ?? [],
69 + $this->settings['payment_methods']['permissions']['scalapay']['amounts'] ?? '{}'
70 + );
71 +
72 + // Compared in euros rather than multiplying the total by 100: a float cents value
73 + // such as 19.99 * 100 => 1998.9999999999998 can fall the wrong side of an exact
74 + // boundary, whereas dividing the (integer) bound by 100 yields the same double as
75 + // the parsed total. Same direction as PayplugGatewayOney3x's amount checks.
76 + $order_total = (float) $this->get_order_total();
77 + if ($order_total < $min_cents / 100 || $order_total > $max_cents / 100) {
78 + throw new \Exception(sprintf(__('The total amount of your order should be between %s€ and %s€ to pay with Scalapay.', 'payplug'), $min_cents / 100, $max_cents / 100));
79 + }
80 + }
81 +
82 + /**
83 + * Remove Scalapay from the available gateways if the order/cart amount is outside the
84 + * merchant's configured bounds (or the account's authorized range, if unconfigured).
85 + *
86 + * @param array $gateways
87 + *
88 + * @return array
89 + */
90 + public function check_gateway($gateways)
91 + {
92 + // WC()->cart is only populated on frontend requests (WooCommerce::is_request('frontend')
93 + // excludes wp-admin and REST requests) but this filter also fires outside that context -
94 + // e.g. the WC Settings > Payments screen calling get_available_payment_gateways() via
95 + // PaymentsController::store_has_enabled_gateways(). get_order_total() dereferences
96 + // WC()->cart->total unconditionally, which would throw a PHP warning on a null cart.
97 + if (isset($gateways[$this->id]) && $gateways[$this->id]->id == $this->id && null !== WC()->cart) {
98 + [$min_cents, $max_cents] = self::effective_bounds(
99 + $this->settings['payment_methods']['configuration']['scalapay'] ?? [],
100 + $this->settings['payment_methods']['permissions']['scalapay']['amounts'] ?? '{}'
101 + );
102 +
103 + // Euro comparison, for the float-precision reason spelled out in validate_checkout().
104 + $order_total = (float) $this->get_order_total();
105 + if ($order_total < $min_cents / 100 || $order_total > $max_cents / 100) {
106 + unset($gateways[$this->id]);
107 + }
108 + }
109 +
110 + return parent::check_gateway($gateways);
111 + }
112 +
113 + /**
114 + * The account's PayPlug-authorized range, in cents: the live API amounts when available,
115 + * otherwise the stored default_amounts fallback. This is the ceiling the merchant may
116 + * only narrow, never widen - shared with the BO display (PaymentMethods.php) and the
117 + * save-time validator's bounds (Ajax.php) so the three can't silently drift apart.
118 + *
119 + * @param array $config payment_methods.configuration.scalapay (default_amounts)
120 + * @param string $api_amounts payment_methods.permissions.scalapay.amounts, JSON-encoded
121 + * {"min":{"EUR":n},"max":{"EUR":n}}
122 + *
123 + * @return array{0: int, 1: int} [min_cents, max_cents]
124 + */
125 + public static function authorized_bounds($config, $api_amounts)
126 + {
127 + $authorized = json_decode($api_amounts ?? '{}', true);
128 + $min = is_array($authorized) ? ($authorized['min']['EUR'] ?? null) : null;
129 + $max = is_array($authorized) ? ($authorized['max']['EUR'] ?? null) : null;
130 +
131 + if (null === $min || null === $max) {
132 + $fallback = json_decode($config['default_amounts'] ?? '', true);
133 + $fallback = is_array($fallback) ? $fallback : [];
134 + $min = $min ?? ($fallback['min'] ?? self::DEFAULT_MIN_AMOUNT);
135 + $max = $max ?? ($fallback['max'] ?? self::DEFAULT_MAX_AMOUNT);
136 + }
137 +
138 + return [(int) $min, (int) $max];
139 + }
140 +
141 + /**
142 + * The merchant's configured min/max (when set) narrowed against the account's live
143 + * PayPlug-authorized range - shared with the BO settings display (PaymentMethods.php)
144 + * so both stay in sync by construction.
145 + *
146 + * @param array $config payment_methods.configuration.scalapay (custom_amounts/default_amounts)
147 + * @param string $api_amounts payment_methods.permissions.scalapay.amounts, JSON-encoded
148 + * {"min":{"EUR":n},"max":{"EUR":n}}
149 + *
150 + * @return array{0: int, 1: int} [min_cents, max_cents]
151 + */
152 + public static function effective_bounds($config, $api_amounts)
153 + {
154 + $custom_amounts = json_decode($config['custom_amounts'] ?? '{}', true);
155 + $custom_amounts = is_array($custom_amounts) ? $custom_amounts : [];
156 +
157 + [$authorized_min, $authorized_max] = self::authorized_bounds($config, $api_amounts);
158 +
159 + // !empty() rather than isset(): a stored 0 is not a meaningful Scalapay threshold,
160 + // so it falls back to the account bound instead of being read as an override.
161 + $min = !empty($custom_amounts['min']) ? (int) $custom_amounts['min'] : $authorized_min;
162 + $max = !empty($custom_amounts['max']) ? (int) $custom_amounts['max'] : $authorized_max;
163 +
164 + return [$min, $max];
40 165 }
41 166
42 167 /**
43 168 * @param \WC_Order $order