#Village-webhook-event
1 messages ยท Page 1 of 1 (latest)
Hi! What are you trying to do?
The best way mostly is just to test.... but happy to talk to you about what might work for your use-case.
I am using subscriptions (mostly, with one time setup fees). I do not pre-create customers. I am only using $checkout_session = \Stripe\Checkout\Session::create($checkout_obj); to send a customer to the payment page.
All i want to know is when they have made a successful payment, the payment was approved, and i need to allow them access on my site. I would need to get back some metadata to know what level of service they paid for.
One of the first issues i have noticed with the 15 events, while im looking to be able to just focus on one event, not a single event contained all the info i needed. Some of the events had the metadata passed in the $checkout_session and others had the metadata saved in dashboard product prices.
Or is it possible to rely on just a single event always firing?
You likely just want to rely on checkout.session.completed and then you can retrieve other relevant objects that you need from there for metadata if you need to.
Have you taken a look at https://stripe.com/docs/payments/checkout/fulfill-orders?
The follow up to this is also going to be when they renew the subscription, in 30 days when they get auto charged, what event is going to fire for that so i know to extend their service? Is the the same event as their first time? I can not find a way in testing to mimic a renewal to test for events.
On renewal you will want to listen for invoice.paid
Have you used test clocks before?
These are a great way to simulate a renewal
So can i safely rely on only checkout.session.completed anytime i collect a payment from a customer? Whether new subscription signup or renewal?
No, i have not seen test-clocks, i will look at that.
Yep checkout.session.completed will mean that a Subscription was successfully started here
So on auto-subscription-renewals the checkout.session.completed event isn't fired?
No, checkout.session.completed will only fire when a Checkout Session is completed due to your Customer being on-session and completing that Checkout Session
You will need separate events for renewals
You could just listen for invoice.paid and then back track to the Subscription from there if you want.
But I would recommend using checkout.session.completed for the initial payment
I will dig into ^^all that^^ but im sure i will have follow up questions later (> 1hr). Should i post back to this thread or start a new thread later for follow up?
I'll leave this open for a bit, but feel free to just post in the main channel and ask someone to re-open your thread if this has been archived before you come back!
When working with subscriptions, you can set monthly, 6 months, yearly, etc. Does stripe always advance the renewal to the following month keeping lockstep with the date? Like a monthly subscription started on Jan 25th will always renew on the 25th of each month vs exact 30 days? And how does it handle shorter months?
If a sub starts Jan 31st what happens in Feb? And if it (logically) shortens to Feb 28th, then in March does it stay at 28th or go back to 31st when it can?
You should always avoid trying to set the renewal date to the last day of the month. It will shorten to the 28th and not go back. It is best practice to use the 1st rather than the last day of the month.
If you specify your recurring interval to 'monthly' you will get an invoice every 1st, 5th, or whichever date (that exists in all months) you select.
I missed this feature, i created products and on the price set the price to recurring. I did not see anything about setting a billing date/cycle.
You can combine the interval and interval_count attributes for a price to configure how the recurring billing behaves.
https://stripe.com/docs/api/prices/create#create_price-recurring-interval_count
Based on just setting a recurring price on a product, what is the default renew behavior?
The Billing date is set by billing_cycle_anchor which is set on the Subscription object.
https://stripe.com/docs/api/subscriptions/create#create_subscription-billing_cycle_anchor
If im understanding the docs you linked, that is for creating products/price via the API? What about when i have created my products/prices in the dashboard? I didn't see anything in the dashboard related to billing cycles. What am i missing?
Since this Discord server is developer focused, we tend to reach for API solutions first. Let me check on DB options
Okay, that's a price and I think the default is an interval_count of 1.
If you are creating a subscription in the dashboard you will see an option to "start billing cycle on".
I have not created subscriptions, just products with a recurring price. On my Dashboard > Payments > Subscriptions it just shows (test) customers that have been auto created by checkouts. If i click on one of those customers it shows Current Period for example "Mar 22 to Apr 22". I assume that is the billing cycle?
Yup. If you are tryiing to create these via the Dashboard you need to first select a Customer. Then you can create a subscription for that customer in the "Actions" menu
Wait... hold on sec. Current period is always forward looking, billing period is always the last completed billing cycle
Im not trying to create anything, im just doing bare minimum. Selling subscriptions, only making products with prices as "service levels".
Selling subscriptions
That means you'll need to create the subscription object for a customer
I think this happens automatically in the background.
I only create a $checkout_session and set 'price' => 'price_xxxxxxxx' and forward them to the payment page.
They make a payment, i get the hook events.
In the dashboard i see a customer was created for them
Okay, you can configure how the subscription gets created by specifying the subscription_data parameter
https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-subscription_data
Complete reference documentation for the Stripe API. Includes code snippets and examples for our Python, Java, PHP, Node.js, Go, Ruby, and .NET libraries.
But it's a subset that does not include billing_cycle_anchor
What is default behavior since i didn't? For example, i just made a testing clock, added a test customer. In the dashboard it shows current period Mar 22 to Apr 22. I advanced the clock to Apr 24th, it fired off invoice.created customer.subscription.updated and setup_intent.created. It doesn't look like they were charged for another month as in the dashboard it is showing...
What date would they actually get billed on?
But it also shows
Invoices generated like this have a 1 hour period where they remain in draft status so you have an opportunity to modify them. I'm not sure how that behaves with Test Clocks as I haven't had a chance to play with them.
Which confuses me, i thought they would have been billed on Apr 23rd, but they weren't. i advanced the clock one more day to 24th, it made the drafts, looks like they still haven't been billed, while at the same time saying next billing is now may 23rd.
Okay, i will try advancing the test clock just a couple of hours, brb...
AFAIK, test clocks will only advance the time but it won't take any actions for you. So if your configuration requires that you manually confirm the invoice advancing the test clock will not perform these actions
Okay, still on Apr 24th, advancing a few hours it fired off payment_intent.succeeded charge.succeeded customer.updated payment_intent.created invoice.updated invoice.paid invoice.payment_succeeded and invoice.finalized
Yeah, auto-advancing and auto-billing a subscription does a lot of events
Back to big picture, im just trying to figure out which events i should watch for and which others are noise i can ignore. And i was also curious on default behavior of renewals so im "advertising" correctly to the customer when their membership ends or gets rebilled. I guess i will just need to setup a test clock for jan 31st and see ๐
Id prefer to only have to listen for and worry about one event that will just always tell me when a payment was paid. Then i can credit that customer on my side.
Whether new started sub or renewal.
Well you might want to listen for some failures as well
Failures in what way? My strategy was just setting a service end date in my system. When they make a successful payment, i get the hook event and i extend their date. If they don't pay, payment fails, nothing happens, date comes and goes and their service is expired.
If their payment method gets declined for example
I dont need to "take away" from them for failed payments. Just let the date run out.
What don't i know?
But as for simple webhooks to listen to, invoice.paid will include the invoice object which also has a billing_reason attribute. This will tell you whether its for a new subscription or a renewal, if that matters for your integration,
https://stripe.com/docs/api/invoices/object#invoice_object-billing_reason
Complete reference documentation for the Stripe API. Includes code snippets and examples for our Python, Java, PHP, Node.js, Go, Ruby, and .NET libraries.
Okay, i will go over this material you have given me so far. Thank you.
As for the metadata question. You can pass metadata to the subscription when creating the Checkout session. But I'm not sure if that goes all the way down to the invoice.
However, you can get the related subscription from the invoice and retrieve the metadata that way.
I tested that already, the metadata i passed to the checkout session was only retained in checkout.session.completed not even in the charge.succeeded or invoice.paid
No, not cheeckout.metadata, you need to use checkout.subscription_data.metadata
All the nesting of attributes can make this tricky but it allows our users to build some flexible integrations
My issue is im not manually creating subscriptions, id have to find a way to go backwards, look up what subscription was created in the act of accepting recurring payment, then attach metadata. Right?
You are manually creating subscriptions, through a Checkout session configured for recurring payments. You can either pass along the metadata to the subscription when you create the Checkout session or you can take your approach, which seems a bit more cumbersome to me.
I agree, im not voting for that method. I didn't see where i can pass subscription related values to the checkout session.
I just told you, twice
Is there a doc page (link) that tells me all possible key:values that can be passed in creating a checkout session?
im re-reading that link
If you expand all the docs it gets really verbose. So I can understand not getting it all right away
Im reading it, i hear it conceptually, but im not seeing syntax, im not seeing how to package that into the object. (PHP)
'subscription_data.metadata` => ['key'=>'value"],
??
Well it's just nested associative arrays.
Or is it another nested layer deep? like subscription_data => metadata => [key=>value] ?
'subscription_data => ['metadata' => ['foo' => 'bar']]
I think, I. mostly use Python
Yeah I think the second one
Okay, i will test that out. Thanks.
๐ค
Oh quick question before i dig into all this with testing.
Is there a way to do the testing clocks while actually going through a checkout session from my site? When i created a testing clock i had to make a fake customer in the dashboard, so i can't really test all of the objects with whatever data they would have attached to them from a real checkout process.
For example, testing adding subscription metadata then watching how it comes back in hooks when i advance time.
Well, test clocks are bound to customers. So what you would do is create a fake customer with a test clock and then specify that customer ID when creating the Checkout Session.
Okay, i will try that.
Still a few hoops to jump through but it gets your test clock wired up to the checkout session
Im having issue getting the sub metadata into the checkout session. In PHP i tried
'subscription_data' => ['metadata' => ['test_key' => 'test_value']],
Which i believe is correct syntax because the API doesn't throw an error. But that key:value pair isn't showing up in the $checkout_session.
I also have this
'metadata' => ['signup_email' => $signup_email],
Which does show up in the $checkout_session
Hello ๐
Taking over for Snufkin here as they had to step away
AFAIK I don't think we copy the metadata to underlying objects
So even its not showing up in the $checkout_session object it still might be returned in the endpoint hook event?
AFAIK subscription_data sets the metadata on the subscription itself.
In theory, checkout_session should have a subscription object you can expand on and get the metadata from it
Is that what you mean? Or is there something else i have to do to "expand" it?
There is no syntax examples in the docs on creating a session showing how to use the "more parameters" with children.
oh wait apologies
I misunderstood the question here.
I thought you were unable to get the metadata on your webhook endpoint
I have to send it before i can get it ๐
Big picture goal, im trying to pass key:value in the checkout session that i can see in the invoice.paid hook to match payment to customer on my end.
I was told the subscription_data.metadata would be a way to do that. I have not figured out the correct syntax in PHP to do that, as im not seeing it show up in the $checkout_session when i dump to screen.
As in this $checkout_session im not seeing the test_key=test_value set in the above code.
ah okay. I'm not super skilled in PHP as well but give me a moment to look into it
Should it be showing in the $checkout_session? Or is it possible to not be visible and still be in the hook event?
I can do a test transaction and see what comes back in the hook. But im guessing if its not in the session it wont be in the hook.
I'd suggest testing as it'd clarify both pieces at once
hmm, okay, so i set it in the code, it doesn't show in the $checkout_session, but it did come back in the hooks as:
'data.object.lines.data.1.metadata.test_key' => 'test_value',
So its returned in lines which are prices of products.
I will have to setup a timer test to see how/if it comes back on a renewal.... this will take a few mins.
Ah okay
Thanks for confirming
As far as I can tell It should work for renewal as well but testing would certainly confirm that assumption
Okay, i test clock to a renewal. It returned the metadata in invoice.paid (and a couple others) as
'data.object.lines.data.0.metadata.subdata_meta_key' => 'test_value'
Not ideal IMO because how do you know where to look? Will it always be in lines.data.[0].metadata vs [1] or some other number?
And i see it fired off events for invoice.paid and invoice.payment_succeeded. What types of scenarios might cause only one of those to be triggered? Or are they both always triggered together?
Im trying to figure out which one event i can rely on to always fire when a customer pays (whether new sub or renewal) and only have to listen for that one event.
They're mostly fired one after the other but if I understand it correctly,
invoice.payment_succeeded might also be triggered on partial payment of the invoice. invoice.paid is triggered when the invoice is marked paid
So safer to rely on invoice.paid
Okay, im going back down the rabbit hole. I'll make more noise if i stumble again.
NP! ๐ Good luck