PluginProbe
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More / 2.2.0
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More v2.2.0
2.3.0 2.2.0 2.1.1 2.1.0 2.0.0 1.10.0 1.9.1 1.9.0 1.2.1 1.2.2 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 All 59 releases
storeengine / includes / classes / price.php

price.php in StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More 2.2.0, at includes/classes/price.php

794 lines 23.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace StoreEngine\Classes;
4
5 use DateInterval;
6 use StoreEngine\Classes\enums\ProductTaxStatus;
7 use StoreEngine\Utils\Formatting;
8 use StoreEngine\Utils\Helper;
9 use StoreEngine\Utils\TaxUtil;
10
11 class Price extends AbstractEntity {
12
13 protected string $table = 'storeengine_product_price';
14 protected string $object_type = 'price';
15
16 protected array $data = [
17 'price_name' => '',
18 'price_type' => 'onetime',
19 'price' => 0.0,
20 'compare_price' => 0.0,
21 'product_id' => 0,
22 'order' => 0,
23 // Settings.
24 'setup_fee' => false,
25 'setup_fee_name' => '',
26 'setup_fee_price' => 0.00,
27 'setup_fee_type' => '',
28 'trial' => false,
29 'trial_days' => 0,
30 'expire' => false,
31 'expire_days' => 0,
32 'payment_duration' => 0,
33 'payment_duration_type' => '',
34 'upgradeable' => false,
35 'tax_status' => ProductTaxStatus::TAXABLE,
36 'enable_license_activation_limit' => false,
37 'license_activation_limit' => 0,
38 'license_prefix' => null,
39 'license_segments' => null,
40 'down_payment' => '',
41 'installments' => [],
42 'is_hidden' => false,
43 ];
44
45 protected ?string $product_title = null;
46
47 protected ?string $product_type = null;
48
49 protected ?string $shipping_type = null;
50
51 protected ?string $digital_auto_complete = null;
52
53 protected array $settings_props = [
54 'setup_fee',
55 'setup_fee_name',
56 'setup_fee_price',
57 'setup_fee_type',
58 'trial',
59 'trial_days',
60 'expire',
61 'expire_days',
62 'payment_duration',
63 'payment_duration_type',
64 'upgradeable',
65 'tax_status',
66 'enable_license_activation_limit',
67 'license_activation_limit',
68 'license_prefix',
69 'license_segments',
70 'down_payment',
71 'installments',
72 'is_hidden',
73 ];
74
75 protected array $data_props = [
76 'price_name',
77 'price_type',
78 'price',
79 'compare_price',
80 'product_id',
81 'order',
82 ];
83
84 protected function read_data(): array {
85 $data = parent::read_data();
86
87 if ( ! empty( $data['settings'] ) ) {
88 $settings = maybe_unserialize( $data['settings'] );
89
90 if ( ! is_array( $settings ) ) {
91 $settings = json_decode( $data['settings'], true );
92 }
93
94 unset( $data['settings'] );
95 if ( $settings ) {
96 $data = array_merge( $data, $settings );
97 }
98 }
99
100 return $data;
101 }
102
103 public function prepare_for_db( string $context = 'create' ): array {
104 $data = [];
105 $settings = [];
106 $format = [];
107
108 foreach ( $this->data_props as $prop ) {
109 $value = $this->{"get_$prop"}( 'edit' );
110 $format[] = $this->predict_format( $prop, $value );
111 $data[ $prop ] = $value;
112 }
113
114 foreach ( $this->settings_props as $prop ) {
115 $settings[ $prop ] = $this->{"get_$prop"}( 'edit' );
116 }
117
118 $data['settings'] = maybe_serialize( $settings );
119 $format[] = '%s';
120
121 return [
122 'data' => apply_filters( 'storeengine/' . $this->object_type . '/db/' . $context, $data, $this ),
123 'format' => $format,
124 ];
125 }
126
127 public function delete( bool $force_delete = true ): bool {
128 $id = $this->get_id();
129
130 $deleted = parent::delete( $force_delete );
131 if ( $deleted ) {
132 /**
133 * @deprecated
134 * Use 'storeengine/price/deleted'
135 * @see parent::delete()
136 */
137 do_action( 'storeengine/price_deleted', $id, $this, $force_delete );
138 }
139
140 return $deleted;
141 }
142
143 public function clear_cache( bool $flush_collection = true ) {
144 parent::clear_cache( $flush_collection );
145 wp_cache_delete( 'storeengine_product_' . $this->get_product_id() . '_prices' );
146 wp_cache_delete( 'storeengine_price_' . $this->get_id() . '_integrations' );
147 }
148
149 // Getters
150
151 public function get_price_name( string $context = 'view' ) {
152 return $this->get_prop( 'price_name', $context );
153 }
154
155 public function get_name(): string {
156 return $this->get_price_name();
157 }
158
159 public function get_price_type( string $context = 'view' ): string {
160 return $this->get_prop( 'price_type', $context );
161 }
162
163 public function get_type( string $context = 'view' ): string {
164 return $this->get_price_type( $context );
165 }
166
167 public function get_price( string $context = 'view' ): float {
168 return apply_filters('storeengine/product/get_price', (float) $this->get_prop( 'price', $context ), $context);
169 }
170
171 public function get_compare_price( string $context = 'view' ): ?float {
172 $compare_price = $this->get_prop( 'compare_price', $context );
173 return apply_filters('storeengine/product/get_compare_price', (float) $compare_price, $context);
174 }
175
176 public function get_product_id( string $context = 'view' ): int {
177 return (int) $this->get_prop( 'product_id', $context );
178 }
179
180 public function get_order( string $context = 'view' ): int {
181 return (int) $this->get_prop( 'order', $context );
182 }
183
184 public function get_menu_order( string $context = 'view' ): int {
185 return $this->get_order( $context );
186 }
187
188 public function get_setup_fee( string $context = 'view' ): bool {
189 return (bool) $this->get_prop( 'setup_fee', $context );
190 }
191
192 public function has_setup_fee(): bool {
193 return $this->get_setup_fee();
194 }
195
196 public function is_setup_fee(): bool {
197 return $this->get_setup_fee();
198 }
199
200 public function get_setup_fee_name( string $context = 'view' ) {
201 return $this->get_prop( 'setup_fee_name', $context );
202 }
203
204 public function get_setup_fee_price( string $context = 'view' ): float {
205 return (float) $this->get_prop( 'setup_fee_price', $context );
206 }
207
208 public function get_setup_fee_type( string $context = 'view' ) {
209 $value = $this->get_prop( 'setup_fee_type', $context );
210 if ( 'fixed' === $value ) {
211 $value = 'fee';
212 }
213
214 return $value;
215 }
216
217 public function get_trial( string $context = 'view' ) {
218 return $this->get_prop( 'trial', $context );
219 }
220
221 public function is_trial(): bool {
222 return (bool) $this->get_trial() && $this->get_trial_days() > 0;
223 }
224
225 public function get_trial_days( string $context = 'view' ): int {
226 return (int) $this->get_prop( 'trial_days', $context );
227 }
228
229 public function get_expire( string $context = 'view' ): bool {
230 return (bool) $this->get_prop( 'expire', $context );
231 }
232
233 public function get_expire_days( string $context = 'view' ): int {
234 return (int) $this->get_prop( 'expire_days', $context );
235 }
236
237 public function get_length( string $context = 'view' ): int {
238 return $this->get_expire_days( $context );
239 }
240
241 public function is_expire(): bool {
242 return $this->get_expire() && $this->get_expire_days();
243 }
244
245 /**
246 * @param Order $order
247 *
248 * @return ?StoreengineDatetime
249 * @throws \DateMalformedIntervalStringException
250 */
251 public function get_access_expire_for_order( Order $order ): ?StoreengineDatetime {
252 if ( ! $this->is_expire() || ! $order->get_date_created_gmt() ) {
253 return null;
254 }
255
256 return $order->get_date_created_gmt()->add( new DateInterval( 'P' . $this->get_expire_days() . 'D' ) );
257 }
258
259 public function get_payment_duration( string $context = 'view' ): int {
260 return (int) $this->get_prop( 'payment_duration', $context );
261 }
262
263 public function get_period_interval( string $context = 'view' ): int {
264 return $this->get_payment_duration( $context );
265 }
266
267 public function get_payment_duration_type( string $context = 'view' ): string {
268 $value = $this->get_prop( 'payment_duration_type', $context );
269 if ( ! in_array( $value, [ 'day', 'week', 'month', 'year' ], true ) ) {
270 $old_values = [
271 'days' => 'day',
272 'weeks' => 'week',
273 'months' => 'month',
274 'years' => 'year',
275 ];
276 $value = $old_values[ $value ] ?? 'month';
277 }
278
279 return $value;
280 }
281
282 public function get_period( string $context = 'view' ): string {
283 return $this->get_payment_duration_type( $context );
284 }
285
286 public function get_upgradeable( string $context = 'view' ): bool {
287 return (bool) $this->get_prop( 'upgradeable', $context );
288 }
289
290 public function get_enable_license_activation_limit( string $context = 'view' ): bool {
291 return (bool) $this->get_prop( 'enable_license_activation_limit', $context );
292 }
293
294 public function get_license_activation_limit( string $context = 'view' ): int {
295 return absint( $this->get_prop( 'license_activation_limit', $context ) );
296 }
297
298 public function get_license_prefix( string $context = 'view' ): ?string {
299 $value = $this->get_prop( 'license_prefix', $context );
300
301 return $value ? (string) $value : null;
302 }
303
304 public function get_license_segments( string $context = 'view' ): ?string {
305 $value = $this->get_prop( 'license_segments', $context );
306
307 return $value ? (string) $value : null;
308 }
309
310
311 public function get_down_payment( string $context = 'view' ): ?float {
312 $value = $this->get_prop( 'down_payment', $context );
313
314 return $value ? (float) $value : null;
315 }
316
317 public function get_installments( string $context = 'view' ): ?array {
318 return $this->get_prop( 'installments', $context );
319 }
320
321 public function get_is_hidden( string $context = 'view' ): bool {
322 return Formatting::string_to_bool( $this->get_prop( 'is_hidden', $context ) );
323 }
324
325 public function get_tax_status() {
326 $tax_status = ProductTaxStatus::TAXABLE;
327
328 if ( $this->get_product()->get_id() ) {
329 $tax_status = $this->get_product()->get_tax_status();
330 }
331
332 return apply_filters( 'storeengine/price/tax_status', $tax_status, $this );
333 }
334
335 /**
336 * Returns whether the product-pricing is taxable.
337 *
338 * @return bool
339 */
340 public function is_taxable(): bool {
341 /**
342 * Filters whether a product is taxable.
343 *
344 * @param bool $taxable Whether the product is taxable.
345 * @param Price $price Product object.
346 */
347 return apply_filters( 'storeengine/price/is_taxable', $this->get_product()->is_taxable(), $this );
348 }
349
350 public function is_subscription(): bool {
351 return 'subscription' === $this->get_type();
352 }
353
354 public function is_upgradeable(): bool {
355 return $this->get_upgradeable();
356 }
357
358 // Setters.
359 public function set_price_name( string $value ) {
360 $this->set_prop( 'price_name', $value );
361 }
362
363 public function set_name( string $value ) {
364 $this->set_price_name( $value );
365 }
366
367 public function set_price_type( string $value ) {
368 $this->set_prop( 'price_type', $value );
369 }
370
371 public function set_type( string $value ) {
372 $this->set_price_type( $value );
373 }
374
375 public function set_price( $value ) {
376 if ( null === $value ) {
377 $this->set_prop( 'price', null );
378
379 return;
380 }
381
382 $this->set_prop( 'price', abs( floatval( $value ) ) );
383 }
384
385 public function set_compare_price( $value ) {
386 if ( null === $value ) {
387 $this->set_prop( 'compare_price', null );
388
389 return;
390 }
391
392 $this->set_prop( 'compare_price', abs( floatval( $value ) ) );
393 }
394
395 public function set_product_id( $value ) {
396 $this->set_prop( 'product_id', absint( $value ) );
397 }
398
399 public function set_order( $value ) {
400 // set sort order.
401 $this->set_prop( 'order', absint( $value ) );
402 }
403
404 public function set_menu_order( $value ) {
405 // set sort order.
406 $this->set_prop( 'order', absint( $value ) );
407 }
408
409 public function set_setup_fee( bool $value ) {
410 $this->set_prop( 'setup_fee', $value );
411 }
412
413 public function set_setup_fee_name( string $value ) {
414 $this->set_prop( 'setup_fee_name', $value );
415 }
416
417 public function set_setup_fee_price( $value ) {
418 $this->set_prop( 'setup_fee_price', abs( floatval( $value ) ) );
419 }
420
421 public function set_setup_fee_type( string $value ) {
422 if ( 'fixed' === $value ) {
423 $value = 'fee';
424 }
425
426 $this->set_prop( 'setup_fee_type', $value );
427 }
428
429 public function set_trial( bool $value ) {
430 $this->set_prop( 'trial', $value );
431 }
432
433 public function set_trial_days( $value ) {
434 $this->set_prop( 'trial_days', absint( $value ) );
435 }
436
437 public function set_expire( bool $value ) {
438 $this->set_prop( 'expire', $value );
439 }
440
441 public function set_expire_days( $value ) {
442 $this->set_prop( 'expire_days', absint( $value ) );
443 }
444
445 public function set_payment_duration( $value ) {
446 $this->set_prop( 'payment_duration', absint( $value ) );
447 }
448
449 public function set_period_interval( $value ) {
450 $this->set_payment_duration( $value );
451 }
452
453 public function set_payment_duration_type( string $value ) {
454 if ( ! in_array( $value, [ 'day', 'week', 'month', 'year' ], true ) ) {
455 $old_values = [
456 'days' => 'day',
457 'weeks' => 'week',
458 'months' => 'month',
459 'years' => 'year',
460 ];
461 $value = $old_values[ $value ] ?? 'month';
462 }
463
464 $this->set_prop( 'payment_duration_type', $value );
465 }
466
467 public function set_period( string $value ) {
468 $this->set_payment_duration_type( $value );
469 }
470
471 public function set_upgradeable( bool $value ) {
472 $this->set_prop( 'upgradeable', $value );
473 }
474
475 public function set_enable_license_activation_limit( bool $value ) {
476 $this->set_prop( 'enable_license_activation_limit', $value );
477 }
478
479 public function set_license_activation_limit( $value ) {
480 $this->set_prop( 'license_activation_limit', $value );
481 }
482
483 public function set_license_prefix( ?string $value = null ) {
484 $this->set_prop( 'license_prefix', $value );
485 }
486
487 public function set_license_segments( ?string $value = null ) {
488 $this->set_prop( 'license_segments', $value );
489 }
490
491 public function set_down_payment( ?string $value = null ) {
492 $this->set_prop( 'down_payment', $value ? abs(floatval( $value ) ) : null );
493 }
494
495 /**
496 * @param array|string|null $value
497 *
498 * @return void
499 */
500 public function set_installments( $value = null ) {
501 $this->set_prop( 'installments', $value ? maybe_unserialize( $value ) : null );
502 }
503
504 /**
505 * @param string|int|bool $value Possible values are yes|no|0|1|true|false.
506 *
507 * @return void
508 */
509 public function set_is_hidden( $value ): void {
510 $this->set_prop( 'is_hidden', Formatting::string_to_bool( $value ) );
511 }
512
513
514 // Extras
515
516 public function get_settings(): array {
517 return $this->get_props( $this->settings_props );
518 }
519
520 public function get_product() {
521 if ( ! $this->get_product_id() ) {
522 return false;
523 }
524
525 return ( new ProductFactory() )->get_product( $this->get_product_id() );
526 }
527
528 public function get_product_title(): string {
529 if ( null === $this->product_title && $this->get_product_id() ) {
530 // Do not load in the read_data.
531 // If we load this inside read_data method, then we need to clear
532 // every price cache for the product from object cache.
533 // @TODO convert product class into entity class to take advantage of the cache.
534 $this->product_title = get_the_title( $this->get_product_id() );
535 }
536
537 return $this->product_title;
538 }
539
540 public function get_post_title(): string {
541 return $this->get_product_title();
542 }
543
544 /**
545 * Allow setting temp product title custom item.
546 *
547 * @param string $value
548 *
549 * @return void
550 */
551 public function set_product_title( string $value ) {
552 $this->product_title = trim( $value );
553 }
554
555 public function get_product_type(): ?string {
556 if ( null === $this->product_type && $this->get_product_id() ) {
557 // Do not load in the read_data.
558 // If we load this inside read_data method, then we need to clear
559 // every price cache for the product from object cache.
560 // @TODO convert product class into entity class to take advantage of the cache.
561 $this->product_type = get_post_meta( $this->get_product_id(), '_storeengine_product_type', true );
562 }
563
564 return $this->product_type;
565 }
566
567 public function get_shipping_type(): ?string {
568 if ( null === $this->shipping_type && $this->get_product_id() ) {
569 // Do not load in the read_data.
570 // If we load this inside read_data method, then we need to clear
571 // every price cache for the product from object cache.
572 // @TODO convert product class into entity class to take advantage of the cache.
573 $this->shipping_type = get_post_meta( $this->get_product_id(), '_storeengine_product_shipping_type', true );
574 }
575
576 return $this->shipping_type;
577 }
578
579 public function get_digital_auto_complete(): bool {
580 if ( null === $this->digital_auto_complete && $this->get_product_id() ) {
581 // Do not load in the read_data.
582 // If we load this inside read_data method, then we need to clear
583 // every price cache for the product from object cache.
584 // @TODO convert product class into entity class to take advantage of the cache.
585 $this->digital_auto_complete = Formatting::string_to_bool( get_post_meta( $this->get_product_id(), '_storeengine_product_digital_auto_complete', true ) );
586 }
587
588 return 'digital' === $this->get_shipping_type() && $this->digital_auto_complete;
589 }
590
591 /**
592 * Allow setting temp product title custom item.
593 *
594 * @param string $value
595 *
596 * @return void
597 */
598 public function set_product_type( string $value ) {
599 $this->product_type = trim( $value );
600 }
601
602 /**
603 * @return Integration[]
604 */
605 public function get_integrations(): array {
606 if ( 0 === $this->id ) {
607 return [];
608 }
609
610 $key = 'storeengine_price_' . $this->get_id() . '_integrations';
611 $cached = wp_cache_get( $key, $this->cache_group );
612 if ( $cached && is_array( $cached ) ) {
613 return array_map( fn( $result ) => ( new Integration() )->set_data( $result ), $cached );
614 }
615
616 global $wpdb;
617 $integrations = [];
618 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.DirectDatabaseQuery.DirectQuery
619 $results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}storeengine_integrations WHERE price_id = %d", $this->get_id() ) );
620 if ( ! $results ) {
621 return $integrations;
622 }
623
624 wp_cache_set( $key . $this->id . '_integrations', $results, $this->cache_group );
625
626 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.DirectDatabaseQuery.DirectQuery
627
628 return array_map( fn( $result ) => ( new Integration() )->set_data( $result ), $results );
629 }
630
631 /**
632 * @param string $provider
633 * @param int $integration_id
634 *
635 * @return false|Integration
636 */
637 public function add_integration( string $provider, int $integration_id ) {
638 $integration = new Integration();
639 $integration->set_product_id( $this->get_product_id() );
640 $integration->set_price_id( $this->get_id() );
641 $integration->set_integration_id( $integration_id );
642 $integration->set_provider( $provider );
643 $integration->save();
644 if ( 0 === $integration->get_id() ) {
645 return false;
646 }
647
648 return $integration;
649 }
650
651 public function remove_integration( string $provider, int $integration_id ): bool {
652 $integration = new Integration();
653 $integration->set_product_id( $this->get_product_id() );
654 $integration->set_price_id( $this->get_id() );
655 $integration->set_integration_id( $integration_id );
656 $integration->set_provider( $provider );
657
658 return $integration->delete_by_price_and_integration();
659 }
660
661 /**
662 * @return string
663 * @deprecated
664 */
665 public function get_formatted_payment_duration(): string {
666 $payment_duration = $this->get_payment_duration();
667
668 // @TODO use array of duration types & i18n properly.
669 if ( empty( $payment_duration ) || 1 === $payment_duration ) {
670 return Formatting::price( $this->get_price() ) . ' / Every ' . ucfirst( $this->get_payment_duration_type() );
671 }
672
673 return Formatting::price( $this->get_price() ) . ' / ' . $payment_duration . '-' . $this->get_payment_duration_type() . 's';
674 }
675
676 /**
677 * Get the suffix to display after prices > 0.
678 *
679 * This is skipped if the suffix
680 * has dynamic values such as {price_excluding_tax} for variable products.
681 *
682 * @param string|float $price to calculate, left blank to just use get_price().
683 * @param int $qty passed on to get_price_including_tax() or get_price_excluding_tax().
684 *
685 * @return string
686 * @see get_price_html for an explanation as to why.
687 */
688 public function get_price_suffix( $price = '', int $qty = 1 ): string {
689 $html = '';
690
691 $suffix = Helper::get_settings( 'price_display_suffix' );
692
693 if ( $suffix && TaxUtil::is_tax_enabled() && ProductTaxStatus::TAXABLE === $this->get_tax_status() ) {
694 if ( '' === $price ) {
695 $price = $this->get_price();
696 }
697
698 $replacements = [
699 '{price_including_tax}' => Formatting::price( Formatting::get_price_including_tax( $this->get_price(), $this->get_id(), $this->get_product_id(), [
700 'qty' => $qty,
701 'price' => $price,
702 ] ) ),
703 // @phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine, WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
704 '{price_excluding_tax}' => Formatting::price( Formatting::get_price_excluding_tax( $this->get_price(), $this->get_id(), $this->get_product_id(), [
705 'qty' => $qty,
706 'price' => $price,
707 ] ) ),
708 // @phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
709 ];
710 $html = str_replace( array_keys( $replacements ), array_values( $replacements ), ' <small class="storeengine-price-suffix">' . wp_kses_post( $suffix ) . '</small>' );
711 }
712
713 return apply_filters( 'storeengine/get_price_suffix', $html, $this, $price, $qty );
714 }
715
716 public function is_product_synced(): bool {
717 return false;
718 }
719
720 public function get_formatted_price(): string {
721 if ( empty( $this->get_price() ) ) {
722 $price = apply_filters( 'storeengine/empty_price_html', '', $this );
723 } elseif ( ! empty( $this->get_compare_price() ) ) {
724 $price = Formatting::format_sale_price(
725 Formatting::get_price_to_display( $this->get_compare_price(), $this->get_id(), $this->get_product_id() ),
726 Formatting::get_price_to_display( $this->get_price(), $this->get_id(), $this->get_product_id() )
727 );
728 } else {
729 $price = Formatting::price( Formatting::get_price_to_display( $this->get_price(), $this->get_id(), $this->get_product_id() ) );
730 }
731
732 $price = $price ? $price . $this->get_price_suffix() : $price;
733
734 return apply_filters( 'storeengine/formatted_price', $price, $this );
735 }
736
737 public function get_price_html() {
738 return apply_filters( 'storeengine/get_price_html', $this->get_formatted_price(), $this );
739 }
740
741 public function print_price_html() {
742 echo wp_kses_post( $this->get_price_html() );
743 }
744
745 public function get_price_summery_html() {
746 return apply_filters( 'storeengine/get_price_summery_html', $this->get_formatted_price(), $this );
747 }
748
749 public function get_formatted_setup_fee(): string {
750 if ( $this->has_setup_fee() ) {
751 $fee_name = $this->get_setup_fee_name() ? $this->get_setup_fee_name() : __( 'Setup Fee', 'storeengine' );
752
753 return sprintf(
754 '<span class="storeengine-price-setup-fee">%s</span> %s',
755 Formatting::price( Formatting::get_price_to_display( $this->get_setup_fee_price(), $this->get_id(), $this->get_product_id() ) ),
756 esc_html( $fee_name )
757 );
758 }
759
760 return '';
761 }
762
763 public function get_formatted_price_meta(): array {
764 $price_meta = [
765 'setup-fee' => $this->get_formatted_setup_fee(),
766 ];
767
768 return array_filter( apply_filters( 'storeengine/get_formatted_price_meta', $price_meta, $this ) );
769 }
770
771 public function get_formatted_price_meta_html() {
772 $details_html = [];
773 foreach ( $this->get_formatted_price_meta() as $key => $detail ) {
774 if ( ! $detail ) {
775 continue;
776 }
777
778 $details_html[] = '<span class="storeengine-price-meta-' . esc_attr( $key ) . '">' . $detail . '</span>';
779 }
780
781 $details_html = implode( '', $details_html );
782
783 return apply_filters( 'storeengine/get_formatted_price_meta_html', $details_html, $this );
784 }
785
786 public function print_price_summery_html() {
787 echo wp_kses_post( $this->get_price_summery_html() );
788 }
789
790 public function print_formatted_price_meta_html() {
791 echo wp_kses_post( $this->get_formatted_price_meta_html() );
792 }
793 }
794