Accept payments through Paddle: a merchant of record platform that handles payments, tax, and compliance. Integrates with the Larapen Shop add-on via the PaymentGatewayInterface contract.

Paddle Checkout Overlay

Customers pay via Paddle’s embedded checkout overlay powered by Paddle.js v2. No redirect away from your site.

Merchant of Record

Paddle handles sales tax, VAT, and compliance worldwide. You receive net payouts.

Webhook-Driven

Order status updates are confirmed via signed webhooks for reliable payment processing.

Refund Support

Process full or partial refunds directly from the admin panel via the Paddle Adjustments API.

Use Cases

Digital Product Sales

You sell digital products (themes, plugins, e-books) through the Larapen Shop and want Paddle to handle global tax compliance and payouts.

  • Install the Paddle add-on alongside the Shop add-on.
  • Configure your Paddle API credentials in the admin panel.
  • Customers select Paddle at checkout and pay via the overlay.
  • Paddle collects tax, processes payment, and sends you net revenue.

Physical or Mixed Product Store

You sell physical goods or a mix of physical and digital products and want a reliable payment gateway.

  • Paddle supports credit/debit cards, PayPal, Apple Pay, Google Pay, and local payment methods.
  • Orders are confirmed via webhooks, ensuring no lost payments even if the customer closes their browser.

Sandbox Testing

Use Paddle’s sandbox environment for development and testing before going live.

  • Set the environment to “Sandbox” in admin settings.
  • Use Paddle’s test card numbers to simulate payments.
  • Switch to “Production” when ready for live payments.

Requirements

  • Larapen CMS v1.0.0 or later
  • PHP 8.3+
  • MySQL 8.0+
  • The Shop add-on must be installed and active (declared dependency)
  • A Paddle account: paddle.com
  • Paddle PHP SDK (paddle/paddle-php-sdk) installed via Composer
Note: The add-on depends on the Shop add-on. It cannot be activated without the Shop add-on being installed and active first.

Installation

Step 1: Upload the Add-on

In the admin panel, go to Admin → Extensions → Add-ons and click the Upload Add-on button. Select the add-on’s ZIP file: the system extracts it automatically and the add-on appears in the installed add-ons list.

Step 2: Activate the Add-on

Find Paddle Payment Gateway in the list and click Activate. Its migrations, seeders (if any) and permissions are set up automatically.

Step 3: Configure

Navigate to Admin → Paddle → Settings and enter your API key, client-side token, seller ID, and webhook secret. See also Getting Paddle Credentials. See Configuration.

Step 4: Set Up Webhooks

  1. Go to your Paddle Dashboard → Developer Tools → Notifications.
  2. Create a new notification destination pointing to https://yoursite.com/paddle/webhook.
  3. Select these events: transaction.completed, transaction.payment_failed, transaction.updated, adjustment.created, adjustment.updated.
  4. Copy the webhook secret and paste it into the admin settings.

Purchase Code (License Key)

Paddle Payment Gateway is sold as a separate product, so it has its own purchase code (license key), distinct from the purchase code of the main application and from the one of every other add-on. You are asked for it when you activate Paddle Payment Gateway in Admin panel → Add-ons.

Our products are sold on three platforms. The way you receive a purchase code depends on where you bought the product.

Platform / Marketplace How you get the purchase code Where to find it again
bedigit.com Store
In-site purchase (Shop)
Generated automatically when the order is paid, then sent by email, either in its own license email, or inside the order confirmation email. My Account → My Licenses on bedigit.com
Gumroad Created as soon as Gumroad notifies us of the sale, then sent in a separate email, in addition to the Gumroad receipt. The license email, your Gumroad Library, and My Account → My Licenses on bedigit.com
Envato Market
CodeCanyon
Issued by Envato, not by us, and never sent by email: you download it yourself from your Envato account. Envato account → Downloads → License certificate & purchase code
Check your spam folder. For both bedigit.com Store and Gumroad purchases, the purchase code is delivered by email. Automated license emails are very often filtered, so if the message is not in your inbox, look in your spam / junk folder before contacting support, and add our sender address to your contacts or allow list.

1. bedigit.com Store (in-site purchase)

  • As soon as the order’s payment status becomes Paid, a license key is generated automatically for every licensed item in the order (one key per purchased unit: buying 3 units gives 3 distinct keys).
  • It is emailed to the address used on the order, either in a dedicated license email or inside the order confirmation email. Check your inbox and your spam / junk folder.
  • The key stays available in your account under My Account → My Licenses. Keys are masked in the list; open the license detail page to reveal and copy the full key, see the domains it is activated on, and deactivate a domain to free an activation slot.
  • The matching invoice is under My Account → My Orders.

