| @@ -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; |
| @@ -278,17 +279,15 @@ | ||
| 278 | 279 | } |
| 279 | 280 | |
| 280 | 281 | public static function getOrderStatuses() |
| 281 | 282 | { |
| 282 | - $statuses = apply_filters_deprecated('fluent-cart/order_statuses', [ | |
| 283 | - [ | |
| 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 | - ], [] | |
| 290 | - ], '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 | + ]; | |
| 291 | 290 | |
| 292 | 291 | return apply_filters('fluent_cart/order_statuses', $statuses, []); |
| 293 | 292 | } |
| 294 | 293 | |
| @@ -293,17 +292,15 @@ | ||
| 293 | 292 | } |
| 294 | 293 | |
| 295 | 294 | public static function getEditableOrderStatuses() |
| 296 | 295 | { |
| 297 | - $statuses = apply_filters_deprecated('fluent-cart/editable_order_statuses', [ | |
| 298 | - [ | |
| 299 | - 'on-hold' => __('On Hold', 'fluent-cart'), | |
| 300 | - 'processing' => __('Processing', 'fluent-cart'), | |
| 301 | - 'completed' => __('Completed', 'fluent-cart'), | |
| 302 | - // 'archived' => __('Archived', 'fluent-cart'), | |
| 303 | - 'cancelled' => __('Cancelled', 'fluent-cart') | |
| 304 | - ], [] | |
| 305 | - ], '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 | + ]; | |
| 306 | 303 | |
| 307 | 304 | return apply_filters('fluent_cart/editable_order_statuses', $statuses, []); |
| 308 | 305 | } |
| 309 | 306 | |
| @@ -308,14 +305,12 @@ | ||
| 308 | 305 | } |
| 309 | 306 | |
| 310 | 307 | public static function getEditableCustomerStatuses() |
| 311 | 308 | { |
| 312 | - $statuses = apply_filters_deprecated('fluent-cart/editable_customer_statuses', [ | |
| 313 | - [ | |
| 314 | - 'active' => __('Active', 'fluent-cart'), | |
| 315 | - 'inactive' => __('Inactive', 'fluent-cart'), | |
| 316 | - ], [] | |
| 317 | - ], '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 | + ]; | |
| 318 | 313 | |
| 319 | 314 | return apply_filters('fluent_cart/editable_customer_statuses', $statuses, []); |
| 320 | 315 | } |
| 321 | 316 | |
| @@ -320,16 +315,14 @@ | ||
| 320 | 315 | } |
| 321 | 316 | |
| 322 | 317 | public static function getShippingStatuses() |
| 323 | 318 | { |
| 324 | - $statuses = apply_filters_deprecated('fluent-cart/shipping_statuses', [ | |
| 325 | - [ | |
| 326 | - 'unshipped' => __('Unshipped', 'fluent-cart'), | |
| 327 | - 'shipped' => __('Shipped', 'fluent-cart'), | |
| 328 | - 'delivered' => __('Delivered', 'fluent-cart'), | |
| 329 | - 'unshippable' => __('Unshippable', 'fluent-cart'), | |
| 330 | - ], [] | |
| 331 | - ], '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 | + ]; | |
| 332 | 325 | |
| 333 | 326 | return apply_filters('fluent_cart/shipping_statuses', $statuses, []); |
| 334 | 327 | } |
| 335 | 328 | |
| @@ -334,16 +327,14 @@ | ||
| 334 | 327 | } |
| 335 | 328 | |
| 336 | 329 | public static function getEditableShippingStatuses() |
| 337 | 330 | { |
| 338 | - $statuses = apply_filters_deprecated('fluent-cart/editable_order_statuses', [ | |
| 339 | - [ | |
| 340 | - 'unshipped' => __('Unshipped', 'fluent-cart'), | |
| 341 | - 'shipped' => __('Shipped', 'fluent-cart'), | |
| 342 | - 'delivered' => __('Delivered', 'fluent-cart'), | |
| 343 | - 'unshippable' => __('Unshippable', 'fluent-cart'), | |
| 344 | - ], [] | |
| 345 | - ], '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 | + ]; | |
| 346 | 337 | |
| 347 | 338 | return apply_filters('fluent_cart/editable_shipping_statuses', $statuses, []); |
| 348 | 339 | } |
| 349 | 340 | |
| @@ -373,18 +364,16 @@ | ||
| 373 | 364 | } |
| 374 | 365 | |
| 375 | 366 | public static function getTransactionStatuses($withLabel = true) |
| 376 | 367 | { |
| 377 | - $statuses = apply_filters_deprecated('fluent-cart/transaction_statuses', [ | |
| 378 | - [ | |
| 379 | - 'pending' => __('Pending', 'fluent-cart'), | |
| 380 | - 'paid' => __('Paid', 'fluent-cart'), | |
| 381 | - 'require_capture' => __('Authorized (Require Capture)', 'fluent-cart'), | |
| 382 | - 'failed' => __('Failed', 'fluent-cart'), | |
| 383 | - 'refunded' => __('Refunded', 'fluent-cart'), | |
| 384 | - 'active' => __('Active', 'fluent-cart'), | |
| 385 | - ], [] | |
| 386 | - ], '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 | + ]; | |
| 387 | 376 | |
| 388 | 377 | $statuses = apply_filters('fluent_cart/transaction_statuses', $statuses, []); |
| 389 | 378 | |
| 390 | 379 | if ($withLabel) { |
| @@ -395,16 +384,14 @@ | ||
| 395 | 384 | } |
| 396 | 385 | |
| 397 | 386 | public static function getEditableTransactionStatuses($withLabel = true) |
| 398 | 387 | { |
| 399 | - $statuses = apply_filters_deprecated('fluent-cart/editable_transaction_statuses', [ | |
| 400 | - [ | |
| 401 | - 'pending' => __('Pending', 'fluent-cart'), | |
| 402 | - 'paid' => __('Paid', 'fluent-cart'), | |
| 403 | - 'failed' => __('Failed', 'fluent-cart'), | |
| 404 | - 'refunded' => __('Refunded', 'fluent-cart'), | |
| 405 | - ], [] | |
| 406 | - ], '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 | + ]; | |
| 407 | 394 | |
| 408 | 395 | $statuses = apply_filters('fluent_cart/editable_transaction_statuses', $statuses, []); |
| 409 | 396 | |
| 410 | 397 | if ($withLabel) { |
| @@ -413,21 +400,8 @@ | ||
| 413 | 400 | |
| 414 | 401 | return array_keys($statuses); |
| 415 | 402 | } |
| 416 | 403 | |
| 417 | - public static function loadSpoutLib() | |
| 418 | - { | |
| 419 | - static $loaded; | |
| 420 | - | |
| 421 | - if ($loaded) { | |
| 422 | - return $loaded; | |
| 423 | - } | |
| 424 | - | |
| 425 | - require_once FLUENTCART_PLUGIN_PATH . 'app/Services/Libs/Spout/Autoloader/autoload.php'; | |
| 426 | - | |
| 427 | - return true; | |
| 428 | - } | |
| 429 | - | |
| 430 | 404 | public static function productStatuses($withLabel = true): array |
| 431 | 405 | { |
| 432 | 406 | $statues = [ |
| 433 | 407 | 'publish' => __('Publish', 'fluent-cart'), |
| @@ -578,8 +552,41 @@ | ||
| 578 | 552 | $amount = (int)round($amount); // Round to nearest integer, then cast |
| 579 | 553 | return $amount; |
| 580 | 554 | } |
| 581 | 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 | + | |
| 582 | 589 | public static function toDecimalWithoutComma($amount) |
| 583 | 590 | { |
| 584 | 591 | |
| 585 | 592 | if (!is_numeric($amount)) { |
| @@ -602,10 +609,12 @@ | ||
| 602 | 609 | if (!$user) { |
| 603 | 610 | return null; |
| 604 | 611 | } |
| 605 | 612 | |
| 613 | + // Identity only — see CustomerResource::getCurrentCustomer() for why an | |
| 614 | + // email match must never reach a customer row. | |
| 606 | 615 | return Customer::query()->where('user_id', $user->ID) |
| 607 | - ->orWhere('email', $user->user_email) | |
| 616 | + ->orderBy('id', 'ASC') | |
| 608 | 617 | ->first(); |
| 609 | 618 | } |
| 610 | 619 | |
| 611 | 620 | /** |
| @@ -633,27 +642,25 @@ | ||
| 633 | 642 | } |
| 634 | 643 | |
| 635 | 644 | public static function getAvailableCurrencyList() |
| 636 | 645 | { |
| 637 | - $currencies = apply_filters_deprecated('fluent-cart/available_currencies', [ | |
| 638 | - [ | |
| 639 | - 'BDT' => [ | |
| 640 | - "label" => __('Bangladeshi Taka', 'fluent-cart'), | |
| 641 | - "value" => 'BDT', | |
| 642 | - "symbol" => '৳', | |
| 643 | - ], | |
| 644 | - 'USD' => [ | |
| 645 | - "label" => __('United State Dollar', 'fluent-cart'), | |
| 646 | - "value" => 'USD', | |
| 647 | - "symbol" => '$', | |
| 648 | - ], | |
| 649 | - 'GBP' => [ | |
| 650 | - "label" => __('United Kingdom', 'fluent-cart'), | |
| 651 | - "value" => 'GBP', | |
| 652 | - "symbol" => '£', | |
| 653 | - ], | |
| 654 | - ], [] | |
| 655 | - ], '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 | + ]; | |
| 656 | 663 | |
| 657 | 664 | return apply_filters('fluent_cart/available_currencies', $currencies, []); |
| 658 | 665 | } |
| 659 | 666 | |
| @@ -1000,15 +1007,13 @@ | ||
| 1000 | 1007 | } |
| 1001 | 1008 | |
| 1002 | 1009 | public static function getCouponStatuses() |
| 1003 | 1010 | { |
| 1004 | - $statuses = apply_filters_deprecated('fluent-cart/coupon_statuses', [ | |
| 1005 | - [ | |
| 1006 | - 'active' => __('Active', 'fluent-cart'), | |
| 1007 | - 'expired' => __('Expired', 'fluent-cart'), | |
| 1008 | - 'disabled' => __('Disabled', 'fluent-cart'), | |
| 1009 | - ], [] | |
| 1010 | - ], '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 | + ]; | |
| 1011 | 1016 | |
| 1012 | 1017 | return apply_filters('fluent_cart/coupon_statuses', $statuses, []); |
| 1013 | 1018 | } |
| 1014 | 1019 | |
| @@ -1274,8 +1279,150 @@ | ||
| 1274 | 1279 | |
| 1275 | 1280 | return !empty($otherInfo) ? $paymentInfo : null; |
| 1276 | 1281 | } |
| 1277 | 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 | + | |
| 1278 | 1425 | public static function generateSetupFeeInfo($otherInfo, $asArray = false) |
| 1279 | 1426 | { |
| 1280 | 1427 | // Convert to array if it's an object |
| 1281 | 1428 | if (is_object($otherInfo)) { |
| @@ -1340,9 +1487,8 @@ | ||
| 1340 | 1487 | |
| 1341 | 1488 | public static function getCountryList(): array |
| 1342 | 1489 | { |
| 1343 | 1490 | $options = App::getInstance('localization')->countriesOptions(); |
| 1344 | - $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.'); | |
| 1345 | 1491 | |
| 1346 | 1492 | return apply_filters('fluent_cart/util/countries', $options, []); |
| 1347 | 1493 | } |
| 1348 | 1494 | |
| @@ -1782,26 +1928,9 @@ | ||
| 1782 | 1928 | } |
| 1783 | 1929 | |
| 1784 | 1930 | public static function subscriptionIntervalInDays($interval) |
| 1785 | 1931 | { |
| 1786 | - switch ($interval) { | |
| 1787 | - case 'daily': | |
| 1788 | - return 1; | |
| 1789 | - case 'weekly': | |
| 1790 | - return 7; | |
| 1791 | - case 'monthly': | |
| 1792 | - return 30; | |
| 1793 | - case 'quarterly': | |
| 1794 | - return 90; | |
| 1795 | - case 'half_yearly': | |
| 1796 | - return 182; | |
| 1797 | - case 'yearly': | |
| 1798 | - return 365; | |
| 1799 | - default: | |
| 1800 | - return apply_filters('fluent_cart/subscription_interval_in_days', 0, [ | |
| 1801 | - 'interval' => $interval, | |
| 1802 | - ]); | |
| 1803 | - } | |
| 1932 | + return PaymentHelper::getIntervalDays($interval); | |
| 1804 | 1933 | } |
| 1805 | 1934 | |
| 1806 | 1935 | public static function parseTermIdsForFilter($filters): array |
| 1807 | 1936 | { |
| @@ -1856,12 +1985,12 @@ | ||
| 1856 | 1985 | { |
| 1857 | 1986 | $allChildVariants = Arr::pluck($variants, 'other_info.bundle_child_ids'); |
| 1858 | 1987 | |
| 1859 | 1988 | $allChildVariants = array_unique(Arr::flatten($allChildVariants)); |
| 1860 | - $allChildVariants = Arr::except( | |
| 1989 | + $allChildVariants = array_values(array_diff( | |
| 1861 | 1990 | $allChildVariants, |
| 1862 | 1991 | Arr::pluck($variants, 'id') |
| 1863 | - ); | |
| 1992 | + )); | |
| 1864 | 1993 | $allChildVariants = array_filter($allChildVariants); |
| 1865 | 1994 | $childVariants = ProductVariation::query() |
| 1866 | 1995 | ->whereIn('id', $allChildVariants) |
| 1867 | 1996 | ->with('product:ID,post_title') |