#dfabulich_integration-choice
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/1298751820251922503
đ Have more to share? Add more details, code, screenshots, videos, etc. below.
sorry reading
It's not acceptable to wait for a webhook to fulfill the purchase.
you never really need to wait for a webhook. The webhook is more here to catch issues like someone closing their browser too fast before they got to your side where you handle fulfillment.
dfabulich_integration-choice
@patent breach does that make sense overall?
If that's true, then the documentation is wrong or misleading
It says that I'd call confirmPayment with a return_url. The return URL can print a message, "success", but that's not what actually fulfills the purchase.
When it comes to actually filfilling the user's purchase, the documentation states clearly that I must use a webhook, and only a webhook
Listen for these events rather than waiting on a callback from the client. On the client, the customer could close the browser window or quit the app before the callback executes, and malicious clients could manipulate the response. Setting up your integration to listen for asynchronous events is what enables you to accept different types of payment methods with a single integration.
Well it doesn't really say you have to right? It tells you why it's dangerous to solely rely on the return_url that's all. So you can do both. That's how I would do this
"rather than"
There's also no sample code that demonstrates fulfilling the purchase from the return url.
The sample code always only displays a status there, never fulfilling
Sure, because if we did that most devs would ignore it entirely (we know because we've seen it) and miss that webhooks are important.
So really once they hit your return_url, it's your own code at that point, you can do fulfillment if you like!
We have something like this in https://docs.stripe.com/checkout/fulfillment for Checkout where we explain the difference.
My team rewrote that entire doc which is why it mentions both options clearly. Maybe that's a good way for you to understand what I am describing?
I did see that, and so I thought maybe I needed to switch to Checkouts for automatic fulfillment
But, reading that documentation, the way to know what to fulfill is to read the line items off of the checkout session, which, in turn, means I have to define all of my products with Stripe in advance before initiating the session
Not really, you can use price_data. Sorry there are dozens of ways to integrate Stripe in general and the first thing to figure out is what you want the flow to look like
Checkout is the easiest integration path
I do recognize that there are a lot of options, and that this makes the problem of documenting it complicated (your job is hard!)
In the https://docs.stripe.com/checkout/fulfillment guide, it documents how to fulfill based on line_items
If I use price_data how do I know what to fulfill? What did the customer even buy?
price_data is a way of saying "listen, just charge the user $X, I'll take care of the rest"
But the only way to figure out what the user bought is to https://docs.stripe.com/api/checkout/sessions/retrieve the checkout session and read the line items
And this is why/where I keep going in circles. Lots of options! I can do anything I want! The easy way is to use Checkout, but I have to define N products in the Dashboard, and the hard way is to use Payment Element, but then there's no documented way to do automatic fulfillment.
I don't really get it. If you don't want to put your product catalog in Stripe, then it means you want to track what they bought on your end and use Stripe just to charge the total amount. Which means whenever they complete the Checkout Session, you look into your own database for what theyr bought
Is there an ID I'd use for this?
And really the "fulfillment" on PaymentElement works the same. Sure it's not documented but it would be 99% the same as that other doc, except Checkout sends to success_url and PaymentElement sends to return_url
When you as the developer call the Create Checkout Session API https://docs.stripe.com/api/checkout/sessions/create you get an object back with the id property like cs_test_123. You can also add your own metadata values, see https://docs.stripe.com/metadata to reference an internal id for the order on your end
Reading between the lines, here's what I think I'd need to do:
- When it comes time to create a checkout session, I'd create an order in my database with an ID, containing line items (I do know what the user wants to buy). None of that will be conveyed to Stripe, though. I'll just create a checkout session with price data saying "charge the user $X" and I'll attach some metadata to the session (where?) including my private order ID
- When the user lands on the return url, I'll read my private order ID off of the session metadata and fulfill the line items there
yep that's exactly it. And your "where?", I recommend reading: https://docs.stripe.com/metadata
OK, that's a start, but, let me throw out my preconceived notions, because this is clearly not the recommended way that anybody do the integration.
Something about the way I'm approaching this is clearly way, way off the beaten path
So off the beaten path that it was a struggle to even explain to you what I wanted
What's confusing about that is that what I'm doing is what I thought basically everybody does: I've got products in my DB, I want to compute a price, charge the user that price, and then fulfill them for the user.
I mean I understood what you wanted from your first message.
So it wasn't confusing to me at the very least. It's an extremely common question and something I explain all the time here.
Is "the easy way" to implement what I want with Stripe Checkout to use this metadata mechanism on a checkout session?
what I'm doing is what I thought basically everybody does
it's not though. It's what people think they want and then realize later how big of a mistake it is. Most people don't want to sync their product catalog with Stripe. But then Checkout says Pay $100 instead of having the breakdown of items purchased, a dedicated name and picture, the ability to do cross-sell or upsells of products, ability to have tax calculated per item, etc.
Later it prevents you from analyzing the performance of your product catalog and which Price/Product are sold the most, handle Price changes, adaptive pricing and many more
So what you are saying makes sense, it's where people start but it's rarely where they end up
But yes, what you described works perfectly fine, and many people start with this and later add more features because they see the value
OK, so, here's another approach I can imagine:
- I use the Stripe API to write a replication engine, ensuring that Stripe knows my entire product database with up-to-date prices at all times. My database is the source of truth, but, just before creating a payment intent, I'd double check that Stripe knows the right line items and prices, and then create a checkout session with line items.
- Then, I'd follow the documented steps, creating a checkout session with line items, fulfilling them with line items.
Is that where I'm going to end up?
Yep! And a middle ground is to use price_data which lets you create ad-hoc Prices/Products in Stripe for each Checkout Sessions
Is there any documentation or examples on how to copy a bunch of products to Stripe and ensure they're up to date?
It seems shocking to me that Stripe Checkout, the "easy way," means "first write a bunch of code to replicate your product DB to Stripe, and then it will all be easy from there on out"
Sorry, I want to help but also am not a fan of "it's crazy you don't do this" even if I don't disagree with you.
My advice is to start simple: use price_data. This allows you to show line items in the Checkout Session and its UI without having to ingest your entire product catalog.
Or forego line items and just pass one price_data for the total amount
Once that works, you can figure out the product catalog later!
Can I read the price_data back out of the Checkout Session during fulfillment?
https://docs.stripe.com/api/checkout/sessions/retrieve doesn't say anything about how to access price_data, only line_items.
I'm not saying anything's crazy. My shock is my own problem!
oh, wait, the price_data is line items.
I thought it was, like, "$X"
price_data is kind of a "Write only" parameter. It's a way to create an "ad-hoc Price".
During fulfillment you look at the individual elements in line_items and each one with have a Price that represents what you passed in price_data.
The idea is that if you sell an apple, a banana and a kiwi in one Session, instead of having to first create 3 separate Prices and Products pairs and for each one get the id and then pass price_A, price_B and price_C you can do it all in one request
Are there examples of using price_data? I'd especially appreciate an end-to-end example including fulfillment, but I recognize that might be too much to ask
Thanks! I did not think to check a migration guide for this
yeah sorry it's not a great guide, it was for when the API years ago just took inline amounts but we changed it a long time ago. It's just the best example I found
OK, so: I'm going to try Stripe Checkout with price_data. For each product, I'll define product_data and attach my internal product_id to the product_data in line_items.price_data.product_data.metadata
When fulfilling, I'll retrieve the session, expanding line_items, verify success, and each line item will have the product_data.metadata I defined when I created the session. I'll use that to fulfill orders to the customer.
I appreciate you taking the time to work with me on this. I had a hunch that I was making a lot of incorrect assumptions about this, but it was hard for me to figure out which ones were which.
Kind of but you are not putting the metadata super deep inside line_items[x].price_data[product_data][metadata]it won't be on the line item. Because the metadata is specific to that really specific object, the Product. So you'll have to look up the Product first which is works but is an extra concept to understand.
Much easier to save this information in your own database for now
Where would/should I put my internal product_id for each product in price_data?
er, let me try to pose the question differently
Here's some PHP code I was imagining using.
<?php
$stripe->checkout->sessions->create([
'line_items' => [
[
'price_data' => [
'currency' => 'usd',
'product_data' => [
'name' => 'T-shirt',
'metadata' => [
'id' => 123456, // MY PRODUCT ID
]
],
'unit_amount' => 2000,
],
],
],
'mode' => 'payment',
'success_url' => 'https://example.com/success',
'cancel_url' => 'https://example.com/cancel',
]);
Is that the wrong place?
In the end, I was gonna retrieve the line items from the session, look at each line item's metadata, and use that to determine what the customer bought.
It's not the wrong place. I'm just warning you that you will be confused when you try to access it later because of the way metadata and our Expand feature work
There's a ton more things you need to learn to get there. The thing is I'd rather have you try for real in Test mode instead of a "meta" discussion high level
So try that code, complete the Session, then try to look at the line_items after and find your metadata
Maybe instead I'd put an order_id or cart_id in my database, which references product_ids, and put that order_id in the session's client_reference_id?
I'm trying to heed your warning to avoid confusing myself later
yeah that works. But really pause and try it. I'll show you and then it'll work
@patent breach any update? I have to run soon but was hoping I could unblock you once you had the next question :p