2. Gumroad

  • A Gumroad purchase produces two separate emails: the Gumroad receipt (sent by Gumroad, giving access to the files) and a license key email (sent by bedigit.com) that contains your purchase code.
  • The license key email is generated as soon as Gumroad notifies us of the sale, so it normally arrives within seconds of the payment. Here too, check your inbox and your spam / junk folder.
  • When the Gumroad product uses Gumroad’s own license-key feature, the same key also appears in your Gumroad receipt and under Library → your purchase on gumroad.com.
  • Use the same email address on bedigit.com as on Gumroad: your keys are then linked to your account automatically and listed under My Account → My Licenses, even if you register after the purchase. You can also add a Gumroad key manually from My Account → My Gumroad Licenses.

3. Envato Market (CodeCanyon)

  • Envato purchase codes are issued and delivered by Envato Market, never emailed by us, so there is nothing to look for in your spam folder: you retrieve the code from your Envato account.
  • Log in to your Envato / CodeCanyon account, open the Downloads page, find the item, and choose License certificate & purchase code from the Download dropdown. The code is written in that certificate.
  • An Envato purchase code looks like 12345678-90ab-cdef-1234-567890abcdef (8-4-4-4-12 characters). It never changes, and renewing item support does not issue a new one.
  • Official Envato article: Where Is My Purchase Code?
Lost your purchase code? Search your mailbox (spam folder included) for “license” or “purchase code”, then check My Account → My Licenses on bedigit.com for Store and Gumroad purchases, or Downloads → License certificate on Envato. If it is still missing, open a ticket on our Help Center with your order number (Store), Gumroad sale ID or buyer email (Gumroad), or Envato username and item name (Envato).

Configuration

All settings are managed in Admin → Paddle → Settings (stored in the settings table, group paddle).

Setting Description Default
paddle_api_key Paddle API key for server-side API calls (transactions, refunds). Encrypted at rest. (empty)
paddle_client_token Paddle client-side token for initializing Paddle.js on the frontend. Encrypted at rest. Sandbox tokens start with test_, live tokens start with live_. (empty)
paddle_webhook_secret Secret key for verifying Paddle webhook signatures (HMAC-SHA256). Encrypted at rest. (empty)
paddle_environment Paddle environment: sandbox for testing, production for live payments. sandbox
paddle_seller_id Your Paddle seller/vendor ID. Required for Paddle.js initialization. (empty)
paddle_currency Currency code (e.g., USD, EUR, GBP). Should match your shop currency. USD
Security: The API key, client token, and webhook secret are encrypted using Laravel’s Crypt::encryptString() before being stored in the database. They are decrypted at runtime when the PaddleServiceProvider boots.

Environment Variables

Environment variables serve as defaults. Settings saved in the admin panel override them.

Note: Environment variables are used as fallback defaults. Settings saved in the admin panel take priority and override them.

Getting Paddle Credentials

API Key & Client-Side Token

  1. Log in to your Paddle Dashboard (or Sandbox Dashboard for testing).
  2. Navigate to Developer Tools → Authentication.
  3. Copy your API Key (used for server-side API calls).
  4. Copy your Client-Side Token (used for Paddle.js initialization).

Seller ID

  1. In the Paddle Dashboard, your Seller ID is displayed in the account settings or URL.
  2. Copy the numeric ID and enter it in the admin settings.

Webhook Secret

  1. Go to Developer Tools → Notifications in the Paddle Dashboard.
  2. Create a new notification destination with the URL https://yoursite.com/paddle/webhook.
  3. Select the events to subscribe to (see Supported Events).
  4. Copy the generated webhook secret and enter it in admin settings.
Important: Sandbox and production environments use different credentials. Sandbox client tokens start with test_ and live tokens start with live_. Make sure your credentials match the selected environment.

Admin: Settings

The settings page (Admin → Paddle → Settings) is organized into two sections.

API Credentials

Three masked password fields with show/hide toggles:

  • API Key: Your Paddle API key from Developer Tools > Authentication. Used for all server-side API calls (creating transactions, processing refunds).
  • Client-Side Token: Your Paddle client-side token. Used to initialize Paddle.js on the checkout page. Sandbox tokens start with test_.
  • Webhook Secret: Used to verify the HMAC-SHA256 signature on incoming webhook requests.
Note: Leave credential fields empty to keep current stored values. All credentials are encrypted before being stored in the database. Never share your API key.

The page also shows an informational panel with direct links to:

  • Paddle Dashboard (production and sandbox)
  • Developer Tools > Authentication (API keys)
  • Developer Tools > Notifications (webhooks)
  • Paddle API documentation
  • Test card numbers for sandbox testing

Payment Options

  • Environment: Dropdown to select sandbox (testing) or production (live).
  • Seller ID: Text input for your Paddle seller/vendor ID. Required for Paddle.js initialization.
  • Currency: 3-character currency code (e.g., USD, EUR, GBP). Should match your shop currency.

