Ad

PDS2 Stripe Success Webhook And Other Issues

- 1 answer

This is in danger of being TLDR - so my question is: On successful payment - stripes sends a "success" payload to my success webhook. Looking through the payload, I am unable to see anything which I can use to find which payment was successful. Should I be saving something from my stripe session to my pending payment?

Greater detail:

To comply with PSD2, I've had to rejig our stripe payments. We support a few different payment options, which has affected how I go about the process.

Before, with stripe, we'd get a token - send it client side... payment made - order saved to DB.. job done.

Now, the flow is reversed...

I have a "Stripe" button - customer clicks on it. A POST is made to the server. On the server I grab the customers cart and create an order with a payment status of pending.

I then create a stripe session - and return the stripe session ID to the client (code is abridged)

                  //creates order and returns Order ID
                  const orderid = await createOrder(cart); 

                  const stripeSession = await stripe.checkout.sessions.create({
                        customer_email: request.payload.billingEmail,
                        payment_method_types: ["card"],
                        line_items: [
                            {
                                name: "###",
                                description: "###" + orderid,
                                amount: cart.total.total,
                                currency: cart.total.currency,
                                quantity: 1
                            }
                        ],
                        success_url: "###" + orderid,
                        cancel_url: "###/checkout"
                    });

                    return {
                        stripeSessionID: stripeSession.id
                    };

and on my client I have this method method to post to the server and automatically redirect to external stripe checkout page:

        stripeCheckout: function () {
           ...
            axios.post('/pay/get-stripe-session', data)
                .then(function (response) {
                    var checkoutSessionID = response.data.stripeSessionID
                    stripe.redirectToCheckout({
                        sessionId: checkoutSessionID
                    }) ...

Upon succesful payment, stripe sends a "success" payload to my success webhook. I check the stripe signature - and receive the message... this all works... however, I can't see any data in the payload that I can use to match the payment with the order (in order to update the orders payment status).

When I create my stripe session is there anything from it that I can use?

** Edit ** -

  1. When creating a stripe session, one can pass client_reference_id. into the create session method as a unique key. However, stripes success webhook does NOT return this key in its payload - so this cannot be used to reconcile a successful payment with an order.

  2. We have our own customer accounts system. Under the old API we could set up a charge thus:

    const charge = await stripe.charges.create({
      amount: total,
      currency: currency,
      source: token, // obtained with Stripe.js
      description: orderid
    })
    

And the description would appear in stripes dashboard making it easy to find a payment (to make a refund or whatever). We don't use Stripes 'customers'. We store orders, and customers in our system (stripe is not a customer management system). If the customer is logged in when they check out, we link them to their order. Guest orders aren't linked to anyone.

However, under the new api where you have to create a stripeSession every session creates a customer in stripes dashboard. Can we prevent this?

Also, there is no way to add a description to the overall session / charge like you could with the old charge api - so in Stripes Payments dashboard, we end up with unusable junk for each payment description...

Does anyone know how to fix this? I hope stripe aren't having to sacrifice their wonderful developer experince to comply with PDS2

Ad

Answer

Solved it:

The trick is to set meta-data on your stripe session:

                        const stripeSession = await stripe.checkout.sessions.create({
                        customer_email: billingEmail,
                        client_reference_id: orderid,
                        payment_method_types: ["card"],
                        line_items: [
                            {
                                name: "My charge",
                                description: "Lorem ipsum",
                                amount: total,
                                currency: currency,
                                quantity: 1
                            }
                        ],
                        payment_intent_data: {
                            description: `orderID: ${orderid}`,
                            metadata: {
                                orderid : orderid
                            }
                        },
                        success_url: "https://example.com/thankyou/",
                        cancel_url: "https://example.com/checkout"
                    });

The metadata is returned in the charge.success event (webhook). Using this metadata, I am able to find the order in my database and update it. In our case, I take the transaction.id, card type and last 4 card digits from the charge.success event and update the payment status to paid.

If you don't need this information - you could simply set your webhook to receive the checkout.session.complete event as that contains the client_reference_id (and I believe is stripes preferred event to confirm a transaction)

Because we're not using Customers accounts inside Stripe, I also remove the customer from stripe:

        // Delete the customer from Stripes Dashboard (we don't use it - its clutter)
        const customerID = event.data.object.customer

        stripe.customers.del(
            customerID,
            function(err, confirmation) {
                // asynchronously called
            }
        );

And thats basically it. Use the meta - it seems to be sent on every event.

Ad
source: stackoverflow.com
Ad