PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk All 48 releases
← All changes | app/Helpers/Helper.php +311 -119 1.5.2 → 1.6.5 View file →
@@ -11,8 +11,9 @@
11 11 use FluentCart\App\Models\Customer;
12 12 use FluentCart\App\Models\ProductVariation;
13 13 use FluentCart\App\Models\User;
14 14 use FluentCart\App\Services\DateTime\DateTime;
15 +use FluentCart\App\Services\Payments\PaymentHelper;
15 16 use FluentCart\App\Services\Localization\LocalizationManager;
16 17 use FluentCart\App\Services\Translations\TransStrings;
17 18 use FluentCart\App\Services\URL;
18 19 use FluentCart\Framework\Http\URL as BaseUrl;
@@ -32,8 +33,33 @@
32 33 const ROLE_CAPABILITY_PREFIX = 'fluent_cart/permissions/';
33 34
34 35 const USER_ROLE = 'fluent_cart_customer';
35 36
37 + const MIN_INSTALLMENT_TIMES = 2;
38 +
39 + /**
40 + * An installment plan must bill at least twice. times = 0 means unlimited
41 + * (a plain recurring subscription) and times = 1 collects a single payment,
42 + * which is a one-time purchase, not an installment plan.
43 + *
44 + * @param array $otherInfo A variant's other_info payload
45 + * @return string|null Error message, or null when valid
46 + */
47 + public static function installmentTimesError($otherInfo)
48 + {
49 + if (Arr::get($otherInfo, 'installment', 'no') !== 'yes') {
50 + return null;
51 + }
52 +
53 + $times = Arr::get($otherInfo, 'times', 0);
54 +
55 + if (!is_numeric($times) || (int)$times < static::MIN_INSTALLMENT_TIMES) {
56 + return __('Installment count must be 2 or more. A single installment is just a one-time payment — set the payment type to one-time instead.', 'fluent-cart');
57 + }
58 +
59 + return null;
60 + }
61 +
36 62 public static function getUidSerial()
37 63 {
38 64 static $id = 0;
39 65
@@ -42,8 +68,46 @@
42 68 return $id;
43 69
44 70 }
45 71
72 + /**
73 + * Build a spec-compliant "Upgrade to Pro" URL.
74 + *
75 + * Follows the shared Fluent* UTM spec:
76 + * utm_source = fluent-cart (fixed vocabulary, never the wp.org slug)
77 + * utm_medium = free_plugin | pro_plugin (acquisition vs cross-sell)
78 + * utm_campaign= upgrade_pro (override for xsell_<target> / license_*)
79 + * utm_content = the exact placement, e.g. feature_lock_advanced_inventory
80 + * utm_term = plugin version that generated the link
81 + * utm_id = promo id, blank normally (omit unless passed)
82 + *
83 + * @param string $content The utm_content placement.
84 + * @param array $overrides Override any utm_* param (e.g. utm_campaign for cross-sell).
85 + * @return string
86 + */
87 + public static function getUpgradeUrl($content = 'upgrade_page', $overrides = []): string
88 + {
89 + $baseUrl = (string) apply_filters(
90 + 'fluent_cart/pro_upgrade_base_url',
91 + 'https://fluentcart.com/discount-deal/'
92 + );
93 +
94 + $params = wp_parse_args($overrides, [
95 + 'utm_source' => 'fluent-cart',
96 + 'utm_medium' => App::isProActive() ? 'pro_plugin' : 'free_plugin',
97 + 'utm_campaign' => 'upgrade_pro',
98 + 'utm_content' => $content,
99 + 'utm_term' => defined('FLUENTCART_VERSION') ? FLUENTCART_VERSION : '',
100 + ]);
101 +
102 + // Drop any blank params (e.g. an unset utm_id) so they never hit the URL.
103 + $params = array_filter($params, function ($value) {
104 + return $value !== '' && $value !== null;
105 + });
106 +
107 + return add_query_arg($params, $baseUrl);
108 + }
109 +
46 110 public static function getRestInfo()
47 111 {
48 112 $app = App::getInstance();
49 113
@@ -215,17 +279,15 @@
215 279 }
216 280
217 281 public static function getOrderStatuses()
218 282 {
219 - $statuses = apply_filters_deprecated('fluent-cart/order_statuses', [
220 - [
221 - 'on-hold' => __('On Hold', 'fluent-cart'),
222 - 'processing' => __('Processing', 'fluent-cart'),
223 - 'completed' => __('Completed', 'fluent-cart'),
224 - //'archived' => __('Archived', 'fluent-cart'),
225 - 'cancelled' => __('Cancelled', 'fluent-cart'),
226 - ], []
227 - ], '1.3.16', 'fluent_cart/order_statuses', 'Use fluent_cart/order_statuses instead of fluent-cart/order_statuses. It will be removed in v1.4.3.');
283 + $statuses = [
284 + 'on-hold' => __('On Hold', 'fluent-cart'),
285 + 'processing' => __('Processing', 'fluent-cart'),
286 + 'completed' => __('Completed', 'fluent-cart'),
287 + //'archived' => __('Archived', 'fluent-cart'),
288 + 'cancelled' => __('Cancelled', 'fluent-cart'),
289 + ];
228 290
229 291 return apply_filters('fluent_cart/order_statuses', $statuses, []);
230 292 }
231 293
@@ -230,17 +292,15 @@
230 292 }
231 293
232 294 public static function getEditableOrderStatuses()
233 295 {
234 - $statuses = apply_filters_deprecated('fluent-cart/editable_order_statuses', [
235 - [
236 - 'on-hold' => __('On Hold', 'fluent-cart'),
237 - 'processing' => __('Processing', 'fluent-cart'),
238 - 'completed' => __('Completed', 'fluent-cart'),
239 - // 'archived' => __('Archived', 'fluent-cart'),
240 - 'cancelled' => __('Cancelled', 'fluent-cart')
241 - ], []
242 - ], '1.3.16', 'fluent_cart/editable_order_statuses', 'Use fluent_cart/editable_order_statuses instead of fluent-cart/editable_order_statuses. It will be removed in v1.4.3.');
296 + $statuses = [
297 + 'on-hold' => __('On Hold', 'fluent-cart'),
298 + 'processing' => __('Processing', 'fluent-cart'),
299 + 'completed' => __('Completed', 'fluent-cart'),
300 + // 'archived' => __('Archived', 'fluent-cart'),
301 + 'cancelled' => __('Cancelled', 'fluent-cart')
302 + ];
243 303
244 304 return apply_filters('fluent_cart/editable_order_statuses', $statuses, []);
245 305 }
246 306
@@ -245,14 +305,12 @@
245 305 }
246 306
247 307 public static function getEditableCustomerStatuses()
248 308 {
249 - $statuses = apply_filters_deprecated('fluent-cart/editable_customer_statuses', [
250 - [
251 - 'active' => __('Active', 'fluent-cart'),
252 - 'inactive' => __('Inactive', 'fluent-cart'),
253 - ], []
254 - ], '1.3.16', 'fluent_cart/editable_customer_statuses', 'Use fluent_cart/editable_customer_statuses instead of fluent-cart/editable_customer_statuses. It will be removed in v1.4.3.');
309 + $statuses = [
310 + 'active' => __('Active', 'fluent-cart'),
311 + 'inactive' => __('Inactive', 'fluent-cart'),
312 + ];
255 313
256 314 return apply_filters('fluent_cart/editable_customer_statuses', $statuses, []);
257 315 }
258 316
@@ -257,16 +315,14 @@
257 315 }
258 316
259 317 public static function getShippingStatuses()
260 318 {
261 - $statuses = apply_filters_deprecated('fluent-cart/shipping_statuses', [
262 - [
263 - 'unshipped' => __('Unshipped', 'fluent-cart'),
264 - 'shipped' => __('Shipped', 'fluent-cart'),
265 - 'delivered' => __('Delivered', 'fluent-cart'),
266 - 'unshippable' => __('Unshippable', 'fluent-cart'),
267 - ], []
268 - ], '1.3.16', 'fluent_cart/shipping_statuses', 'Use fluent_cart/shipping_statuses instead of fluent-cart/shipping_statuses. It will be removed in v1.4.3.');
319 + $statuses = [
320 + 'unshipped' => __('Unshipped', 'fluent-cart'),
321 + 'shipped' => __('Shipped', 'fluent-cart'),
322 + 'delivered' => __('Delivered', 'fluent-cart'),
323 + 'unshippable' => __('Unshippable', 'fluent-cart'),
324 + ];
269 325
270 326 return apply_filters('fluent_cart/shipping_statuses', $statuses, []);
271 327 }
272 328
@@ -271,16 +327,14 @@
271 327 }
272 328
273 329 public static function getEditableShippingStatuses()
274 330 {
275 - $statuses = apply_filters_deprecated('fluent-cart/editable_order_statuses', [
276 - [
277 - 'unshipped' => __('Unshipped', 'fluent-cart'),
278 - 'shipped' => __('Shipped', 'fluent-cart'),
279 - 'delivered' => __('Delivered', 'fluent-cart'),
280 - 'unshippable' => __('Unshippable', 'fluent-cart'),
281 - ], []
282 - ], '1.3.16', 'fluent_cart/editable_shipping_statuses', 'Use fluent_cart/editable_shipping_statuses instead of fluent-cart/editable_order_statuses. It will be removed in v1.4.3.');
331 + $statuses = [
332 + 'unshipped' => __('Unshipped', 'fluent-cart'),
333 + 'shipped' => __('Shipped', 'fluent-cart'),
334 + 'delivered' => __('Delivered', 'fluent-cart'),
335 + 'unshippable' => __('Unshippable', 'fluent-cart'),
336 + ];
283 337
284 338 return apply_filters('fluent_cart/editable_shipping_statuses', $statuses, []);
285 339 }
286 340
@@ -310,18 +364,16 @@
310 364 }
311 365
312 366 public static function getTransactionStatuses($withLabel = true)
313 367 {
314 - $statuses = apply_filters_deprecated('fluent-cart/transaction_statuses', [
315 - [
316 - 'pending' => __('Pending', 'fluent-cart'),
317 - 'paid' => __('Paid', 'fluent-cart'),
318 - 'require_capture' => __('Authorized (Require Capture)', 'fluent-cart'),
319 - 'failed' => __('Failed', 'fluent-cart'),
320 - 'refunded' => __('Refunded', 'fluent-cart'),
321 - 'active' => __('Active', 'fluent-cart'),
322 - ], []
323 - ], '1.3.16', 'fluent_cart/transaction_statuses', 'Use fluent_cart/transaction_statuses instead of fluent-cart/transaction_statuses. It will be removed in v1.4.3.');
368 + $statuses = [
369 + 'pending' => __('Pending', 'fluent-cart'),
370 + 'paid' => __('Paid', 'fluent-cart'),
371 + 'require_capture' => __('Authorized (Require Capture)', 'fluent-cart'),
372 + 'failed' => __('Failed', 'fluent-cart'),
373 + 'refunded' => __('Refunded', 'fluent-cart'),
374 + 'active' => __('Active', 'fluent-cart'),
375 + ];
324 376
325 377 $statuses = apply_filters('fluent_cart/transaction_statuses', $statuses, []);
326 378
327 379 if ($withLabel) {
@@ -332,16 +384,14 @@
332 384 }
333 385
334 386 public static function getEditableTransactionStatuses($withLabel = true)
335 387 {
336 - $statuses = apply_filters_deprecated('fluent-cart/editable_transaction_statuses', [
337 - [
338 - 'pending' => __('Pending', 'fluent-cart'),
339 - 'paid' => __('Paid', 'fluent-cart'),
340 - 'failed' => __('Failed', 'fluent-cart'),
341 - 'refunded' => __('Refunded', 'fluent-cart'),
342 - ], []
343 - ], '1.3.16', 'fluent_cart/editable_transaction_statuses', 'Use fluent_cart/editable_transaction_statuses instead of fluent-cart/editable_transaction_statuses. It will be removed in v1.4.3.');
388 + $statuses = [
389 + 'pending' => __('Pending', 'fluent-cart'),
390 + 'paid' => __('Paid', 'fluent-cart'),
391 + 'failed' => __('Failed', 'fluent-cart'),
392 + 'refunded' => __('Refunded', 'fluent-cart'),
393 + ];
344 394
345 395 $statuses = apply_filters('fluent_cart/editable_transaction_statuses', $statuses, []);
346 396
347 397 if ($withLabel) {
@@ -350,21 +400,8 @@
350 400
351 401 return array_keys($statuses);
352 402 }
353 403
354 - public static function loadSpoutLib()
355 - {
356 - static $loaded;
357 -
358 - if ($loaded) {
359 - return $loaded;
360 - }
361 -
362 - require_once FLUENTCART_PLUGIN_PATH . 'app/Services/Libs/Spout/Autoloader/autoload.php';
363 -
364 - return true;
365 - }
366 -
367 404 public static function productStatuses($withLabel = true): array
368 405 {
369 406 $statues = [
370 407 'publish' => __('Publish', 'fluent-cart'),
@@ -515,8 +552,41 @@
515 552 $amount = (int)round($amount); // Round to nearest integer, then cast
516 553 return $amount;
517 554 }
518 555
556 + /**
557 + * Normalize a value that is ALREADY in cents to a whole-cent int.
558 + *
559 + * Unlike toCent(), this does not scale — use it when the incoming value is a
560 + * cents amount that may have arrived as a float. Clients computing cents in
561 + * JavaScript send artifacts like 1998.9999999999998 for 1999, and a bare
562 + * (int) cast truncates those, silently undercharging by a cent. Non-numeric
563 + * input (including the null the request pipeline injects for omitted keys)
564 + * normalizes to 0.
565 + *
566 + * Magnitudes beyond float's exact-integer range (2^53) are REJECTED, not
567 + * coerced: is_numeric() accepts exponential notation like 1e19, and casting
568 + * that float to int wraps to -8446744073709551616 — a VALID signed BIGINT —
569 + * so the corrupt value would persist silently where an un-normalized float
570 + * used to fail loudly at MySQL. No real cents amount approaches this bound.
571 + */
572 + public static function roundCent($amount): int
573 + {
574 + if (!is_numeric($amount)) {
575 + return 0;
576 + }
577 +
578 + $amount = (float) $amount;
579 +
580 + if (!is_finite($amount) || $amount > 9.0e15 || $amount < -9.0e15) {
581 + throw new \InvalidArgumentException(
582 + 'Value is out of the representable cents range: ' . var_export($amount, true)
583 + );
584 + }
585 +
586 + return (int) round($amount);
587 + }
588 +
519 589 public static function toDecimalWithoutComma($amount)
520 590 {
521 591
522 592 if (!is_numeric($amount)) {
@@ -539,10 +609,12 @@
539 609 if (!$user) {
540 610 return null;
541 611 }
542 612
613 + // Identity only — see CustomerResource::getCurrentCustomer() for why an
614 + // email match must never reach a customer row.
543 615 return Customer::query()->where('user_id', $user->ID)
544 - ->orWhere('email', $user->user_email)
616 + ->orderBy('id', 'ASC')
545 617 ->first();
546 618 }
547 619
548 620 /**
@@ -570,27 +642,25 @@
570 642 }
571 643
572 644 public static function getAvailableCurrencyList()
573 645 {
574 - $currencies = apply_filters_deprecated('fluent-cart/available_currencies', [
575 - [
576 - 'BDT' => [
577 - "label" => __('Bangladeshi Taka', 'fluent-cart'),
578 - "value" => 'BDT',
579 - "symbol" => '৳',
580 - ],
581 - 'USD' => [
582 - "label" => __('United State Dollar', 'fluent-cart'),
583 - "value" => 'USD',
584 - "symbol" => '$',
585 - ],
586 - 'GBP' => [
587 - "label" => __('United Kingdom', 'fluent-cart'),
588 - "value" => 'GBP',
589 - "symbol" => '£',
590 - ],
591 - ], []
592 - ], '1.3.16', 'fluent_cart/available_currencies', 'Use fluent_cart/available_currencies instead of fluent-cart/available_currencies. It will be removed in v1.4.3.');
646 + $currencies = [
647 + 'BDT' => [
648 + "label" => __('Bangladeshi Taka', 'fluent-cart'),
649 + "value" => 'BDT',
650 + "symbol" => '৳',
651 + ],
652 + 'USD' => [
653 + "label" => __('United State Dollar', 'fluent-cart'),
654 + "value" => 'USD',
655 + "symbol" => '$',
656 + ],
657 + 'GBP' => [
658 + "label" => __('United Kingdom', 'fluent-cart'),
659 + "value" => 'GBP',
660 + "symbol" => '£',
661 + ],
662 + ];
593 663
594 664 return apply_filters('fluent_cart/available_currencies', $currencies, []);
595 665 }
596 666
@@ -937,15 +1007,13 @@
937 1007 }
938 1008
939 1009 public static function getCouponStatuses()
940 1010 {
941 - $statuses = apply_filters_deprecated('fluent-cart/coupon_statuses', [
942 - [
943 - 'active' => __('Active', 'fluent-cart'),
944 - 'expired' => __('Expired', 'fluent-cart'),
945 - 'disabled' => __('Disabled', 'fluent-cart'),
946 - ], []
947 - ], '1.3.16', 'fluent_cart/coupon_statuses', 'Use fluent_cart/coupon_statuses instead of fluent-cart/coupon_statuses. It will be removed in v1.4.3.');
1011 + $statuses = [
1012 + 'active' => __('Active', 'fluent-cart'),
1013 + 'expired' => __('Expired', 'fluent-cart'),
1014 + 'disabled' => __('Disabled', 'fluent-cart'),
1015 + ];
948 1016
949 1017 return apply_filters('fluent_cart/coupon_statuses', $statuses, []);
950 1018 }
951 1019
@@ -1211,8 +1279,150 @@
1211 1279
1212 1280 return !empty($otherInfo) ? $paymentInfo : null;
1213 1281 }
1214 1282
1283 + /**
1284 + * Billing-cycle text for a subscription with a config-defined schedule
1285 + * (see SubscriptionHelper::getBillingSchedule()) — cadences the
1286 + * billing_interval enum cannot express, e.g. "$100 per 3 years on Aug 19".
1287 + * Subscriptions without a schedule keep generateSubscriptionInfo().
1288 + */
1289 + public static function generateScheduleSubscriptionInfo(array $schedule, $otherInfo, $itemPrice, $currencyCode = null): ?string
1290 + {
1291 + if (is_object($otherInfo)) {
1292 + $otherInfo = json_decode(json_encode($otherInfo), true);
1293 + }
1294 +
1295 + $count = max(1, (int) Arr::get($schedule, 'interval', 1));
1296 +
1297 + switch (Arr::get($schedule, 'period')) {
1298 + case 'day':
1299 + $unitLabel = _n('day', 'days', $count, 'fluent-cart');
1300 + break;
1301 + case 'week':
1302 + $unitLabel = _n('week', 'weeks', $count, 'fluent-cart');
1303 + break;
1304 + case 'month':
1305 + $unitLabel = _n('month', 'months', $count, 'fluent-cart');
1306 + break;
1307 + case 'year':
1308 + $unitLabel = _n('year', 'years', $count, 'fluent-cart');
1309 + break;
1310 + default:
1311 + return self::generateSubscriptionInfo($otherInfo, $itemPrice, $currencyCode);
1312 + }
1313 +
1314 + $price = self::toDecimal($itemPrice, true, $currencyCode);
1315 + $recurringDiscountAmount = Arr::get($otherInfo, 'recurring_discounts.amount', 0);
1316 +
1317 + if ($recurringDiscountAmount) {
1318 + $newRecurringAmount = $itemPrice - $recurringDiscountAmount;
1319 + $price = "<del>" . $price . "</del> " . self::toDecimal($newRecurringAmount, true, $currencyCode);
1320 + }
1321 +
1322 + $interval = $count === 1
1323 + ? sprintf(
1324 + /* translators: %s is the interval unit (e.g., day, week, month, year) */
1325 + __('per %s', 'fluent-cart'),
1326 + $unitLabel
1327 + )
1328 + : sprintf(
1329 + /* translators: %1$d is the count number, %2$s is the plural unit name (e.g., days, months, years) */
1330 + __('per %1$d %2$s', 'fluent-cart'),
1331 + $count,
1332 + $unitLabel
1333 + );
1334 +
1335 + if ($anchorText = self::getScheduleAnchorText($schedule['period'], Arr::get($schedule, 'anchor', []))) {
1336 + $interval .= ' ' . $anchorText;
1337 + }
1338 +
1339 + $occurrence = (int) Arr::get($otherInfo, 'times', 0);
1340 +
1341 + if (empty($occurrence)) {
1342 + return sprintf(
1343 + /* translators: %1$s is the price, %2$s is the interval, %3$s is "until cancel" text */
1344 + __('%1$s %2$s %3$s', 'fluent-cart'),
1345 + $price,
1346 + $interval,
1347 + __('until cancel', 'fluent-cart')
1348 + );
1349 + }
1350 +
1351 + return sprintf(
1352 + /* translators: %1$s is the price, %2$s is the interval, %3$s is the occurrence count, %4$s is "cycle(s)" */
1353 + __('%1$s %2$s, for %3$s %4$s', 'fluent-cart'),
1354 + $price,
1355 + $interval,
1356 + $occurrence,
1357 + _n('cycle', 'cycles', $occurrence, 'fluent-cart')
1358 + );
1359 + }
1360 +
1361 + /**
1362 + * Human-readable billing anchor, e.g. "on Friday", "on the 10th",
1363 + * "on the last day", "on Aug 19". Anchor day 31 encodes "last day of
1364 + * the month" (see SubscriptionHelper::getBillingSchedule()).
1365 + */
1366 + private static function getScheduleAnchorText(string $period, $anchor): string
1367 + {
1368 + if (!is_array($anchor) || !$anchor) {
1369 + return '';
1370 + }
1371 +
1372 + if ($period === 'week' && !empty($anchor['weekday'])) {
1373 + $weekdays = [
1374 + 1 => __('Monday', 'fluent-cart'),
1375 + 2 => __('Tuesday', 'fluent-cart'),
1376 + 3 => __('Wednesday', 'fluent-cart'),
1377 + 4 => __('Thursday', 'fluent-cart'),
1378 + 5 => __('Friday', 'fluent-cart'),
1379 + 6 => __('Saturday', 'fluent-cart'),
1380 + 7 => __('Sunday', 'fluent-cart'),
1381 + ];
1382 +
1383 + if (isset($weekdays[$anchor['weekday']])) {
1384 + /* translators: %s is a weekday name, e.g. "on Friday" */
1385 + return sprintf(__('on %s', 'fluent-cart'), $weekdays[$anchor['weekday']]);
1386 + }
1387 +
1388 + return '';
1389 + }
1390 +
1391 + if ($period === 'month' && !empty($anchor['day'])) {
1392 + $day = (int) $anchor['day'];
1393 +
1394 + if ($day === 31) {
1395 + return __('on the last day', 'fluent-cart');
1396 + }
1397 +
1398 + /* translators: %s is an ordinal day of month, e.g. "on the 10th" */
1399 + return sprintf(__('on the %s', 'fluent-cart'), gmdate('jS', gmmktime(12, 0, 0, 1, $day, 2001)));
1400 + }
1401 +
1402 + if ($period === 'year' && (!empty($anchor['day']) || !empty($anchor['month']))) {
1403 + $day = (int) Arr::get($anchor, 'day', 0);
1404 + $month = (int) Arr::get($anchor, 'month', 0);
1405 +
1406 + if ($month && $day) {
1407 + /* translators: %s is a date, e.g. "on Aug 19" */
1408 + // wp_date(), not gmdate(): gmdate() has no locale, so this month
1409 + // name stayed English inside an otherwise translated sentence.
1410 + return sprintf(__('on %s', 'fluent-cart'), wp_date('M', gmmktime(12, 0, 0, $month, 1, 2001), new \DateTimeZone('UTC')) . ' ' . $day);
1411 + }
1412 +
1413 + if ($month) {
1414 + /* translators: %s is a month name, e.g. "in August" */
1415 + return sprintf(__('in %s', 'fluent-cart'), wp_date('F', gmmktime(12, 0, 0, $month, 1, 2001), new \DateTimeZone('UTC')));
1416 + }
1417 +
1418 + /* translators: %s is an ordinal day of month, e.g. "on the 10th" */
1419 + return sprintf(__('on the %s', 'fluent-cart'), gmdate('jS', gmmktime(12, 0, 0, 1, $day, 2001)));
1420 + }
1421 +
1422 + return '';
1423 + }
1424 +
1215 1425 public static function generateSetupFeeInfo($otherInfo, $asArray = false)
1216 1426 {
1217 1427 // Convert to array if it's an object
1218 1428 if (is_object($otherInfo)) {
@@ -1277,9 +1487,8 @@
1277 1487
1278 1488 public static function getCountryList(): array
1279 1489 {
1280 1490 $options = App::getInstance('localization')->countriesOptions();
1281 - $options = apply_filters_deprecated('fluent-cart/util/countries', [$options, []], '1.3.16', 'fluent_cart/util/countries', 'Use fluent_cart/util/countries instead of fluent-cart/util/countries. It will be removed in v1.4.3.');
1282 1491
1283 1492 return apply_filters('fluent_cart/util/countries', $options, []);
1284 1493 }
1285 1494
@@ -1719,26 +1928,9 @@
1719 1928 }
1720 1929
1721 1930 public static function subscriptionIntervalInDays($interval)
1722 1931 {
1723 - switch ($interval) {
1724 - case 'daily':
1725 - return 1;
1726 - case 'weekly':
1727 - return 7;
1728 - case 'monthly':
1729 - return 30;
1730 - case 'quarterly':
1731 - return 90;
1732 - case 'half_yearly':
1733 - return 182;
1734 - case 'yearly':
1735 - return 365;
1736 - default:
1737 - return apply_filters('fluent_cart/subscription_interval_in_days', 0, [
1738 - 'interval' => $interval,
1739 - ]);
1740 - }
1932 + return PaymentHelper::getIntervalDays($interval);
1741 1933 }
1742 1934
1743 1935 public static function parseTermIdsForFilter($filters): array
1744 1936 {
@@ -1793,12 +1985,12 @@
1793 1985 {
1794 1986 $allChildVariants = Arr::pluck($variants, 'other_info.bundle_child_ids');
1795 1987
1796 1988 $allChildVariants = array_unique(Arr::flatten($allChildVariants));
1797 - $allChildVariants = Arr::except(
1989 + $allChildVariants = array_values(array_diff(
1798 1990 $allChildVariants,
1799 1991 Arr::pluck($variants, 'id')
1800 - );
1992 + ));
1801 1993 $allChildVariants = array_filter($allChildVariants);
1802 1994 $childVariants = ProductVariation::query()
1803 1995 ->whereIn('id', $allChildVariants)
1804 1996 ->with('product:ID,post_title')