Payment Flow

The Paddle add-on uses an overlay checkout flow powered by Paddle.js v2. Here is the complete payment lifecycle:

  1. Customer selects Paddle: On the shop checkout page, the customer selects “Paddle” as their payment method. An informational message appears: “You will be redirected to Paddle’s secure checkout.”
  2. Form submission (AJAX): When the customer submits the checkout form, JavaScript intercepts the submission and sends it via fetch() with JSON headers.
  3. Server creates Paddle transaction: The PaddleGateway::createPaymentIntent() method:
    • Creates a non-catalog transaction via the Paddle API with the order amount, currency, and metadata.
    • Stores a local paddle_transactions record linking the Paddle transaction ID to the order.
    • Links the Paddle customer to the local user (if authenticated).
    • Returns the Paddle transaction ID to the frontend.
  4. Paddle.js overlay opens: The JavaScript opens the Paddle checkout overlay using Paddle.Checkout.open() with the transaction ID.
  5. Customer completes payment: The customer enters payment details within the Paddle overlay.
  6. Checkout completed callback: On checkout.completed, the JavaScript redirects to the return URL (/paddle/return?_ptxn={transaction_id}).
  7. Return URL confirmation: The PaddleController::return() method calls confirmPayment() to check the transaction status via the Paddle API. If completed, the order is marked as paid and the customer is redirected to the success page.
  8. Webhook confirmation: Paddle also sends a transaction.completed webhook as a reliable backup. This ensures orders are marked as paid even if the customer closes their browser before the return redirect.

Webhook Handling

Webhooks are received at POST /paddle/webhook. This endpoint is excluded from CSRF protection and web middleware.

Supported Events

Event Action
transaction.completed Marks the local transaction as completed, calls markAsPaid() on the payable (order), and creates a shop_transactions record with payment details.
transaction.payment_failed Marks the local transaction as failed, calls markPaymentFailed() on the payable, and creates a failed transaction record with the error code.
adjustment.created If the adjustment action is refund and status is approved or completed, updates the order’s payment status to refunded.
adjustment.updated Same handling as adjustment.created: checks for refund status changes.
transaction.updated Configured in webhook events list but currently handled by the default case (ignored).

Signature Verification

All incoming webhooks are verified using HMAC-SHA256 signatures. Paddle sends the signature in the Paddle-Signature HTTP header in the format:

The verification process:

  1. Parse the ts (timestamp) and h1 (hash) values from the header.
  2. Compute HMAC-SHA256(timestamp + ':' + rawBody, webhookSecret).
  3. Compare the computed hash with the received h1 value using hash_equals().
  4. If the signature is invalid or the webhook secret is empty, return a 400 response.
Important: The webhook secret must be configured for signature verification to work. Without it, all webhook requests will be rejected with an “Invalid signature” error.

Refunds

The add-on supports full and partial refunds via the Paddle Adjustments API.

How Refunds Work

  1. An admin initiates a refund from the shop order management page.
  2. The PaddleGateway::refund() method:
    • Retrieves the Paddle transaction to get the item IDs.
    • Creates a partial adjustment with Action::Refund() against the first line item.
    • Creates a shop_transactions record of type refund.
  3. Paddle processes the refund and sends an adjustment.created webhook.
  4. The webhook handler updates the order status to refunded if the adjustment is approved.

Refund Statuses

Status Description
approved Refund approved and processed immediately. Order marked as refunded.
pending Refund submitted but awaiting Paddle approval. Order status updated when the adjustment.updated webhook arrives.
rejected Refund was rejected by Paddle. No order status change.

Updating

There are two ways to update this add-on: via the admin panel (recommended) or manually replacing files.

Method 1: Admin Panel Upload (Recommended)

  1. Download the latest .zip file of this add-on.
  2. Go to Admin panel → Add-ons and click the Upload button.
  3. Select or drag the .zip file into the upload area.
  4. A confirmation prompt will show the current and new version numbers. Click Replace to proceed.
  5. Go to Admin panel → System Update (/admin/update) to apply any pending database migrations.

Method 2: Manual File Replacement

Step 1: Replace Files

Replace the add-on directory with the new version.

Step 2: Update Composer Dependencies

Step 3: Run Migrations

php artisan migrate

Pending migrations run once, so the command is safe to repeat.

Step 4: Clear Caches

php artisan config:clear
php artisan route:clear
php artisan view:clear

Step 5: Verify

Visit Admin → Paddle → Settings and confirm your credentials are still configured. Process a test payment in sandbox mode to verify the integration.

Backup first: Always back up your database before running migrations on a production system.

Uninstallation

