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
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
- Go to your Paddle Dashboard → Developer Tools → Notifications.
- Create a new notification destination pointing to
https://yoursite.com/paddle/webhook. - Select these events:
transaction.completed,transaction.payment_failed,transaction.updated,adjustment.created,adjustment.updated. - 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 |
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?
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 |
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.
Getting Paddle Credentials
API Key & Client-Side Token
- Log in to your Paddle Dashboard (or Sandbox Dashboard for testing).
- Navigate to Developer Tools → Authentication.
- Copy your API Key (used for server-side API calls).
- Copy your Client-Side Token (used for Paddle.js initialization).
Seller ID
- In the Paddle Dashboard, your Seller ID is displayed in the account settings or URL.
- Copy the numeric ID and enter it in the admin settings.
Webhook Secret
- Go to Developer Tools → Notifications in the Paddle Dashboard.
- Create a new notification destination with the URL
https://yoursite.com/paddle/webhook. - Select the events to subscribe to (see Supported Events).
- Copy the generated webhook secret and enter it in admin settings.
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.
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) orproduction(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:
- 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.”
- Form submission (AJAX): When the customer submits the checkout form, JavaScript intercepts
the submission and sends it via
fetch()with JSON headers. - 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_transactionsrecord 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.
- Paddle.js overlay opens: The JavaScript opens the Paddle checkout overlay using
Paddle.Checkout.open()with the transaction ID. - Customer completes payment: The customer enters payment details within the Paddle overlay.
- Checkout completed callback: On
checkout.completed, the JavaScript redirects to the return URL (/paddle/return?_ptxn={transaction_id}). - Return URL confirmation: The
PaddleController::return()method callsconfirmPayment()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. - Webhook confirmation: Paddle also sends a
transaction.completedwebhook 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:
- Parse the
ts(timestamp) andh1(hash) values from the header. - Compute
HMAC-SHA256(timestamp + ':' + rawBody, webhookSecret). - Compare the computed hash with the received
h1value usinghash_equals(). - If the signature is invalid or the webhook secret is empty, return a
400response.
Refunds
The add-on supports full and partial refunds via the Paddle Adjustments API.
How Refunds Work
- An admin initiates a refund from the shop order management page.
- 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_transactionsrecord of typerefund.
- Paddle processes the refund and sends an
adjustment.createdwebhook. - The webhook handler updates the order status to
refundedif 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)
- Download the latest
.zipfile of this add-on. - Go to Admin panel → Add-ons and click the Upload button.
- Select or drag the
.zipfile into the upload area. - A confirmation prompt will show the current and new version numbers. Click Replace to proceed.
- 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.
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:
- Deactivate Paddle Payment Gateway (see Uninstallation).
- 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
migrationstable, so a later reinstall migrates from scratch; - deletes its published assets:
public/addons/paddle/,public/vendor/paddle/andstorage/app/public/addons/paddle/; - deletes the add-on directory
extensions/addons/paddle/; - deletes its row in the
addonstable (the recorded purchase code goes with it) and clears the application cache.
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_tokenis 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 withlive_. - Make sure Paddle.js is loading: check that
https://cdn.paddle.com/paddle/v2/paddle.jsis 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_secretis set and matches the secret in your Paddle Dashboard. - Check
storage/logs/laravel.logfor webhook-related errors. - In the Paddle Dashboard under Notifications, check if webhook deliveries are failing.
Webhook returns 400: “Invalid signature”
- The
paddle_webhook_secretin 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_keyis correct and has not been revoked. - Ensure the
paddle_seller_idis 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_keyandpaddle_seller_idare 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_customersrecord. - 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_KEYhas changed, previously encrypted values cannot be decrypted. - Re-enter all credentials after an
APP_KEYchange. - Check that the
settingstable is writable.
Paddle Payment Gateway v1.0.0: Part of the Larapen CMS platform.
© BeDigit. All rights reserved.