#oracle-checkout
1 messages · Page 1 of 1 (latest)
@steady vale Checkout doesn't "run a function". It's our hosted product/offering. We own the transaction/charge the customer and then we redirect back to your own website/app
exports.checkout = async (req, res) => {
const { _id } = req.user;
const { price } = req.body;
const paymentCompleted = new Date().getTime();
try {
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
line_items: [
{
price_data: {
currency: "cad",
product_data: {
name: "T-shirt",
description:
"Subscribe to alpacka.ca to avoid paying for per return orders.",
images: [
"https://files.stripe.com/links/MDB8YWNjdF8xSklRU3hIZmIyeDZxS0g1fGZsX3Rlc3RfOTNqT2VLdG5WTXlBTHcxVE1MM3Q5RHds00jh6k5cZH",
],
metadata: {},
name: "Gold Special",
},
unit_amount: price,
},
quantity: 1,
},
],
mode: "payment",
success_url: `http://localhost:3000/order/success/${_id}/${paymentCompleted}`,
cancel_url: "http://localhost:3000/error",
});
console.log("SESSION", session.url);
console.log(session);
console.log("Payment sent successfully.....");
return res.status(200).json({
url: session.url,
});
} catch (error) {
console.log(error);
console.log("PAYMENT ERROR : ", error);
return res.status(400).json({
message: error.message,
});
}
};
so I wouldnt be able to run a function once a successful payment has occured?
I am not using the 'Stripe Checkout' that is simply a link, my controller is just called checkout
unless I am mistaken
1/ Create a Session
2/ Redirect to Checkout
3/ Customer pays on Checkout, you have no control over this while they do that
4/ Checkout sends a checkout.session.completed event to your webhook endpoint + redirects back to success_url on your server
5/ You get the customer back (and/or process the event) and run the code you want to at that point
Yes, as of now, a user creates an order, he/she is redirected to checkout, when he is successful, he is sent back to my site, and once he is redirected to the page his order is created.
my issue is, if someone really wanted to, they could hypothetically make an order and then save the url of the page that creates orders and skip the strip checkout in between
I was hoping there was a simple way to run a function in between "Checkout sends a checkout.session.completed event to your webhook endpoint + redirects back to success_url on your server"
I'm not sure I understand the concern and how "code running in a function" would help in this flow
You should know who is placing the order and which Checkout Session they are tied to (the cs_live_123) and then when they hit the success_url you can confirm their Checkout Session was paid successfully before giving them access to anything
that's something your code does before displaying a success/error after the redirect
As of now I know which user is placing the order, but I am not sure where I can find their Checkout Session that you described, so that I may confirm successful purchase
The checkout function above is all I use to process payments.
You usually store this information in a cookie. The customer visits your website and you "track them" or have them register with an email/password for example. Otherwise you put some order id in your database that you can look for
So right now this is how the order process works:
Attach order details to user ==> User Pays for Order ==> User's Order Details are Created
Each step represents a different page/url
but this really doesnt work, because i can technically do step 1, and skip step 2 and go straight to step 3, by pasting the success URL in my searchbar, and have my order created.
I want to make sure no one can have their order created unless they have already paid for it.
does that make it easier to understand @sturdy river ?
yes and so when someone loads step 3, you should check their current cookie you have for them and confirm their Checkout Session has been paid successfully and if not error
Okay so I attached an session Id to the user (lets say randomly generated).
They are redirected to stripe checkout to pay.
And upon Successful completion they are redirected a 'success' page
And before this page is even loaded it checks if the session Id has been paid successfully.
So my question is, How do I tell if the session Id has been paid successfully?
I just attached an Id to the users current Order, he went to use the Stripe payment process, and then was redirected to the success page, nothing happened to the session Id to indicate a successful payment
What have you tried?
Like if you look at the docs for the Session https://stripe.com/docs/api/checkout/sessions/object do you see anything obvious that tells them it's been paid?
payment_status
yes!
Okay, so how would I then send the payment_status to the success url?
so that when a user is redirected the success url we can verify that they did in fact pay, and didnt just copy/paste the success url into their search bar to create an order.
You don't really do that at all
when we redirect to the success url, your code first looks at the cookie for example, finds which customer this is for, retrieves the Checkout Session via the API https://stripe.com/docs/api/checkout/sessions/retrieve and looks at payment_status. Based on this you decide if you display an error or if you move forward with your order creation and displaying a success page
If you don't want to use cookies, you could also provide your own customer info when you create the Checkout session, either using a stripe customer, the client_reference_id or custom metadata, and you can have Stripe include the session id in the query parameters when we send your customer to your success url:
https://stripe.com/docs/payments/checkout/custom-success-page#modify-success-url
you can then get that session id out of the URL and have your server retrieve the session from Stripe to check the payment_status like you discussed above
Im really confused...
I posted by checkout function above, I havent attached a customer Id to anything, a session ID or anything
considering my function above, what changes do i need to make...
We can't write your code for you, just trying to provide you some options that allow you to do what you asked: ensure you check your customer has paid before fulfilling an order
I dont want you to write my code for me, im just asking if there is something you noticed im missing, seeing that session Id, and attaching a a checkout id to a user, then confirming it with payment_status, and it appears i havent done any of that
For example: you could do what Koopajah mentioned with cookie tracking to check the status
But given how your code is written, it you be a small change to use the template value for the session id like i just mentioned, adding that to you success_url
Okay, that sounds good, how do I add the session id to the success_url?
Then when a customer arrives at http://localhost:3000/order/success/${_id}/${paymentCompleted}?session=cs_test_123 you can defer the fulfillment until you go and check cs_test_123 was actually paid
and not attributed to any previous orders in your system (to avoid a clever customer reusing one session for many orders in your system)
Check out the docs i linked here
okay
Hello! I'm taking over and can help you further. To recap, here's a summary of what's been suggested above:
- Attach order details to user
- This is where you would create the Checkout Session on your server and associate the Checkout Session ID with the user in your system
- User Pays for Order
- This is where you redirect your customer to Checkout and they pay
- User's Order Details are Created
- When the user is redirected back to your site you can include the Checkout Session ID in the URL. From there you can confirm the Checkout Session both belongs to the user from #1, and that the Checkout Session has been paid (by fetching the Checkout Session from the Stripe API). This will prevent people from loading your success page directly and getting access to something they shouldn't.
Let me know if you have questions or run into issues with any of that!
Oh, also, you also need to set up a webhook endpoint to listen for events (likely checkout.session.completed) so you can be notified of payments that happen in scenarios where steps #1 and #2 complete but #3 never happens (like someone on a train who clicks pay, the payment goes through, but then the train enters a tunnel and they lose their connection before the redirect to your success page happens).
okay, but in step 1, how do I associate a checkout ID with a user.
I am looking at this doc, and it doesnt seem to suggest associating anything with the user: https://stripe.com/docs/payments/checkout/custom-success-page#create-success-page
@past cairn
That would be something you do on your end, in your own code.
oh ok
You would create the Checkout Session, then store the Checkout Session ID in your database and associate it with the user you're about to send to Checkout.
Then when someone hits your success page you grab the Checkout Session ID from the URL, find the matching user in your DB, confirm, etc.
Okay, so considering the checkout function above.
When user clicks a button to be redirected to the Stripe Checkout, the above function will run,
Then...
-
I attach to the user a randomly generated code, a session Id.
i.e. user.sessionID = Random generated code -
The user uses the Stripe checkout, and is redirected to the success page
-
On the success page, we firstly check is the sessionId in the URL query is the same as the user.sessionID we attached to the user. If they match then its done, and the order gets created. Afterwards I can clear the user.sessionID, so that they wont be able to reuse
would this be okay @past cairn ?
No, you should use the ID of the Checkout Session, the one that starts with cs_. The one that Stripe creates.
Like cs_test_CUnF3oeuW7vvoEAjGzek06ffwS7DJHsb0ahXagZgiDspoSWTIrV3crA9 in test mode, for example.
That's the ID that will be added to the success URL as described here: https://stripe.com/docs/payments/checkout/custom-success-page#modify-success-url
I need to step away, but @timid escarp can help you further if you need anything!
👋
hi
So I can do this:
- User clicks a button to visit the stripe checkout (function can be seen at the start)
- A session_id is immediately generated, and I attach it to the user
(i.e. user.sessionId = session.id ) - If successful the user is redirected to the success page, with the same session_ID in the query of the URL
- On the success page, it immediately attempts to verify that the session_id in the URL query and the session ID attached to the user are the same.
- IF they match, then the order is created. Once the order is created, I can clear the sessionId attached to the user, so the link cant be reused
is this okay @timid escarp ?
It sounds mostly okay, but can you clarify what you mean by "I can clear the sessionId attached to the user, so the link cant be reused"
So if the user makes it to the success page, and the session_id in the URL query, and the sessionID attached to the user are the same, The order details tied to their account will be created and be a legitimate order in the database.
Technically if they save this link, they can just copy/paste it again and again
and receive the same benefit, without paying
But if i clear the sessionId of the user, once his/her order is created, Then its safe.
Ahhhh gotcha I see what you mean - yes, that would be more of an implementation detail on your end to ensure that integration is robust against people visiting the success link more than once
Right
But does that whole process sound agreeable to you?
(it may not be the best approach, but does it sound like one that would work?)
Have webhooks already been mentioned to you? Overall your flow is not bad, but something to keep in mind is that you can't necessarily guarantee that a customer will make it to your success page (they could close the window too early, lose connection, etc.) and webhooks are a way to ensure you're always notified when these sessions hav ebeen successfully completed
Rueben explained the necessity of webhooks.
But it seems complicated to implement.
I figured I would implement it this way, and later add-on webhooks.
unless webhooks makes my implementation obsolete?
Not necessarily obsolete - there's still value in displaying the necessary information on the success page, it's just not as necessary to do fulfillment specific logic there
Okay then, I think Ill go ahead with the implementation I suggested earlier, and if all goes well, explore webhooks afterwards.
Does that sound like a good idea?
It's really up to you - as long as youre aware of the risk that not everyone will go through the success page and you're okay with that then feel free to go ahead. If that's not something you want, then I'd just go ahead and look into webhooks