#paulc7053_api
1 messages · Page 1 of 1 (latest)
👋 Welcome to your new thread!
⏲️ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.
⏱️ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.
🔗 This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1219982054201229332
📝 Have more to share? Add more details, code, screenshots, videos, etc. below.
Below are links to other discussions we've had with you in the past week in case you want to review that information. If your question is related to one of these previous discussions, please provide a comprehensive summary of the current state and what you need help with now. We help many users simultaneously, so a summary allows us to resolve your issue as soon as possible.
- paul_subscription-testclock, 11 hours ago, 75 messages
- paulc7053_webhooks, 16 hours ago, 50 messages
- paulc7053_webhooks, 1 day ago, 21 messages
- paulc7053_code, 1 day ago, 36 messages
- paul_ece-googlepay, 4 days ago, 58 messages
- paulc7053_code, 5 days ago, 99 messages
and 2 more
Hey! It seems like creating a schedule creates 2 subscriptions?
Does creating a subscription schedule with multiple phases create multiple subscriptions?
Each Subscription scheduler is linked to a single subscription
Okay, that makes sense
This is how I create a schedule
`//create the subscription
const subscription = await stripe.subscriptions.create({
customer: stripeCustomerId,
items: [{
price: priceId,
}],
payment_behavior: 'default_incomplete',
payment_settings: { save_default_payment_method: 'on_subscription' },
expand: ['latest_invoice.payment_intent'],
metadata: metadata,
});
console.log("Stripe subscription created.");
//create the subscription schedule
await stripe.subscriptionSchedules.create({
customer: stripeCustomerId,
start_date: 'now',
phases: [
{
items: [{ price: standard, quantity: 1 }],
iterations: 3,
},
{
items: [{ price: storage, quantity: 1 }],
},
],
});
console.log("Subscription schedule created.");`
But in the dashboard, 2 subscriptions pop up
with 2 IDs
with CORNELIUS@hey.com I commented out the subscription schedule and it works as expected
No the other subscription was created from another request
you can check the creation requestId of each Subscription
I invite you to double check your tests, or delete all your test subscriptions and create a new one in other to not get confused.
I've just deleted them, but will try again
Same outcome
The last created sub has the Upcoming phases section
While the previous one doesnt
Can you share the subscription Ids ?
Initial sub id: sub_1OwO8iJ4ILijjURSEsIlRe8l
Second sub id: sub_1OwO8jJ4ILijjURSLoFeKiro
thanks, checking...
Thank you!
Your backend made two request
req_RYiILoW4MSHhoY and req_rSqME6TZRrBL5V
Ah wait
why you are calling both stripe.subscriptions.create and stripe.subscriptionSchedules.create ?
yes
You need just to call the subscriptionSchedules call.
Ah...
so that become something like this await stripe.subscriptionSchedules.create({ customer: stripeCustomerId, start_date: 'now', phases: [ { items: [{ price: standard, quantity: 1 }], iterations: 3, }, { items: [{ price: storage, quantity: 1 }], }, ], payment_behavior: 'default_incomplete', payment_settings: { save_default_payment_method: 'on_subscription' }, expand: ['latest_invoice.payment_intent'], metadata: metadata, });?
You need to create just the scheduler.
I also don't see it in the docs -but just making sure, I don't need to pass payment_behavior: 'default_incomplete'? (I'm using custom stripe elements on the client and then using .confirmPayment)
yes you can use default_incomplete
like creating a normal subscription following the recommended guide : https://docs.stripe.com/billing/subscriptions/build-subscriptions
Ah, I don;t see these being listed in here https://docs.stripe.com/api/subscription_schedules/create
Do I need to also pass the payment_settings: { save_default_payment_method: 'on_subscription' and expand: ['latest_invoice.payment_intent'] ?
save_default_payment_method isn't supported for Subscirption Scheduler
Creating a subscription like that doesn't seem to contain the clinet_secret needed to convirm the payment on the client
while the expand is needed in order to get the latest inoivce's payment intent in order to collect the payment method
Ah, then it should countain the client secret
yes
Actually, I don't see it
Can you share the Subscription Scheduler Id ?
yes, sub_sched_1OwOP9J4ILijjURSSx389ndQ
Actually, you need to expand the subscription's latest invoice's payment intent
expand: ['subscription.latest_invoice.payment_intent']
Yes, this is how I created it const subscription = await stripe.subscriptionSchedules.create({ customer: stripeCustomerId, start_date: 'now', phases: [ { items: [{ price: standard, quantity: 1 }], iterations: 3, }, { items: [{ price: storage, quantity: 1 }], }, ], expand: ['latest_invoice.payment_intent'], metadata: metadata, });
You need to update the expand
You need to specify fields that existing on the object it self
Ah, that doesnt have subscription
I'll try it
Okay, just to double check. This is my request
const subscription = await stripe.subscriptionSchedules.create({ customer: stripeCustomerId, start_date: 'now', phases: [ { items: [{ price: standard, quantity: 1 }], iterations: 3, }, { items: [{ price: storage, quantity: 1 }], }, ], expand: ['subscription.latest_invoice.payment_intent'], metadata: metadata, });
I still don't see the client_secret
The ID is sub_sched_1OwOazJ4ILijjURS2yZ8LLwb
What is the status of the invoice ?
Can you please share the requestId of each call ?
Each call from the frontend checkout to creating the schedule?
No just the requestId of each code you post here
I see that the invoice status is Draft:
https://dashboard.stripe.com/test/invoices/in_1OwOP9J4ILijjURShVrtRnRZ
It will be finalized in 1hour
It's evt_1OwOb0J4ILijjURSM3F1MFyA
Why?
Is there any way I can create the subscription like I initially did (with stripe.subscriptions.create), and the add a schedule to it?
then*
Trying it now, 1 sec please
Okay, now it seems I cannot add phases to such a schedule
hi!
⨯ StripeInvalidRequestError: You cannot set phasesiffrom_subscription is set.
You can set the phases once the schedule has been created using Update Subscription Schedules API
So two separate requests
One to create the schedule and one to update it with phases
Alright, will try it right now
1 sec plase
please*
That then throws StripeInvalidRequestError: You cannot migrate a subscription that is currently in the incompletestatus. It must be inactive, past_due, unpaid, trialing status to be released.
I need it in incomplete, as I use convirmPayment on the client
`//create the subscription
const subscription = await stripe.subscriptions.create({
customer: stripeCustomerId,
items: [{
price: priceId,
}],
payment_behavior: 'default_incomplete',
payment_settings: { save_default_payment_method: 'on_subscription' },
expand: ['latest_invoice.payment_intent'],
metadata: metadata,
});
console.log("Stripe subscription created.");
//create the subscription schedule
const subscriptionSchedule = await stripe.subscriptionSchedules.create({
from_subscription: subscription.id,
});
console.log("Subscription schedule created.");
//update the subscription schedule
await stripe.subscriptionSchedules.update(
subscriptionSchedule.id,
{
phases: [
{
items: [{ price: standard, quantity: 1 }],
iterations: 3,
},
{
items: [{ price: storage, quantity: 1 }],
},
],
}
);`
^ This is my code now
okay, let's take a step back.
What usecase are you trying to use subscription schedules for?
I need to use phases in a user's subscription
For what though?
You can use Subscription Schedules API for this directly. Is there a reason you're trying to create a subscription first and then create a schedule?
The subscription starts at $X, after Y iterations, I want the subscription to cost $Y (so I change the priceId)
I did use the Subscriptions Schedules API directly, but it did create two subscriptions vor the same user (as I still needed the client secret). Or how exactly do you see it?
but it did create two subscriptions vor the same user
A subscription schedule would never create two subscriptions. Did you callsubscriptions.create(...)separately along withsubscriptions_schedules.create(...)?
Yes
You don't need to use both.
But what about the client secret?
the subscription scheduler doesnt seem to return a client_secret
Subscription Schedule creates a subscriptions automatically.
You can retreive the client-seceret from the subscription the schedule ends up creating
Okay, will try that, 1 sec please
Does this seem correct to you?
It does but you don't need to use both APIs here as far as I can tell.
You can retrieve a schedule or listen to webhook events to find the associated subscription
Okay, I'll try it rn
I get the sub like this const subscription = await stripe.subscriptions.retrieve(subscriptionSchedule.subscription.id);
But it doesnt have a client secret
client-secret would be under latest_invoice
Can you share the whole object you're looking at?
{ subscription: { id: 'sub_1OwPAcJ4ILijjURSFhDA9mqn', object: 'subscription', application: null, application_fee_percent: null, .... currency: 'gbp', current_period_end: 1742477226, current_period_start: 1710941226, customer: 'cus_Plx4J90Pi6WprW', days_until_due: null, default_payment_method: null, default_source: null, default_tax_rates: [], description: null, discount: null, ended_at: null, invoice_settings: { account_tax_ids: null, issuer: [Object] }, items: { object: 'list', data: [Array], has_more: false, total_count: 1, url: '/v1/subscription_items?subscription=sub_1OwPAcJ4ILijjURSFhDA9mqn' }, latest_invoice: 'in_1OwPAcJ4ILijjURSclH8pcZn', livemode: false, metadata: {}, next_pending_invoice_item_invoice: null, on_behalf_of: null, pause_collection: null, payment_settings: { payment_method_options: null, payment_method_types: null, save_default_payment_method: null }, pending_invoice_item_interval: null, pending_setup_intent: null, pending_update: null, plan: { id: 'price_1OuGSLJ4ILijjURSgay6C1iI', object: 'plan', active: true, aggregate_usage: null, amount: 4900, amount_decimal: '4900', billing_scheme: 'per_unit', created: 1710431073, currency: 'gbp', interval: 'year', interval_count: 1, livemode: false, metadata: {}, nickname: null, product: 'prod_PjjwPUwP3rSMq5', tiers_mode: null, transform_usage: null, trial_period_days: null, usage_type: 'licensed' }, quantity: 1, schedule: 'sub_sched_1OwPAcJ4ILijjURSgiBBkT7x', ...... } }
Can I just not pass phases to subsciptions.create and avoid all this damn mess? I just don't see it in the docs
Can I just not pass phases to subsciptions.create and avoid all this damn mess? I just don't see it in the docs
that's not something the API supports today.
Looking at the object you shared, you'd need to expand latest_invoice to get the payment intent client-secret
passing that when creating the schedule throws an error
I tried it
expand: ['latest_invoice.payment_intent'], || expand: ['subscription.latest_invoice.payment_intent'],
What error are you seeing exactly? you can remove ['latest_invoice.payment_intent'] as the payment_intent would always be under the subscription parameter
You may need to retrieve the schedule separately
⨯ StripeInvalidRequestError: This property cannot be expanded (latest_invoice).
`const subscriptionSchedule =await stripe.subscriptionSchedules.create({
customer: stripeCustomerId,
phases: [
{
items: [{ price: standard, quantity: 1 }],
iterations: 3,
},
{
items: [{ price: storage, quantity: 1 }],
},
],
expand: ['latest_invoice.payment_intent']
});
const subsciption = await stripe.subscriptions.retrieve(subscriptionSchedule.subscription.id);
console.log({subsciption});
`
I think you misundertood. You need to set expand to ['subscription.latest_invoice.payment_intent'] and NOT ['latest_invoice.payment_intent']
id: 'sub_1OwPUnJ4ILijjURSrxC98LXM', object: 'subscription', .... cancellation_details: { comment: null, feedback: null, reason: null }, collection_method: 'charge_automatically', created: 1710942477, currency: 'gbp', current_period_end: 1742478477, current_period_start: 1710942477, customer: 'cus_PlxP6Vd6zODxTo', days_until_due: null, default_payment_method: null, default_source: null, default_tax_rates: [], description: null, discount: null, ended_at: null, invoice_settings: { account_tax_ids: null, issuer: [Object] }, items: { object: 'list', data: [Array], has_more: false, total_count: 1, url: '/v1/subscription_items?subscription=sub_1OwPUnJ4ILijjURSrxC98LXM' }, latest_invoice: 'in_1OwPUnJ4ILijjURS1kCYl6Si', livemode: false, metadata: {}, next_pending_invoice_item_invoice: null, on_behalf_of: null, pause_collection: null, payment_settings: { payment_method_options: null, payment_method_types: null, save_default_payment_method: null }, pending_invoice_item_interval: null, pending_setup_intent: null, pending_update: null, plan: { id: 'price_1OuGSLJ4ILijjURSgay6C1iI', object: 'plan', active: true, aggregate_usage: null, amount: 4900, amount_decimal: '4900', billing_scheme: 'per_unit', created: 1710431073, currency: 'gbp', interval: 'year', interval_count: 1, livemode: false, metadata: {}, nickname: null, product: 'prod_PjjwPUwP3rSMq5', tiers_mode: null, transform_usage: null, trial_period_days: null, usage_type: 'licensed' }, quantity: 1, ....
Still no client secret
Are you sure that the client secret is not only returned when the payment_behavior: 'default_incomplete', is set on the subscription?
Oh 🤦♂️
I think I missed the part where you're retrieving the subscription directly by calling
const subsciption = await stripe.subscriptions.retrieve(subscriptionSchedule.subscription.id);
console.log({subsciption});
Sorry about that.
You're likely looking at the wrong object.
Try printing subscriptionSchedule instead
Let me write out the exact code, hang on
const subscriptionSchedule =await stripe.subscriptionSchedules.create({
customer: stripeCustomerId,
phases: [
{
items: [{ price: standard, quantity: 1 }],
iterations: 3,
},
{
items: [{ price: storage, quantity: 1 }],
},
],
expand: ['subscription','subscription.latest_invoice']
});
console.log(subscriptionSchedule)
This is what the latest_invoice object looks like
{ ..... account_tax_ids: null, amount_due: 4900, amount_paid: 0, amount_remaining: 4900, amount_shipping: 0, .... charge: null, collection_method: 'charge_automatically', created: 1710942963, currency: 'gbp', custom_fields: null, customer: 'cus_PlxXj1uSP4mtCM', customer_address: null, ..... lines: { object: 'list', data: [ [Object] ], has_more: false, total_count: 1, url: '/v1/invoices/in_1OwPcdJ4ILijjURSGtKt3kKR/lines' }, livemode: false, metadata: {}, next_payment_attempt: 1710946563, number: null, on_behalf_of: null, paid: false, paid_out_of_band: false, payment_intent: null, payment_settings: { default_mandate: null, payment_method_options: null, payment_method_types: null }, period_end: 1710942963, period_start: 1710942963, post_payment_credit_notes_amount: 0, pre_payment_credit_notes_amount: 0, quote: null, receipt_number: null, rendering: null, rendering_options: null, shipping_cost: null, shipping_details: null, starting_balance: 0, statement_descriptor: null, status: 'draft', status_transitions: { finalized_at: null, marked_uncollectible_at: null, paid_at: null, voided_at: null }, subscription: 'sub_1OwPcdJ4ILijjURSieJyGaGV', subscription_details: { metadata: {} }, subtotal: 4900, subtotal_excluding_tax: 4900, tax: null, test_clock: null, total: 4900, total_discount_amounts: [], total_excluding_tax: 4900, total_tax_amounts: [], transfer_data: null, webhooks_delivered_at: 1710942963 }
I can't see any client_secret
But this makes no sense. How am I supposed to return to the client a secret, to then convirm the payment?
Also, I cannot create a schedule in the webhook, using the sub_id. that will throw that I cannot pass phases. So what can I do ?
Give a moment, trying to think of alternatives here..
Okay, thanks!
Okay so this should work
1/ Create a subscription by calling the Create a subscription API
2/ Confirm the underlying client-secret which moves subscription to active
3/ Once subscription is active, create a subscription schedule from the existing subscription by calling Create Subscription Schedules API with from_subscription parameter
4/ After the schedule has been created, Update it with phases
4 shouldn't trigger an error given the subscription is in active state already
okay, Ill try it right now
Okay this is getting ridiculous
⨯ StripeInvalidRequestError: You can not modify the start date of the current phase.
`//creating the schedule
const subscriptionSchedule = await stripe.subscriptionSchedules.create({
from_subscription: subscriptionId,
});
console.log("Subscription schedule created.");
//creating the phases
await stripe.subscriptionSchedules.update(subscriptionSchedule.id,{
phases: [
{
items: [{ price: standard, quantity: 1 }],
iterations: 3,
start_date: 'now', <-- added because ov previous error
},
{
items: [{ price: storage, quantity: 1 }],
},
],
});`
When you pass start_date in the first phase, this means you're trying to update the current phase which isn't what you're trying to do correct?
Correct
Where should I pass it then?
I did it because ⨯ StripeInvalidRequestError: The subscription schedule update is missing at least one phase with astart_date to anchor end dates to.
You need to pass the existing phase first without start_date and then add start_date for the second phase.
with what value?
It seems not as a best practice vor me to compute new Date() + 3 years , and then all in unix
Hello, catching up here
Sorry for the delay, was looking for this doc: https://docs.stripe.com/billing/subscriptions/subscription-schedules/use-cases#changing-subscriptions
It explains what you need to set and how to set it
There are multiple ways to use timestamps with subscription schedule API
The example in the doc I shared above shows how you can configure the future phases while keeping current phase/state as is
Now I get ⨯ StripeInvalidRequestError: You cannot migrate a subscription that is already attached to a schedule:sub_sched_1OwPxWJ4ILijjURSi3qXUuzJ.
With await stripe.subscriptionSchedules.update(subscriptionSchedule.id, { phases: [ { items: [{ price: standard, quantity: 1 }], start_date: schedule.phases[0].start_date, }, { items: [{ price: storage, quantity: 1 }], start_date: '' }, ], });
Can you share the request ID where you're seeing this error?
https://support.stripe.com/questions/finding-the-id-for-an-api-request
Yes, it's req_Wq9B4Qq7MIjirP
You're seeing the error as your code is trying to create a new schedule
https://dashboard.stripe.com/test/logs/req_Wq9B4Qq7MIjirP
You can skip that part by commenting out schedule creation API call
It is not the .update call that's causing this error
it is the .create call before that
const subscriptionSchedule = await stripe.subscriptionSchedules.create({ from_subscription: subscriptionId, });
The reason you're seeing the error is because the subscription is already attached to a schedule
But then what subscriptionScheduleId will I pass to the update?
The existing one
Hm alright, it's possible, because I'm resending the same webhook request
If you retrieve the subscription object it should have the schedule's ID as its schedule parameter
Ah yep that would do it
Okay, triggered a new webhook request
But this throws StripeInvalidRequestError: Invalid integer: I guess should be something with start_date: subscriptionSchedule.phases[0].start_date, ?
That makes sense to me as a way to get a valid timestamp
Here's the req ID req_zzrEQ1kdDyqvDg
Thank you
Np
You passed an empty string for the schedule's start date https://dashboard.stripe.com/test/logs/req_zzrEQ1kdDyqvDg
Sign in to the Stripe Dashboard to manage business payments and operations in your account. Manage payments and refunds, respond to disputes and more.
If you want the phase to do some number of complete cycles, you can just pass iterations: 1 or however many cycles to the phase before it
If you pass iterations: 3 to that first phase, we will automatically transition to the second phase after the schedule has cycled three times, no calculation necessary
You're right
Sorry, I literally passed an empty string
But now StripeInvalidRequestError: Phase 0 is invalid. Each phase must specify either iterations(recommended) orend_date(advanced), unless it is the last phase andend_behaviorisrelease. Then I tried to add it in the schedule creation const subscriptionSchedule = await stripe.subscriptionSchedules.create({ from_subscription: subscriptionId, end_behavior: 'release', });. But now it throws ⨯ StripeInvalidRequestError: You cannot setend_behavioriffrom_subscription is set.
So that first one is saying you should only specify iterations OR an end_date for the phase. If you specify both that is conflicting because they may not line up
unless it is the last phase and end_behavior is release
The second error is saying that from_subscription and end_behavior can't be set in the same create call. You can do this by making the create call with from_subscription and then make an update call to set the phases and end behavior
Look....
I know you're busy
But I've been here vor 3 hours
Can you just take a look at my snippet and tell mw what's wrong?
const subscriptionSchedule = await stripe.subscriptionSchedules.create({ from_subscription: subscriptionId, }); await stripe.subscriptionSchedules.update(subscriptionSchedule.id, { phases: [ { items: [{ price: standard, quantity: 1 }], start_date: subscriptionSchedule.phases[0].start_date, }, { items: [{ price: storage, quantity: 1 }] }, ], });
it seems it misses iterations
Yep, I don't see a way for that schedule to know when to transition to the next phase
Specifying iterations in the first phase should work I believe
I had it before but deleted it.. my brain's fried
Also, in the case I want to update the same subscription to go directly to the last phase, am I correct to just update the subscription with one item (the last phase) :await stripe.subscriptions.update(stripeSubscriptionId, { items: [{ id: subscriptionItems[0], price: priceId }], });
Okay...
thanks
Yep, that should work, though it sounds like you may have already figured that out. Apologies I missed that latest question