PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 2.0.0
Subscriptions for WooCommerce with Stripe Recurring Payments v2.0.0
2.0.0 1.11.2 1.11.1 1.11.0 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.6 1.9.5 trunk 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 All 61 releases
← All changes | src/index.js +113 -65 1.10.42.0.0 View file →
@@ -1,5 +1,10 @@
1 -import { ExperimentalOrderMeta, registerCheckoutFilters, TotalsItem } from "@woocommerce/blocks-checkout";
1 +import {
2 + ExperimentalOrderMeta,
3 + registerCheckoutFilters,
4 + TotalsItem,
5 + TotalsWrapper,
6 +} from "@woocommerce/blocks-checkout";
2 7 import { FormattedMonetaryAmount } from "@woocommerce/blocks-components";
3 8 import { __, sprintf } from "@wordpress/i18n";
4 9 import { registerPlugin } from "@wordpress/plugins";
5 10
@@ -6,9 +11,8 @@
6 11 import { formatPrice, getCurrencyFromPriceResponse } from "@woocommerce/price-format";
7 12 // import { useStoreCart } from "@woocommerce/base-context/hooks";
8 13
9 14 const modifyCartItemPrice = (defaultValue, extensions, args, validation) => {
10 - const { sdevs_subscription } = extensions;
11 15 const { cartItem } = args;
12 16 const { totals } = cartItem;
13 17
14 18 if (totals === undefined) {
@@ -16,19 +20,10 @@
16 20 }
17 21 if (totals.line_total === "0") {
18 22 return `<price/> ${__("Due Today", "subscription")}`;
19 23 }
20 - if (sdevs_subscription && sdevs_subscription.type) {
21 - // Capitalize the first letter to match product page display
22 - const capitalizedType = sdevs_subscription.type.charAt(0).toUpperCase() + sdevs_subscription.type.slice(1);
23 -
24 - // Check max_no_payment - handle string, number, null, undefined
25 - const maxPayments = parseInt(sdevs_subscription.max_no_payment, 10);
26 - const paymentInfo = !isNaN(maxPayments) && maxPayments > 0 ? ` x ${maxPayments}` : "";
27 - return `<price/> / ${
28 - sdevs_subscription.time && sdevs_subscription.time > 1 ? " " + sdevs_subscription.time + "-" : ""
29 - }${capitalizedType}${paymentInfo}`;
30 - }
24 + // The per-unit price shows no cadence — the recurring cadence appears on the
25 + // line total (subtotalPriceFormat) and the "Recurring totals" summary.
31 26 return defaultValue;
32 27 };
33 28
34 29 const modifySubtotalPriceFormat = (defaultValue, extensions, args, validation) => {
@@ -70,65 +65,118 @@
70 65 const currency = getCurrencyFromPriceResponse(cartTotals);
71 66 const multiplier = Math.pow(10, currency.minorUnit);
72 67
73 68 return (
74 - <TotalsItem
75 - className="wc-block-components-totals-footer-item"
76 - label={__("Recurring totals", "subscription")}
77 - description={
78 - <div style={{ display: "grid" }}>
79 - {recurrings.map((recurring) => {
80 - // Capitalize the first letter to match product page display
81 - const capitalizedType = recurring.type.charAt(0).toUpperCase() + recurring.type.slice(1);
69 + <TotalsWrapper>
70 + <TotalsItem
71 + className="wc-block-components-totals-footer-item"
72 + label={__("Recurring totals", "subscription")}
73 + description={
74 + <div style={{ display: "grid" }}>
75 + {recurrings.map((recurring) => {
76 + // Capitalize the first letter to match product page display
77 + const capitalizedType = recurring.type.charAt(0).toUpperCase() + recurring.type.slice(1);
82 78
83 - return (
84 - <div style={{ margin: "20px 0", float: "right" }}>
85 - <div style={{ fontSize: "18px" }}>
86 - <FormattedMonetaryAmount currency={currency} value={Math.round(recurring.price * multiplier)} />
87 - <span class="wpsubs-subscription-timing">
88 - &nbsp;/&nbsp;
89 - {recurring.time && recurring.time > 1
90 - ? `${recurring.time + "-" + capitalizedType} `
91 - : capitalizedType}
92 - </span>
93 - </div>
94 - <small>{recurring.description}</small>
95 - {recurring.can_user_cancel === "yes" &&
96 - (recurring.max_no_payment === "" || parseInt(recurring.max_no_payment) === 0) && (
79 + const priceFormatArgs = {
80 + currency: currency.code,
81 + currency_symbol: currency.symbol,
82 + decimal_separator: currency.decimalSeparator,
83 + thousand_separator: currency.thousandSeparator,
84 + precision: currency.minorUnit,
85 + price_format: currency.priceFormat,
86 + };
87 +
88 + /**
89 + * Format an amount the same way the surrounding totals are formatted.
90 + *
91 + * @param {number} amount Amount in major units.
92 + * @return {string} Formatted price.
93 + */
94 + const format = (amount) => formatPrice(Math.round(amount * multiplier), priceFormatArgs);
95 +
96 + const recurringLimit = parseInt(recurring.recurring_limit, 10) || 0;
97 +
98 + // Billing period as plain text, so notes can quote a price the same way the row does.
99 + const periodLabel =
100 + recurring.time && recurring.time > 1 ? `${recurring.time}-${capitalizedType}` : capitalizedType;
101 +
102 + return (
103 + <div style={{ margin: "8px 0px 0px", float: "right" }}>
104 + <div style={{ fontSize: "18px" }}>
105 + {recurring.has_recurring_discount && (
106 + <del aria-hidden="true" style={{ marginRight: "6px", opacity: 0.6 }}>
107 + {format(recurring.full_price)}
108 + </del>
109 + )}
110 + <FormattedMonetaryAmount currency={currency} value={Math.round(recurring.price * multiplier)} />
111 + <span class="wpsubs-subscription-timing">
112 + &nbsp;/&nbsp;
113 + {recurring.time && recurring.time > 1
114 + ? `${recurring.time + "-" + capitalizedType} `
115 + : capitalizedType}
116 + </span>
117 + </div>
118 + <small>{recurring.description}</small>
119 + {recurring.has_one_time_discount && (
97 120 <>
98 121 <br />
99 - <small>{__("You can cancel subscription at any time!", "subscription")} </small>
122 + <small>
123 + {sprintf(
124 + // translators: 1: amount paid today, 2: amount charged on each renewal.
125 + __("You pay %1$s today. %2$s will be charged from the next renewal.", "subscription"),
126 + format(recurring.first_price),
127 + format(recurring.price),
128 + )}
129 + </small>
100 130 </>
101 131 )}
102 - {parseInt(recurring.max_no_payment) > 0 && (
103 - <>
104 - <br />
105 - <small>
106 - {sprintf(
107 - // translators: 1: number of installments, 2: total amount.
108 - __(
109 - "This subscription will be billed in %1$s installments, for a total of %2$s.",
110 - "subscription",
111 - ),
112 - recurring.max_no_payment,
113 - formatPrice(Math.round(recurring.price * multiplier * parseInt(recurring.max_no_payment)), {
114 - currency: currency.code,
115 - currency_symbol: currency.symbol,
116 - decimal_separator: currency.decimalSeparator,
117 - thousand_separator: currency.thousandSeparator,
118 - precision: currency.minorUnit,
119 - price_format: currency.priceFormat,
120 - }),
121 - )}
122 - </small>
123 - </>
124 - )}
125 - </div>
126 - );
127 - })}
128 - </div>
129 - }
130 - ></TotalsItem>
132 + {recurring.has_recurring_discount && recurringLimit > 1 && (
133 + <>
134 + <br />
135 + <small>
136 + {sprintf(
137 + // translators: 1: number of discounted payments, 2: full price charged afterwards.
138 + __("Discount applies to your first %1$s payments. After that, %2$s.", "subscription"),
139 + recurringLimit,
140 + `${format(recurring.full_price)} / ${periodLabel}`,
141 + )}
142 + </small>
143 + </>
144 + )}
145 + {recurring.can_user_cancel === "yes" &&
146 + (recurring.max_no_payment === "" || parseInt(recurring.max_no_payment) === 0) && (
147 + <>
148 + <br />
149 + <small>{__("You can cancel subscription at any time!", "subscription")} </small>
150 + </>
151 + )}
152 + {parseInt(recurring.max_no_payment) > 0 && (
153 + <>
154 + <br />
155 + <small>
156 + {sprintf(
157 + // translators: 1: number of installments, 2: total amount.
158 + __(
159 + "This subscription will be billed in %1$s installments, for a total of %2$s.",
160 + "subscription",
161 + ),
162 + recurring.max_no_payment,
163 + format(
164 + recurring.split_total != null
165 + ? recurring.split_total
166 + : recurring.price * parseInt(recurring.max_no_payment),
167 + ),
168 + )}
169 + </small>
170 + </>
171 + )}
172 + </div>
173 + );
174 + })}
175 + </div>
176 + }
177 + ></TotalsItem>
178 + </TotalsWrapper>
131 179 );
132 180 };
133 181
134 182 const render = () => {