#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/1217811761462509628
๐ 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.
- paulc7053_code, 2 days ago, 114 messages
hi! there's some differences yes. What error did you get?
Hey! I'm trying to implement ECE + subsriptions, but I see that there are some differences in the docs. In the ECE docs, I see that the confirmPayment method is passed client_secret (https://docs.stripe.com/elements/express-checkout-element/accept-a-payment#submit-the-payment ), while in the subsriptions, it isn't (https://docs.stripe.com/billing/subscriptions/build-subscriptions?ui=elements#complete-payment).
Although the difference is that the ECE docs are for a normal payment
yeah the Billing docs are written for the use case where you use the PaymentElement and create the Subscription object when the page loads
ultimately it should work but requires a bit of manual work connecting the docs together
So I only need to pass the elements, and the return url to an ECE subscription? (no subscriptionId, customerId, client_secret)?
well like for example the way it works is in the part of the ECE guide where it says
/ Create the PaymentIntent and obtain clientSecret
const res = await fetch('/create-intent', {
what you do at that point instead is call your "create Subscription" backend that creates a Subscription with default_incomplete and returns back the subscription.latest_invoice.payment_intent.client_secret (https://docs.stripe.com/billing/subscriptions/build-subscriptions?ui=elements#create-subscription) and that's what you use for the confirmPayment call.
yes, but I don't see it passed anywhere
you do need though to "already know' some of the details of the subscription you're going to create , at page load, so you can init Elements with the details(https://docs.stripe.com/elements/express-checkout-element/accept-a-payment#additional-options) , which unfortuantely is messy
what does that mean, what is "it"?
the client secret obtained avter creating the subscription
I only see this const options = { clientSecret: '{{CLIENT_SECRET}}', // Fully customizable with appearance API. appearance: {/*...*/}, }; but it obviously refers to the PK key, right?
I'm a bit confused..
This is my code now :`const onConfirm = async (data) => {
if (!stripe) {
return;
};
const { error: submitError } = await elements.submit();
if (submitError) {
// setErrorMessage(submitError.message);
return;
};
// create the customer
const stripeCustomerIdRes = await fetch('/stripe/findOrCreateCustomer', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: email ? email : 'test@test.com',
}),
});
const stripeCustomerId = await stripeCustomerIdRes.json();
// create the subscription
const subscriptionRes = await fetch('/stripe/createSubscription', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
priceId: priceId,
customerId: stripeCustomerId,
}),
});
const { error } = await stripe.confirmPayment({
elements,
// clientSecret,
confirmParams: {
return_url: `${process.env.NEXT_PUBLIC_HOST}/payment-successful`,
},
});........}`
you can initialise elements without a clientSecret do you don't need it at that point.
Yep, that's why I didn;t think it was that
if youre using ECE, use this guide https://docs.stripe.com/elements/express-checkout-element/accept-a-payment and that demonstrates not initialising with a clientSecret
That's the one I was looking at. It does use a clientSecret , line 33 (https://docs.stripe.com/elements/express-checkout-element/accept-a-payment#submit-the-payment)
I'm talking about passing it to the confirmPayment
sure
that is what I addressed above
well like for example the way it works is in the part of the ECE guide where it says
/ Create the PaymentIntent and obtain clientSecret
const res = await fetch('/create-intent', {what you do at that point instead is call your "create Subscription" backend that creates a Subscription with default_incomplete and returns back the subscription.latest_invoice.payment_intent.client_secret (https://docs.stripe.com/billing/subscriptions/build-subscriptions?ui=elements#create-subscription) and that's what you use for the confirmPayment call
okay, so I do pass the client secret from the subscription creation to the confirmPayment method
because in the docs it is not passed
(normal subscription docs)
yes
yeah like I said you have to stitch the docs together, there is no one ECE+Subscription guide
because "the Billing docs are written for the use case where you use the PaymentElement and create the Subscription object when the page loads"
but you should be able to do it the other way and create the Subscription on demand when the ECE needs it instead, yep
got it! Also, may I ask where do I get the paymentMethodId?
could you expand on that? when you do need it, what context etc
and wether it can be passed to the confirmPayment method
I want to store it for upgreades/downgrades
you don't need it to be, confirmPayment handles creating and using the PaymentMethod for you
it will be saved to the Customer object involved in the Subscription automatically if it's used to confirm that subscription.latest_invoice.payment_intent
so it's not like something you have to store and pass yourself, the PaymentMethod is attached to the Customer object.
make sure you use payment_settings: { save_default_payment_method: 'on_subscription' } too (https://docs.stripe.com/billing/subscriptions/build-subscriptions?ui=elements#create-subscription:~:text=to save the payment method as the default for a subscription when a payment succeeds) to make things easier
Iv I understand correctly, this would be appropriate? 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'], });
Ah okay, so when upgrading/downgrading I just pass the customerId to some method? Do I ned to store anything else besides the stripe customer id?
let's take it one step a time I think and just get the subscription created/active/paid for
sure
the guide for doing upgrades is https://docs.stripe.com/billing/subscriptions/upgrade-downgrade but that's a separate discussion
I thought this is everything, am I wrong? `const { error: submitError } = await elements.submit();
if (submitError) {
// setErrorMessage(submitError.message);
return;
};
// create the customer
const stripeCustomerIdRes = await fetch('/stripe/findOrCreateCustomer', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: email ? email : 'test@test.com',
}),
});
const stripeCustomerId = await stripeCustomerIdRes.json();
// create the subscription
const subscriptionRes = await fetch('/stripe/createSubscription', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
priceId: priceId,
customerId: stripeCustomerId,
}),
});
const clientSecret = (await subscriptionRes.json()).clientSecret;
// confirm the payment
const { error } = await stripe.confirmPayment({
elements,
clientSecret,
confirmParams: {
return_url: `${process.env.NEXT_PUBLIC_HOST}/payment-successful`,
},
});`
seems sensible, what happens when you try it?
Can I use the 4242** credit card with GPay to test it?
I was just trying to understand it before testing it, but I'll do it rn
you can't add Stripe test card to Google Pay really, you generally just test on your normal browser with your real cards and logged into your Google account(they're not actually charged in test mode)
sorry I assumed you had the ECE at least showing and with wallets enabled in your browser, not sure how far in the journey you are
Hi there ๐ I'm jumping in as my teammate needs to step away soon. I'll work on catching up on context while you test that.
Hey! Sure
almost there
Can I not use the 3D secure cards as test cards?(https://docs.aciworldwide.com/tutorials/threeDSecure/3DSTestCards) with Google pay?
Hello?
No, you can't add Stripe test cards to non-Stripe systems, they aren't valid cards to providers like Google. As my teammate mentioned earlier, you typically use your real cards in Google Pay which are swapped for a test card behind the scenes when working in testmode.
seems to work (so far)
Back to the subsription upgrade/downgrade question: do I only need the cstomerId to do that (without showing the checkout page again)?
More precisely, I can retrieve the subs like this const subscriptions = await stripe.subscriptions.list({ customer: '{{CUSTOMER_ID}}', });, but in the expected output, I don;t see anything with sub_, whereas the .update is somethis like const subscription = await stripe.subscriptions.update( 'sub_xxxxxxxxx', { items: [ { id: '{{SUB_ITEM_ID}}', price: '{{NEW_PRICE_ID}}', }, ], } );
No, updating a Subscription requires the ID of the Subscription object. Our endpoint for updating a Subscription, along with what parameters it supports/requires, is documented here:
https://docs.stripe.com/api/subscriptions/update
Complete reference documentation for the Stripe API. Includes code snippets and examples for our Python, Java, PHP, Node.js, Go, Ruby, and .NET libraries.
Can you clarify what you're referring to by "expected output" here?
{ "object": "list", "url": "/v1/subscriptions", "has_more": false, "data": [ { "id": "su_1NXPiE2eZvKYlo2COk9fohqA", "object": "subscription", "application": null, "application_fee_percent": null, "automatic_tax": { "enabled": false }, "items": { "object": "list", "data": [ { "id": "si_OK3pbS1dvdQYJP", "object": "subscription_item", "billing_thresholds": null, "created": 1690208774, "metadata": {}, "price": { "id": "price_1NOhvg2eZvKYlo2CqkpQDVRT", "object": "price" } } ] } } ] }
If you make a request to list Subscriptions for a Customer, the expected output is a list of Subscription objects if any exist.
and the subsription id is the one with sub_, right?
Okay, so that's all I need to store in order to upgrade/downgrade the subscription? Because I thought your colleague said I only need the customerId?
Wasn't that when you were asking about retrieving a Payment Method?
Hm, could be
So just to double check, storing this subscription's id 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'], }); is the only thing I will need in the future in order to upgrade/downgrade a customer without asking for payment details again
Seems right
Got it, thanks!
But let me know if your testing shows otherwise.
I'll test it, but I'm not sure how long it'll take (need to have an SSL domain)
will have to dump it all in a codespace, so will take a bit of time!
Thanks a lot for your time, if there are any issues I'll open a new thread!
Any time!