#paulc7053_api

1 messages · Page 1 of 1 (latest)

frank juncoBOT
#

👋 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.

primal crestBOT
#

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.

vast echo
#

Hey! It seems like creating a schedule creates 2 subscriptions?

primal crestBOT
hollow path
#

Does creating a subscription schedule with multiple phases create multiple subscriptions?
Each Subscription scheduler is linked to a single subscription

vast echo
#

Okay, that makes sense

vast echo
#

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

hollow path
#

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.

vast echo
#

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

hollow path
#

Can you share the subscription Ids ?

vast echo
#

Initial sub id: sub_1OwO8iJ4ILijjURSEsIlRe8l

#

Second sub id: sub_1OwO8jJ4ILijjURSLoFeKiro

hollow path
#

thanks, checking...

vast echo
#

Thank you!

hollow path
#

Your backend made two request

#

req_RYiILoW4MSHhoY and req_rSqME6TZRrBL5V

#

Ah wait

#

why you are calling both stripe.subscriptions.create and stripe.subscriptionSchedules.create ?

vast echo
#

yes

hollow path
#

You need just to call the subscriptionSchedules call.

vast echo
#

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, });?

hollow path
#

You need to create just the scheduler.

vast echo
#

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)

hollow path
#

yes you can use default_incomplete

vast echo
#

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'] ?

hollow path
#

save_default_payment_method isn't supported for Subscirption Scheduler

vast echo
#

Creating a subscription like that doesn't seem to contain the clinet_secret needed to convirm the payment on the client

hollow path
#

while the expand is needed in order to get the latest inoivce's payment intent in order to collect the payment method

vast echo
hollow path
#

yes

vast echo
#

Actually, I don't see it

hollow path
#

Can you share the Subscription Scheduler Id ?

vast echo
#

yes, sub_sched_1OwOP9J4ILijjURSSx389ndQ

hollow path
#

Actually, you need to expand the subscription's latest invoice's payment intent

#

expand: ['subscription.latest_invoice.payment_intent']

vast echo
#

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, });

hollow path
#

You need to update the expand

#

You need to specify fields that existing on the object it self

vast echo
#

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

hollow path
#

What is the status of the invoice ?

#

Can you please share the requestId of each call ?

vast echo
#

Each call from the frontend checkout to creating the schedule?

hollow path
hollow path
#

It will be finalized in 1hour

vast echo
#

It's evt_1OwOb0J4ILijjURSM3F1MFyA

vast echo
vast echo
#

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*

hollow path
#

Yes you can create a Scheduler from an existing subscription

vast echo
#

Trying it now, 1 sec please

primal crestBOT
vast echo
#

Okay, now it seems I cannot add phases to such a schedule

#

hi!

#

⨯ StripeInvalidRequestError: You cannot set phasesiffrom_subscription is set.

signal wagon
#

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

vast echo
#

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

signal wagon
#

okay, let's take a step back.
What usecase are you trying to use subscription schedules for?

vast echo
#

I need to use phases in a user's subscription

signal wagon
#

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?

vast echo
#

The subscription starts at $X, after Y iterations, I want the subscription to cost $Y (so I change the priceId)

vast echo
signal wagon
#

but it did create two subscriptions vor the same user
A subscription schedule would never create two subscriptions. Did you call subscriptions.create(...) separately along with subscriptions_schedules.create(...) ?

vast echo
#

Yes

signal wagon
#

You don't need to use both.

vast echo
#

But what about the client secret?

#

the subscription scheduler doesnt seem to return a client_secret

signal wagon
#

Subscription Schedule creates a subscriptions automatically.
You can retreive the client-seceret from the subscription the schedule ends up creating

vast echo
#

Okay, will try that, 1 sec please

vast echo
signal wagon
#

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

vast echo
#

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

signal wagon
#

client-secret would be under latest_invoice
Can you share the whole object you're looking at?

vast echo
#

{ 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

signal wagon
#

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

vast echo
#

I tried it

#

expand: ['latest_invoice.payment_intent'], || expand: ['subscription.latest_invoice.payment_intent'],

signal wagon
#

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

vast echo
#

⨯ 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});

`

signal wagon
#

I think you misundertood. You need to set expand to ['subscription.latest_invoice.payment_intent'] and NOT ['latest_invoice.payment_intent']

vast echo
#

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?

signal wagon
#

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)
vast echo
#

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

signal wagon
#

oh huh

#

payment_intent is null

#

🤔

vast echo
#

Why?

#

...

signal wagon
#

Ah the invoice is still in draft status

#

it takes about an hour for it to finalize

vast echo
#

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 ?

signal wagon
#

Give a moment, trying to think of alternatives here..

vast echo
#

Okay, thanks!

signal wagon
#

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

vast echo
#

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 }],
                    },
                ],
            });`
signal wagon
#

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?

vast echo
#

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.

signal wagon
#

You need to pass the existing phase first without start_date and then add start_date for the second phase.

vast echo
#

with what value?

#

It seems not as a best practice vor me to compute new Date() + 3 years , and then all in unix

primal crestBOT
hexed silo
#

Hello, catching up here

vast echo
#

hey1

#

!*

signal wagon
#

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

vast echo
#

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: '' }, ], });

signal wagon
vast echo
#

Yes, it's req_Wq9B4Qq7MIjirP

signal wagon
vast echo
#

I am so confused right now

#

I thought .update simply updates?

signal wagon
#

It is not the .update call that's causing this error

#

it is the .create call before that

vast echo
#

const subscriptionSchedule = await stripe.subscriptionSchedules.create({ from_subscription: subscriptionId, });

signal wagon
#

The reason you're seeing the error is because the subscription is already attached to a schedule

vast echo
#

But then what subscriptionScheduleId will I pass to the update?

hexed silo
#

The existing one

vast echo
#

Hm alright, it's possible, because I'm resending the same webhook request

hexed silo
#

If you retrieve the subscription object it should have the schedule's ID as its schedule parameter

#

Ah yep that would do it

vast echo
#

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, ?

hexed silo
#

That makes sense to me as a way to get a valid timestamp

vast echo
#

Here's the req ID req_zzrEQ1kdDyqvDg

hexed silo
#

Thank you

vast echo
#

Np

hexed silo
vast echo
#

So I have to compute it...?

#

Seems like a good best practice 🙂

hexed silo
#

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

vast echo
#

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.

hexed silo
#

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

vast echo
#

unless it is the last phase and end_behavior is release

hexed silo
#

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

vast echo
#

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

hexed silo
#

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

vast echo
#

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

hexed silo
#

Yep, that should work, though it sounds like you may have already figured that out. Apologies I missed that latest question