Integration
Checkout
Fulfillment

Fulfill orders with Checkout

Learn how to fulfill orders after a customer pays with Checkout or Payment links.

After you integrate with Flex Checkout or crate Payment Link to take your customers to a payment form, you need a notification to fulfill their order after a successful payment.

In this guide, you'll learn how to use webhooks to fulfill orders after a payment.

There are a couple webhooks that are valuable when when interacting with checkout session and depending on your goals you can take advantage of.

Select webhook events

Choos the webhook events that are relevant to your checkout session.

Below are the common ones related to checkout session.

  • customer.created - When a customer gets created with Flex. Once a user has entered their contact information this event will be sent.

  • payment_intent.created - Once a customer is ready to enter their payment information a payment intent will get created.

  • payment_intent.suceeded - After a succesful payment this event gets sent. In case of a split cart this event will be sent twice.

  • customer.subscription.created - If the checkout session is of type subscription, this event will be sent when a subscription is created.

  • customer.subscription.updated - If the checkout session is of type subscription, this event will be sent when a subscription is updated. For example, moving from incomplete to active.

  • checkout_session.completed - Sent after a checkout session has been completed.

Create an event handler

Set up an event handler to receive events from Flex. Start by printing out the event you receive.

If testing locally you can use ngrok to forward events to your local environment or use https://webhook.site (opens in a new tab) to visualize the events.

// Using Express
const app = require('express')();
 
// Use body-parser to retrieve the raw body as a buffer
const bodyParser = require('body-parser');
 
app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => {
  const payload = request.body;
 
  console.log("Got payload: " + payload);
 
  response.status(200).end();
});
 
app.listen(4242, () => console.log('Running on port 4242'));

Create a webhook endpoint

Once you have a URL for your event handler, from your partner dashboard or the API set up an endpoint to receive events.

In this example we will listen to the checkout_session.completed webhook event.

curl --request POST \
  --url https://api-stg.withflex.com/v1/webhooks \
  --header 'authorization: Bearer fsk_MzkxYzU2MTAtZGYxOC00OWUyLThlODQtYTA0ODMwNjhiYmVm' \
  --header 'content-type: application/json' \
  --data '{"webhook": {"url": "https://withflex.com/handle_events","events": ["checkout.session.completed"]}}'

Create a checkout session

Lets verify we're getting events. We can create a checkout session and check our application logs.

curl --request POST \
  --url https://api-stg.withflex.com/v1/checkout/sessions \
  --header 'authorization: Bearer fsk_MzkxYzU2MTAtZGYxOC00OWUyLThlODQtYTA0ODMwNjhiYmVm' \
  --header 'content-type: application/json' \
  --data '{"checkout_session": {"line_items": [{"price": "fprice_01HW5NTAB88NK7HPD0H688EPG9","quantity": 1}],"success_url": "https://withflex.com/thank-you?success=true","mode": "payment","cancel_url": "https://withflex.com/thank-you?canceled=true"}}'

Next, go through the checkout as a customer.

  • Enter contact information
  • Fill out the intake form (if applicable)
  • Fill out your payment form with test data
    • Enter 4242 4242 4242 4242 as the card number
    • Enter any future date for card expiry
    • Enter any 3-digit number for CVV
    • Enter any billing postal code (90210)
  • Click the Pay button

You should've gotten back a payload that resembles the following.

{
  "event_dt": 1713904733,
  "event_id": "fevt_01HW6AXK8QPNABRXQZAN5F882A",
  "event_type": "checkout.session.completed",
  "object": {
    "checkout_session": {
      "allow_promotion_codes": false,
      "amount_total": 5099,
      "amount_subtotal": 5099,
      "cancel_url": "https://withflex.com/thank-you?canceled=true",
      "capture_method": "automatic",
      "checkout_session_id": "fcs_01HW6AVP9WE8NC1JDK6ZBA6DA5",
      "created_at": 1713904671,
      "customer": "fcus_01HTDY6MDYMVY8EAXVBW3T9MHD",
      "customer_email": "miguel@withflex.com",
      "expires_at": 1713908271,
      "invoice": null,
      "hsa_fsa_eligible": true,
      "letter_of_medical_necessity_required": true,
      "metadata": null,
      "mode": "payment",
      "multiple_subscriptions": false,
      "payment_intent": "fpi_01HW6AXFHAPWXMCDJWB1TCHDHC",
      "redirect_url": "https://checkout-stg.withflex.com/pay/c/fcs_01HW6AVP9WE8NC1JDK6ZBA6DA5",
      "setup_intent": null,
      "shipping_options": null,
      "shipping_address_collection": false,
      "shipping_details": null,
      "status": "complete",
      "success_url": "https://withflex.com/thank-you?success=true",
      "subscription": null,
      "tax_rate": null,
      "test_mode": false,
      "total_details": {
        "amount_discount": null,
        "amount_tax": null,
        "amount_shipping": null
      },
      "visit_type": "gym"
    }
  }
}

Verify events came from Flex

Anyone can POST data to your event handler. Before processing an event verify that it came from Flex. You can find instructions on how to do so here.

Fulfill order

Handle the checkout.session.completed event to fulfill the order. Depending on your setup, you might have some additional events to handle. This event includes the Checkout Session object, which contains details about your customer and their payment. When handling this event, you might also consider:

  • Saving a copy of the order in your own database.
  • Sending the customer a receipt email.
  • Reconciling the line items and quantity purchased by the customer.

Let's say we want to gather the price the line items that belong to this checkout session to update our inventory. We can do so by using the Line Items API.

curl --request GET \
  --url https://api-stg.withflex.com/v1/checkout/sessions/fcs_01HW6AVP9WE8NC1JDK6ZBA6DA5/line_items \
  --header 'authorization: Bearer fsk_MzkxYzU2MTAtZGYxOC00OWUyLThlODQtYTA0ODMwNjhiYmVm' \
  --header 'content-type: application/json' \

Which would return

{
  "line_items": [
    {
      "line_item_id": "fli_01HW6AVPA48MM665T25TFJ8GQ3",
      "amount_discount": 0,
      "amount_subtotal": 5099,
      "amount_tax": 0,
      "amount_total": 5099,
      "amount_shipping": 0,
      "description": "Test Product",
      "price": {
        "price_id": "fprice_01HW5NTAB88NK7HPD0H688EPG9",
        "description": "Our awesome new price",
        "unit_amount": 5099,
        "price": 5099,
        "recurring": null,
        "active": true,
        "product": {
          "product_id": "fprod_01HW5MXAPBE79RHMMJJGB4ACAB",
          "name": "Test Product",
          "description": "Test Product Description",
          "created_at": "2024-04-23T14:14:16.029253Z",
          "visit_type": "gym",
          "active": true,
          "upc_code": null,
          "hsa_fsa_eligibility": "letter_of_medical_necessity"
        },
        "created_at": "2024-04-23T14:30:05.943282Z",
        "type": "one_time",
        "metadata": null
      },
      "quantity": 1,
      "eligibility": "letter_of_medical_necessity"
    }
  ]
}

From here we can go back to our inventory management system and mark this product/price as purchased.

Go live in production

After you’ve deployed your event handler endpoint to production, you need to register your live URL with Flex. Follow the guide to register a webhook.