3
votes

After a user visits the payments page and successfully makes a payment, Stripe will redirect the customer to whatever url is provided to success_url

Stripe.api_key = 'sk_test_51HYHSFGtUKse83O9J4QeAib3cp8sHzGaOQRrn7sba92Hd8dCHE3AIHe5ModevMK7TVAUCyJU0ADSwIUoX00qxZmBI9r'

session = Stripe::Checkout::Session.create({
  payment_method_types: ['card'],
  line_items: [{
    name: 'Kavholm rental',
    amount: 1000,
    currency: 'aud',
    quantity: 1,
  }],
  payment_intent_data: {
    application_fee_amount: 123,
    transfer_data: {
      destination: '{{CONNECTED_STRIPE_ACCOUNT_ID}}',
    },
  },
  success_url: 'https://example.com/success',
  cancel_url: 'https://example.com/failure',
})

Now the app/platform must arrange 'fulfillment':

After the payment is completed, you’ll need to handle any fulfillment necessary on your end. A home-rental company that requires payment upfront, for instance, would connect the homeowner with the renter after a successful payment.

Also (bold from me):

Do not rely on the redirect to the success_url param alone for fulfilling purchases as:

  • Malicious users could directly access the success_url without paying and gain access to your goods or services.
  • Customers may not always reach the success_url after a successful payment. It is possible they close their browser tab before the redirect occurs.

Up to this point, everything is very well explained in the stripe docs and very understandable.

But I want to know: what is the best thing to do next, noting that:

  1. a payment may not go through immediately, so simply loading the success_url might be premature
  2. a webhook can be (easily) configured to listen for events, however if the success_url is loaded even 1 second before the webhook receives a success message, then it won't know that the payment went through successfully (so, for example, displaying "Congrats, your product will be shipped!" message could be presumptuous

Question

So, finally, the question: what is best practice for the flow on from success_url? I am just confused as to what is the best pattern..

Ideas

Here are some things I've considered:

  • Upon routing to success_url, simply use sleep(5) and then check the webhook in the controller for the success_url so it takes 5 extra seconds to load giving the webhook a chance to receive incoming events, so you can display either "Congrats your product will be shipped!" or "Oh, no, something went wrong with your payment, please try again or contact your bank".

  • Routing directly to success_url, but then having a message saying "please refresh in a moment" (then listening for a webhook indicating the payment was successful, and then conditionally showing the "Congrats your product will be shipped!" message)

1

1 Answers

2
votes

The documentation for fulfilling orders has this in an info box under the code snippet:

Your webhook endpoint redirects your customer to the success_url when you acknowledge you received the event. In scenarios where your endpoint is down or the event isn’t acknowledged properly, your handler redirects the customer to the success_url 10 seconds after a successful payment.

The event referenced above is the checkout.session.completed event sent to your webhook endpoint. The delay is designed to let you confirm the payment is successful and customize the success page based on the result of the Checkout Session's outcome.

In other words, Checkout won't send someone to your success_url until you've responded to the checkout.session.completed event request from your webhook endpoint with a successful (2xx) response, giving you time to customize the success page based on the outcome.

You can, for example, default to a "your payment is being processed" success page, but if you confirm the Checkout Session and payment succeeded after receiving the checkout.session.completed event, you can instead change the page to read "congrats your product will be shipped".

This approach means you can reliably present a success page that always has displays accurate information.