Reputation: 39889
I recently changed the names of my plans, including their ID.
Some of my plans have the same structure (same amount, period, trial, etc), it's just the name that have changed.
Using the Stripe API, I'd like to move all my current customers to the new plan that is similar in term of features.
The problem is that I don't want to charge them now, nor change their period, just change the Stripe subscription.
I'm pretty sure it's possible using the update a subscription API endpoint, but by playing with the trials, billing_cycle_anchor and days_until_due.
I'm hoping this has already been done and someone already has a working solution for this :)
Since it's API related, there is no need for a specific language.
Upvotes: 2
Views: 1723
Reputation: 39889
Here's a pratical response to my issue. It updates the plans to another that have the same properties (recurring, amount, etc).
processed = 0
starting_after = None
while True:
results = stripe('get', '/subscriptions', params={
'limit': 100,
'starting_after': starting_after
}).json()
for sub in results.get('data'):
print("Checking {0} for customer {1}".format(sub.get('id'), sub.get('customer')))
starting_after = sub.get('id')
if sub.get('items').get('total_count') != 1:
print('Customer {0} has {1} subscriptions ????'.format(sub.get('customer'), sub.get('items').get('total_count')))
continue
plan = sub.get('items').get('data')[0].get('plan')
if plan.get('id') not in maps:
print('Plan {0} was not found'.format(plan.get('id')))
continue
try:
invoice = stripe('get', '/invoices/upcoming', params={
'customer': sub.get('customer')
}).json()
except requests.exceptions.HTTPError as e:
if e.response.status_code != 404:
print(e.json())
continue
print("Ignoring customer {0} because subscription will end ...".format(sub.get('customer')))
continue
expected_amount = invoice.get('total')
try:
new_invoice = stripe('get', '/invoices/upcoming', params={
'customer': sub.get('customer'),
'subscription': sub.get('id'),
'subscription_items[0][id]': sub.get('items').get('data')[0].get('id'),
'subscription_items[0][deleted]': True,
'subscription_items[1][plan]': maps[plan.get('id')],
'subscription_prorate': False
}).json()
except Exception as e:
print(e.response.json())
continue
assert invoice.get('total') == new_invoice.get('total')
assert invoice.get('amount_due') == new_invoice.get('amount_due')
assert invoice.get('next_payment_attempt') == new_invoice.get('next_payment_attempt')
assert invoice.get('period_start') == new_invoice.get('period_start')
assert invoice.get('period_end') == new_invoice.get('period_end')
stripe('post', '/subscriptions/{0}'.format(sub.get('id')), data={
'items[0][id]': sub.get('items').get('data')[0].get('id'),
'items[0][deleted]': True,
'items[1][plan]': maps[plan.get('id')],
'prorate': False
}).json()
updated_invoice = stripe('get', '/invoices/upcoming', params={
'customer': sub.get('customer')
}).json()
assert invoice.get('total') == updated_invoice.get('total')
assert invoice.get('amount_due') == updated_invoice.get('amount_due')
assert invoice.get('next_payment_attempt') == updated_invoice.get('next_payment_attempt')
assert invoice.get('period_start') == updated_invoice.get('period_start')
assert invoice.get('period_end') == updated_invoice.get('period_end')
processed += 1
if not results.get('has_more'):
break
And the stripe
function:
def stripe(method, url, **kwargs):
headers = {
'Stripe-Version': '2019-08-14'
}
response = getattr(requests, method)(
'https://api.stripe.com/v1{0}'.format(url),
auth=('STRIPE_PRIVATE_KEY', ''),
headers=headers,
timeout=15,
**kwargs
)
response.raise_for_status()
return response
Upvotes: 1
Reputation: 5470
From what you've described, it sounds like this should be a fairly easy transition! That said, my advice when attempting any kind of Subscription change, is to try it out in Stripe's test mode first.
If the period of your old and new subscription plans are the same (i.e. monthly -> monthly
), the billing period will not change, and Stripe will not attempt to collect payment until the current billing period ends.
Here's what Stripe has to say about it:
https://stripe.com/docs/billing/subscriptions/upgrading-downgrading
If both plans have the same billing periods—combination of
interval
andinterval_count
, the subscription retains the same billing dates.Stripe immediately attempts payment for these subscription changes:
- From a subscription that doesn’t require payment (e.g., due to a trial or free plan) to a paid subscription
- When the billing period changes
As far as how to implement this process:
proration
from a change part-way through a billing cycle, be sure to pass prorate=false
https://stripe.com/docs/api/subscriptions/update The /v1/invoices/upcoming
endpoint can also be a helpful tool to preview what subscription changes might look like at the next billing period.
At any time, you can preview the upcoming invoice for a customer. This will show you all the charges that are pending, including subscription renewal charges, invoice item charges, etc. It will also show you any discount that is applicable to the customer. [...] You can preview the effects of updating a subscription, including a preview of what proration will take place.
https://stripe.com/docs/api/invoices/upcoming
Upvotes: 2