To reproduce:

- create an hourly billing schedule
- create a subscription product priced at 1000
- create a user and use them to purchase the product
- edit the product and change its price to 2000
- either wait an hour, or hack commerce_order__billing_period so the period ends sooner, then run cron

Result:

The recurring order which was in draft is now complete, with a total price of 1000 and a payment of 1000.

Expected:

The recurring order should charge the user the current cost of the product.

Comments

joachim created an issue. See original summary.

bojanz’s picture

Category: Bug report » Support request
Priority: Major » Normal

What you're describing as a bug was one of the biggest missing D7 features :)

Subscribers are grandfathered in to plan changes. This is how subscription services generally work by default.
To switch subscribers to a new price, you will need custom code that loads & updates subscriptions (the amount, to be precise) and we'll want to document that on a page somewhere.

bojanz’s picture

Title: Recurring orders do not take into account changes to price of product variation » Document grandfathering and getting around it
bojanz’s picture

Category: Support request » Task
joachim’s picture

> Subscribers are grandfathered in to plan changes. This is how subscription services generally work by default.

That really isn't.

If I buy a magazine subscription, it increases in price. If I buy a membership to a club, that increases in price. My monthly broadband bill increases in price.

> To switch subscribers to a new price, you will need custom code that loads & updates subscriptions (the amount, to be precise) and we'll want to document that on a page somewhere.

Why should this be custom code? I think this is a feature that the module should provide.

bojanz’s picture

Here are some relevant links from my notes.

General idea:
https://www.chargebee.com/blog/ask-us-grandfathering-prices-important-saas/

Recurly grandfathers by default:
https://docs.recurly.com/v1.0/docs/faq#section-what-happens-to-accounts-...

Chargify grandfathers by default via revisions:
https://reference.chargify.com/v1/subscriptions/subscription-output-attr... (see product_version_number)

Chargebee makes it manual, just like CLB did:
https://www.chargebee.com/docs/plans.html#grandfathering-existing-plans

Stripe makes it manual by forbidding price changes of plans:

Once a plan has been created, only the metadata and statement description can be modified; the amount, currency, and interval are fixed. Should you need to change any of these parameters, a new plan must be created instead.
This restriction avoids the potential surprise and confusion that would arise from having different customers on the same plan but at different price points.

https://stripe.com/docs/subscriptions/plans

Braintree doesn't care: https://developers.braintreepayments.com/guides/recurring-billing/plans

So, back to you.

Why should this be custom code? I think this is a feature that the module should provide.

I'm not against it. Let's discuss how that would look/work.

joachim’s picture

The difference between all those services and Drupal Commerce is that those services don't know about your products. They only know there is a subscription and that they should charge the customer. So of course they need to be told to change the amount of that.

A better comparison would be with something like Magento, which AFAIK is integrated like Commerce is -- but a quick google for that throws up a load of different paid-for extensions, none of which tell me much about it.

> Let's discuss how that would look/work.

Two questions spring to mind:

1. Where should the setting(s) for this be? I would say the Billing Schedule, since that already has the pro-rata settings for prices.

2. Is this an on/off setting, or is there more than 1 way to handle price updates?
- If the former, then it's just a setting on the Billing Schedule, and the logic in in something like a service that's called by the entity update hook (in the absence of #2551893: Add events for matching entity hooks)
- If the latter, then it should probably be a new plugin type.

bojanz’s picture

The difference between all those services and Drupal Commerce is that those services don't know about your products.

All those services have the concept of a Plan which maps 1-1 to how we use variations. So I'd argue there's no difference at all.

WooCommerce grandfathers by default too:
https://docs.woocommerce.com/document/subscriptions/renewal-process/
"If the prices for any product line items on the subscription have changed, the customer’s new subscription will be grandfathered with the original, older prices".

1. Where should the setting(s) for this be? I would say the Billing Schedule, since that already has the pro-rata settings for prices.

Yeah, the billing schedule is pretty much the only place where we can put settings on.

The crux of the logic is a class that would get a variation, and start a batch, loading subscriptions with that variation ID, and changing their amounts. The price would probably be a resolved price, to allow for price lists (per currency, role).
Thus the logic is:
1) Get a variation.
2) Resolve its price
3) Find matching subscription IDs, split them into groups (of 25? more?)
4) Load each group, and update the amounts.
This allows for a lot of freedom code-wise, updating only subscriptions for a specific variation / billing schedule / subscription type.

I looked into having a flag that tells the subscription type "just resolve a fresh price and use that", but it adds complexity to subscription types that I'm not convinced is worth it, compared to just doing a bulk price update, especially since that bulk update will also allow us to schedule changes for a future date, once we have that API.