PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.0
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.0
1.6.6 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 All 49 releases
fluent-cart / api / StoreSettings.php

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

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