Paddle Integration for SaaS Subscriptions

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    848
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Paddle Integration for SaaS Subscriptions

Paddle is a payment platform acting as Merchant of Record (MoR): handles taxes (VAT, GST, sales tax), refunds and compliance. For SaaS companies outside US and EU this is often the only way to legally sell in 200+ countries without opening legal entity in each. Paddle calculates and pays local taxes — developers don't need to think about it.

Paddle Billing (v2) is newer product, differs from Paddle Classic API. Integration takes 3–5 working days.

Paddle Billing vs Classic

Paddle Classic uses overlay checkout — JavaScript widget embedded on page. Paddle Billing uses own checkout URL or inline checkout via iframe. New projects should start with Billing if Classic not needed for compatibility.

Creating Products and Prices

// Paddle Billing REST API
const product = await paddle.products.create({
    name: 'Pro Plan',
    tax_category: 'saas',
});

const price = await paddle.prices.create({
    product_id: product.id,
    description: 'Pro Monthly',
    billing_cycle: { interval: 'month', frequency: 1 },
    trial_period: { interval: 'day', frequency: 14 },
    unit_price: { amount: '2900', currency_code: 'USD' },
});

Frontend Checkout

Paddle Billing provides @paddle/paddle-js SDK:

import { initializePaddle } from '@paddle/paddle-js';

const paddle = await initializePaddle({
    environment: 'production', // or 'sandbox'
    token: 'live_...',
    eventCallback(event) {
        if (event.name === 'checkout.completed') {
            const { transaction_id, customer } = event.data;
            // Redirect or update UI
            window.location.href = `/dashboard?checkout=success&txn=${transaction_id}`;
        }
    },
});

// Open checkout
paddle.Checkout.open({
    items: [{ priceId: 'pri_...', quantity: 1 }],
    customer: { email: currentUser.email },
    customData: { user_id: currentUser.id },
    successUrl: `${window.location.origin}/dashboard`,
});

Webhooks and Synchronization

Paddle sends subscription events. Signatures verified via ECDSA with public key from dashboard:

use Paddle\Webhooks\Verify;

public function handleWebhook(Request $request): Response
{
    $verified = Verify::signature(
        $request->getContent(),
        $request->header('Paddle-Signature'),
        config('services.paddle.webhook_secret')
    );

    if (!$verified) {
        return response('Forbidden', 403);
    }

    $payload = $request->json()->all();

    match ($payload['event_type']) {
        'subscription.created'   => $this->onSubscriptionCreated($payload['data']),
        'subscription.updated'   => $this->onSubscriptionUpdated($payload['data']),
        'subscription.cancelled' => $this->onSubscriptionCancelled($payload['data']),
        'transaction.completed'  => $this->onTransactionCompleted($payload['data']),
        'transaction.payment_failed' => $this->onPaymentFailed($payload['data']),
        default => null,
    };

    return response('OK', 200);
}

private function onSubscriptionCreated(array $data): void
{
    $userId = $data['custom_data']['user_id'];
    $user   = User::findOrFail($userId);

    $user->update([
        'paddle_subscription_id' => $data['id'],
        'paddle_customer_id'     => $data['customer_id'],
        'subscription_status'    => $data['status'],
        'plan'                   => $data['items'][0]['price']['id'],
        'trial_ends_at'          => $data['current_billing_period']['starts_at'] ?? null,
        'renews_at'              => $data['next_billed_at'],
    ]);
}

Upgrade and Cancellation

Paddle Billing allows updating subscription via API. Prorated billing calculated automatically:

// Upgrade to new plan
$paddleClient->subscriptions()->update($subscription_id, [
    'items' => [[
        'price_id' => 'pri_new_plan',
        'quantity' => 1,
    ]],
    'proration_billing_mode' => 'prorated_immediately',
]);

// Cancel at end of period
$paddleClient->subscriptions()->cancel($subscription_id, [
    'effective_from' => 'next_billing_period',
]);

Customer Portal

Unlike Stripe, Paddle doesn't have ready Customer Portal. Subscription management implemented via Paddle Update Payment Method URL and own API calls. To change payment method:

$updateUrl = $paddleClient->subscriptions()->getPaymentMethodUpdateTransaction($subscription_id);
// Redirect to $updateUrl->url

Taxes and Currencies

Paddle automatically determines customer country by IP and adds local tax (VAT in EU, GST in Australia etc.). Final price user sees already includes tax. Paddle pays it to local tax authorities — no additional action from developer needed.

Paddle supports dynamic pricing: same product displayed in different currencies depending on user's country.