Switching an add-on off without losing anything is a deactivation: go to Admin panel → Add-ons, find Paddle Payment Gateway and click Deactivate.

  • Its routes, views, admin menu entries and permissions stop being registered, and its front-end pages stop answering.
  • Its database tables and all the data they hold are kept, and its files stay under extensions/addons/paddle/. Nothing is deleted.
  • The purchase code recorded at activation is kept too, so activating the add-on again does not ask for it.
  • Deactivation is refused while another active add-on depends on this one: deactivate that add-on first.

Click Activate on the same card to switch it back on. Pending migrations are re-run, assets are republished, and the add-on picks up exactly where it left off.

Removing

Removing is permanent and destroys the add-on's data. The Remove button only appears on a deactivated add-on, so removal is always two steps:

  1. Deactivate Paddle Payment Gateway (see Uninstallation).
  2. Click Remove on its card and confirm the prompt.

The admin panel then, in one pass:

  • runs the add-on's uninstall hook, if it ships one, while its code is still on disk;
  • revokes the permissions declared in its addon.json;
  • rolls back its migrations (this drops its database tables and every row they hold) and purges its entries from the migrations table, so a later reinstall migrates from scratch;
  • deletes its published assets: public/addons/paddle/, public/vendor/paddle/ and storage/app/public/addons/paddle/;
  • deletes the add-on directory extensions/addons/paddle/;
  • deletes its row in the addons table (the recorded purchase code goes with it) and clears the application cache.
This cannot be undone. Back up your database before removing an add-on whose data you may still need: installing it again later creates empty tables, not your old content.

Removal is refused, with an explanatory message and before anything is destroyed, when the add-on is still active, when another active add-on depends on it, or when the web server (PHP) user cannot delete extensions/addons/paddle/. In that last case, give that user write permission on the directory and on its parent, then try again.

Deleting the folder over FTP or SSH is not equivalent: the add-on's tables, its entries in the migrations table and its addons row are all left behind, and its card stays in the list. Use Remove in the admin panel instead.

Troubleshooting

Paddle checkout overlay does not open

  • Ensure the paddle_client_token is configured in admin settings.
  • Check the browser console for JavaScript errors. A missing or invalid client token will log Paddle client token not configured.
  • Verify that the token matches the environment: sandbox tokens start with test_, production tokens start with live_.
  • Make sure Paddle.js is loading: check that https://cdn.paddle.com/paddle/v2/paddle.js is not blocked by your Content Security Policy or ad blocker.

Payment not confirming: order stays in “pending”

  • Check that your webhook URL (/paddle/webhook) is accessible from the internet. Paddle must be able to send POST requests to it.
  • Verify the paddle_webhook_secret is set and matches the secret in your Paddle Dashboard.
  • Check storage/logs/laravel.log for webhook-related errors.
  • In the Paddle Dashboard under Notifications, check if webhook deliveries are failing.

Webhook returns 400: “Invalid signature”

  • The paddle_webhook_secret in admin settings does not match the secret from Paddle Dashboard.
  • If you recently rotated the webhook secret, update it in admin settings.
  • Ensure the raw request body is not being modified by middleware before signature verification.

Transaction creation fails: “Paddle transaction creation failed”

  • Check that the paddle_api_key is correct and has not been revoked.
  • Ensure the paddle_seller_id is set.
  • Verify the currency code is valid and supported by Paddle.
  • Check server logs for the full error message from the Paddle API.
  • If using sandbox, make sure the API key is a sandbox key (not production).

Refund fails: “Paddle refund failed”

  • Check that the original transaction was completed (only completed transactions can be refunded).
  • Verify the refund amount does not exceed the original transaction amount.
  • Some Paddle adjustments require manual approval: check the Paddle Dashboard.
  • Check server logs for the detailed Paddle API error.

Gateway not appearing on checkout page

  • Ensure the Paddle add-on is activated in Admin → Add-ons.
  • Ensure both paddle_api_key and paddle_seller_id are configured (isAvailable() checks both).
  • Verify the Shop add-on is active (Paddle depends on it).

Customer not linked to Paddle customer ID

  • Customer linking happens automatically during transaction creation when the payable has a user ID.
  • Guest checkouts (no authenticated user) will not create a paddle_customers record.
  • Check server logs for warnings from linkCustomer().

Settings not saving: credentials appear empty after save

  • Credentials are encrypted before storage and decrypted for display. If the APP_KEY has changed, previously encrypted values cannot be decrypted.
  • Re-enter all credentials after an APP_KEY change.
  • Check that the settings table is writable.

Paddle Payment Gateway v1.0.0: Part of the Larapen CMS platform.

© BeDigit. All rights reserved.

Was this article helpful?

Thank you for your feedback!

Still need help? Create a support ticket

Create a Ticket
Apr 07, 2026