The question a subscription raises
A 20% discount on a one-off product is clear enough. On a subscription it could cover the first payment only, or every renewal for the life of the subscription. CartPresets discounts every payment unless you set a limit. That’s what a preset price on a subscription usually means, and it matches the cart line: “$5.00 every month” is exactly true.What the free version does
It discounts every payment and charges the sign-up fee, whatever the item’s stored settings say. The editor says so where you set the discount. An item’s Subscription panel shows Discounted payments locked at Every payment, with the note The discount applies to every payment for as long as the subscription runs. Waive the sign-up fee is shown off and locked, because the fee is always charged here. The item’s line reads “every payment” too.Both versions store the two settings. A limit or a waiver set on Pro is kept on the free version but not applied. When you upgrade, your existing answers apply again with nothing to set up.
Choosing on Pro
Each subscription item’s Subscription panel has a Discounted payments field and a Waive the sign-up fee switch.Discounted payments
Payments are counted the way WooCommerce Subscriptions counts them, including the first one. “3” means the initial order and two renewals. The note under the field spells out the result in your numbers, for example The first 3 payments are discounted, then $9.99 resumes.
When the discount runs out
The line is set back to the product’s own live price. It has to be reset, because the discounted price has already been written onto the line. The limit and the price to return to are written onto the subscription at checkout. Editing the preset later doesn’t change what an existing subscriber agreed to. The price renewals return to is the same baseline the editor quoted. If the item uses Use the regular price instead of the sale price, renewals bill the regular price even if a sale is running at the time, so a sale can’t stamp a lower renewal price than you set.When a customer resubscribes
The count carries over. A customer who cancels and resubscribes from My account picks up where they left off, on the terms their first subscription had.- Two of three discounted payments used: the resubscribe’s first payment is the third, still discounted. Renewals after it bill the full price.
- All three used: the new subscription is at the full price from the start.
Waive the sign-up fee
Zeroes the product’s sign-up fee for that line only. The mark travels in memory and is never saved to the product, so other customers buying the same product outside this preset still pay the fee.Renewal protections
Three protections apply automatically:- Renewal orders never use up a purchase-limit slot. They inherit the preset’s meta from the subscription, and without the guard one subscriber’s renewals would use up a capped preset on their own.
- A renewal paid through the cart is never repriced. The subscription decides what a renewal costs, not the preset.
- Products that can only be bought as a subscription plan (All Products for Subscriptions’ forced mode) are refused as preset items, and the product picker leaves them out. A preset adds by product ID with no plan, and when two pricing engines both price one line, one of the prices shown is wrong.
A known limitation
Where a discount covers a fixed number of payments, WooCommerce’s cart line still shows a recurring price string such as “36.00 every month”, even though the discount won’t last for the whole subscription. The string comes from Subscriptions’ own templates, built before the preset’s terms are worked out. The order confirmation and the subscription record are correct. Say so in your offer copy if the difference matters to your customers.Settings reference
For developers
Both are resolved throughcartpresets_item_subscription_terms, returning [ 'payments' => int, 'waive_fee' => bool ], where payments counts the payments the discount covers, including the one being made, and 0 means every payment for the life of the subscription.
It’s seeded with the free version’s reading: payments => 0 (every payment) and the fee charged. A listener that returns no payments key keeps the seed, so an integration that only answers waive_fee can’t cut the discount short by accident. A payments value goes through absint(). See the hooks reference.