PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / trunk
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler vtrunk
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 1.2.0 1.2.1 All 46 releases
fluent-cart / api / StoreSettings.php

StoreSettings.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler trunk, at api/StoreSettings.php

1,754 lines 89.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\Api;
4
5 use FluentCart\App\App;
6 use FluentCart\App\Vite;
7 use FluentCart\App\CPT\Pages;
8 use FluentCart\App\Helpers\AddressHelper;
9 use FluentCart\App\Helpers\CurrenciesHelper;
10 use FluentCart\App\Services\OrderService;
11 use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager;
12 use FluentCart\App\Modules\StoreManagedRenewal\Services\RenewalService;
13 use FluentCart\App\Modules\Subscriptions\Services\SubscriptionManagementMode;
14 use FluentCart\Framework\Support\Arr;
15 use FluentCart\Framework\Support\ArrayableInterface;
16 use FluentCart\Framework\Support\Str;
17 use FluentCart\App\Services\Permission\PermissionManager;
18
19 class StoreSettings implements ArrayableInterface
20 {
21 const CACHE_KEY = 'store_settings';
22 const CACHE_GROUP = 'fluentcart';
23
24 /**
25 * @var string
26 *
27 * Store settings option name
28 */
29 protected string $optionKey = 'fluent_cart_store_settings';
30
31 /**
32 * @var array key value pair
33 *
34 * Store settings parsed from fields
35 */
36 protected array $storeSettings;
37
38 protected static $cachedStoreSettings = null;
39
40 public static function clearCache(): void
41 {
42 self::$cachedStoreSettings = null;
43 wp_cache_delete(self::CACHE_KEY, self::CACHE_GROUP);
44 }
45
46 public function __construct()
47 {
48 if (self::$cachedStoreSettings !== null) {
49 $this->storeSettings = self::$cachedStoreSettings;
50 return;
51 }
52 $defaultSettings = $this->getDefaultSettings();
53 $storeSettings = get_option($this->optionKey, []);
54 $settings = wp_parse_args($storeSettings, $defaultSettings);
55 $this->storeSettings = $settings;
56 self::$cachedStoreSettings = $this->storeSettings;
57 }
58
59 protected function getDefaultSettings(): array
60 {
61 $defaultSettings = [
62 'store_name' => get_bloginfo('name'),
63 'company_name' => '',
64 'legal_registration_id' => '',
65 'seller_vat_id' => '',
66 'seller_tax_id' => '',
67 'note_for_user_account_creation' => __('An user account will be created', 'fluent-cart'),
68 'checkout_button_text' => __('Checkout', 'fluent-cart'),
69 'view_cart_button_text' => __('View Cart', 'fluent-cart'),
70 'cart_button_text' => __('Add To Cart', 'fluent-cart'),
71 'popup_button_text' => __('View Product', 'fluent-cart'),
72 'out_of_stock_button_text' => __('Not Available', 'fluent-cart'),
73 'currency_position' => 'before',
74 // 'thousand_separator' => 'comma',
75 'decimal_separator' => 'dot',
76 'checkout_method_style' => 'logo',
77 'enable_modal_checkout' => 'no',
78 'require_logged_in' => 'no',
79 'show_cart_icon_in_nav' => 'no',
80 'show_cart_icon_in_body' => 'yes',
81 'additional_address_field' => 'yes',
82 'hide_coupon_field' => 'no',
83 'user_account_creation_mode' => 'all',
84 'checkout_page_id' => '',
85 'custom_payment_page_id' => '',
86 'registration_page_id' => '',
87 'login_page_id' => '',
88 'cart_page_id' => '',
89 'receipt_page_id' => '',
90 'shop_page_id' => '',
91 'customer_profile_page_id' => '',
92 'customer_profile_page_slug' => '',
93 'currency' => 'USD',
94 'store_address1' => '',
95 'store_address2' => '',
96 'store_city' => '',
97 'store_country' => '',
98 'store_postcode' => '',
99 'store_state' => '',
100 'show_relevant_product_in_single_page' => 'yes',
101 'show_relevant_product_in_modal' => '',
102 'order_mode' => 'test',
103 'variation_view' => 'both',
104 'variation_columns' => 'masonry',
105 'enable_early_payment_for_installment' => 'yes',
106 'subscription_management_mode' => 'gateway_managed',
107 'subscription_system_charge' => 'no',
108 'modules_settings' => [],
109 'min_receipt_number' => '1',
110 'inv_prefix' => 'INV-',
111 'weight_unit' => 'kg',
112 'dimension_unit' => 'cm'
113 ];
114
115 return apply_filters('fluent_cart/store_settings/values', $defaultSettings, []);
116 }
117
118 /**
119 * @return array
120 *
121 * Get all store settings fields
122 * @hook to use apply_filters("fluent_cart/store_setting_fields", $fields)
123 */
124 public function fields($params = []): array
125 {
126
127 $pages = Pages::getPages('');
128 $previewLinks = [
129 'shop_page_id' => $this->getShopPage(),
130 'customer_profile_page_id' => $this->getCustomerProfilePage(),
131 'cart_page_id' => $this->getCartPage(),
132 'checkout_page_id' => $this->getCheckoutPage(),
133 'receipt_page_id' => $this->getReceiptPage()
134 ];
135 $isProActive = App::isProActive();
136 $proFeatureIcon = Vite::getAssetUrl('images/crown.svg');
137
138 // Read-only schedule for store-managed subscription renewals. Pulled live
139 // from the same map the scheduler uses, so it stays accurate under the
140 // fluent_cart/renewal/advance_creation_days filter. No editable knob — the timing is
141 // deliberately built-in; developers tune it via that filter.
142 $invoiceScheduleMap = RenewalService::getAdvanceCreationDaysMap();
143 $invoiceScheduleLabels = [
144 'daily' => __('Daily', 'fluent-cart'),
145 'weekly' => __('Weekly', 'fluent-cart'),
146 'monthly' => __('Monthly', 'fluent-cart'),
147 'quarterly' => __('Quarterly', 'fluent-cart'),
148 'half_yearly' => __('Half-yearly', 'fluent-cart'),
149 'yearly' => __('Yearly', 'fluent-cart'),
150 ];
151 // Structured renewal-order schedule for the SubscriptionModeManager
152 // component (status card + guarded edit dialog).
153 $invoiceScheduleList = [];
154 foreach ($invoiceScheduleLabels as $invoiceScheduleKey => $invoiceScheduleLabel) {
155 if (!isset($invoiceScheduleMap[$invoiceScheduleKey])) {
156 continue;
157 }
158 $invoiceScheduleDays = (int) $invoiceScheduleMap[$invoiceScheduleKey];
159 $invoiceScheduleList[] = [
160 'label' => $invoiceScheduleLabel,
161 'when' => $invoiceScheduleDays <= 0
162 ? __('on the due date', 'fluent-cart')
163 /* translators: %d: number of days before the due date */
164 : sprintf(_n('%d day before due date', '%d days before due date', $invoiceScheduleDays, 'fluent-cart'), $invoiceScheduleDays),
165 ];
166 }
167
168 // Gateways declaring `system_subscription` — the only ones
169 // subscription_system_charge can ever auto-charge.
170 $systemChargeGateways = [];
171 foreach (GatewayManager::getInstance()->all() as $systemChargeGateway) {
172 if (!$systemChargeGateway->has('system_subscription') || $systemChargeGateway->isUpcoming()) {
173 continue;
174 }
175 $systemChargeGatewayMeta = $systemChargeGateway->getMeta();
176 $systemChargeGateways[] = [
177 'label' => Arr::get($systemChargeGatewayMeta, 'admin_title')
178 ?: Arr::get($systemChargeGatewayMeta, 'label')
179 ?: Arr::get($systemChargeGatewayMeta, 'title'),
180 'active' => $systemChargeGateway->isEnabled(),
181 ];
182 }
183
184 $fields = [
185 'setting_tabs' => [
186 'type' => 'section',
187 'disable_nesting' => true,
188 'default_tab' => 'store_setup',
189 'hide_tab_switch' => true,
190 'schema' => [
191 'store_setup' => [
192 'id' => '',
193 'title' => __('Store Setup', 'fluent-cart'),
194 'show_title' => false,
195 'type' => 'section',
196 'disable_nesting' => true,
197 'columns' => [
198 'default' => 1,
199 'md' => 1
200 ],
201 'schema' => [
202 'name_grid' => [
203 'type' => 'grid',
204 'columns' => [
205 'default' => 1,
206 'md' => 3
207 ],
208 'disable_nesting' => true,
209 'schema' => [
210 'label' => [
211 'type' => 'html',
212 'value' => '<span class="setting-label">' . __('Store Name', 'fluent-cart') . '</span>
213 <div class="form-note">' . __('Enter the public name of your online store.', 'fluent-cart') . '</div>'
214 ],
215 "store_name" => [
216 'wrapperClass' => 'col-span-2 flex items-center',
217 "label" => '',
218 "type" => "input",
219 "value" => "",
220 ],
221 ]
222 ],
223
224 'hr' => [
225 'type' => 'html',
226 'value' => '<hr class="settings-divider">'
227 ],
228
229
230 'logo_grid' => [
231 'type' => 'grid',
232 'columns' => [
233 'default' => 1,
234 'md' => 3
235 ],
236 'disable_nesting' => true,
237 'schema' => [
238 'label' => [
239 'type' => 'html',
240 'value' => '<span class="setting-label">' . __('Store Logo', 'fluent-cart') . '</span>
241 <div class="form-note">' . __("Upload your brand's logo. Recommended width: 512 pixels minimum.", 'fluent-cart') . '</div>'
242 ],
243 "store_logo" => [
244 "label" => false,
245 "type" => "media",
246 "value" => "",
247 'multiple' => false,
248 // if `condition_type` not specified then all condition type will be `and`
249
250 // 'conditions' => [
251 // [
252 // 'key' => 'store_name',
253 // 'operator' => '==',
254 // 'value' => [
255 // 'accessor' => 'order_mode'
256 // ]
257 // ],
258 // ],
259
260 ],
261 ]
262 ],
263
264 'hr2' => [
265 'type' => 'html',
266 'value' => '<hr class="settings-divider">'
267 ],
268
269 'mode_grid' => [
270 'type' => 'grid',
271 'columns' => [
272 'default' => 1,
273 'md' => 3
274 ],
275 'disable_nesting' => true,
276 'schema' => [
277 'label' => [
278 'type' => 'html',
279 'value' => '<span class="setting-label">' . __('Store Mode', 'fluent-cart') . '</span>
280 <div class="form-note">' . __("Select your store's operating mode: `Test` for setup, `Live` for real transactions.", 'fluent-cart') . '</div>'
281 ],
282 'order_mode' => [
283 'wrapperClass' => 'col-span-2 flex items-center',
284 "label" => '',
285 "type" => "radio",
286 "options" => [
287 [
288 "label" => __('Live', 'fluent-cart'),
289 "value" => 'live',
290 ],
291 [
292 "label" => __('Test', 'fluent-cart'),
293 "value" => 'test',
294 ],
295 ],
296 "value" => "live"
297 ],
298 ]
299 ],
300
301 'settings_hr' => [
302 'type' => 'html',
303 'value' => '<hr class="settings-divider">'
304 ],
305
306 'address_grid' => [
307 'type' => 'grid',
308 'columns' => [
309 'default' => 1,
310 'md' => 3
311 ],
312 'disable_nesting' => true,
313 'schema' => [
314 'label' => [
315 'type' => 'html',
316 'value' => '<span class="setting-label">' . __('Store Address', 'fluent-cart') . '</span>
317 <div class="form-note">' . __("Provide your physical business address details.", 'fluent-cart') . '</div>'
318 ],
319 'address_input_grid' => [
320 'wrapperClass' => 'fct-compact-form',
321 'type' => 'grid',
322 'disable_nesting' => true,
323 'class' => 'col-span-2',
324 'schema' => [
325 'store_address_component' => [
326 'type' => 'component',
327 'component' => 'StoreSettings/AddressComponent',
328 'wrapperClass' => 'col-span-full'
329 ],
330 'store_address1' => [
331 'type' => 'hidden',
332 ],
333 'store_address2' => [
334 'type' => 'hidden',
335 ],
336 'store_city' => [
337 'type' => 'hidden',
338 ],
339 'store_postcode' => [
340 'type' => 'hidden',
341 ],
342 'store_country' => [
343 'type' => 'hidden',
344 ],
345 'store_state' => [
346 'type' => 'hidden',
347 ],
348 ]
349 ],
350 ]
351 ],
352
353
354 'settings_hr_2' => [
355 'type' => 'html',
356 'value' => '<hr class="settings-divider">'
357 ],
358
359 'business_details_grid' => [
360 'type' => 'grid',
361 'id' => 'business_details',
362 'columns' => [
363 'default' => 1,
364 'md' => 3
365 ],
366 'disable_nesting' => true,
367 'schema' => [
368 'label' => [
369 'type' => 'html',
370 'value' => '<span class="setting-label">' . __('Business Details', 'fluent-cart') . '</span>
371 <div class="form-note">' . __('Add your legal business identity details used for store records and compliance.', 'fluent-cart') . '</div>'
372 ],
373 'business_details_fields' => [
374 'type' => 'grid',
375 'columns' => [
376 'default' => 1,
377 'md' => 2
378 ],
379 'disable_nesting' => true,
380 'wrapperClass' => 'col-span-2',
381 'schema' => [
382 'company_name' => [
383 'label' => __('Company Name', 'fluent-cart'),
384 'type' => 'input',
385 'value' => '',
386 ],
387 'legal_registration_id' => [
388 'label' => __('Legal Registration ID', 'fluent-cart'),
389 'type' => 'input',
390 'value' => '',
391 ],
392 'seller_vat_id' => [
393 'label' => __('Seller VAT ID', 'fluent-cart'),
394 'type' => 'input',
395 'value' => '',
396 ],
397 'seller_tax_id' => [
398 'label' => __('Seller Tax ID', 'fluent-cart'),
399 'type' => 'input',
400 'value' => '',
401 ],
402 ]
403 ],
404 ]
405 ],
406
407 'settings_hr_3' => [
408 'type' => 'html',
409 'value' => '<hr class="settings-divider">'
410 ],
411
412 'currency_grid' => [
413 'type' => 'grid',
414 'columns' => [
415 'default' => 1,
416 'md' => 3
417 ],
418 'disable_nesting' => true,
419 'schema' => [
420 'label' => [
421 'type' => 'html',
422 'value' => '<span class="setting-label">' . __('Checkout Currency', 'fluent-cart') . '</span>
423 <div class="form-note">' . __("Select the primary currency for your store.", 'fluent-cart') . '</div>'
424 ],
425 "currency" => [
426 'wrapperClass' => 'col-span-2 flex items-center',
427 "label" => '',
428 "type" => "select",
429 'filterable' => true,
430 "options" => CurrencySettings::getFormattedCurrencies(),
431 "value" => "USD"
432 ],
433 ]
434 ],
435
436 'hr3' => [
437 'type' => 'html',
438 'value' => '<hr class="settings-divider">'
439 ],
440
441 'decimal_separator_grid' => [
442 'type' => 'grid',
443 'columns' => [
444 'default' => 1,
445 'md' => 3
446 ],
447 'disable_nesting' => true,
448 'schema' => [
449 'label' => [
450 'type' => 'html',
451 'value' => '<span class="setting-label">' . __('Number Format', 'fluent-cart') . '</span>
452 <div class="form-note">' . __("Select the character used to separate thousands or decimals in prices.", 'fluent-cart') . '</div>'
453 ],
454 'decimal_separator' => [
455 'wrapperClass' => 'col-span-2 flex items-start flex-col',
456 "label" => '',
457 "type" => "radio",
458 "options" => [
459 [
460 "label" => __('Comma & Dot (eg 10,000.00)', 'fluent-cart'),
461 "value" => 'dot'
462 ],
463 [
464 "label" => __('Dot & Comma (eg 10.000,00)', 'fluent-cart'),
465 "value" => 'comma'
466 ],
467 ],
468 "value" => "dot"
469 ],
470 ]
471 ],
472
473 'hr4' => [
474 'type' => 'html',
475 'value' => '<hr class="settings-divider">'
476 ],
477
478 'currency_position_grid' => [
479 'type' => 'grid',
480 'columns' => [
481 'default' => 1,
482 'md' => 3
483 ],
484 'disable_nesting' => true,
485 'schema' => [
486 'label' => [
487 'type' => 'html',
488 'value' => '<span class="setting-label">' . __('Currency Formatting', 'fluent-cart') . '</span>
489 <div class="form-note">' . __("Select how the currency should be formatted.", 'fluent-cart') . '</div>'
490 ],
491 'currency_position' => [
492 'wrapperClass' => 'col-span-2 flex items-center',
493 "label" => '',
494 "type" => "select",
495 "options" => [
496 [
497 "label" => __('Symbol before (eg: $100)', 'fluent-cart'),
498 "value" => 'before',
499 ],
500 [
501 "label" => __('Symbol after (eg: 100$)', 'fluent-cart'),
502 "value" => 'after',
503 ],
504 [
505 "label" => __('ISO before (eg: USD 100)', 'fluent-cart'),
506 "value" => 'iso_before',
507 ],
508 [
509 "label" => __('ISO after (eg: 100 USD)', 'fluent-cart'),
510 "value" => 'iso_after',
511 ],
512 [
513 "label" => __('Symbol & ISO (eg: $100 USD)', 'fluent-cart'),
514 "value" => 'symbool_before_iso',
515 ],
516 [
517 "label" => __('ISO & Symbol (eg: USD 100$)', 'fluent-cart'),
518 "value" => 'symbool_after_iso',
519 ],
520 [
521 "label" => __('ISO-Symbol (eg: USD $100)', 'fluent-cart'),
522 "value" => 'symbool_and_iso',
523 ],
524 ],
525 "value" => "before"
526 ],
527 ]
528 ],
529
530 'hr5' => [
531 'type' => 'html',
532 'value' => '<hr class="settings-divider">'
533 ],
534
535
536 'checkout_style_grid' => [
537 'type' => 'grid',
538 'columns' => [
539 'default' => 1,
540 'md' => 3
541 ],
542 'disable_nesting' => true,
543 'schema' => [
544 'label' => [
545 'type' => 'html',
546 'value' => '<span class="setting-label">' . __('Payment View', 'fluent-cart') . '</span>
547 <div class="form-note">' . __("Select how payment options are visually presented on checkout.", 'fluent-cart') . '</div>'
548 ],
549 "checkout_method_style" => [
550 'wrapperClass' => 'col-span-2 flex items-center',
551 "type" => "component",
552 'component' => 'PaymentView',
553 'label' => false,
554 "options" => [
555 [
556 "label" => '',
557 "value" => 'logo',
558 ],
559 [
560 "label" => __('Label Selector', 'fluent-cart'),
561 "value" => 'radio',
562 ],
563 ],
564 "value" => "logo"
565 ],
566 ]
567 ],
568
569 // 'settings_hr_modal' => [
570 // 'type' => 'html',
571 // 'value' => '<hr class="settings-divider">'
572 // ],
573 //
574 // 'modal_checkout_grid' => [
575 // 'type' => 'grid',
576 // 'columns' => [
577 // 'default' => 1,
578 // 'md' => 3
579 // ],
580 // 'disable_nesting' => true,
581 // 'schema' => [
582 // 'label' => [
583 // 'type' => 'html',
584 // 'value' => '<span class="setting-label">' . __('Buy Now Button Behavior', 'fluent-cart') . '</span>
585 // <div class="form-note">' . __("Choose how the Buy Now button behaves. Modal checkout provides a seamless experience without leaving the product page.", 'fluent-cart') . '</div>'
586 // ],
587 // "enable_modal_checkout" => [
588 // 'wrapperClass' => 'col-span-2 flex items-center',
589 // "label" => '',
590 // "type" => "radio",
591 // "options" => [
592 // [
593 // "label" => __('Redirect to Checkout Page', 'fluent-cart'),
594 // "value" => 'no',
595 // ],
596 // [
597 // "label" => __('Open Checkout in Modal', 'fluent-cart'),
598 // "value" => 'yes',
599 // ],
600 // ],
601 // "value" => "no"
602 // ],
603 // ]
604 // ],
605 ],
606 ],
607 // 'button_setup' => [
608 // 'title' => __('Button Setup', 'fluent-cart'),
609 // 'type' => 'tab-pane',
610 // 'disable_nesting' => true,
611 // 'schema' => [
612 // 'button_setup_settings' => [
613 // 'title' => __('Button Setup', 'fluent-cart'),
614 // 'type' => 'section',
615 // 'disable_nesting' => true,
616 // 'columns' => [
617 // 'default' => 1,
618 // 'md' => 2
619 // ],
620 // 'schema' => [
621 // "checkout_button_text" => [
622 // "label" => __('Checkout Button Text', 'fluent-cart'),
623 // "type" => "input",
624 // "value" => __('Buy now', 'fluent-cart')
625 // ],
626 // "cart_button_text" => [
627 // "label" => __('Cart Button Text', 'fluent-cart'),
628 // "type" => "input",
629 // "value" => __('Add to cart', 'fluent-cart')
630 // ],
631 //
632 // "popup_button_text" => [
633 // "label" => __('Popup Button Text', 'fluent-cart'),
634 // "type" => "input",
635 // "value" => __('View Product', 'fluent-cart')
636 // ],
637 // "out_of_stock_button_text" => [
638 // "label" => __('Out of Stock Button Text', 'fluent-cart'),
639 // "type" => "input",
640 // "value" => __('Out of stock', 'fluent-cart')
641 // ],
642 // "view_cart_button_text" => [
643 // "label" => __('View Cart Button Text', 'fluent-cart'),
644 // "type" => "input",
645 // "value" => __('View Cart', 'fluent-cart')
646 // ],
647 //
648 // "note_for_user_account_creation" => [
649 // "label" => __('Note for User Account Creation,', 'fluent-cart'),
650 // "type" => "input",
651 // "value" => __('You have subscription product in your cart, an account will be created', 'fluent-cart')
652 // ],
653 // ],
654 // ]
655 // ]
656 // ],
657 'pages_setup' => [
658 'id' => '',
659 'title' => __('Pages Setup', 'fluent-cart'),
660 'show_title' => false,
661 'type' => 'section',
662 'disable_nesting' => true,
663 'columns' => [
664 'default' => 1,
665 'md' => 1
666 ],
667 'schema' => [
668
669 'shop_page_grid' => [
670 'type' => 'grid',
671 'columns' => [
672 'default' => 1,
673 'md' => 3,
674 ],
675 'disable_nesting' => true,
676 'schema' => [
677 'label' => [
678 'type' => 'html',
679 'value' => '<span class="setting-label">' . __('Select Shop Page', 'fluent-cart') . '</span>
680 <div class="form-note">' . __("Select the page that showcases all of your products.", 'fluent-cart') . '</div>'
681 ],
682 'shop_page_id' => [
683 'wrapperClass' => 'col-span-2',
684 'page_title' => __('Shop', 'fluent-cart'),
685 'type' => 'component',
686 'component' => 'StoreSettings/PageSelector',
687 'page_key' => 'shop_page_id',
688 'preview_link' => $previewLinks['shop_page_id'],
689 'options' => $pages,
690 'hide_note' => true,
691 'value' => '',
692 'note' => \FluentCart\App\Helpers\Helper::getShortcodeInstructionString(
693 '[fluent_cart_products]',
694 __('Products', 'fluent-cart')
695 ),
696 ],
697 ]
698 ],
699
700 'hr1' => [
701 'type' => 'html',
702 'value' => '<hr class="settings-divider">'
703 ],
704
705 'customer_profile_grid' => [
706 'type' => 'grid',
707 'columns' => [
708 'default' => 1,
709 'md' => 3
710 ],
711 'disable_nesting' => true,
712 'schema' => [
713 'label' => [
714 'type' => 'html',
715 'value' => '<span class="setting-label">' . __('Select Customer Profile Page', 'fluent-cart') . '</span>
716 <div class="form-note">' . __("Select the page where customers will manage their profile, orders, and downloads.", 'fluent-cart') . '</div>'
717 ],
718 'customer_profile_page_id' => [
719 'wrapperClass' => 'col-span-2',
720 'label' => false,
721 'page_title' => __('Account', 'fluent-cart'),
722 'type' => 'component',
723 'component' => 'StoreSettings/PageSelector',
724 'page_key' => 'customer_profile_page_id',
725 'preview_link' => $previewLinks['customer_profile_page_id'],
726 'hide_note' => true,
727 'options' => $pages,
728 'value' => '',
729 'note' => \FluentCart\App\Helpers\Helper::getShortcodeInstructionString(
730 '[fluent_cart_customer_profile]',
731 __('Account', 'fluent-cart')
732 ),
733 ],
734 ]
735 ],
736
737 'hr2' => [
738 'type' => 'html',
739 'value' => '<hr class="settings-divider">'
740 ],
741
742 'cart_page_grid' => [
743 'type' => 'grid',
744 'columns' => [
745 'default' => 1,
746 'md' => 3
747 ],
748 'disable_nesting' => true,
749 'schema' => [
750 'label' => [
751 'type' => 'html',
752 'value' => '<span class="setting-label">' . __('Select Cart Page', 'fluent-cart') . '</span>
753 <div class="form-note">' . __("Select the page that will display customer's current shopping cart.", 'fluent-cart') . '</div>'
754 ],
755 'cart_page_id' => [
756 'wrapperClass' => 'col-span-2',
757 'label' => false,
758 'page_title' => __('Cart', 'fluent-cart'),
759 'type' => 'component',
760 'component' => 'StoreSettings/PageSelector',
761 'page_key' => 'cart_page_id',
762 'preview_link' => $previewLinks['cart_page_id'],
763 'hide_note' => true,
764 'options' => $pages,
765 'value' => '',
766 'note' => \FluentCart\App\Helpers\Helper::getShortcodeInstructionString(
767 '[fluent_cart_cart]',
768 __('Cart', 'fluent-cart')
769 ),
770 ],
771 ]
772 ],
773
774 'hr3' => [
775 'type' => 'html',
776 'value' => '<hr class="settings-divider">'
777 ],
778
779 'receipt_page_grid' => [
780 'type' => 'grid',
781 'columns' => [
782 'default' => 1,
783 'md' => 3
784 ],
785 'disable_nesting' => true,
786 'schema' => [
787 'label' => [
788 'type' => 'html',
789 'value' => '<span class="setting-label">' . __('Select Receipt Page', 'fluent-cart') . '</span>
790 <div class="form-note">' . __("Select the page that will display order summary after a successful purchase.", 'fluent-cart') . '</div>'
791 ],
792 'receipt_page_id' => [
793 'wrapperClass' => 'col-span-2',
794 'label' => false,
795 'page_title' => __('Receipt', 'fluent-cart'),
796 'type' => 'component',
797 'component' => 'StoreSettings/PageSelector',
798 'page_key' => 'receipt_page_id',
799 'preview_link' => $previewLinks['receipt_page_id'],
800 'hide_note' => true,
801 'options' => $pages,
802 'value' => '',
803 'note' => \FluentCart\App\Helpers\Helper::getShortcodeInstructionString(
804 '[fluent_cart_receipt]',
805 __('Receipt', 'fluent-cart')
806 ),
807 ],
808 ]
809 ],
810
811 'hr4' => [
812 'type' => 'html',
813 'value' => '<hr class="settings-divider">'
814 ],
815
816 'checkout_page_grid' => [
817 'type' => 'grid',
818 'columns' => [
819 'default' => 1,
820 'md' => 3
821 ],
822 'disable_nesting' => true,
823 'schema' => [
824 'label' => [
825 'type' => 'html',
826 'value' => '<span class="setting-label">' . __('Select Checkout Page', 'fluent-cart') . '</span>
827 <div class="form-note">' . __("Select the page where customers will finalize their purchase.", 'fluent-cart') . '</div>'
828 ],
829 'checkout_page_id' => [
830 'wrapperClass' => 'col-span-2',
831 'label' => false,
832 'page_title' => __('Checkout', 'fluent-cart'),
833 'type' => 'component',
834 'component' => 'StoreSettings/PageSelector',
835 'page_key' => 'checkout_page_id',
836 'preview_link' => $previewLinks['checkout_page_id'],
837 'hide_note' => true,
838 'options' => $pages,
839 'value' => '',
840 'note' => \FluentCart\App\Helpers\Helper::getShortcodeInstructionString(
841 '[fluent_cart_checkout]',
842 __('Checkout', 'fluent-cart')
843 ),
844 ],
845 ]
846 ]
847 ]
848 ],
849 'single_product_setup' => [
850 'title' => __('Product Page', 'fluent-cart'),
851 'show_title' => false,
852 'type' => 'section',
853 'disable_nesting' => true,
854 'columns' => [
855 'default' => 1,
856 'md' => 1
857 ],
858 'schema' => [
859 'product_settings_grid' => [
860 'type' => 'grid',
861 'columns' => [
862 'default' => 1,
863 'md' => 3
864 ],
865 'disable_nesting' => true,
866 'schema' => [
867 'label' => [
868 'type' => 'html',
869 'value' => '<span class="setting-label">' . __('Single Product Setup', 'fluent-cart') . '</span>
870 <div class="form-note">' . __("Control the display of relevant information.", 'fluent-cart') . '</div>'
871 ],
872 'fields' => [
873 'type' => 'grid',
874 'columns' => [
875 'default' => 1,
876 'md' => 1
877 ],
878 'disable_nesting' => true,
879 'schema' => [
880 "show_relevant_product_in_single_page" => [
881 "label" => __('Show Relevant In Single Page', 'fluent-cart'),
882 "type" => "checkbox",
883 "value" => "yes"
884 ],
885 "show_relevant_product_in_modal" => [
886 "label" => __('Show Relevant In Product Modal', 'fluent-cart'),
887 "type" => "checkbox",
888 "value" => "no"
889 ],
890 ]
891 ]
892
893 ]
894 ],
895 'hr' => [
896 'type' => 'html',
897 'value' => '<hr class="settings-divider">'
898 ],
899
900 'image_zoom_grid' => [
901 'type' => 'grid',
902 'columns' => [
903 'default' => 1,
904 'md' => 3
905 ],
906 'disable_nesting' => true,
907 'schema' => [
908 'label' => [
909 'type' => 'html',
910 'value' => '<span class="setting-label">' . __('Image Zooming', 'fluent-cart') . '</span>
911 <div class="form-note">' . __("Enable Image zoom in single product.", 'fluent-cart') . '</div>'
912 ],
913 'fields' => [
914 'type' => 'grid',
915 'columns' => [
916 'default' => 1,
917 'md' => 1
918 ],
919 'disable_nesting' => true,
920 'schema' => [
921 "enable_image_zoom_in_single_product" => [
922 "label" => __('Enable Zoom in Single Product', 'fluent-cart'),
923 "type" => "checkbox",
924 "value" => "no"
925 ],
926 "enable_image_zoom_in_modal" => [
927 "label" => __('Enable Zoom in Modal', 'fluent-cart'),
928 "type" => "checkbox",
929 "value" => "no"
930 ],
931 ]
932 ]
933
934 ]
935 ],
936
937 'hr_image_zoom' => [
938 'type' => 'html',
939 'value' => '<hr class="settings-divider">'
940 ],
941
942 'variation_grid' => [
943 'type' => 'grid',
944 'columns' => [
945 'default' => 1,
946 'md' => 3
947 ],
948 'disable_nesting' => true,
949 'schema' => [
950 'label' => [
951 'type' => 'html',
952 'value' => '<span class="setting-label">' . __('Variation View', 'fluent-cart') . '</span>
953 <div class="form-note">' . __("Defines how product variations are visually presented to customers.", 'fluent-cart') . '</div>'
954 ],
955 "variation_view" => [
956 'wrapperClass' => 'col-span-2 flex items-center',
957 "label" => false,
958 "type" => "select",
959 "options" => [
960 [
961 "label" => __('Image', 'fluent-cart'),
962 "value" => 'image',
963 ],
964 [
965 "label" => __('Text', 'fluent-cart'),
966 "value" => 'text',
967 ],
968 [
969 "label" => __('Image with Text', 'fluent-cart'),
970 "value" => 'both',
971 ],
972 ],
973 "value" => ""
974 ],
975
976 ]
977 ],
978 'hr2' => [
979 'type' => 'html',
980 'value' => '<hr class="settings-divider">'
981 ],
982 'variation_column_grid' => [
983 'type' => 'grid',
984 'columns' => [
985 'default' => 1,
986 'md' => 3
987 ],
988 'disable_nesting' => true,
989 'schema' => [
990 'label' => [
991 'type' => 'html',
992 'value' => '<span class="setting-label">' . __('Variation Columns', 'fluent-cart') . '</span>
993 <div class="form-note">' . __("Set the column layout for how product variations are displayed within product sections.", 'fluent-cart') . '</div>'
994 ],
995 "variation_columns" => [
996 'wrapperClass' => 'col-span-2 flex items-center',
997 "label" => false,
998 "type" => "select",
999 "options" => [
1000 [
1001 "label" => __('One Column', 'fluent-cart'),
1002 "value" => 'one',
1003 ],
1004 [
1005 "label" => __('Two Columns', 'fluent-cart'),
1006 "value" => 'two',
1007 ],
1008 [
1009 "label" => __('Three Columns', 'fluent-cart'),
1010 "value" => 'three',
1011 ],
1012 [
1013 "label" => __('Four Columns', 'fluent-cart'),
1014 "value" => 'four',
1015 ],
1016 [
1017 "label" => __('Masonry', 'fluent-cart'),
1018 "value" => 'masonry',
1019 ],
1020 ],
1021 "value" => ""
1022 ],
1023
1024 ]
1025 ],
1026
1027 'hr3' => [
1028 'type' => 'html',
1029 'value' => '<hr class="settings-divider">'
1030 ],
1031 'product_slug_grid' => [
1032 'type' => 'grid',
1033 'columns' => [
1034 'default' => 1,
1035 'md' => 3
1036 ],
1037 'disable_nesting' => true,
1038 'schema' => [
1039 'label' => [
1040 'type' => 'html',
1041 'value' => '<span class="setting-label">' . __('Product Slug', 'fluent-cart') . '</span>
1042 <div class="form-note">' . __("Set Product Slug.", 'fluent-cart') . '</div>'
1043 ],
1044 "product_slug" => [
1045 'wrapperClass' => 'col-span-2 flex items-center',
1046 "label" => false,
1047 "type" => "input",
1048 "value" => ""
1049 ],
1050
1051 ]
1052 ],
1053 ],
1054 ],
1055 'cart_and_checkout' => [
1056 'title' => __('Cart & checkout', 'fluent-cart'),
1057 'show_title' => false,
1058 'type' => 'section',
1059 'disable_nesting' => true,
1060 'columns' => [
1061 'default' => 1,
1062 'md' => 1
1063 ],
1064 'schema' => [
1065 "show_cart_icon_in_body" => [
1066 "label" => __('Cart Icon In Body', 'fluent-cart'),
1067 "type" => "checkbox",
1068 "value" => "yes",
1069 'note' => sprintf(
1070 /* translators: %1$s: Display instruction text, %2$s: "Use this" text, %3$s: Copy to clipboard tooltip, %4$s: CSS class name, %5$s: CSS class description text */
1071 "<div class='pl-6'><p>%1\$s</p><p>%2\$s <code class='copyable-content' title='%3\$s'>%4\$s</code> %5\$s</p></div>",
1072 __('Display the cart icon in the body of the website', 'fluent-cart'),
1073 __('Use this', 'fluent-cart'),
1074 __('Copy to clipboard', 'fluent-cart'),
1075 'fcart-cart-toggle-button',
1076 __('CSS class to display the cart link/icon anywhere else.', 'fluent-cart')
1077 )
1078 ],
1079 'hr2' => [
1080 'type' => 'html',
1081 'value' => '<hr class="settings-divider">'
1082 ],
1083 "require_logged_in" => [
1084 "label" => __('Require user to be logged in for checkout (Coming soon)', 'fluent-cart'),
1085 "type" => "checkbox",
1086 "value" => "no",
1087 'note' => "<div class='pl-6'>" . __('Enforce that customers must be logged into their account to complete a purchase.', 'fluent-cart') . "</div>",
1088 'wrapperClass' => 'disabled'
1089 ],
1090 'hr3' => [
1091 'type' => 'html',
1092 'value' => '<hr class="settings-divider">'
1093 ],
1094
1095 'user_account_creation_mode_grid' => [
1096 'type' => 'grid',
1097 'columns' => [
1098 'default' => 1,
1099 'md' => 3
1100 ],
1101 'disable_nesting' => true,
1102 'schema' => [
1103 'label' => [
1104 'type' => 'html',
1105 'value' => '<span class="setting-label">' . __('User Account Creation Mode', 'fluent-cart') . '</span>
1106 <div class="form-note">' . __("User account will be created regardless of the mode if the order has a subscription.", 'fluent-cart') . '</div>'
1107 ],
1108 'user_account_input_grid' => [
1109 'type' => 'grid',
1110 'disable_nesting' => true,
1111 'class' => 'col-span-2',
1112 'schema' => [
1113 'user_account_creation_mode' => [
1114 'wrapperClass' => 'col-span-2 flex items-center',
1115 "label" => '',
1116 "type" => "radio",
1117 "options" => [
1118 [
1119 "label" => __('Create User Account Automatically after payment
1120 ', 'fluent-cart'),
1121 "value" => 'all',
1122 'note' => "<div class='my-[2px] form-note'>" . __('User account will be created automatically after payment.', 'fluent-cart') . "</div>",
1123 'option_class' => 'mb-2.5'
1124 ],
1125 [
1126 "label" => __('Give checkbox to create account on checkout page', 'fluent-cart'),
1127 "value" => 'user_choice',
1128 'note' => "<div class='my-[2px] form-note'>" . __('User will have an option to create an account during checkout.', 'fluent-cart') . "</div>",
1129 'option_class' => 'mb-2.5'
1130 ],
1131 [
1132 "label" => __('No need to create account for onetime purchases', 'fluent-cart'),
1133 "value" => 'only_subscription',
1134 'note' => "<div class='my-[2px] form-note'>" . __('User account will not be created for onetime purchases.', 'fluent-cart') . "</div>",
1135 ],
1136 ],
1137 "value" => "all"
1138 ],
1139 ]
1140 ],
1141 ]
1142 ],
1143
1144
1145 // "allow_checkout_signup" => [
1146 // "label" => __('Allow customers to create account during one-time checkout', 'fluent-cart'),
1147 // "type" => "checkbox",
1148 // "value" => "no",
1149 // 'note' => "<div class='pl-6'>" . __('Provide an option for customers to create a user account during a single, non-recurring checkout process.', 'fluent-cart') . "</div>"
1150 // ],
1151 // "force_ssl" => [
1152 // "label" => __('Force page to SSL (https)', 'fluent-cart'),
1153 // "type" => "checkbox",
1154 // "value" => "no"
1155 // ],
1156 'hr5' => [
1157 'type' => 'html',
1158 'value' => '<hr class="settings-divider">'
1159 ],
1160 "hide_coupon_field" => [
1161 "label" => __('Hide coupon field on checkout', 'fluent-cart'),
1162 "type" => "checkbox",
1163 "value" => "no",
1164 'note' => "<div class='pl-6'>" . __('Hide the coupon code input field on the checkout page.', 'fluent-cart') . "</div>"
1165 ],
1166 'hr6' => [
1167 'type' => 'html',
1168 'value' => '<hr class="settings-divider">'
1169 ],
1170 'order_invoice_grid' => [
1171 'type' => 'grid',
1172 'columns' => [
1173 'default' => 1,
1174 'md' => 3
1175 ],
1176 'disable_nesting' => true,
1177 'schema' => [
1178 'label' => [
1179 'type' => 'html',
1180 'value' => '<span class="setting-label">' . __('Receipt Settings', 'fluent-cart') . '</span>
1181 <div class="form-note">' . __("Setup your receipt.", 'fluent-cart') . '</div>'
1182 ],
1183 'invoice_input_grid' => [
1184 'type' => 'grid',
1185 'disable_nesting' => true,
1186 'class' => 'col-span-2',
1187 'schema' => [
1188 "min_receipt_number" => [
1189 'wrapperClass' => 'col-span-2 flex flex-col',
1190 "label" => __('Minimum Receipt Number', 'fluent-cart'),
1191 "type" => "input",
1192 "value" => "",
1193 'note' => sprintf(
1194 "<div class=''>%s</div>",
1195 sprintf(
1196 /* translators: %s is the next receipt number */
1197 __('Next Receipt Number: %s', 'fluent-cart'),
1198 OrderService::getNextReceiptNumber()
1199 )
1200 ),
1201
1202 ],
1203 "inv_prefix" => [
1204 'wrapperClass' => 'col-span-2 flex flex-col',
1205 "label" => __('Receipt Prefix', 'fluent-cart'),
1206 "type" => "input",
1207 "value" => "",
1208 ],
1209 ]
1210 ],
1211 ]
1212 ],
1213 ]
1214 ],
1215 'subscriptions_setup' => [
1216 'title' => __('Subscriptions', 'fluent-cart'),
1217 'show_title' => false,
1218 'type' => 'section',
1219 'disable_nesting' => true,
1220 'columns' => [
1221 'default' => 1,
1222 'md' => 1
1223 ],
1224 'schema' => [
1225 'subscription_early_payment_grid' => [
1226 'type' => 'grid',
1227 'wrapperClass' => 'items-start mb-6',
1228 'columns' => [
1229 'default' => 1,
1230 'md' => 3
1231 ],
1232 'disable_nesting' => true,
1233 'schema' => [
1234 'label' => [
1235 'type' => 'html',
1236 'value' => '<span class="setting-label">' . __('Early Payment', 'fluent-cart') . (!$isProActive ? ' <img src="' . esc_url($proFeatureIcon) . '" alt="' . esc_attr__('Pro feature', 'fluent-cart') . '" class="pro-feature-icon" style="margin-left: 6px; width: 14px; height: 14px; display: inline-block; vertical-align: text-top;" />' : '') . '</span>
1237 <div class="form-note">' . __('Let customers pay remaining installments before their due date.', 'fluent-cart') . '</div>'
1238 ],
1239 'fields' => [
1240 'type' => 'grid',
1241 'columns' => [
1242 'default' => 1,
1243 'md' => 1
1244 ],
1245 'disable_nesting' => true,
1246 'class' => 'col-span-2',
1247 'schema' => [
1248 'enable_early_payment_for_installment' => [
1249 'label' => __('Enable early payment for installment', 'fluent-cart'),
1250 'type' => 'checkbox',
1251 'value' => 'yes',
1252 'disabled' => !$isProActive,
1253 'wrapperClass' => !$isProActive ? 'disabled' : '',
1254 'note' => !$isProActive
1255 ? "<div class='pl-6'>" . __('This is a FluentCart Pro feature.', 'fluent-cart') . "</div>"
1256 : "<div class='pl-6'>" . __('Allow customers to pay remaining installments early from subscription screens.', 'fluent-cart') . "</div>"
1257 ]
1258 ]
1259 ]
1260 ]
1261 ],
1262 'subscription_management_mode_grid' => [
1263 'type' => 'grid',
1264 'wrapperClass' => 'items-start',
1265 'columns' => [
1266 'default' => 1,
1267 'md' => 3
1268 ],
1269 'disable_nesting' => true,
1270 'schema' => [
1271 'label' => [
1272 'type' => 'html',
1273 'value' => sprintf(
1274 /* translators: 1: setting label, 2: setting description */
1275 '<span class="setting-label">%1$s</span>
1276 <div class="form-note">%2$s</div>',
1277 __('Renewal Billing', 'fluent-cart'),
1278 __('Choose how recurring subscription payments are collected.', 'fluent-cart')
1279 )
1280 ],
1281 'fields' => [
1282 'type' => 'grid',
1283 'columns' => [
1284 'default' => 1,
1285 'md' => 1
1286 ],
1287 'disable_nesting' => true,
1288 'class' => 'col-span-2',
1289 'schema' => [
1290 // Compact, guarded control: shows only the CURRENT mode with a
1291 // short how-it-works summary and a Change button. Editing goes
1292 // through a disclaimer confirm + dialog so the store-wide
1293 // billing decision can never be flipped by a stray click.
1294 'subscription_management_mode' => [
1295 'label' => false,
1296 'component' => 'StoreSettings/SubscriptionModeManager',
1297 'disable_nesting' => true,
1298 'value' => 'gateway_managed',
1299 // Read-only renewal-order schedule, built live from the
1300 // same map the scheduler uses (filterable).
1301 'schedule_items' => $invoiceScheduleList,
1302 // Which gateways auto-charge is actually possible on.
1303 'system_charge_gateways' => $systemChargeGateways,
1304 ],
1305 // Keep these in the form payload — the component above writes
1306 // them; without a schema entry the keys would drop out of the
1307 // saved values.
1308 'subscription_system_charge' => [
1309 'type' => 'hidden',
1310 'value' => 'no',
1311 ],
1312 'subscription_manual_fallback' => [
1313 'type' => 'hidden',
1314 'value' => 'no',
1315 ],
1316 ]
1317 ]
1318 ]
1319 ],
1320 ]
1321 ],
1322 ]
1323 ],
1324 ];
1325
1326
1327 // Only show weight/dimension unit fields to users with shipping-sensitive permission
1328 if (PermissionManager::hasPermission(['store/sensitive'])) {
1329 $storeSchema = &$fields['setting_tabs']['schema']['store_setup']['schema'];
1330 $storeSchema['weight_unit_grid'] = [
1331 'type' => 'grid',
1332 'columns' => ['default' => 1, 'md' => 3],
1333 'disable_nesting' => true,
1334 'schema' => [
1335 'label' => [
1336 'type' => 'html',
1337 'value' => '<span class="setting-label">' . __('Weight Unit', 'fluent-cart') . '</span>
1338 <div class="form-note">' . __('Unit used for product weight measurements.', 'fluent-cart') . '</div>'
1339 ],
1340 'weight_unit' => [
1341 'wrapperClass' => 'col-span-2 flex items-center',
1342 'label' => '',
1343 'type' => 'select',
1344 'options' => [
1345 ['label' => __('kg', 'fluent-cart'), 'value' => 'kg'],
1346 ['label' => __('g', 'fluent-cart'), 'value' => 'g'],
1347 ['label' => __('lbs', 'fluent-cart'), 'value' => 'lbs'],
1348 ['label' => __('oz', 'fluent-cart'), 'value' => 'oz'],
1349 ],
1350 'value' => 'kg'
1351 ],
1352 ]
1353 ];
1354 $storeSchema['dimension_unit_grid'] = [
1355 'type' => 'grid',
1356 'columns' => ['default' => 1, 'md' => 3],
1357 'disable_nesting' => true,
1358 'schema' => [
1359 'label' => [
1360 'type' => 'html',
1361 'value' => '<span class="setting-label">' . __('Dimension Unit', 'fluent-cart') . '</span>
1362 <div class="form-note">' . __('Unit used for product dimension measurements.', 'fluent-cart') . '</div>'
1363 ],
1364 'dimension_unit' => [
1365 'wrapperClass' => 'col-span-2 flex items-center',
1366 'label' => '',
1367 'type' => 'select',
1368 'options' => [
1369 ['label' => __('cm', 'fluent-cart'), 'value' => 'cm'],
1370 ['label' => __('mm', 'fluent-cart'), 'value' => 'mm'],
1371 ['label' => __('in', 'fluent-cart'), 'value' => 'in'],
1372 ['label' => __('m', 'fluent-cart'), 'value' => 'm'],
1373 ],
1374 'value' => 'cm'
1375 ],
1376 ]
1377 ];
1378 }
1379
1380 return apply_filters("fluent_cart/store_settings/fields", $fields, []);
1381 }
1382
1383 public function isModuleTabEnabled(): bool
1384 {
1385 $fields = $this->fields();
1386 return isset($fields['setting_tabs']['schema']['modules_tab']);
1387 }
1388
1389 /**
1390 * @param string|null $key like stripe or paypal
1391 * All store settings if key is not provided
1392 * @return mixed
1393 *
1394 */
1395
1396 public function get($key = null, $default = null)
1397 {
1398 if (empty($key)) {
1399 return $this->storeSettings;
1400 }
1401
1402 if (is_array($key)) {
1403 $data = Arr::only($this->storeSettings, $key);
1404 } else {
1405 $data = Arr::get($this->storeSettings, $key);
1406 }
1407
1408 return empty($data) ? $default : $data;
1409 }
1410
1411 public function moduleSettings(): array
1412 {
1413 $settings = $this->get('modules_settings', []);
1414 return Arr::wrap($settings);
1415 }
1416
1417 /**
1418 * @param array $settings like Stripe or PayPal settings array
1419 * Save store settings
1420 * @return array
1421 *
1422 */
1423 public function save(array $settings)
1424 {
1425 $prevSettings = get_option($this->optionKey, []);
1426 $prevSettings = wp_parse_args($prevSettings, $this->getDefaultSettings());
1427
1428 $settings = wp_parse_args($settings, $prevSettings);
1429
1430 $customerProfilePageId = Arr::get($settings, 'customer_profile_page_id', 0);
1431 if ($customerProfilePageId) {
1432 $settings['customer_profile_page_slug'] = $this->getCustomerDashboardPageSlug($customerProfilePageId, false);
1433 }
1434
1435 update_option($this->optionKey, $settings, true);
1436 $this->storeSettings = $settings;
1437 self::$cachedStoreSettings = $this->storeSettings;
1438 wp_cache_delete(self::CACHE_KEY, self::CACHE_GROUP);
1439
1440 $isSlugChanged = Arr::get($prevSettings, 'product_slug') !== Arr::get($settings, 'product_slug');
1441 $isAccountPageChanged = Arr::get($prevSettings, 'customer_profile_page_slug') !== Arr::get($settings, 'customer_profile_page_slug');
1442
1443 if ($isSlugChanged || $isAccountPageChanged) {
1444 flush_rewrite_rules();
1445 delete_option('rewrite_rules');
1446 }
1447
1448 return $this->storeSettings;
1449 }
1450
1451 public function set(string $key, $value)
1452 {
1453 return $this->save([
1454 $key => $value
1455 ]);
1456 }
1457
1458 public function getCurrency()
1459 {
1460 return $this->get('currency', 'USD');
1461 }
1462
1463 public function getCurrencySymbol()
1464 {
1465 $currency = $this->getCurrency();
1466 return CurrenciesHelper::getCurrencySign($currency);
1467 }
1468
1469 public function isCheckoutPage(): bool
1470 {
1471 global $post;
1472 $pageId = $this->getCheckoutPageId();
1473 return intval($pageId) === intval($post->ID);
1474 }
1475
1476 public function getCheckoutPageId()
1477 {
1478 return Arr::get($this->storeSettings, 'checkout_page_id');
1479 }
1480
1481 public function getPagesSettings(): array
1482 {
1483 return Arr::only(
1484 $this->storeSettings,
1485 [
1486 'checkout_page_id',
1487 'custom_payment_page_id',
1488 'registration_page_id',
1489 'login_page_id',
1490 'cart_page_id',
1491 'receipt_page_id',
1492 'shop_page_id',
1493 'customer_profile_page_id',
1494 'customer_profile_page_slug',
1495 ]
1496 );
1497 }
1498
1499 public function getCheckoutPage(): string
1500 {
1501 if ($pageID = $this->getCheckoutPageId()) {
1502 return $this->getPageLink($pageID);
1503 }
1504
1505 return '';
1506 }
1507
1508
1509 public function getCustomerProfilePageId()
1510 {
1511 return Arr::get($this->storeSettings, 'customer_profile_page_id');
1512 }
1513
1514 public function getCustomerProfilePage(): string
1515 {
1516 if ($pageId = $this->getCustomerProfilePageId()) {
1517 return $this->getPageLink($pageId);
1518 }
1519 return '';
1520 }
1521
1522
1523 public function getCartPageId()
1524 {
1525 return Arr::get($this->storeSettings, 'cart_page_id');
1526 }
1527
1528 public function getCartPage(): string
1529 {
1530 if ($pageId = $this->getCartPageId()) {
1531 if (Pages::isPage($pageId)) {
1532 return $this->getPageLink($pageId);
1533 }
1534 }
1535 return '';
1536 }
1537
1538 public function getShopPageId()
1539 {
1540 return Arr::get($this->storeSettings, 'shop_page_id');
1541 }
1542
1543 public function getShopPage(): string
1544 {
1545 if ($pageId = $this->getShopPageId()) {
1546 if (Pages::isPage($pageId)) {
1547 return $this->getPageLink($pageId);
1548 }
1549 }
1550
1551 return '';
1552 }
1553
1554 public function getReceiptPageId()
1555 {
1556 return Arr::get($this->storeSettings, 'receipt_page_id');
1557 }
1558
1559 public function getReceiptPage(): string
1560 {
1561 if ($pageId = $this->getReceiptPageId()) {
1562 if (Pages::isPage($pageId)) {
1563 return $this->getPageLink($pageId);
1564 }
1565 }
1566 return home_url();
1567 }
1568
1569
1570 public function getBuyButtonText(): string
1571 {
1572 return esc_html(Arr::get($this->storeSettings, 'checkout_button_text', __('Buy now', 'fluent-cart')));
1573 }
1574
1575 public function getViewCartButtonText(): string
1576 {
1577 return esc_html(Arr::get($this->storeSettings, 'view_cart_button_text', __('View Cart', 'fluent-cart')));
1578 }
1579
1580 /**
1581 * @return string
1582 *
1583 * Get cart button text
1584 */
1585 public function getCartButtonText(): string
1586 {
1587 return esc_html(Arr::get($this->storeSettings, 'cart_button_text', __('Add To Cart', 'fluent-cart')));
1588 }
1589
1590 public function toArray(): array
1591 {
1592 return $this->storeSettings;
1593 }
1594
1595 /**
1596 * @return string
1597 *
1598 * Get the base address1 for the store.
1599 */
1600 public function getBaseAddressLine1()
1601 {
1602 return Arr::get($this->storeSettings, 'store_address1');
1603 }
1604
1605 public function getFormattedFullAddress()
1606 {
1607 $addressParts = [
1608 trim(Arr::get($this->storeSettings, 'store_address1') ?? ''),
1609 trim(Arr::get($this->storeSettings, 'store_address2') ?? ''),
1610 trim(Arr::get($this->storeSettings, 'store_city') ?? ''),
1611 trim(AddressHelper::getStateNameByCode(
1612 Arr::get($this->storeSettings, 'store_state'),
1613 Arr::get($this->storeSettings, 'store_country')
1614 )),
1615 trim(Arr::get($this->storeSettings, 'store_postcode') ?? ''),
1616 trim(AddressHelper::getCountryNameByCode(Arr::get($this->storeSettings, 'store_country')))
1617 ];
1618
1619 // Filter out empty or null parts
1620 $addressParts = array_filter($addressParts, function ($part) {
1621 return $part !== '';
1622 });
1623
1624 // Join parts with comma and space
1625 $addressString = implode(', ', $addressParts);
1626 return $addressString;
1627 }
1628
1629 /**
1630 * @return string
1631 *
1632 * Get the base address2 for the store.
1633 */
1634 public function getBaseAddressLine2()
1635 {
1636 return Arr::get($this->storeSettings, 'store_address2');
1637 }
1638
1639 /**
1640 * @return string
1641 *
1642 * Get the base country for the store.
1643 */
1644 public function getBaseCountry()
1645 {
1646 return Arr::get($this->storeSettings, 'store_country');
1647 }
1648
1649 /**
1650 * @return string
1651 *
1652 * Get the base state for the store.
1653 */
1654 public function getBaseState()
1655 {
1656 return Arr::get($this->storeSettings, 'store_state');
1657 }
1658
1659 /**
1660 * @return string
1661 *
1662 * Get the base city for the store.
1663 */
1664 public function getBaseCity()
1665 {
1666 return Arr::get($this->storeSettings, 'store_city');
1667 }
1668
1669 /**
1670 * @return string
1671 *
1672 * Get the base postcode for the store.
1673 */
1674 public function getBasePostcode()
1675 {
1676 return Arr::get($this->storeSettings, 'store_postcode');
1677 }
1678
1679 public function getInvoicePrefix()
1680 {
1681 // @todo: make this dynamic from settings
1682 return $this->get('inv_prefix', '');
1683 }
1684
1685 public function getInvoiceSuffix(): string
1686 {
1687 return '';
1688 }
1689
1690 public function getCustomerDashboardPageSlug($pageId = null, $cached = true)
1691 {
1692 $slug = Arr::get($this->storeSettings, 'customer_profile_page_slug', '');
1693
1694 if ($cached && $slug) {
1695 return $slug;
1696 }
1697
1698 if (!$pageId) {
1699 $pageId = Arr::get($this->storeSettings, 'customer_profile_page_id');
1700 }
1701
1702 if (!$pageId || !get_post($pageId)) {
1703 return $slug;
1704 }
1705
1706 $url = get_page_link($pageId);
1707 $url = rtrim($url, '/');
1708
1709 if (!$url) {
1710 return $slug;
1711 }
1712
1713 return trim(str_replace(home_url('/'), '', $url), '/');
1714 }
1715
1716 /**
1717 * Get theme colors as CSS variable string.
1718 *
1719 * @return string
1720 */
1721 public function getThemeColors(): string
1722 {
1723 $themeSetup = $this->get('theme_setup');
1724 $colors = '';
1725
1726 if (!empty($themeSetup) && is_array($themeSetup)) {
1727 foreach ($themeSetup as $key => $value) {
1728 if (!empty($value)) {
1729 $safeKey = preg_replace('/[^a-zA-Z0-9\-_]/', '', $key);
1730 $safeValue = preg_replace('/[;\{\}]/', '', $value);
1731 $colors .= "$safeKey: $safeValue;";
1732 }
1733 }
1734 }
1735
1736 return $colors;
1737 }
1738
1739 public function getPageLink($pageId = null): string
1740 {
1741 if (!$pageId || !get_post($pageId)) {
1742 return '';
1743 }
1744
1745 $link = get_page_link($pageId);
1746
1747 if (!empty($link)) {
1748 return Str::endsWith($link, '/') ? $link : $link . '/';
1749 }
1750 return '';
1751 }
1752
1753 }
1754