*/ public function lines( \WC_Product $product, ?\DateTimeImmutable $now = null, ?Line_Terms $terms = null ): array { $now = $now ?? new \DateTimeImmutable( 'now', new \DateTimeZone( 'UTC' ) ); $terms = $terms ?? Line_Terms::for_product( $product ); $schedule = $terms->schedule(); $price = $terms->recurring_price(); $fee = $terms->signup_fee(); $lines = array(); if ( $schedule->has_trial() ) { $lines[] = array( 'key' => 'trial', /* translators: %s: trial length such as "14 days" or "1 month" */ 'text' => sprintf( __( '%s free', 'submex' ), $schedule->describe_trial() ), ); } if ( ! $fee->is_zero() ) { $lines[] = array( 'key' => 'signup_fee', /* translators: %s: sign-up fee amount */ 'text' => sprintf( __( '%s sign-up fee today', 'submex' ), $this->amount( $fee ) ), ); } // Always state what is charged first and when. This is the single most common // point of confusion: am I paying now, or after the trial? $first_date = $schedule->trial_end_from( $now ); $first_due = $schedule->has_trial() ? $price : $price->add( $fee ); $lines[] = array( 'key' => 'first_payment', 'text' => $first_date /* translators: 1: amount, 2: date */ ? sprintf( __( 'First payment %1$s on %2$s', 'submex' ), $this->amount( $first_due ), $this->date( $first_date ) ) /* translators: %s: amount */ : sprintf( __( 'First payment %s today', 'submex' ), $this->amount( $first_due ) ), ); $lines[] = array( 'key' => 'recurring', /* translators: 1: amount, 2: billing interval such as "every month" */ 'text' => sprintf( __( 'Then %1$s %2$s', 'submex' ), $this->amount( $price ), $schedule->describe() ), ); $lines[] = array( 'key' => 'cancel', 'text' => __( 'Cancel anytime', 'submex' ), ); /** * Filter the disclosure lines. * * @param array $lines * @param \WC_Product $product * @param Line_Terms $terms What they were written from; its cart_item() or order_item() is the line, if any. */ return apply_filters( 'submex_disclosure_lines', $lines, $product, $terms ); } /** * The headline price, e.g. "$29.00 / month". */ public function price_line( \WC_Product $product, ?Line_Terms $terms = null ): string { $terms = $terms ?? Line_Terms::for_product( $product ); /** * Filter the headline price line, e.g. to state an instalment plan instead. * * @param string $line * @param \WC_Product $product * @param Line_Terms $terms */ return (string) apply_filters( 'submex_disclosure_price_line', $this->default_price_line( $terms ), $product, $terms ); } /** * The product's price as WooCommerce prints it, made to say how often it recurs. * * WooCommerce's own markup is kept and the interval appended, rather than replaced by * price_line(): it carries the sale strikethrough and the tax suffix, and a plain * amount would drop both. The exception is a headline something has rewritten - an * instalment or split plan - where the bare amount plus "every month" would state a * price the customer is not going to pay. */ public function price_html( string $woo_html, \WC_Product $product ): string { if ( '' === $woo_html || ! Subscription_Product::is_subscription( $product ) ) { return $woo_html; } $terms = Line_Terms::for_product( $product ); $line = $this->price_line( $product, $terms ); if ( $line !== $this->default_price_line( $terms ) ) { return $line; } return $woo_html . ' ' . esc_html( $terms->schedule()->describe() ) . ''; } private function default_price_line( Line_Terms $terms ): string { return sprintf( /* translators: 1: price, 2: billing interval such as "every month" */ __( '%1$s %2$s', 'submex' ), $this->amount( $terms->recurring_price() ), $terms->schedule()->describe() ); } /** * With $terms, for a line the caller has decided is recurring, whatever the product is. */ public function render( \WC_Product $product, ?Line_Terms $terms = null ): string { if ( null === $terms && ! Subscription_Product::is_subscription( $product ) ) { return ''; } // No headline price here: the product's own price line says it, and saying it twice // is what made the page read "$5.00" then "$5.00 every month". $html = '