Back to Blog
5 min read

Stripe Integration Essentials: Quick Checkout and Webhooks Setup

A comprehensive guide to implementing Stripe, covering the checkout process, secure webhook handling, vital events to monitor, and how to react to them.

by Jonathan Beurel

One of the very first things to do when creating your project is to set up a payment process. This step can take a lot of time if you don't know where to start, but it can be done in under an hour if you have a straightforward guide to follow. That's exactly what I want to offer you here: how to get your first payment!

The Stripe Checkout Process

Before implementing Stripe, I want to quickly explain how the payment process works. This will help you know if you're on the right track when you start your implementation.

The typical flow involves:

  1. User Action (Client-Side)
    The user clicks on a "Pay" or "Subscribe" button on your frontend application to initiate the checkout process.
  2. Stripe Session Creation (Server-Side)
    This action triggers a request to your backend which creates a Stripe Checkout Session (via the SDK). This session includes details like line items, prices, success URLs, and cancel URLs.
  3. User Redirection to Stripe
    Your backend provides the Checkout Session ID, and your frontend redirects the user to the Stripe-hosted payment page.
  4. Payment Completion
    The user enters their payment information on the Stripe page that processes the payment.
  5. Return to Website
    Stripe redirects the user back to your specified success_url or cancel_url.
  6. Webhook Notification
    Asynchronously, Stripe sends a webhook event (typically checkout.session.completed) to your server to confirm the payment status and provide details.

Here's a visual representation of the Checkout flow:

sequenceDiagram
    participant Client
    participant Server
    participant Stripe

    Client->>Server: Initiate Checkout (e.g., on button click)
    Server->>Stripe: Create Checkout Session (line items, success/cancel URLs)
    Stripe-->>Server: Checkout Session ID
    Server-->>Client: Checkout Session ID
    Client->>Stripe: Redirect to Stripe Checkout Page (with Session ID)
    Stripe->>Client: Display Payment Form
    Client->>Client: User fills the form
    Client->>Stripe: Submit Payment Details
    Stripe->>Stripe: Process Payment
    alt Payment Successful
        Stripe->>Client: Redirect to success_url
        Stripe->>Server: Webhook: checkout.session.completed
    else Payment Failed
        Stripe->>Client: Redirect to cancel_url / Show Error
        Stripe->>Server: Webhook: payment_intent.payment_failed
    end

Stripe Webhooks

As the user progresses through the payment process, Stripe sends events to your backend using webhooks.

Webhooks are HTTP callbacks triggered by Stripe. They are essential for receiving real-time notifications about payment statuses, subscription changes, and other important occurrences. This allows your backend to react accordingly, even if the user closes their browser or loses connection after payment.

Verifying Webhook Signatures

Security is paramount. Anyone could send a fake webhook request to your endpoint. To ensure that a webhook request genuinely came from Stripe, you must verify its signature.

Stripe signs each webhook event it sends. This signature is included in the Stripe-Signature header of the request. The verification process involves:

  1. Retrieve Your Endpoint's Secret:
    You get this secret from your Stripe Dashboard when you set up a webhook endpoint.

  2. Get the Payload and Signature:
    Extract the raw request body (payload) and the Stripe-Signature header from the incoming request.

  3. Construct and Compare:
    Using the endpoint secret, the payload, and the timestamp from the header, Stripe's SDK can construct the expected signature. Compare this expected signature with the one received in the header. If they match, the event is authentic.

sequenceDiagram
    participant Stripe
    participant YourServer as Webhook Endpoint

    Stripe->>YourServer: HTTP POST Request (Event Payload + Stripe-Signature Header)
    YourServer->>YourServer: Retrieve Webhook Secret
    YourServer->>YourServer: Get Raw Request Body (Payload)
    YourServer->>YourServer: Get Stripe-Signature Header
    YourServer->>YourServer: Construct Expected Signature (using Secret, Payload, Timestamp)
    alt Signatures Match
        YourServer->>YourServer: Process Event (Authentic)
    else Signatures Do Not Match
        YourServer->>YourServer: Discard Event (Potentially Malicious)
    end

