PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.15
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.15
6.2.15 6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 All 197 releases
fluentform / app / Modules / Payments / PaymentHelper.php

PaymentHelper.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 6.2.15, at app/Modules/Payments/PaymentHelper.php

1,290 lines 48.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentForm\App\Modules\Payments;
4
5 if (!defined('ABSPATH')) {
6 exit; // Exit if accessed directly.
7 }
8
9 use FluentForm\App\Helpers\Helper;
10 use FluentForm\App\Modules\Form\FormFieldsParser;
11 use FluentForm\App\Services\FormBuilder\ShortCodeParser;
12 use FluentForm\Framework\Helpers\ArrayHelper;
13 use FluentForm\App\Services\Form\SubmissionHandlerService;
14
15 class PaymentHelper
16 {
17 public static function getFormCurrency($formId)
18 {
19 $settings = self::getFormSettings($formId, 'public');
20 return $settings['currency'];
21 }
22
23 public static function formatMoney($amountInCents, $currency)
24 {
25 $currencySettings = self::getCurrencyConfig(false, $currency);
26 $symbol = \html_entity_decode($currencySettings['currency_sign']);
27 $position = $currencySettings['currency_sign_position'];
28 $decimalSeparator = '.';
29 $thousandSeparator = ',';
30 if ($currencySettings['currency_separator'] != 'dot_comma') {
31 $decimalSeparator = ',';
32 $thousandSeparator = '.';
33 }
34 $decimalPoints = 2;
35 if ((int) round($amountInCents) % 100 == 0 && $currencySettings['decimal_points'] == 0) {
36 $decimalPoints = 0;
37 }
38
39 $amount = number_format($amountInCents / 100, $decimalPoints, $decimalSeparator, $thousandSeparator);
40
41 if ('left' === $position) {
42 return $symbol . $amount;
43 } elseif ('left_space' === $position) {
44 return $symbol . ' ' . $amount;
45 } elseif ('right' === $position) {
46 return $amount . $symbol;
47 } elseif ('right_space' === $position) {
48 return $amount . ' ' . $symbol;
49 }
50 return $amount;
51 }
52
53 /**
54 * Check payment settings available or not
55 * @return bool
56 */
57 public static function hasPaymentSettings()
58 {
59 return !!get_option('__fluentform_payment_module_settings');
60 }
61
62 public static function getFormSettings($formId, $scope = 'public')
63 {
64 static $cachedSettings = [];
65
66 if (isset($cachedSettings[$scope . '_' . $formId])) {
67 return $cachedSettings[$scope . '_' . $formId];
68 }
69
70
71 $defaults = [
72 'currency' => '',
73 'push_meta_to_stripe' => 'no',
74 'receipt_email' => self::getFormInput($formId,'input_email'),
75 'customer_name' => self::getFormInput($formId,'input_name'),
76 'customer_address' => self::getFormInput($formId,'address'),
77 'transaction_type' => 'product',
78 'stripe_checkout_methods' => ['card'],
79 'stripe_meta_data' => [
80 [
81 'item_value' => '',
82 'label' => ''
83 ]
84 ],
85 'stripe_account_type' => 'global',
86 'disable_stripe_payment_receipt' => 'no',
87 'stripe_custom_config' => [
88 'payment_mode' => 'live',
89 'publishable_key' => '',
90 'secret_key' => ''
91 ],
92 'custom_paypal_id' => '',
93 'custom_paypal_mode' => 'live',
94 'paypal_account_type' => 'global'
95 ];
96 $settings = Helper::getFormMeta($formId, '_payment_settings', []);
97 $settings = wp_parse_args($settings, $defaults);
98 if (empty($settings['receipt_email'])) {
99 $settings['receipt_email'] = self::getFormInput($formId, 'input_email');
100 }
101 if (empty($settings['customer_name'])) {
102 $name = self::getFormInput($formId, 'input_name');
103 if (!empty($name)) {
104 $settings['customer_name'] = sprintf('{inputs.%s}', $name);
105 }
106 }
107 if (empty($settings['customer_address'])) {
108 $settings['customer_address'] = self::getFormInput($formId, 'address');
109 }
110
111 $globalSettings = self::getPaymentSettings();
112
113 if (!$settings['currency']) {
114 $settings['currency'] = $globalSettings['currency'];
115 }
116
117 if ($scope == 'public') {
118 $settings = wp_parse_args($settings, $globalSettings);
119 }
120
121
122 $cachedSettings[$scope . '_' . $formId] = $settings;
123
124 return $settings;
125
126 }
127
128 public static function getCurrencyConfig($formId = false, $currency = false)
129 {
130 if ($formId) {
131 $settings = self::getFormSettings($formId, 'public');
132 } else {
133 $settings = self::getPaymentSettings();
134 }
135
136 if ($currency) {
137 $settings['currency'] = $currency;
138 }
139
140 $settings = ArrayHelper::only($settings, ['currency', 'currency_sign_position', 'currency_separator', 'decimal_points']);
141
142 $settings['currency_sign'] = self::getCurrencySymbol($settings['currency']);
143 return $settings;
144 }
145
146 public static function getPaymentSettings()
147 {
148 static $paymentSettings;
149 if ($paymentSettings) {
150 return $paymentSettings;
151 }
152
153 $paymentSettings = get_option('__fluentform_payment_module_settings');
154 $defaults = [
155 'status' => 'no',
156 'currency' => 'USD',
157 'currency_sign_position' => 'left',
158 'currency_separator' => 'dot_comma',
159 'decimal_points' => "2",
160 'business_name' => '',
161 'business_logo' => '',
162 'business_address' => '',
163 'debug_log' => 'no',
164 'all_payments_page_id' => '',
165 'receipt_page_id' => '',
166 'user_can_manage_subscription' => 'yes'
167 ];
168
169 $paymentSettings = wp_parse_args($paymentSettings, $defaults);
170
171 return $paymentSettings;
172 }
173
174 public static function updatePaymentSettings($data)
175 {
176 $existingSettings = self::getPaymentSettings();
177 $settings = wp_parse_args($data, $existingSettings);
178 update_option('__fluentform_payment_module_settings', $settings, 'yes');
179
180 return self::getPaymentSettings();
181 }
182
183 /**
184 * https://support.stripe.com/questions/which-currencies-does-stripe-support
185 */
186 public static function getCurrencies()
187 {
188 $currencies = [
189 'AED' => __('United Arab Emirates Dirham', 'fluentform'),
190 'AFN' => __('Afghan Afghani', 'fluentform'),
191 'ALL' => __('Albanian Lek', 'fluentform'),
192 'AMD' => __('Armenian Dram', 'fluentform'),
193 'ANG' => __('Netherlands Antillean Gulden', 'fluentform'),
194 'AOA' => __('Angolan Kwanza', 'fluentform'),
195 'ARS' => __('Argentine Peso','fluentform'), // non amex
196 'AUD' => __('Australian Dollar', 'fluentform'),
197 'AWG' => __('Aruban Florin', 'fluentform'),
198 'AZN' => __('Azerbaijani Manat', 'fluentform'),
199 'BAM' => __('Bosnia & Herzegovina Convertible Mark', 'fluentform'),
200 'BBD' => __('Barbadian Dollar', 'fluentform'),
201 'BDT' => __('Bangladeshi Taka', 'fluentform'),
202 'BIF' => __('Burundian Franc', 'fluentform'),
203 'BGN' => __('Bulgarian Lev', 'fluentform'),
204 'BMD' => __('Bermudian Dollar', 'fluentform'),
205 'BND' => __('Brunei Dollar', 'fluentform'),
206 'BOB' => __('Bolivian Boliviano', 'fluentform'),
207 'BRL' => __('Brazilian Real', 'fluentform'),
208 'BSD' => __('Bahamian Dollar', 'fluentform'),
209 'BWP' => __('Botswana Pula', 'fluentform'),
210 'BZD' => __('Belize Dollar', 'fluentform'),
211 'CAD' => __('Canadian Dollar', 'fluentform'),
212 'CDF' => __('Congolese Franc', 'fluentform'),
213 'CHF' => __('Swiss Franc', 'fluentform'),
214 'CLP' => __('Chilean Peso', 'fluentform'),
215 'CNY' => __('Chinese Renminbi Yuan', 'fluentform'),
216 'COP' => __('Colombian Peso', 'fluentform'),
217 'CRC' => __('Costa Rican Colón', 'fluentform'),
218 'CVE' => __('Cape Verdean Escudo', 'fluentform'),
219 'CZK' => __('Czech Koruna', 'fluentform'),
220 'DJF' => __('Djiboutian Franc', 'fluentform'),
221 'DKK' => __('Danish Krone', 'fluentform'),
222 'DOP' => __('Dominican Peso', 'fluentform'),
223 'DZD' => __('Algerian Dinar', 'fluentform'),
224 'EGP' => __('Egyptian Pound', 'fluentform'),
225 'ETB' => __('Ethiopian Birr', 'fluentform'),
226 'EUR' => __('Euro', 'fluentform'),
227 'FJD' => __('Fijian Dollar', 'fluentform'),
228 'FKP' => __('Falkland Islands Pound', 'fluentform'),
229 'GBP' => __('British Pound', 'fluentform'),
230 'GEL' => __('Georgian Lari', 'fluentform'),
231 'GHS' => __('Ghanaian Cedi', 'fluentform'),
232 'GIP' => __('Gibraltar Pound', 'fluentform'),
233 'GMD' => __('Gambian Dalasi', 'fluentform'),
234 'GNF' => __('Guinean Franc', 'fluentform'),
235 'GTQ' => __('Guatemalan Quetzal', 'fluentform'),
236 'GYD' => __('Guyanese Dollar', 'fluentform'),
237 'HKD' => __('Hong Kong Dollar', 'fluentform'),
238 'HNL' => __('Honduran Lempira', 'fluentform'),
239 'HRK' => __('Croatian Kuna', 'fluentform'),
240 'HTG' => __('Haitian Gourde', 'fluentform'),
241 'HUF' => __('Hungarian Forint', 'fluentform'),
242 'IDR' => __('Indonesian Rupiah', 'fluentform'),
243 'ILS' => __('Israeli New Sheqel', 'fluentform'),
244 'INR' => __('Indian Rupee', 'fluentform'),
245 'ISK' => __('Icelandic Króna', 'fluentform'),
246 'JMD' => __('Jamaican Dollar', 'fluentform'),
247 'JPY' => __('Japanese Yen', 'fluentform'),
248 'KES' => __('Kenyan Shilling', 'fluentform'),
249 'KGS' => __('Kyrgyzstani Som', 'fluentform'),
250 'KHR' => __('Cambodian Riel', 'fluentform'),
251 'KMF' => __('Comorian Franc', 'fluentform'),
252 'KRW' => __('South Korean Won', 'fluentform'),
253 'KYD' => __('Cayman Islands Dollar', 'fluentform'),
254 'KZT' => __('Kazakhstani Tenge', 'fluentform'),
255 'LAK' => __('Lao Kip', 'fluentform'),
256 'LBP' => __('Lebanese Pound', 'fluentform'),
257 'LKR' => __('Sri Lankan Rupee', 'fluentform'),
258 'LRD' => __('Liberian Dollar', 'fluentform'),
259 'LSL' => __('Lesotho Loti', 'fluentform'),
260 'MAD' => __('Moroccan Dirham', 'fluentform'),
261 'MDL' => __('Moldovan Leu', 'fluentform'),
262 'MGA' => __('Malagasy Ariary', 'fluentform'),
263 'MKD' => __('Macedonian Denar', 'fluentform'),
264 'MNT' => __('Mongolian Tögrög', 'fluentform'),
265 'MOP' => __('Macanese Pataca', 'fluentform'),
266 'MRO' => __('Mauritanian Ouguiya', 'fluentform'),
267 'MUR' => __('Mauritian Rupee', 'fluentform'),
268 'MVR' => __('Maldivian Rufiyaa', 'fluentform'),
269 'MWK' => __('Malawian Kwacha', 'fluentform'),
270 'MXN' => __('Mexican Peso', 'fluentform'),
271 'MYR' => __('Malaysian Ringgit', 'fluentform'),
272 'MZN' => __('Mozambican Metical', 'fluentform'),
273 'NAD' => __('Namibian Dollar', 'fluentform'),
274 'NGN' => __('Nigerian Naira', 'fluentform'),
275 'NIO' => __('Nicaraguan Córdoba', 'fluentform'),
276 'NOK' => __('Norwegian Krone', 'fluentform'),
277 'NPR' => __('Nepalese Rupee', 'fluentform'),
278 'NZD' => __('New Zealand Dollar', 'fluentform'),
279 'PAB' => __('Panamanian Balboa', 'fluentform'),
280 'PEN' => __('Peruvian Nuevo Sol', 'fluentform'),
281 'PGK' => __('Papua New Guinean Kina', 'fluentform'),
282 'PHP' => __('Philippine Peso', 'fluentform'),
283 'PKR' => __('Pakistani Rupee', 'fluentform'),
284 'PLN' => __('Polish Złoty', 'fluentform'),
285 'PYG' => __('Paraguayan Guaraní', 'fluentform'),
286 'QAR' => __('Qatari Riyal', 'fluentform'),
287 'RON' => __('Romanian Leu', 'fluentform'),
288 'RSD' => __('Serbian Dinar', 'fluentform'),
289 'RUB' => __('Russian Ruble', 'fluentform'),
290 'RWF' => __('Rwandan Franc', 'fluentform'),
291 'SAR' => __('Saudi Riyal', 'fluentform'),
292 'SBD' => __('Solomon Islands Dollar', 'fluentform'),
293 'SCR' => __('Seychellois Rupee', 'fluentform'),
294 'SEK' => __('Swedish Krona', 'fluentform'),
295 'SGD' => __('Singapore Dollar', 'fluentform'),
296 'SHP' => __('Saint Helenian Pound', 'fluentform'),
297 'SLL' => __('Sierra Leonean Leone', 'fluentform'),
298 'SOS' => __('Somali Shilling', 'fluentform'),
299 'SRD' => __('Surinamese Dollar', 'fluentform'),
300 'STD' => __('São Tomé and Príncipe Dobra', 'fluentform'),
301 'SVC' => __('Salvadoran Colón', 'fluentform'),
302 'SZL' => __('Swazi Lilangeni', 'fluentform'),
303 'THB' => __('Thai Baht', 'fluentform'),
304 'TJS' => __('Tajikistani Somoni', 'fluentform'),
305 'TOP' => __('Tongan Paʻanga', 'fluentform'),
306 'TRY' => __('Turkish Lira', 'fluentform'),
307 'TTD' => __('Trinidad and Tobago Dollar', 'fluentform'),
308 'TWD' => __('New Taiwan Dollar', 'fluentform'),
309 'TZS' => __('Tanzanian Shilling', 'fluentform'),
310 'UAH' => __('Ukrainian Hryvnia', 'fluentform'),
311 'UGX' => __('Ugandan Shilling', 'fluentform'),
312 'USD' => __('United States Dollar', 'fluentform'),
313 'UYU' => __('Uruguayan Peso', 'fluentform'),
314 'UZS' => __('Uzbekistani Som', 'fluentform'),
315 'VND' => __('Vietnamese Đồng', 'fluentform'),
316 'VUV' => __('Vanuatu Vatu', 'fluentform'),
317 'WST' => __('Samoan Tala', 'fluentform'),
318 'XAF' => __('Central African Cfa Franc', 'fluentform'),
319 'XCD' => __('East Caribbean Dollar', 'fluentform'),
320 'XOF' => __('West African Cfa Franc', 'fluentform'),
321 'XPF' => __('Cfp Franc', 'fluentform'),
322 'YER' => __('Yemeni Rial', 'fluentform'),
323 'ZAR' => __('South African Rand', 'fluentform'),
324 'ZMW' => __('Zambian Kwacha', 'fluentform')
325 ];
326
327 $currencies = apply_filters_deprecated(
328 'fluentform_accepted_currencies',
329 [
330 $currencies
331 ],
332 FLUENTFORM_FRAMEWORK_UPGRADE,
333 'fluentform/accepted_currencies',
334 'Use fluentform/accepted_currencies instead of fluentform_accepted_currencies.'
335 );
336
337 return apply_filters('fluentform/accepted_currencies', $currencies);
338 }
339
340 /**
341 * Get a specific currency symbol
342 *
343 * https://support.stripe.com/questions/which-currencies-does-stripe-support
344 */
345 public static function getCurrencySymbol($currency = '')
346 {
347 if (!$currency) {
348 // If no currency is passed then default it to USD
349 $currency = 'USD';
350 }
351 $currency = strtoupper($currency);
352
353 $symbols = self::getCurrencySymbols();
354 $currency_symbol = isset($symbols[$currency]) ? $symbols[$currency] : '';
355
356 $currency_symbol = apply_filters_deprecated(
357 'fluentform_currency_symbol',
358 [
359 $currency_symbol,
360 $currency
361 ],
362 FLUENTFORM_FRAMEWORK_UPGRADE,
363 'fluentform/currency_symbol',
364 'Use fluentform/currency_symbol instead of fluentform_currency_symbol.'
365 );
366
367 return apply_filters('fluentform/currency_symbol', $currency_symbol, $currency);
368 }
369
370 public static function getCurrencySymbols()
371 {
372 $symbols = [
373 'AED' => '&#x62f;.&#x625;',
374 'AFN' => '&#x60b;',
375 'ALL' => 'L',
376 'AMD' => 'AMD',
377 'ANG' => '&fnof;',
378 'AOA' => 'Kz',
379 'ARS' => '&#36;',
380 'AUD' => '&#36;',
381 'AWG' => '&fnof;',
382 'AZN' => 'AZN',
383 'BAM' => 'KM',
384 'BBD' => '&#36;',
385 'BDT' => '&#2547;&nbsp;',
386 'BGN' => '&#1083;&#1074;.',
387 'BHD' => '.&#x62f;.&#x628;',
388 'BIF' => 'Fr',
389 'BMD' => '&#36;',
390 'BND' => '&#36;',
391 'BOB' => 'Bs.',
392 'BRL' => '&#82;&#36;',
393 'BSD' => '&#36;',
394 'BTC' => '&#3647;',
395 'BTN' => 'Nu.',
396 'BWP' => 'P',
397 'BYR' => 'Br',
398 'BZD' => '&#36;',
399 'CAD' => '&#36;',
400 'CDF' => 'Fr',
401 'CHF' => '&#67;&#72;&#70;',
402 'CLP' => '&#36;',
403 'CNY' => '&yen;',
404 'COP' => '&#36;',
405 'CRC' => '&#x20a1;',
406 'CUC' => '&#36;',
407 'CUP' => '&#36;',
408 'CVE' => '&#36;',
409 'CZK' => '&#75;&#269;',
410 'DJF' => 'Fr',
411 'DKK' => 'DKK',
412 'DOP' => 'RD&#36;',
413 'DZD' => '&#x62f;.&#x62c;',
414 'EGP' => 'EGP',
415 'ERN' => 'Nfk',
416 'ETB' => 'Br',
417 'EUR' => '&euro;',
418 'FJD' => '&#36;',
419 'FKP' => '&pound;',
420 'GBP' => '&pound;',
421 'GEL' => '&#x10da;',
422 'GGP' => '&pound;',
423 'GHS' => '&#x20b5;',
424 'GIP' => '&pound;',
425 'GMD' => 'D',
426 'GNF' => 'Fr',
427 'GTQ' => 'Q',
428 'GYD' => '&#36;',
429 'HKD' => '&#36;',
430 'HNL' => 'L',
431 'HRK' => 'Kn',
432 'HTG' => 'G',
433 'HUF' => '&#70;&#116;',
434 'IDR' => 'Rp',
435 'ILS' => '&#8362;',
436 'IMP' => '&pound;',
437 'INR' => '&#8377;',
438 'IQD' => '&#x639;.&#x62f;',
439 'IRR' => '&#xfdfc;',
440 'ISK' => 'Kr.',
441 'JEP' => '&pound;',
442 'JMD' => '&#36;',
443 'JOD' => '&#x62f;.&#x627;',
444 'JPY' => '&yen;',
445 'KES' => 'KSh',
446 'KGS' => '&#x43b;&#x432;',
447 'KHR' => '&#x17db;',
448 'KMF' => 'Fr',
449 'KPW' => '&#x20a9;',
450 'KRW' => '&#8361;',
451 'KWD' => '&#x62f;.&#x643;',
452 'KYD' => '&#36;',
453 'KZT' => 'KZT',
454 'LAK' => '&#8365;',
455 'LBP' => '&#x644;.&#x644;',
456 'LKR' => '&#xdbb;&#xdd4;',
457 'LRD' => '&#36;',
458 'LSL' => 'L',
459 'LYD' => '&#x644;.&#x62f;',
460 'MAD' => '&#x62f;. &#x645;.',
461 'MDL' => 'L',
462 'MGA' => 'Ar',
463 'MKD' => '&#x434;&#x435;&#x43d;',
464 'MMK' => 'Ks',
465 'MNT' => '&#x20ae;',
466 'MOP' => 'P',
467 'MRO' => 'UM',
468 'MUR' => '&#x20a8;',
469 'MVR' => '.&#x783;',
470 'MWK' => 'MK',
471 'MXN' => '&#36;',
472 'MYR' => '&#82;&#77;',
473 'MZN' => 'MT',
474 'NAD' => '&#36;',
475 'NGN' => '&#8358;',
476 'NIO' => 'C&#36;',
477 'NOK' => '&#107;&#114;',
478 'NPR' => '&#8360;',
479 'NZD' => '&#36;',
480 'OMR' => '&#x631;.&#x639;.',
481 'PAB' => 'B/.',
482 'PEN' => 'S/.',
483 'PGK' => 'K',
484 'PHP' => '&#8369;',
485 'PKR' => '&#8360;',
486 'PLN' => '&#122;&#322;',
487 'PRB' => '&#x440;.',
488 'PYG' => '&#8370;',
489 'QAR' => '&#x631;.&#x642;',
490 'RMB' => '&yen;',
491 'RON' => 'lei',
492 'RSD' => '&#x434;&#x438;&#x43d;.',
493 'RUB' => '&#8381;',
494 'RWF' => 'Fr',
495 'SAR' => '&#x631;.&#x633;',
496 'SBD' => '&#36;',
497 'SCR' => '&#x20a8;',
498 'SDG' => '&#x62c;.&#x633;.',
499 'SEK' => '&#107;&#114;',
500 'SGD' => '&#36;',
501 'SHP' => '&pound;',
502 'SLL' => 'Le',
503 'SOS' => 'Sh',
504 'SRD' => '&#36;',
505 'SSP' => '&pound;',
506 'STD' => 'Db',
507 'SYP' => '&#x644;.&#x633;',
508 'SZL' => 'L',
509 'THB' => '&#3647;',
510 'TJS' => '&#x405;&#x41c;',
511 'TMT' => 'm',
512 'TND' => '&#x62f;.&#x62a;',
513 'TOP' => 'T&#36;',
514 'TRY' => '&#8378;',
515 'TTD' => '&#36;',
516 'TWD' => '&#78;&#84;&#36;',
517 'TZS' => 'Sh',
518 'UAH' => '&#8372;',
519 'UGX' => 'UGX',
520 'USD' => '&#36;',
521 'UYU' => '&#36;',
522 'UZS' => 'UZS',
523 'VEF' => 'Bs F',
524 'VND' => '&#8363;',
525 'VUV' => 'Vt',
526 'WST' => 'T',
527 'XAF' => 'Fr',
528 'XCD' => '&#36;',
529 'XOF' => 'Fr',
530 'XPF' => 'Fr',
531 'YER' => '&#xfdfc;',
532 'ZAR' => '&#82;',
533 'ZMW' => 'ZK',
534 ];
535
536 $symbols = apply_filters_deprecated(
537 'fluentform_currency_symbols',
538 [
539 $symbols
540 ],
541 FLUENTFORM_FRAMEWORK_UPGRADE,
542 'fluentform/currencies_symbols',
543 'Use fluentform/currencies_symbols instead of fluentform_currency_symbols.'
544 );
545
546 return apply_filters('fluentform/currencies_symbols', $symbols);
547 }
548
549 public static function zeroDecimalCurrencies()
550 {
551 $zeroDecimalCurrencies = [
552 'BIF' => esc_html__('Burundian Franc', 'fluentform'),
553 'CLP' => esc_html__('Chilean Peso', 'fluentform'),
554 'DJF' => esc_html__('Djiboutian Franc', 'fluentform'),
555 'GNF' => esc_html__('Guinean Franc', 'fluentform'),
556 'JPY' => esc_html__('Japanese Yen', 'fluentform'),
557 'KMF' => esc_html__('Comorian Franc', 'fluentform'),
558 'KRW' => esc_html__('South Korean Won', 'fluentform'),
559 'MGA' => esc_html__('Malagasy Ariary', 'fluentform'),
560 'PYG' => esc_html__('Paraguayan Guaraní', 'fluentform'),
561 'RWF' => esc_html__('Rwandan Franc', 'fluentform'),
562 'VND' => esc_html__('Vietnamese Dong', 'fluentform'),
563 'VUV' => esc_html__('Vanuatu Vatu', 'fluentform'),
564 'XAF' => esc_html__('Central African Cfa Franc', 'fluentform'),
565 'XOF' => esc_html__('West African Cfa Franc', 'fluentform'),
566 'XPF' => esc_html__('Cfp Franc', 'fluentform'),
567 ];
568
569 $zeroDecimalCurrencies = apply_filters_deprecated(
570 'fluentform_zero_decimal_currencies',
571 [
572 $zeroDecimalCurrencies
573 ],
574 FLUENTFORM_FRAMEWORK_UPGRADE,
575 'fluentform/zero_decimal_currencies',
576 'Use fluentform/zero_decimal_currencies instead of fluentform_zero_decimal_currencies.'
577 );
578
579 return apply_filters('fluentform/zero_decimal_currencies', $zeroDecimalCurrencies);
580 }
581
582 public static function isZeroDecimal($currencyCode)
583 {
584 $currencyCode = strtoupper($currencyCode);
585 $zeroDecimals = self::zeroDecimalCurrencies();
586 return isset($zeroDecimals[$currencyCode]);
587 }
588
589 public static function getPaymentStatuses()
590 {
591 $paymentStatuses = [
592 'paid' => __('Paid', 'fluentform'),
593 'processing' => __('Processing', 'fluentform'),
594 'pending' => __('Pending', 'fluentform'),
595 'failed' => __('Failed', 'fluentform'),
596 'refunded' => __('Refunded', 'fluentform'),
597 'partially-refunded' => __('Partial Refunded', 'fluentform'),
598 'cancelled' => __('Cancelled', 'fluentform')
599 ];
600
601 $paymentStatuses = apply_filters_deprecated(
602 'fluentform_available_payment_statuses',
603 [
604 $paymentStatuses
605 ],
606 FLUENTFORM_FRAMEWORK_UPGRADE,
607 'fluentform/available_payment_statuses',
608 'Use fluentform/available_payment_statuses instead of fluentform_available_payment_statuses.'
609 );
610
611 return apply_filters('fluentform/available_payment_statuses', $paymentStatuses);
612 }
613
614 public static function reversedPaymentStatuses()
615 {
616 return apply_filters('fluentform/reversed_payment_statuses', [
617 'refunded', 'partially-refunded', 'cancelled',
618 ]);
619 }
620
621 public static function isReversedPaymentStatus($status)
622 {
623 $status = is_null($status) ? '' : (string) $status;
624
625 if ('' === $status) {
626 return false;
627 }
628
629 return in_array($status, static::reversedPaymentStatuses(), true);
630 }
631
632 public static function getFormPaymentMethods($formId)
633 {
634 $inputs = FormFieldsParser::getInputs($formId, ['element', 'settings']);
635 foreach ($inputs as $field) {
636 if ($field['element'] == 'payment_method') {
637 $methods = ArrayHelper::get($field, 'settings.payment_methods') ?: ArrayHelper::get($field, 'raw.settings.payment_methods');
638 if (is_array($methods)) {
639 return array_filter($methods, function ($method) {
640 return $method['enabled'] == 'yes';
641 });
642 }
643 }
644 }
645 return [];
646 }
647
648 public static function getCustomerEmail($submission, $form = false)
649 {
650
651 $formSettings = PaymentHelper::getFormSettings($submission->form_id, 'admin');
652 $customerEmailField = ArrayHelper::get($formSettings, 'receipt_email');
653
654 if ($customerEmailField) {
655 $email = ArrayHelper::get($submission->response, $customerEmailField);
656 if ($email) {
657 return $email;
658 }
659 }
660
661 $user = get_user_by('ID', get_current_user_id());
662
663 if ($user) {
664 return $user->user_email;
665 }
666
667 if (!$form) {
668 return '';
669 }
670
671 $emailFields = FormFieldsParser::getInputsByElementTypes($form, ['input_email'], ['attributes']);
672
673 foreach ($emailFields as $field) {
674 $fieldName = $field['attributes']['name'];
675 if (!empty($submission->response[$fieldName])) {
676 return $submission->response[$fieldName];
677 }
678 }
679
680 return '';
681
682 }
683
684 static function getCustomerPhoneNumber($submission, $form) {
685 $phoneFields = FormFieldsParser::getInputsByElementTypes($form, ['phone'], ['attributes']);
686
687 foreach ($phoneFields as $field) {
688 $fieldName = $field['attributes']['name'];
689 if (!empty($submission->response[$fieldName])) {
690 return $submission->response[$fieldName];
691 }
692 }
693 return '';
694 }
695
696 /**
697 * Trim a string and append a suffix.
698 *
699 * @param string $string String to trim.
700 * @param integer $chars Amount of characters.
701 * Defaults to 200.
702 * @param string $suffix Suffix.
703 * Defaults to '...'.
704 * @return string
705 */
706 public static function formatPaymentItemString($string, $chars = 200, $suffix = '...')
707 {
708 $string = wp_strip_all_tags($string);
709 if (strlen($string) > $chars) {
710 if (function_exists('mb_substr')) {
711 $string = mb_substr($string, 0, ($chars - mb_strlen($suffix))) . $suffix;
712 } else {
713 $string = substr($string, 0, ($chars - strlen($suffix))) . $suffix;
714 }
715 }
716
717 return html_entity_decode($string, ENT_NOQUOTES, 'UTF-8');
718 }
719
720 /**
721 * Limit length of an arg.
722 *
723 * @param string $string Argument to limit.
724 * @param integer $limit Limit size in characters.
725 * @return string
726 */
727 public static function limitLength($string, $limit = 127)
728 {
729 $str_limit = $limit - 3;
730 if (function_exists('mb_strimwidth')) {
731 if (mb_strlen($string) > $limit) {
732 $string = mb_strimwidth($string, 0, $str_limit) . '...';
733 }
734 } else {
735 if (strlen($string) > $limit) {
736 $string = substr($string, 0, $str_limit) . '...';
737 }
738 }
739 return $string;
740 }
741
742 public static function floatToString($float)
743 {
744 if (!is_float($float)) {
745 return $float;
746 }
747
748 $locale = localeconv();
749 $string = strval($float);
750 $string = str_replace($locale['decimal_point'], '.', $string);
751
752 return $string;
753 }
754
755 public static function convertToCents($amount)
756 {
757 if (!$amount) {
758 return 0;
759 }
760
761 $amount = floatval($amount);
762
763 return intval(round($amount * 100));
764 }
765
766 public static function getCustomerName($submission, $form = false)
767 {
768 $formSettings = PaymentHelper::getFormSettings($submission->form_id, 'admin');
769 $customerNameCode = ArrayHelper::get($formSettings, 'customer_name');
770 if ($customerNameCode) {
771 $customerName = ShortCodeParser::parse($customerNameCode, $submission->id, $submission->response);
772 if ($customerName) {
773 return $customerName;
774 }
775 }
776
777 $user = get_user_by('ID', get_current_user_id());
778
779 if ($user) {
780 $customerName = trim($user->first_name . ' ' . $user->last_name);
781 if (!$customerName) {
782 $customerName = $user->display_name;
783 }
784 if ($customerName) {
785 return $customerName;
786 }
787 }
788
789 if (!$form) {
790 return '';
791 }
792
793 $nameFields = FormFieldsParser::getInputsByElementTypes($form, ['input_name'], ['attributes']);
794
795 $fieldName = false;
796 foreach ($nameFields as $field) {
797 if ($field['element'] === 'input_name') {
798 $fieldName = $field['attributes']['name'];
799 break;
800 }
801 }
802
803 $name = '';
804 if ($fieldName) {
805 if (!empty($submission->response[$fieldName])) {
806 $names = array_filter($submission->response[$fieldName]);
807 return trim(implode(' ', $names));
808 }
809 }
810
811 return $name;
812 }
813
814 public static function getStripeInlineConfig($formId)
815 {
816 $methods = static::getFormPaymentMethods($formId);
817
818 $stripe = ArrayHelper::get($methods, 'stripe');
819 $stripeInlineStyles = ArrayHelper::get(Helper::getFormMeta($formId, '_ff_form_styles', []), 'stripe_inline_element_style', false);
820 if ($stripe) {
821 $config = [
822 'is_inline' => ArrayHelper::get($stripe, 'settings.embedded_checkout.value') == 'yes',
823 'inline_styles' => $stripeInlineStyles,
824 'verifyZip' => ArrayHelper::get($methods['stripe'], 'settings.verify_zip_code.value') === 'yes',
825 'disable_link' => false,
826 'enable_payment_element' => static::isStripePaymentElement($stripe),
827 'is_zero_decimal' => static::isZeroDecimal(static::getFormCurrency($formId)),
828 ];
829
830 // Classic and conversational forms both create the Payment Element with these options
831 if ($config['enable_payment_element']) {
832 $config['payment_element_options'] = apply_filters(
833 'fluentform/stripe_payment_element_options',
834 [
835 'layout' => 'tabs',
836 'wallets' => ['applePay' => 'auto', 'googlePay' => 'auto', 'link' => 'never'],
837 'fields' => ['billingDetails' => ['address' => $config['verifyZip'] ? 'auto' : 'if_required']],
838 'appearance' => [],
839 ],
840 $formId
841 );
842 }
843
844 return apply_filters('fluentform/stripe_inline_config', $config, $formId);
845 }
846
847 return [];
848 }
849
850 /**
851 * Stripe's Payment Element (card plus Apple Pay / Google Pay) replaces the Card Element on the embedded field,
852 * so it only applies with Embedded Checkout on.
853 */
854 public static function isStripePaymentElement($stripeMethod)
855 {
856 return ArrayHelper::get($stripeMethod, 'settings.embedded_checkout.value') === 'yes'
857 && ArrayHelper::get($stripeMethod, 'settings.enable_payment_element.value') === 'yes';
858 }
859
860 public static function log($data, $submission = false, $forceInsert = false)
861 {
862 if (!$forceInsert) {
863 static $paymentSettings;
864 if (!$paymentSettings) {
865 $paymentSettings = self::getPaymentSettings();
866 }
867
868 if (!isset($paymentSettings['debug_log']) || $paymentSettings['debug_log'] != 'yes') {
869 return false;
870 }
871 }
872
873 $defaults = [
874 'component' => 'Payment',
875 'status' => 'info',
876 'created_at' => current_time('mysql')
877 ];
878
879 if ($submission) {
880 $defaults['parent_source_id'] = $submission->form_id;
881 $defaults['source_type'] = 'submission_item';
882 $defaults['source_id'] = $submission->id;
883 } else {
884 $defaults['source_type'] = 'system_log';
885 }
886
887 $data = wp_parse_args($data, $defaults);
888
889 return \FluentForm\App\Models\Log::create($data)->id;
890
891 }
892
893 public static function maybeFireSubmissionActionHok($submission)
894 {
895 if (Helper::getSubmissionMeta($submission->id, 'is_form_action_fired') == 'yes') {
896 return false;
897 }
898
899 $form = \FluentForm\App\Models\Form::find($submission->form_id);
900
901 if (!apply_filters('fluentform/should_process_submission_actions', true, $submission, $form)) {
902 return false;
903 }
904
905 $formData = $submission->response;
906 if (!is_array($formData)) {
907 $formData = json_decode($formData, true);
908 }
909
910 (new SubmissionHandlerService())->processSubmissionData(
911 $submission->id, $formData, $form
912 );
913 Helper::setSubmissionMeta($submission->id, 'is_form_action_fired', 'yes');
914 return true;
915 }
916
917 public static function loadView($fileName, $data)
918 {
919 // normalize the filename
920 $fileName = str_replace(array('../', './'), '', $fileName);
921
922 $basePath = FLUENTFORM_DIR_PATH . 'app/Views/receipt/';
923 $basePath = apply_filters_deprecated(
924 'fluentform_payment_receipt_template_base_path',
925 [
926 $basePath,
927 $fileName,
928 $data
929 ],
930 FLUENTFORM_FRAMEWORK_UPGRADE,
931 'fluentform/payment_receipt_template_base_path',
932 'Use fluentform/payment_receipt_template_base_path instead of fluentform_payment_receipt_template_base_path.'
933 );
934
935 $basePath = apply_filters('fluentform/payment_receipt_template_base_path', $basePath, $fileName, $data);
936
937 $filePath = $basePath . $fileName . '.php';
938 extract($data);
939 ob_start();
940 include $filePath;
941 return ob_get_clean();
942 }
943
944 public static function recordSubscriptionCancelled($subscription, $vendorData, $logData = [])
945 {
946 \FluentForm\App\Models\Subscription::where('id', $subscription->id)
947 ->update([
948 'status' => 'cancelled',
949 'updated_at' => current_time('mysql')
950 ]);
951
952 $subscription = \FluentForm\App\Models\Subscription::find($subscription->id);
953
954 $submission = \FluentForm\App\Models\Submission::find($subscription->submission_id);
955
956 $logDefaults = [
957 'parent_source_id' => $subscription->form_id,
958 'source_type' => 'submission_item',
959 'source_id' => $subscription->submission_id,
960 'component' => 'Payment',
961 'status' => 'info',
962 'title' => __('Subscription has been cancelled', 'fluentform'),
963 'description' => __('Subscription has been cancelled from ', 'fluentform') . $submission->payment_method
964 ];
965
966 $logs = wp_parse_args($logData, $logDefaults);
967
968 do_action('fluentform/log_data', $logs);
969
970 do_action_deprecated(
971 'fluentform_subscription_payment_canceled',
972 [
973 $subscription,
974 $submission,
975 $vendorData
976 ],
977 FLUENTFORM_FRAMEWORK_UPGRADE,
978 'fluentform/subscription_payment_canceled',
979 'Use fluentform/subscription_payment_canceled instead of fluentform_subscription_payment_canceled.'
980 );
981 // New Payment Made so we have to fire some events here
982 do_action('fluentform/subscription_payment_canceled', $subscription, $submission, $vendorData);
983
984 do_action_deprecated(
985 'fluentform_subscription_payment_canceled_' . $submission->payment_method,
986 [
987 $subscription,
988 $submission,
989 $vendorData
990 ],
991 FLUENTFORM_FRAMEWORK_UPGRADE,
992 'fluentform/subscription_payment_canceled_' . $submission->payment_method,
993 'Use fluentform/subscription_payment_canceled_' . $submission->payment_method . ' instead of fluentform_subscription_payment_canceled_' . $submission->payment_method
994 );
995 do_action('fluentform/subscription_payment_canceled_' . $submission->payment_method, $subscription, $submission, $vendorData);
996 }
997
998 public static function getPaymentSummaryText($plan, $formId, $currency, $withMarkup = true)
999 {
1000 $paymentSummaryText = [
1001 'has_signup_fee' => __('{first_interval_total} for first {billing_interval} then {subscription_amount} for each {billing_interval}', 'fluentform'),
1002 'has_trial' => __('Free for {trial_days} days then {subscription_amount} for each {billing_interval}', 'fluentform'),
1003 'onetime_only' => __('One time payment of {first_interval_total}', 'fluentform'),
1004 'normal' => __('{subscription_amount} for each {billing_interval}', 'fluentform'),
1005 'bill_times' => __(', for {bill_times} installments', 'fluentform'),
1006 'single_trial' => __('Free for {trial_days} days then {subscription_amount} one time', 'fluentform')
1007 ];
1008
1009 $paymentSummaryText = apply_filters_deprecated(
1010 'fluentform_recurring_payment_summary_texts',
1011 [
1012 $paymentSummaryText,
1013 $plan,
1014 $formId
1015 ],
1016 FLUENTFORM_FRAMEWORK_UPGRADE,
1017 'fluentform/recurring_payment_summary_texts',
1018 'Use fluentform/recurring_payment_summary_texts instead of fluentform_recurring_payment_summary_texts.'
1019 );
1020
1021 $cases = apply_filters('fluentform/recurring_payment_summary_texts', $paymentSummaryText, $plan, $formId);
1022
1023 // if is trial
1024 $hasTrial = ArrayHelper::get($plan, 'has_trial_days') == 'yes' && ArrayHelper::get($plan, 'trial_days');
1025 if ($hasTrial) {
1026 $plan['signup_fee'] = 0;
1027 }
1028
1029 $signupFee = 0;
1030 $hasSignupFee = ArrayHelper::get($plan, 'has_signup_fee') == 'yes' && ArrayHelper::get($plan, 'signup_fee');
1031 if ($hasSignupFee) {
1032 $plan['trial_days'] = 0;
1033 $signupFee = ArrayHelper::get($plan, 'signup_fee');
1034 }
1035
1036 $firstIntervalTotal = PaymentHelper::formatMoney(
1037 PaymentHelper::convertToCents($signupFee + ArrayHelper::get($plan, 'subscription_amount')),
1038 $currency
1039 );
1040
1041 if ($signupFee) {
1042 $signupFee = PaymentHelper::formatMoney(
1043 PaymentHelper::convertToCents($signupFee),
1044 $currency
1045 );
1046 }
1047
1048 $subscriptionAmount = PaymentHelper::formatMoney(
1049 PaymentHelper::convertToCents(ArrayHelper::get($plan, 'subscription_amount')),
1050 $currency
1051 );
1052
1053 $billingInterval = $plan['billing_interval'];
1054 $billingInterval = ArrayHelper::get(self::getBillingIntervals(), $billingInterval, $billingInterval);
1055 $billingInterval = esc_html($billingInterval);
1056 $trialDays = esc_html(ArrayHelper::get($plan, 'trial_days'));
1057 $billTimes = esc_html(ArrayHelper::get($plan, 'bill_times'));
1058 $replaces = array(
1059 '{signup_fee}' => '<span class="ff_bs ffbs_signup_fee">' . $signupFee . '</span>',
1060 '{first_interval_total}' => '<span class="ff_bs ffbs_first_interval_total">' . $firstIntervalTotal . '</span>',
1061 '{subscription_amount}' => '<span class="ff_bs ffbs_subscription_amount">' . $subscriptionAmount . '</span>',
1062 '{billing_interval}' => '<span class="ff_bs ffbs_billing_interval">' . $billingInterval . '</span>',
1063 '{trial_days}' => '<span class="ff_bs ffbs_trial_days">' . $trialDays . '</span>',
1064 '{bill_times}' => '<span class="ff_bs ffbs_bill_times">' . $billTimes . '</span>',
1065 );
1066
1067 if (ArrayHelper::get($plan, 'user_input') == 'yes') {
1068 $cases['{subscription_amount}'] = '<span class="ff_dynamic_input_amount">' . $subscriptionAmount . '</span>';
1069 }
1070
1071 foreach ($cases as $textKey => $text) {
1072 $cases[$textKey] = str_replace(array_keys($replaces), array_values($replaces), $text);
1073 }
1074
1075 $customText = '';
1076 if ($hasSignupFee && isset($plan['bill_times']) && $plan['bill_times'] == 1) {
1077 $customText = $cases['onetime_only'];
1078 } else if ($hasSignupFee) {
1079 $customText = $cases['has_signup_fee'];
1080 } else if ($hasTrial) {
1081 if (ArrayHelper::get($plan, 'bill_times') == 1) {
1082 $customText = $cases['single_trial'];
1083 } else {
1084 $customText = $cases['has_trial'];
1085 }
1086 } else if (isset($plan['bill_times']) && $plan['bill_times'] == 1) {
1087 $customText = $cases['onetime_only'];
1088 } else {
1089 $customText = $cases['normal'];
1090 }
1091
1092 if (isset($plan['bill_times']) && $plan['bill_times'] > 1) {
1093 $customText .= $cases['bill_times'];
1094 }
1095 if($withMarkup) {
1096 $class = $plan['is_default'] === 'yes' ? '' : 'hidden_field';
1097 return '<div class="ff_summary_container ff_summary_container_' . esc_attr($plan['index']) . ' ' . $class . '">' . $customText . '</div>';
1098 }
1099 return $customText;
1100 }
1101
1102 public static function getCustomerAddress($submission)
1103 {
1104 $formSettings = PaymentHelper::getFormSettings($submission->form_id, 'admin');
1105 $customerAddressField = ArrayHelper::get($formSettings, 'customer_address');
1106
1107 if ($customerAddressField) {
1108 return ArrayHelper::get($submission->response, $customerAddressField);
1109 }
1110
1111 return null;
1112 }
1113
1114 public static function getBillingIntervals()
1115 {
1116 return [
1117 'day' => __('day', 'fluentform'),
1118 'week' => __('week', 'fluentform'),
1119 'month' => __('month', 'fluentform'),
1120 'year' => __('year', 'fluentform')
1121 ];
1122 }
1123
1124 public static function getSubscriptionStatuses()
1125 {
1126 return [
1127 'active' => __('active', 'fluentform'),
1128 'trialling' => __('trialling', 'fluentform'),
1129 'failing' => __('failing', 'fluentform'),
1130 'cancelled' => __('cancelled', 'fluentform')
1131 ];
1132 }
1133
1134 public static function getFormInput($formId,$inputType)
1135 {
1136 $form = fluentFormApi()->form($formId);
1137 $fields = FormFieldsParser::getInputsByElementTypes($form, [$inputType], ['attributes']);
1138 if (!empty($fields)) {
1139 $field = array_shift($fields);
1140 return ArrayHelper::get($field, 'attributes.name');
1141 }
1142 return '';
1143 }
1144
1145 /**
1146 * Stable encryption key, decoupled from WordPress salts (which some hosts and
1147 * security plugins rotate, silently breaking salt-encrypted payment keys).
1148 *
1149 * For stronger at-rest protection, define FLUENTFORM_ENCRYPTION_KEY in
1150 * wp-config.php so the key lives on the filesystem, not the database next to
1151 * the ciphertext. Set it BEFORE saving keys — defining it after keys are
1152 * stored makes existing values unreadable and requires re-entering them.
1153 *
1154 * @return string
1155 */
1156 public static function getEncryptionKey()
1157 {
1158 if (defined('FLUENTFORM_ENCRYPTION_KEY') && FLUENTFORM_ENCRYPTION_KEY) {
1159 return FLUENTFORM_ENCRYPTION_KEY;
1160 }
1161
1162 $key = get_option('_fluentform_encryption_key');
1163 if (!$key) {
1164 $key = base64_encode(openssl_random_pseudo_bytes(32)); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
1165 if (!add_option('_fluentform_encryption_key', $key, '', 'no')) {
1166 $key = get_option('_fluentform_encryption_key');
1167 }
1168 }
1169
1170 return $key;
1171 }
1172
1173 public static function encryptKey($value)
1174 {
1175 if(!$value) {
1176 return $value;
1177 }
1178
1179 if ( ! extension_loaded( 'openssl' ) ) {
1180 return $value;
1181 }
1182
1183 $key = self::getEncryptionKey();
1184 $method = 'aes-256-ctr';
1185 $ivlen = openssl_cipher_iv_length( $method );
1186 $iv = openssl_random_pseudo_bytes( $ivlen );
1187
1188 $ciphertext = openssl_encrypt( $value, $method, $key, OPENSSL_RAW_DATA, $iv );
1189 if ( $ciphertext === false ) {
1190 return false;
1191 }
1192
1193 $hmac = hash_hmac( 'sha256', $iv . $ciphertext, $key, true );
1194
1195 return 'v2:' . base64_encode( $iv . $hmac . $ciphertext ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
1196 }
1197
1198 public static function decryptKey( $raw_value ) {
1199
1200 if(!$raw_value) {
1201 return $raw_value;
1202 }
1203
1204 if ( strpos( $raw_value, 'v2:' ) !== 0 ) {
1205 return self::legacyDecryptKey( $raw_value );
1206 }
1207
1208 // A v2 blob is unrecoverable without openssl, so fail loud instead of
1209 // handing the ciphertext back as if it were the key.
1210 if ( ! extension_loaded( 'openssl' ) ) {
1211 return false;
1212 }
1213
1214 $key = self::getEncryptionKey();
1215 $decoded = base64_decode( substr( $raw_value, 3 ), true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
1216 $method = 'aes-256-ctr';
1217 $ivlen = openssl_cipher_iv_length( $method );
1218 $sha2len = 32;
1219
1220 if ( $decoded === false || strlen( $decoded ) < $ivlen + $sha2len ) {
1221 return false;
1222 }
1223
1224 $iv = substr( $decoded, 0, $ivlen );
1225 $hmac = substr( $decoded, $ivlen, $sha2len );
1226 $ciphertext = substr( $decoded, $ivlen + $sha2len );
1227
1228 // Encrypt-then-MAC: verify integrity (constant time) before decrypting.
1229 $calcmac = hash_hmac( 'sha256', $iv . $ciphertext, $key, true );
1230 if ( ! hash_equals( $hmac, $calcmac ) ) {
1231 return false;
1232 }
1233
1234 $value = openssl_decrypt( $ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv );
1235
1236 return $value !== false ? $value : false;
1237 }
1238
1239 /**
1240 * Reads keys encrypted before v2, i.e. tied to LOGGED_IN_KEY / LOGGED_IN_SALT.
1241 *
1242 * @param string $raw_value
1243 * @return string|bool
1244 */
1245 public static function legacyDecryptKey( $raw_value ) {
1246
1247 if(!$raw_value) {
1248 return $raw_value;
1249 }
1250
1251 if ( ! extension_loaded( 'openssl' ) ) {
1252 return $raw_value;
1253 }
1254
1255 $raw_value = base64_decode( $raw_value, true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
1256
1257 $method = 'aes-256-ctr';
1258 $ivlen = openssl_cipher_iv_length( $method );
1259 $iv = substr( $raw_value, 0, $ivlen );
1260
1261 $raw_value = substr( $raw_value, $ivlen );
1262
1263 $salt = (defined( 'LOGGED_IN_SALT' ) && '' !== LOGGED_IN_SALT) ? LOGGED_IN_SALT : 'this-is-a-fallback-salt-but-not-secure';
1264 $key = ( defined( 'LOGGED_IN_KEY' ) && '' !== LOGGED_IN_KEY ) ? LOGGED_IN_KEY : 'this-is-a-fallback-key-but-not-secure';
1265
1266 $value = openssl_decrypt( $raw_value, $method, $key, 0, $iv );
1267 if ( ! $value || substr( $value, - strlen( $salt ) ) !== $salt ) {
1268 return false;
1269 }
1270
1271 return substr( $value, 0, - strlen( $salt ) );
1272 }
1273
1274 public static function isPlanExpiredAndHidden($plan)
1275 {
1276 if (ArrayHelper::get($plan, 'has_end_date') !== 'yes') {
1277 return false;
1278 }
1279 if (ArrayHelper::get($plan, 'expire_behavior') !== 'hide') {
1280 return false;
1281 }
1282 $endDateStr = ArrayHelper::get($plan, 'subscription_end_date');
1283 if (!$endDateStr) {
1284 return false;
1285 }
1286 $endDate = strtotime($endDateStr . ' +1 day');
1287 return !$endDate || $endDate <= current_time('timestamp');
1288 }
1289 }
1290