#oracle-checkout

1 messages · Page 1 of 1 (latest)

sturdy river
#

@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

steady vale
#

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

sturdy river
#

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

steady vale
#

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"

sturdy river
#

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

steady vale
#

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.

sturdy river
#

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

steady vale
#

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 ?

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

steady vale
#

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

sturdy river
#

What have you tried?

steady vale
#

payment_status

sturdy river
#

yes!

steady vale
#

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.

sturdy river
#

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

foggy coral
#

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

steady vale
#

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...

foggy coral
#

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

steady vale
#

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

foggy coral
#

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

steady vale
#

Okay, that sounds good, how do I add the session id to the success_url?

foggy coral
#

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)

foggy coral
steady vale
#

okay

past cairn
#

Hello! I'm taking over and can help you further. To recap, here's a summary of what's been suggested above:

  1. 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
  2. User Pays for Order
    • This is where you redirect your customer to Checkout and they pay
  3. 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).

steady vale
#

@past cairn

past cairn
#

That would be something you do on your end, in your own code.

steady vale
#

oh ok

past cairn
#

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.

steady vale
#

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...

  1. I attach to the user a randomly generated code, a session Id.
    i.e. user.sessionID = Random generated code

  2. The user uses the Stripe checkout, and is redirected to the success page

  3. 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 ?

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.

#

I need to step away, but @timid escarp can help you further if you need anything!

timid escarp
#

👋

steady vale
#

hi

#

So I can do this:

  1. User clicks a button to visit the stripe checkout (function can be seen at the start)
  2. A session_id is immediately generated, and I attach it to the user
    (i.e. user.sessionId = session.id )
  3. If successful the user is redirected to the success page, with the same session_ID in the query of the URL
  4. 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.
  5. 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 ?

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"

steady vale
#

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.

timid escarp
#

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

steady vale
#

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?)

timid escarp
#

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

steady vale
#

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?

timid escarp
#

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

steady vale
#

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?

timid escarp
#

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