Guest checkout business logic for server/database with Stripe

114 Views Asked by At

I am making an a simple app which allows users to post ads to the website for a fee, as in pay per post. No account is required, it is in essence a guest checkout by default.

The issue I am dealing with is the order and best way to implement the business logic on the server side.

My flow is as follows:

  1. (Front end) User fills out a form with ad details
  2. (Front end) User hits submit
  3. (Back end) Post request is processed, new database entry is created
  4. (Back end) User is redirected to Stripe checkout
  5. Payment fails or succeeds

Notice how in this flow I create an object/entry in my database before any payment has been made. This already feels wrong because I feel the entry shouldn't exist unless it has been paid for. Because spammer could in theory fill my database with endless entries by filling the form and not completing payment. So if Stripe payment fails in step 5, I have a choice: delete the entry, or return the user to the form prefilled. Clearly, the later is the better option, however if the user never finishes or fixes the payment I will still have a dangling entry in my database. What is the best way to approach this situation? And what should I do with the entry if I want to implement a pay later option (via sending a payment url to an email)?

Also, if payment succeeds everything operates as normal. However, I want to use webhooks as it seems more reliable for payment status. But Stripe checkout also uses a "success_url". So in the case that the checkout succeeds, but the payment is rejected. I will still forward the user to a success_url, which seems strange as user might get the impression that everything went smoothly. So, how would that case be handled?

I feel like a lot of the trouble stems from Stripe checkout making me leave my server environment, whereby I lack control of how to change my database for each case should it arise.

1

There are 1 best solutions below

0
karllekko On

One option for the first part might be to not create the database records until after the payment. For example, you could effectively store some state in the metadata of the CheckoutSession you create, as to what the customer picked and the information you will need in order to create the record. Then when you handle the checkout.session.completed webhook event for the customer having paid, you can inspect what the customer bought(https://dev.to/stripe/purchase-fulfilment-with-checkout-or-wait-what-was-i-paid-for-335d) and the metadata you previously set, and create the things in your database.

https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-metadata

So in the case that the checkout succeeds, but the payment is rejected. I will still forward the user to a success_url, which seems strange

I'm not sure what you're referring to here as that doesn't really happen. On the Checkout page if the customer's payment fails like their card is declined, they just get an error message on the page and can try again multiple times and change their details and so on, they don't just fail and get redirected away. You can see how it works by using test mode and test cards. https://stripe.com/docs/testing#declined-payments

If you recieve the checkout.session.completed webhook event then the payment was successful. If the success_url is visited, you can have your backend retrieve the CheckoutSession ID and check if it has been paid(since anyone could visit that URL directly) but in general the redirect to the URL only happens once the customer has paid(and Stripe waits up to 10 seconds for you to recieve and acknowledge the checkout.session.completed webhook event before it redirects).

https://stripe.com/docs/payments/checkout/custom-success-page