Streamlining SaaS Trials: Integrating Paddle Checkout with Laravel

Introduction

Offering a seamless free trial is crucial for SaaS products. On the Reimpact platform, we recently enhanced our user onboarding by integrating Paddle for a 30-day free trial checkout experience. This not only streamlines the path from prospect to active user but also provides a robust foundation for subscription management.

The Challenge

Implementing a new checkout flow involved several key considerations:

  1. Guest Registration: How to capture essential user and company details (name, email, password, company) for unauthenticated visitors directly within the trial signup flow.
  2. Paddle Integration: Seamlessly invoking the Paddle.js checkout overlay while securely handling sensitive payment information and trial specifics.
  3. Backend Synchronization: Ensuring that successful Paddle subscriptions (specifically, the subscription.created event) correctly activate the corresponding SaaSQuote, provision features to the Company, and notify the user.
  4. Error Handling & Fallback: Providing a graceful user experience when Paddle credentials or configurations are not yet fully set up, preventing blockers during deployment.

The Solution

Our approach involved a full-stack implementation, from frontend user experience to backend webhook processing.

First, the existing 'Pay Now' button on the SaaS quotes page was replaced with a prominent 'Start Free Trial' call to action. For guest visitors, an inline registration block was introduced, collecting basic user and company information. Upon submission, the system creates the Company and an admin User, logs them in, and redirects to a transition page.

This transition page then leverages Paddle.js to open the checkout overlay, pre-populated with trial details. Key to this integration is mapping our internal service items to Paddle's price IDs, which include variants for different billing cycles (monthly/annual) and market segments (_micro). This mapping is managed via a jsonb column on our service_items table and populated by an Artisan command.

The real power comes from the SubscriptionCreated webhook listener. When Paddle successfully processes a new subscription (or trial activation), it sends a webhook notification to our application. This event triggers a listener that performs critical backend tasks:

// app/Listeners/ActivateSaaSQuoteOnSubscriptionCreated.php

namespace App\Listeners;

use App\Events\PaddleSubscriptionCreatedEvent;
use App\Models\Company;
use App\Models\SaaSQuote;
use App\Mail\WelcomeTrialMail;
use Illuminate\Support\Facades\Mail;

class ActivateSaaSQuoteOnSubscriptionCreated
{
    public function handle(PaddleSubscriptionCreatedEvent $event): void
    {
        $payload = $event->payload;
        $subscriptionId = data_get($payload, 'data.id');
        $customerId = data_get($payload, 'data.customer_id');
        $priceId = data_get($payload, 'data.items.0.price.id');

        $company = Company::findByPaddleCustomerId($customerId); // Custom method
        $saasQuote = SaaSQuote::findBySubscriptionId($subscriptionId); // Custom method

        if ($company && $saasQuote) {
            $saasQuote->markAsActive();
            $company->attachFeaturesForPrice($priceId); // Logic to map price to features
            $company->updateAuthorizedUsers(data_get($payload, 'data.items.0.quantity', 1));

            Mail::to($company->adminUser->email)->send(
                new WelcomeTrialMail($company, data_get($payload, 'data.trial_ends_at'))
            );
        }
    }
}

This listener ensures that once Paddle confirms a new subscription, the SaaSQuote is marked as active, the appropriate features are associated with the Company (e.g., enabling access to specific modules), the quantity of authorized users is updated, and a welcome email is dispatched to the admin with crucial trial details.

Crucially, a fallback mechanism was implemented: if Paddle environment variables are not configured, the user is redirected to a friendly success page, allowing development and deployment without external dependencies initially.

Key Decisions

  1. Inline Guest Registration: To minimize friction, guests can register and start the trial on the same page, creating an immediate user and company context.
  2. Webhook-Driven Activation: Relying on Paddle's subscription.created webhook ensures data consistency and reliability for activating trials and provisioning features, decoupling our system from Paddle's frontend success flow.
  3. Configurable Price IDs: Storing Paddle price IDs directly in our database allows for flexible mapping of our internal service offerings to Paddle's product catalog without code changes.
  4. Graceful Degration: Implementing a clear success page fallback for unconfigured Paddle environments prevents blocking other development or staging workflows.

Results

This integration provides a robust and scalable solution for managing SaaS trials and subscriptions. It significantly reduces manual intervention, enhances the user onboarding experience, and lays the groundwork for future subscription model expansions. The webhook-based activation ensures that our system is always in sync with Paddle's state, providing accurate feature access and billing information.

Lessons Learned

When integrating third-party payment gateways, invest heavily in robust webhook handling and a clear separation of concerns between your application's business logic and the external provider's APIs. Always design for graceful degradation to ensure that core functionalities remain operational even if external services are not fully configured.

Streamlining SaaS Trials: Integrating Paddle Checkout with Laravel
GERARDO RUIZ

GERARDO RUIZ

Author

Share: