#Ludvig-Bartholdsson-subscriptions
1 messages · Page 1 of 1 (latest)
đź‘‹
Hello! That's a great question... let me think about this for a minute
Thanks
So I believe the only way to do this would be to have a check BEFORE the checkout session and ask the customer to provide an email address they want to checkout out with. If it's one that already has a customer object for your Stripe Account, you would take that existing customer ID and use it to create the Checkout session. Otherwise, you can pass in the email address they've entered in when you create the checkout session.
Hmm. Or maybe actually multiple people can have the same email.
How does subscriptions work in practic?
-
Does it send like an invoice via email every month or does it just take the money of their card?
-
What if the card has become invalid? How does the customer know when they have to update their card-details? Where does the customer do that?
Does it send like an invoice via email every month or does it just take the money of their card?
You can have either, controlled by the collection_method parameter (https://stripe.com/docs/api/subscriptions/create?lang=cli#create_subscription-collection_method). When collection_method: send_invoice we'll email the invoice to the customer which can be paid through our hosted invoice page, and when collection_method: charge_automatically we attempt to charge the customer's saved payment information
What if the card has become invalid? How does the customer know when they have to update their card-details? Where does the customer do that?
There are a number of settings you can enable through the dashboard (https://dashboard.stripe.com/settings/billing/automatic) that can handle things like this. For example, we have a setting that you can enable to email your customer if the recurring payment for a subscription has failed with a link to update their paymetn info
Thank you. I'm using this code snippet to create my checkout sessions.
How can I implement a collection_method in this?
Just adding it like in the link you sent me doesn't work.
$session = \Stripe\Checkout\Session::create([
'success_url' => 'https://'. $domain .'success.html?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'https://'. $domain .'canceled.html',
'payment_method_types' => ['card'],
'mode' => 'subscription',
//'collection_method' => 'charge_automatically',
'line_items' => [[
'price' => $priceId,
'quantity' => 1,
]],
]);
Ah, I forgot you were using Checkout... for Checkout the only option you have is charge_automatically, so if that's what you want then you should be all set 👍
Thank you, I enabled both of the settings.
Is it something I have to do to actually make the "Update card details" button etc work? Or does you (Stripe) handle that?
You want the settings here: https://dashboard.stripe.com/settings/billing/automatic
under "Manage failed payments for subscriptions"
You should see a setting called "Send emails when card payments fail", and if you click the eye icon you can see a preview of what those emails will look like
Perfect, enabled that too.
What about the button on that form, does it work automatically?
Or do I need to code something?
When you click the "eye" button you'll see that you can specify a link - that should be your own page where users can update their payment info from
Gotcha, and is there like a template or something I can use?
Which apis should I use ... etc?
There are a number of different ways you could do this - you could generate a link back to the customer portal to have you customers update their payment methods from there, you could have your UI w/ elements that sets up a new payment method and sets it as the customer's new default (at invoice_settings.default_payment_method)
Yup! That's what I was referring to 🙂
Ok awesome. And in this example, as I've understood it,
https://stripe.com/docs/billing/subscriptions/integrating-customer-portal#redirect
they use client side code to create a button and when the customer clicks on the button they are being redirected to the customer portal.
I don't want that button step, I just want a link to a customer portal directly in my email.
Is that possible?
I thought one way was to link directly to the .php file from the email, so you skip the client side part. Would that work or do you have a better idea?
Yes you can link directly to the customer portal or redirect to it
When you create the Session object it will have a url property that you can use
To create this session object I need the id of the customer and first after that it'll create the url property.
How do I even get the customer_id of the customer?
It will be attached to the subscription https://stripe.com/docs/api/subscriptions/object#subscription_object-customer
Is that helpful or are you talking about figuring out which subscription/customer this is even for?
Yes I'm talking about how to even figure out who the guy who clicked the link is
Unfortunately it does not look like there is a way to customize that URL to go directly to the customer portal
If you listen to webhooks it may be possible to send them this email form your server with the webhook info
I am looking in to what we have suggested in the past for this
Thank you. I'll be back tomorrow morning (gmt+2), please keep this ticket open
Okay. I will respond here in the meantime. This thread will likely be archived by then but if you ask in #dev-help we can un-archive it again.
awesome
Hey, so unfortunately that failed payment email cannot be customized to include a link directly to your customer portal. If the goal is to have the customer to get an email about this and be linked directly from the email to your customer portal, the best way to currently do that would be to listen to the invoice.payment_failed webhook which will include the Customer id which you can then use to generate the portal link and send your own email about the failed payment.
Hello
hello
Thank you!
So I should do like this?
checkout.session.completed = subscription started
invoice.paid = Subscription renewed
invoice.payment_failed = Subcription renewal failed (update card details)
It looks right to me
Sweet. After the invoice payment fails, how many days does the customer have before the next try?
Or before the invoice gets deleted
you can config in your dashboard and ask Stripe to auto retry https://dashboard.stripe.com/settings/billing/automatic
The invoice will be in uncollectible status but it could be paid again.
Ok good thanks.
Is there any webhook triggered a few days prior the due date of the invoice?
there is an invoice.upcoming event https://stripe.com/docs/api/events/types#event_types-invoice.upcoming
Awesome!
- Is there any webhook event available that triggers when a card has expired?
Like this setting in the dashboard - ”Send emails about expiring cards”
- Which event triggers when a subscription is canceled?
Amazing
I need to step away so I’m going to archive this thread. If you need help with anything else please ask in #dev-help or contact Stripe Support: https://support.stripe.com/contact
Hello again!
New day, new problem >:)
This is my setup
Issue is that when a customer starts a subscription both checkout.session.completed AND invoice.paid gets triggered
That should be the proper behavior. What issue does that cause for you?
Ah, wondering how to tell these webhooks are the same event so you don't double up on the Discord message?
Yup
And e.g the unsuspend user part.
Because I noticed that with checkout.session.completed there comes alot of other webhook events aswell.
e.g
invoice.paid
payment_method.suceeded
etc
Understood, looking in to the best way to tell they are the same here.
Can you clarify a bit more on what is going wrong with unsuspending the user?
Nevermind regarding that. The issue is that invoice.paid triggers when checkout.session.completed trigger.
So for the session.completed event with a subscription of sub_123 will be about the same payment that as invoice.paid with sub_123 and a reason of subscription_create
Uhm wdym. Sorry
I thought the question was how to tell that session.completed and invoice.paid were about the same thing so you wouldn't double up on things when reacting to it
Did I misunderstand you a bit?
I think we're misunderstanding eachother.
When a new subscription is received I just want the session.completed event to trigger, not invoice.paid
So not both of them trigger
Yup I understand, thats my issue
I see, looking in to that. Do you have an example id of a subscription this happened for?
e.g "sub_1JdFE5CZAbM0xWOwE3CsqbWn"
Perfect thank you
So I see that the invoice.paid event for that subscription is pending https://dashboard.stripe.com/test/events/evt_1JdFE9CZAbM0xWOwl1iMXm74
Where can you see that? I've read the return json and can't find any
Sorry I phrased that a bit incorrectly, we tried to send you the event for that, ran in to a TLS error, so the event sending will be retried at some point https://stripe.com/docs/webhooks/best-practices#retry-logic
Yes okay, but how does that solve my issue?
I know there was a tls error there, because I use stripe listen --forward-to
thats prob why
I ended up with just skipping the suspend part and I will go directly to termination.
with customer.subscription.deleted.
Btw, when is that triggered? 1 week after end-date? 1 month? 2 weeks?
hey, catching up
customer.subscription.deleted.
should be triggered when the sub is canceled , not sure I have full context though
Ok thanks
and regarding event invoice.payment_failed, can I see in the object what went wrong?
Or is the only failure expired/invalid card-details?
could be numerous things that failed the charge. Bank declining it and deciding not to honor it, bank not accepting authentication, many things
I don't recall off hand if an invoice.payment_failed event has all the things you need on it around the Charge failing, if you have an example ID I can quickly check
gotcha, so that Invoice has a payment_intent: pi_123 field on it, you fetch that PaymentIntent from the API and it will have a last_payment_error field on it
That will give you details on what the decline was, including a customer facing error message too
there's a few things you can, I would personally email the customer and bring them on to my payment page and have them pay that Invoice's PaymentIntent with a different card
I think you can also send the hosted Invoice page to the user via the hosted_invoice_url and that (iirc) has a payment page on it
and yep you can send the customer to Customer Portal to update their credit card, then you get a webhook event payment_method.attached event and then update that PaymentMethod on the Subscription default_payment_method so on the next retry, the Subscription uses that new PaymentMethod
there's a few things you can, I would personally email the customer and bring them on to my payment page and have them pay that Invoice's PaymentIntent with a different card
Like this? https://stripe.com/docs/connect/creating-a-payments-page?platform=web
But instead of doing this,
$session = \Stripe\Checkout\Session::create([
'payment_method_types' => ['card'],
'line_items' => [[
'name' => 'Kavholm rental',
'amount' => 1000,
'currency' => 'sek',
'quantity' => 1,
]],
'payment_intent_data' => [
'application_fee_amount' => 123,
'transfer_data' => [
'destination' => '{{CONNECTED_ACCOUNT_ID}}',
],
],
'mode' => 'payment',
'success_url' => 'https://example.com/success',
'cancel_url' => 'https://example.com/failure',
]);
Can I specify just a paymentIntent id?
just to check, this is in context for paying an already created Invoice right?
Yup so the flow for my customers is like this:
subscription.session.completed = subscription started
invoice.payment_failed = renewal invoice failed
on invoice.payment_failed I will send a email/sms to them
And I want them to be able to pay and update their payment details on a link click
yeah so you don't want to present them Checkout in that case, you cannot link Checkout to an existing PaymentIntent
you can try Checkout in mode: setup that just collects card from Customer with no payment
Then you can take that new card and attach it to the Subscription and also pay the previously failing Invoice with that new PaymentMethod
Sounds good
So like this
$stripe = new \Stripe\StripeClient('');
$stripe->checkout->sessions->create([
'success_url' => 'https://example.com/success',
'cancel_url' => 'https://example.com/cancel',
'mode' => 'setup',
]);
yep
Awesome
After that, will the paymentIntent automatically get paid?
(if the new card details are valid)
After that, will the paymentIntent automatically get paid?
no, you need to do 2 things after
with the above setup Checkout, you just collect a new card
1/ pay the customer's latest failing Invoice's payment_intent: with that PaymentMethod
2/ if 1/ succeeds, attach the PaymentMethod as the default_payment_method: on the Subscription
and done
1/ pays the current open Invoice
2/ attaches that good new card to the Subscription for new recurring charges
if 1/ fails, then you repeat the setup Checkout step and collect a new card from the Customer
Step 1, can I do this automatically?
Step 2, gotcha? https://stripe.com/docs/api/payment_methods/attach
step 1 is one additional API call, like calling
https://stripe.com/docs/api/payment_intents/confirm
what you linked for step 2 is attaching the PaymentMethod to the Customer (which you don't need to, just pass the customer: cus_123 Customer ID in your setup Checkout and that will auto-attach the PaymentMethod to the Customer)
so you do not need to do https://stripe.com/docs/api/payment_methods/attach
if you create Checkout with a Customer (one param to add in your Checkout snippet earlier)
step 2/ would actually be https://stripe.com/docs/api/subscriptions/update#update_subscription-default_payment_method
Update card-details
$stripe = new \Stripe\StripeClient($stripe_client_key);
$stripe->checkout->sessions->create([
'cancel_url' => 'https://swingtrejdarna.se',
'mode' => 'setup',
'customer' => $object['customer']
]);
Step 2:
Do I need to do this if I specify customer as above?
Step 3:
$stripe = new \Stripe\StripeClient('');
$stripe->subscriptions->update(
'subscription id',
'default_payment_method' => 'what'
);
so renumbering as our numbers are out of sync now haha
1/ create mode: setup Checkout with a Customer
this will collect a PaymentMethod and attach it to said Customer
2/ pay the latest Invoice's PaymentIntent with new PaymentMethod
3/ after 2/, update the Subscription to make new PaymentMethod the default on that Sub going forward
haha yeah
1/
$stripe = new \Stripe\StripeClient($stripe_client_key);
$stripe->checkout->sessions->create([
'cancel_url' => 'https://swingtrejdarna.se',
'mode' => 'setup',
'customer' => $object['customer']
]);
2/
$stripe->invoices->pay('in_123', ["payment_method" => "pm_123"]);
3/
$stripe->subscriptions->update(
'sub id',
'default_payment_method' => '????'
);
Will the first (1) step give me a pm_123 object that I can use in this step? (replacement of ????)
You listen to the checkout.session.completed event (or the redirect) so that you find the PaymentMethod on that Session and then you can call the https://stripe.com/docs/api/invoices/pay with payment_method: 'pm_123' to see if that works
Note that the SetupIntent is just an id on the Session by default, you have to use the Expand feature (https://stripe.com/docs/expand) to explicitly get the SetupIntent object and the underlying PaymentMethod id.
Like that?
yep
But go step by step as there are multiple sequential step: get the session, understand how to get the PM id, pay the invoice, then make it the default
How do I get the pm_123 from the first step afterwards?
what did you try?
Sorry I'm a bit nitpicky with this, if I give you code you won't really understand/internalize this
Yup I understand.
Give me 2sec.
take a completion Session already, and try retrieving it and expanding the setup_intent like I said. It's fine if it doesn't work, just try and then share the code and I can help tweak it
So checkout.session.completed triggers after the user is done with first step?
Because that will cause an issue for me
what issue will it cause exactly?
that's only a small bit of the info. What's the issue with that? You can know in the event what it's for, look at the mode for example and if mode: 'setup' do something. You could also store the Session id in your database so that in the event you know what operation it was for
there's a lot you can specialize there, event adding metadata to that Session to track its "reason"
look at the mode for example and if mode: 'setup' do something
Good idea thank you.
@spiral creek are you unblocked?
Unblocked?
Did that give you the info you need to move forward?
I haven’t tried everything yet. Please wait
@spiral creek just wanted to check in before I archive this thread, are you all set?