Search code examples
securityhttpsstripe-paymentspayment-processingpci-dss

Stripe - Securely handling payments on the server


I'm using Stripe Elements in my frontend (React) to take payment information and everything. Currently, I have a function handleSubmit on the frontend that directly runs await stripe.confirmPayment({ elements, ... }), where const elements = useElements(); and const stripe = useStripe();. A backend server (Express) handles the publishable key and creating the payment intent and sends back the pk and client secret when the intent is created. However, I would like to move all my payment logic to the server to avoid any potential data fabrication client-side.

First of all, is this a good idea? And was confirming payments on the frontend a bad idea in the first place

Second, how will I actually send the user's payment data to the server? Is it safe to just do it through post requests, and does Stripe provide a way to do it (e.g. can I just send the client secret back to the server for the payment intent to be processed)?

Apologies if this is a dumb question. This is my first time dealing with payments and seriously worrying about security, I guess.


Solution

  • First of all, is this a good idea?

    It's not in my opinion.

    First of all, if your Stripe account is in anyway new, it simply won't let you do that - see this thread as an example.

    Furthermore, doing this will require you to be PCI compliant, and prove it, both to Stripe and the PCI authorities, with SAQ forms:
    https://stripe.com/docs/security/guide

    Using frontend Stripe iframes (Elements) to collect card details (or other solutions like Stripe Checkout) is the most worry-free option.

    The confirmPayment / confirmXxxPayment functions are also handy since they collect the payment details, authenticate if necessary, and attempt the charge all in one go.
    It would take more work to implement each of these actions server-side.

    That said, you're right in wanting to decouple your integration from the frontend as much as possible - it is likely the most vulnerable point in your app.

    For this reason, you should do the following:
    -DO NOT pass numeric amounts for your order total from client to server -> use item dics instead.
    -DO NOT rely on frontend redirects and url params to initiate your processes for successful orders -> notify yourself with webhooks instead.
    -Make sure to verify these webhooks' origin, and implement an idempotency logic for them (not only for replay attacks, Stripe webhooks can be sent multiple times).