Always use Stripe's official libraries to perform signature verification, as the process can be complex and error-prone if implemented manually.

Essential Stripe Events to Monitor

While Stripe sends many types of events, some are more critical for core business logic than others. Here's a list of indispensable events you should typically monitor:

EventDescription
checkout.session.completedA Checkout Session has been successfully completed. This is often the primary event for fulfilling one-time purchases.
payment_intent.succeededA payment has succeeded. Useful for tracking successful payments directly.
payment_intent.payment_failedA payment attempt has failed. Important for notifying users or prompting them to update payment methods.
invoice.payment_succeededAn invoice (often for subscriptions or manual invoices) has been successfully paid.
invoice.payment_failedAn attempt to pay an invoice has failed. This can trigger dunning processes or subscription cancellations.
invoice.paidSimilar to invoice.payment_succeeded, indicates an invoice is paid.
invoice.finalizedAn invoice has been finalized and is ready for payment.
customer.subscription.createdA new subscription has been created for a customer.
customer.subscription.updatedA subscription has been updated (e.g., plan change, quantity change, trial end).
customer.subscription.deletedA subscription has been canceled.
customer.subscription.trial_will_endA subscription trial is about to end. Useful for sending reminders.
charge.succeededA charge was successful.
charge.failedA charge attempt failed.
charge.refundedA charge has been partially or fully refunded.

Corresponding Actions for Events

Once you receive and verify a webhook event, your application needs to take appropriate actions. The exact logic will depend on your business model.

Here's a general guide to actions based on common events:

EventTypical Actions
checkout.session.completedFulfill the order (e.g., grant access to a digital product, ship a physical item), update database, send a confirmation email.
payment_intent.succeededUpdate payment status in your database, confirm service provisioning.
payment_intent.payment_failedNotify the user, log the failure, potentially update subscription status to "past due", request payment method update.
invoice.payment_succeededUpdate subscription status to "active", record payment, grant/continue service access, send receipt.
invoice.payment_failedNotify the user, initiate dunning (retry payment), potentially restrict service access or mark subscription as "unpaid".
customer.subscription.createdProvision subscription services, update user's account status, send welcome email.
customer.subscription.updatedAdjust service levels based on the new plan/quantity, update database records, notify user of changes.
customer.subscription.deletedRevoke access to subscription services at the end of the billing period, update database, send cancellation confirmation.
charge.refundedUpdate financial records, potentially adjust inventory or service access, notify the user.

Here's a conceptual flow for handling different events:

graph TD
    A[Stripe Event Received] --> B{Verify Signature};
    B -- Valid --> C{Event Type?};
    B -- Invalid --> D[Discard/Log Error];

    C -- checkout.session.completed --> E[Fulfill Order <br/> Update DB <br/> Notify User];
    C -- invoice.payment_succeeded --> F[Activate/Continue Service <br/> Update DB <br/> Send Receipt];
    C -- invoice.payment_failed --> G[Notify User <br/> Retry Logic <br/> Update Subscription Status];
    C -- customer.subscription.deleted --> H[Deactivate Service <br/> Update DB <br/> Notify User];
    C -- Other Events --> I[Handle Specific Logic];

    E --> Z[End Process];
    F --> Z;
    G --> Z;
    H --> Z;
    I --> Z;

Conclusion

A robust Stripe integration hinges on correctly handling the checkout flow and, critically, processing webhook events securely and reliably. By verifying webhook signatures and responding appropriately to essential events like checkout.session.completed, invoice.payment_succeeded, and customer.subscription.updated, you can automate your payment workflows, manage subscriptions effectively, and provide a seamless experience for your customers. Remember to consult Stripe's extensive documentation for the most up-to-date information and advanced features.