#lumen-subscription
1 messages ยท Page 1 of 1 (latest)
It should work if you create the subscription once when they start their trial
That payment intent and client secret won't expire
That's perfect, thank you!
And what if their subscription runs our, or they cancel and they want to resubscribe again, or need to update their payment info?
Do I just create one Subscription per user (during signup), store it in my db, and keep using it all the time?
If the subscription is cancelled you would create a new subscription for them but you can use the payment info they already entered if you don't want/need them to enter it again
Updating the payment method can be done on the same subscription object
How are you setting the payment method for the subscription?
I don't know yet, I'm still trying to figure it out. I'm following this guide:
https://stripe.com/docs/billing/subscriptions/build-subscription
I'm trying to use the PaymentElement to let the user enter the payment info, since this seems like the default way to go, but I'm a bit stuck. I can't seem to find an example of doing this with React, so at the moment I'm trying to find a way to set up stripe elements and just make the PaymentElement form to show up.
How are you setting the payment method for the subscription?
To be honest, I don't even entirely understand what you mean by "setting the payment method".
๐ @soft kraken had to hop off, but I can help!
I have some questions, but I'll let you type first ๐
Hey, thanks, I'd really appreciate it. I'm really new to this stuff, and some guidance would be super helpful.
So at the moment I'm going through the steps 5 and 6 of this guide:
https://stripe.com/docs/billing/subscriptions/build-subscription
And I'm struggling with two questions.
1. It still seems really weird to me that I have to create a Subscription, store the returned client_secret in the database, and send it to the client just to be able to show my users the PaymentElement form.
To access the client secret from the newly created subscription, I have to use this: subscription.latest_invoice.payment_intent.client_secret. Intuitively, it really doesn't seem like a permanent piece of data that I'm supposed to be storing in my database (I might just be misunderstanding stuff).
It would make a lot more sense if I could just display PaymentElement without this stuff (just to show users the form), and request the client_secret only when the user has actually entered their payment information and submitted the form.
2. To move to the next steps, it would be very helpful to figure out just how to make the PaymentElement show up on the frontend, and I'm struggling to find an example of integrating it with React.
The docs say that you set up the PaymentElement with regular JavaScript like so:
const options = {
clientSecret: '{{CLIENT_SECRET}}',
// Fully customizable with appearance API.
appearance: {/*...*/},
};
// Set up Stripe.js and Elements to use in checkout form, passing the client secret obtained in step 5
const elements = stripe.elements(options);
// Create and mount the Payment Element
const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');
I'm using React, and trying to set it up like so:
// _app.tsx
import { Elements } from '@stripe/react-stripe-js'
import { loadStripe } from '@stripe/stripe-js'
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_CLIENT_SECRET)
function App({ Component, pageProps }) {
return (
<Elements stripe={stripePromise}>
<PurchaseModal />
</Elements>
)
}
// PurchaseModal.tsx
import { PaymentElement, useStripe, useElements } from '@stripe/react-stripe-js'
export default function PurchaseModal() {
const stripe = useStripe()
const elements = useElements()
async function makePayment() {
const { error } = await stripe.confirmPayment({
//`Elements` instance that was used to create the Payment Element
elements,
confirmParams: { return_url: 'https://my-site.com/order/123/complete' },
})
}
return (
<Modal>
<h1>Purchase</h1>
<PaymentElement />
<button onClick={makePayment}>Checkout ($20/mo)</button>
</Modal>
)
}
And I can't seem to figure out at which point am I supposed to be requesting the client_secret from the server, and where am I supposed to plug it in in my app.
Backing up for a minute here - it sounds like you want to be creating a trialing Subscription from the start and collect Payment Information up front but charge the user at a later date, correct?
I don't want to collect the payment information upfront.
- My user signs up for an app (just entering their email and password, no payment info).
- They can use the app for free for 1 month, then some key features become locked.
- At any point they can click "Upgrade Account" button. It shows them a modal where they see the
PaymentElement, can enter their credit card info, and click submit. This upgrades their account to the premium one, unlocking all the features.
@soft kraken recommended me to create a subscription when the user creates an account, because that seems to make sense. But I don't know, I'm open to exploring other approaches, I'm just not sure which would be the best one.
If Subscription is required to display the PaymentElement to my users, and I want my users to be able to look at the payment form any time, it seems to make sense that I'd create Subscription when they make an account. If Subscription wasn't required to show the payment form, I'd probably create it after they fill in the payment info and click "upgrade". But it doesn't seem like this is the way it works...
Gotcha, and thanks for clarifying! So let me give a few options....
- Create the Subscription on a trial (as Pompey suggested), and use the Setup Intent found at
pending_setup_intenton the Subscription to collect payment information with the Payment Element. When they press submit you would usestripe.confirmSetupto finish setting up the payment method, set it as the default payment method (eithe ron the customer or on the subscription), and then you would make the separate request to update the Subscription to the premium one. The update request should then automatically use the Payment method that was just set as the default
- You could also just opt to create the Subscription when they click the "Upgrade Account" button, but as you mention earlier they'll have to create the Subscription BEFORE they go through the flow and it's possible they won't finish entering in their payment details. These Subscription that are never paid will automatically cancel after 24 hours, but that stil leaves a lot of unpaid subs hanging around
Thank you!
Just to make sure I understand you correctly, if I choose the first option:
- When the user creates an account, I create a Setup Intent.
- It gives me some kind of id or a secret which I store in my db, and can use to show the Payment Element form to my users.
- When they fill in the payment form, I use
stripe.confirmSetup, which saves their credit card info in their account. - Right after that, I make a request to update the subscription, which uses the stored payment data to charge the user and upgrade the account.
Correct?
*1. When the user creates an account, I create a Setup Intent. *
Slight correction - you don't have to create the Setup Intent, you would create the trialing Subscription which will create a Setup Intent for you under the hood
2. It gives me some kind of id or a secret which I store in my db, and can use to show the Payment Element form to my users.
You can store the Setup Intent ID or the Subscription ID. You should not be storing the client secret (we mention this here https://stripe.com/docs/api/setup_intents/object#setup_intent_object-client_secret), but as long as you have the Setup Intent ID Or the Subscription ID you should easily be able to retrieve the client secret from Stripe.
3. When they fill in the payment form, I use stripe.confirmSetup, which saves their credit card info in their account.
Yes, as long as it's successful it should attach the card to the customer, but you should make a separate request to set the created pyament method toinvoice_settings[default_payment_method] on the Customer or default_payment_method on the Subscription
4. Right after that, I make a request to update the subscription, which uses the stored payment data to charge the user and upgrade the account.
Yup, as long as you set the defaults from the previous step this should happen
Thank you so much, this makes sense!
Is there a difference between setting the default payment method on the Customer or the Subscription? Which one should I go with?
Setting it on the Customer will have it be the default payment method for ALL billing payments, while setting it on the Subscription will only use it for that specific Subscription (not the other Susbcriptions the customer may have)
Ok, that clears things up, thanks!
Could you take a look and let me know if this set up makes sense:
import { Elements } from '@stripe/react-stripe-js'
import { loadStripe } from '@stripe/stripe-js'
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_CLIENT_SECRET)
export default function PurchaseModal() {
const stripe = useStripe()
const elements = useElements()
// 1. Before rendering the form, fetch the client secret using the Subscription ID (which I saved in the db when the user has created their account)
const options = {
clientSecret: '{{CLIENT_SECRET}}',
}
async function submit() {
// 2. Use stripe.confirmSetup to save the payment info the user has entered.
// 3. Make request to the server, which will:
// - Set the collected payment info as the default payment method on Customer
// - Make a request to upgrade the subscription.
}
return (
<Elements stripe={stripePromise} options={options}>
<h1>Purchase</h1>
<PaymentElement />
<button onClick={submit}>
Checkout ($20/mo)
</button>
</Elements>
)
}
I'm not a react expert, but overall it looks good to me!
I need to hop off now, but if you need anything else @soft kraken is around and can help
Thank you SO much for helping me out! Now I have a clear plan I can work with. You've just probably saved me a week of beating my head against the wall =)