Laravel Cashier (Stripe) payments with form validation, database record and a success message

Hey @tuto1902 I was following your YouTube livestream about the cool way of using Laravel Cashier with Stripe and I got all bits and pieces working, however there were a few small issues in the final result. I left a few comments on the video but I've seen it has been inactive for some time so I figured out to ask here (no blame at all, just a question πŸ˜‰ ) So in the videos you've covered a simple scenario of having some premium subscription plans and a lifetime payment. Since the plans were recurring, Stripe responds correctly and returns the proper actions after payment. However if you try the code with the lifetime payment, Stripe returns an error that at least one subscription is required. Anyway, my question is not about that as my current project has only reoccurring subscriptions. What I need to achieve is the following process: 1,. A resource form is shown to allow the user to add premium content. 2. The form POST data gets validated upon publishing. 3. If the validation succeeded and all inputs are OK, the user should get redirected to Stripe to pay for the resource to be added 4. If/Once the payment has succeeded, the resource should get stored in the database and the user should get the success (or error message in a case where the payment did not go through and the resource gets stored as a draft until paid).
3 Replies
MilenKo
MilenKoOPβ€’5mo ago
So for step 1 - zero issues - form setup in a view is easy-peasy. For form validation after POST to a route - same thing. For the payment process - this is where the issue appear as the Stripe redirect for payment has it's own return URLs ('success_url' or 'cancel_url') so any other code executed in the same controller after would not get called/executed). My question is - what would be your suggested and most secure workflow to achieve a safe DB record after a successful payment only making sure that the data cannot be tampered by the end user un/intentionally? My thoughts are to store the ressource with a status as Draft BEFORE forwarding to Stripe payment. Once the Stripe processing succeeds and the user is redirected to the success_url, I can execute an Ajax POST request to a custom route and trigger a controller method to update the status of the ressource to Active. It would all have been simple, but the 'success_url' and 'cancel_url' are GET type routes, so this means that I can navigate to the routes directly and have a record in the database manipulated. Not sure if I worry too much about security and missing some valuable point that prevents such an action or it is a bad workflow so that is why I am checking up to see what would you have done (or any other) in a similar scenario to guarantee the record is only switched to PREMIUM after a true payment success and not with some tampered data, route response and POSTMAN πŸ˜‰ Thanks in advance for sharing the idea/approach they see as safe and secure enough...
tuto1902
tuto1902β€’5mo ago
I apologize for the slow responses on the comments. Allow me some time to respond to your post as I’m currently behind on pretty much everything channel related. Thanks for your understanding and patience. I’ll have an answer as soon as I can
MilenKo
MilenKoOPβ€’5mo ago
Hey Tuto, no worries at all man, you can't realize (maybe not to it's full extent) how much you are helping us with your amazing videos. I think I got the proper workflow for my needs so here it is what I came up with as a working workflow, in case someone else needs the use: 1. Create a form with all the required by design inputs. I am using jquery-validator for the frontend validation of the form. 2. Using a small JavaScript I make sure to trigger the form validation before passing the form data to the controller. (for sure the form points to a route accepting POST request) 3. Since the browser validation can easily get me tampered using dev console or any other tools, we need to secure our stored data with a server side validation on the data from $request (if data passes the validation, we move on, if not, return to the required URL/route with a message of your choice. 4. Since our data is validated, we store the resource with a status (inactive or draft as it is in my case) 5. We pass the Stripe price_id to checkout (see tuto's video if still unclear) and after payment we get redirected to the required URL for success or cancel. In my case I returned to my success_urk with a confirmation message passed to the route as a parameter, e.g. ?result=success Since we've spoke about Webhooks, time to use those (again, tuto's "fault" for showing us the Stripe powerhouse weapons πŸ˜‰ ) So I've set a new POST route to allow Stripe to trigger the events on that route. Don't forget to point to Stripe dashboard and provide either the FQDN URL or use the steps to communicate with Stripe CLI on your local server. After making sure we get 200 response on our local event listener, I load the entire payload of Stripe from the request, extract the user stripe_id to pull up all his resources and from there with some extra payload code, I find the specific resource that the user paid to have it posted (as per the needs of my project, but yours might differ). Once we've found the resource that has been paid for successfully, all that is left is to just watch it's status to Active/published etc. For sure, if we have an expiring subscriptkon, we need to configure another webhooks listener for delete event so that once it happens, the status of the resource gets back to it's previous one (prior to the payment success). Stripe is full of webhooks and some I will explore further (e.g. the one that notifies the user via email 7 days prior to the subscription expiration). This way user gets a nice email advising to pay to avoid service/premium disruption... There you go my friends, maybe not the simplest/best/optimal way but it seems to work so far well on my side. P.S. It took me quite a while to figure out the 403 error so make.sute that your webhook route is not behind a.middleware (auth or others). Also you would want to exclude Stripe from the default CSRF verification of Laravel by adding it to exceptions in CSRFValidation config in /app/Middleware. That is it, that is all, thanks again Tuto for.yhe cool video right on time for my project needs πŸ˜‰ @tuto1902 Actually, I seem to still have a small question now that it is all moving. Since I am offering a trial period for my subscription, what Webhooks should I specifically listen to so that: 1. When the user has passed his payment details (I won't allow a trial without providing a payment method) what will be the hook for starting the trial, ending the trial and switching to the actual paid premium. There are so many Webhooks and I am confused now which webhook to listen to when: 1. User has provided the payment data and is now on Trial 2. Trial notification of the expiry 3. User cancelled the trial 4. Trial ended 5. User is now an official premium. Knowing those would allow to code any action and switch successfully a user to the trial, from trial to the premium of to removed if the subscription got cancelled...
Want results from more Discord servers?
Add your server