Skip to documentation

    Paddle Payment Gateway

    Payment Gateway
    v1.0.43
    Ready
    See archive-bound Requirements

    Accept payments globally with Paddle as your Merchant of Record. Automatic tax calculation and remittance across 200+ countries and territories. One-time payments via hosted checkout with automatic course enrolment.

    Merchant of Record for Global Tax Compliance

    Paddle acts as your Merchant of Record (MoR), meaning Paddle is the legal seller of your courses. This eliminates the need for you to register for VAT/GST in every country, calculate local tax rates, collect taxes, or file returns. Paddle handles all of this automatically across 200+ countries and territories, so you can sell globally from day one without worrying about international tax obligations.

    30 Currencies

    USD, EUR, GBP, AUD, CAD, JPY, NZD, SGD & more

    Automatic Tax

    VAT, GST, sales tax calculated per buyer location

    HMAC Webhooks

    SHA-256 signature verification on every event

    Refund Handling

    Auto-unenrol on refund or chargeback

    In-Moodle™ Reports

    Transaction history + CSV export

    Idempotent Webhooks

    Duplicate event protection built-in

    How It Works

    1. Student clicks "Enrol" on a course with Paddle payment enabled
    2. Plugin creates a Paddle transaction via the Billing API v2, embedding course metadata (component, paymentarea, itemid, userid) in custom_data
    3. Student is redirected to Paddle checkout — a hosted overlay powered by Paddle.js v2 that handles card entry, Apple Pay, Google Pay, and local payment methods
    4. Paddle calculates tax automatically based on the student's IP address and billing country, and processes the payment
    5. Paddle sends a transaction.completed webhook to your Moodle™ site at /payment/gateway/paddle/webhook.php
    6. Plugin verifies the HMAC-SHA256 signature, checks idempotency (skips duplicate events), extracts the custom_data, and calls Moodle™'s deliver_order()
    7. Student is enrolled automatically via Moodle™'s core Payment API — no manual intervention required

    Refund flow: When a refund or chargeback is issued in the Paddle Dashboard, Paddle sends an adjustment.created or adjustment.updated webhook. The plugin verifies the signature, finds the original transaction, updates its status to "refunded" or "chargeback", and automatically unenrols the student from the course.

    Understanding Moodle™'s Payment System

    Before installing Paddle, it helps to understand how Moodle™ handles payments. Moodle™ has a built-in payment system(introduced in Moodle™ 3.11 and refined in Moodle™ 4.0+) that consists of three layers working together. If you already know how Moodle™ payments work, you can skip ahead to the Installation section below.

    Layer 1: Payment Gateways

    A payment gateway is a plugin that connects Moodle™ to an external payment processor (such as PayPal, Stripe, or Paddle). Each gateway is a separate plugin that lives in the /payment/gateway/ directory of your Moodle™ installation. When you install the Paddle plugin, it appears alongside any other gateways you have installed.

    You manage which gateways are available at Site Administration > Plugins > Payment Gateways > Manage payment gateways. Here you can enable or disable each gateway, reorder them, and access their settings. Installing a gateway does not mean students can pay with it yet — you still need to create a Payment Account and link it to a course.

    Layer 2: Payment Accounts

    A payment account is a container that holds your credentials for one or more payment gateways. Think of it as a "wallet" where you configure how your site accepts money. You can have multiple payment accounts — for example, one for live payments and one for sandbox/testing, or different accounts for different departments.

    You manage payment accounts at Site Administration > General > Payment accounts (or Site Administration > Payments > Payment accountsdepending on your Moodle™ version). When you create or edit a payment account, you will see a list of all enabled payment gateways. Click a gateway name (e.g. "Paddle") to open its configuration form where you enter your API credentials.

    Key concept: Payment gateway credentials (API keys, secrets, etc.) are configured per payment account, not as global plugin settings. This is how all Moodle™ payment gateways work — it allows you to have different credentials for different contexts (e.g. sandbox vs. live, or different Paddle accounts for different business units).

    Layer 3: Enrolment on Payment

    Enrolment on payment is a built-in Moodle™ enrolment method that connects courses to payment accounts. When you add this enrolment method to a course, students see a payment button on the course page. After paying successfully, they are automatically enrolled — no manual approval needed.

    Each course can have its own enrolment-on-payment configuration with its own price, currency, and payment account. This means different courses can charge different amounts and even use different payment gateways. The student sees the available payment options (based on which gateways are configured on the selected payment account) and chooses how to pay.

    You add this method in each course under Course Administration > Users > Enrolment methods(or click the Participants tab and then Enrolment methods in Moodle™ 4.x). You set the cost, currency, enrolment duration (optional), and which payment account to use.

    How it all fits together

    1. You install one or more payment gateway plugins (Paddle, Stripe, PayPal, etc.) and enable them
    2. You create a payment account and configure your credentials for each gateway you want to use
    3. You add Enrolment on payment to a course, set the price, and select the payment account
    4. Students visit the course, see the price, click Pay, choose a payment method, complete checkout, and are enrolled automatically

    Installation

    1. Download the plugin ZIP from the button above
    2. Upload or extract the ZIP so the paddle folder sits at /payment/gateway/paddle/ in your Moodle™ directory. You can either:
      • Use Site Administration > Plugins > Install plugins and upload the ZIP directly
      • Or manually extract the ZIP via FTP/SSH so the path is payment/gateway/paddle/version.php
    3. Trigger the database upgrade — visit Site Administration > Notifications. Moodle™ will detect the new plugin and create the required database tables (transactions, webhook events, price mappings)
    4. Enable the gateway — go to Site Administration > Plugins > Payment Gateways > Manage payment gateways. Find "Paddle Payment Gateway" in the list and make sure it is enabled (the eye icon should be open/visible). You should also see a "Settings" link next to it

    After installation: The plugin is installed but not yet usable. You need to complete the configuration steps below — set up your Paddle Dashboard, create a Payment Account in Moodle™, enter your credentials, and then add "Enrolment on payment" to your courses.

    Configuration Guide

    Step 1: Create Your Paddle Account

    • Sign up at paddle.com and complete business verification
    • Paddle offers a Sandbox environment for testing — you can sign up for a sandbox account at sandbox-vendors.paddle.com to test everything before going live
    • Go to Developer Tools > Authentication to find your API Key (server-side) and Client-Side Token

    Step 2: Create a Product and Price in Paddle

    • In the Paddle Dashboard, go to Catalog > Products and create a product (e.g. "Course Enrolment")
    • Under the product, click Add Price — set billing type to One-time, choose your currency, and enter the amount
    • Copy the Price ID (starts with pri_) — this will be your Default Price ID in Moodle™
    • If you have courses at different price points, create additional prices now — you will map them to individual courses later using Price Mapping

    Step 3: Set Up Webhooks in Paddle

    Webhooks are how Paddle notifies your Moodle™ site when a payment is completed, fails, or is refunded. Without webhooks, students will not be enrolled after paying.

    • Go to Developer Tools > Notifications in your Paddle Dashboard
    • Click New destination and enter a description (e.g. "Moodle™ LMS")
    • Set the URL to your Moodle™ site's webhook endpoint:https://yourmoodle.com/payment/gateway/paddle/webhook.php
    • Subscribe to these four events:
      • transaction.completed — triggers student enrolment
      • transaction.payment_failed — logs failed payments
      • adjustment.created — handles refunds
      • adjustment.updated — handles chargebacks
    • Save and copy the Webhook Signing Secret — you will need this in the next step

    Step 4: Approve Your Domain in Paddle

    • In your Paddle Dashboard, go to Checkout > Checkout settings
    • Add your Moodle™ domain (e.g. moodle.yoursite.com) to the Approved domains list
    • This is required — without it, the Paddle checkout overlay will refuse to load on your Moodle™ pages and students will see a blank popup

    Step 5: Create a Payment Account in Moodle™

    Now switch to your Moodle™ site. You need to create a Payment Account and configure Paddle's credentials inside it.

    1. Go to Site Administration > General > Payment accounts (some Moodle™ versions show this under Site Administration > Payments > Payment accounts)
    2. Click Create payment account
    3. Give it a name that describes its purpose — for example:
      • "Live Payments" for your production Paddle account
      • "Sandbox Testing" for your Paddle sandbox account
      • "Department A Payments" if different departments have different Paddle accounts
    4. Save the payment account
    5. You will now see a list of available payment gateways under the account. Find Paddle and click its name to open the configuration form
    6. Fill in the Paddle credentials:
      • API Key — your server-side API key from Paddle Dashboard > Developer Tools > Authentication
      • Client-Side Token — used by Paddle.js in the student's browser to render the checkout overlay
      • Webhook Signing Secret — the secret you copied in Step 3 above
      • Environment — select Sandbox for testing or Live for real payments
      • Default Price ID — the Price ID you created in Step 2 (e.g. pri_01jm5kp7...). All courses will use this price unless you set up per-course Price Mapping
    7. Click Save changes

    Multiple accounts: You can create as many payment accounts as you need. A common pattern is to have a "Sandbox" account for testing and a "Live" account for production. Each account has its own set of Paddle credentials. You choose which account to use when setting up course enrolment.

    Step 6: Add "Enrolment on Payment" to a Course

    This is where you connect your course to the payment account so students can pay and enrol. You need to do this for each course you want to sell.

    1. Navigate to the course you want to add paid enrolment to
    2. Click Participants in the course navigation (or go to Course Administration > Users > Enrolment methods)
    3. Click the Enrolment methods link (in Moodle™ 4.x, this is a dropdown or tab on the Participants page)
    4. From the "Add method" dropdown, select Enrolment on payment
    5. Configure the enrolment method:
      • Payment account — select the payment account you created in Step 5 (e.g. "Live Payments")
      • Enrolment fee — enter the cost that will be displayed to students (e.g. 99.00). Note: the actual amount charged is determined by the Paddle Price ID, not this field — this is for display purposes
      • Currency — select the currency to display (e.g. AUD, USD). Again, the actual currency comes from the Paddle Price ID
      • Enrol period (optional) — how long the enrolment lasts. Leave empty for unlimited access
      • Role — usually "Student". This is the role assigned after successful payment
    6. Click Add method

    What students see: When a student visits the course page, they will see the course price and a payment button. Clicking it opens the Paddle checkout overlay where they can pay with their card, Apple Pay, Google Pay, or other local payment methods. After successful payment, they are enrolled immediately and can start learning straight away.

    Step 7: Test the Full Flow

    Before going live, always test the complete payment flow using Paddle's Sandbox environment.

    1. Create a payment account with your Sandbox Paddle credentials and set Environment to "Sandbox"
    2. Add "Enrolment on payment" to a test course using this sandbox payment account
    3. Log in as a test student (or use a different browser/incognito window)
    4. Visit the test course, click Pay, and complete checkout using Paddle's test card: 4242 4242 4242 4242 with any future expiry and any CVC
    5. Verify the student is enrolled in the course after payment
    6. Check the Transaction Reports page to confirm the transaction was recorded
    7. Check the Webhook Events tab to confirm the webhook was received and processed successfully
    8. Once everything works, switch to your Live payment account for production courses

    Common Price ID Mistakes

    If your Price ID doesn't appear to be working, check these three issues first — they account for the vast majority of problems:

    1

    Entered in the wrong place

    The Price ID is entered per payment account, not in any global Paddle plugin settings page. The correct path is:
    Site Administration → Payments → Payment accounts → click your account name → click "Paddle" in the gateway list → Default Price ID field

    There is no standalone global settings page for the Paddle gateway — all credentials including the Price ID live inside the payment account.

    2

    Used a Product ID instead of a Price ID

    Paddle has two different IDs that look similar — do not mix them up:

    • pri_... — Price ID. This is what goes in the Default Price ID field. Found under Catalog → Products → open a product → copy the Price ID from the price list.
    • pro_... — Product ID. This goes in the separate Default Product ID field (used only for dynamic pricing). Do not paste this into the Price ID field.
    3

    A per-course price mapping is silently overriding it

    If a course-specific mapping exists in the Paddle Price Mapping page, it takes priority over the Default Price ID — even if that mapping is wrong or outdated. Check /payment/gateway/paddle/admin/pricemap.php to see if there is an active mapping for the course you are testing. If there is, either update the Price ID on that mapping row or disable/delete it to fall back to the default.

    Per-Course Price Mapping

    Quick access via AI Dashboard QuickLinks block (v2.3.34+):

    If you have the AI Dashboard QuickLinks block installed, the Price Mapping page is now one click away — open the Reports dropdown in the QuickLinks block and click Paddle Payment Gateway. It links directly to the Price Mapping admin page.

    Or navigate directly: /payment/gateway/paddle/admin/pricemap.php

    What is Price Mapping?

    By default, all courses use the same Default Price ID from your payment account gateway configuration. Price mapping lets you override this on a per-course basis, so each course can have its own Paddle Price with a different amount, currency, or product. In Paddle, every price is a separate object with a unique ID (e.g. pri_01abc123).

    Lookup priority: (1) Course-specific price mapping — checked first. (2) Default Price ID from gateway settings — fallback if no mapping exists. (3) Default Product ID — last resort for dynamic inline pricing. The first non-empty value wins.

    When do I need this?

    • You have courses at different price points (e.g. $99 for a short course, $499 for a diploma)
    • You want to charge in different currencies for different courses
    • You have promotional or discounted pricing for specific courses
    • You want to track revenue per course as separate products in your Paddle Dashboard

    If all your courses cost the same amount, you do not need price mapping — the Default Price ID covers everything.

    Step-by-Step: Get a Price ID from Paddle

    1. Log in to your Paddle Dashboard (or Sandbox Dashboard for testing)
    2. Go to Catalog > Products and open an existing product or click New product
    3. Under the product, click Add Price
    4. Set Billing type to One-time, choose your currency (e.g. AUD), and enter the amount (e.g. 499.00)
    5. Optionally give the price a name (e.g. "Certificate IV in WHS — AUD")
    6. Click Save
    7. The new price now appears in the product's price list. Copy the Price ID — it starts with pri_ (e.g. pri_01jm5kp7abc123xyz)
    8. Repeat for each course that needs a different price

    Step-by-Step: Set the Default Price ID (applies to all courses)

    1. Go to Site Administration > Payments > Payment accounts
    2. Click your payment account name to edit it
    3. Find Paddle in the list of gateways and click its name to open the configuration form
    4. In the Default Price ID field, paste the pri_... value you copied from Paddle
    5. Click Save changes

    This price will be used for every course that does not have its own specific price mapping.

    Step-by-Step: Map a Price ID to a Specific Course

    1. Open the Price Mapping page using one of these methods:
      • QuickLinks block (v2.3.34+): Reports dropdown → Paddle Payment Gateway
      • Direct URL: https://yourmoodle.com/payment/gateway/paddle/admin/pricemap.php
    2. Click Add Price Mapping
    3. Select the Moodle™ course from the dropdown — this is the course that will use its own price
    4. Paste the Paddle Price ID (pri_...) for that course into the Paddle Price ID field
    5. Enter the Amount and Currency — these are for your own reference only (the actual charge comes from the Price ID in Paddle)
    6. Optionally enter a Description (e.g. "Cert IV WHS — $499 AUD")
    7. Click Save

    When a student enrols in that course, the plugin automatically uses this course-specific Price ID instead of the default one. Courses without a mapping continue to use the Default Price ID. You can disable a mapping temporarily without deleting it using the Enable/Disable toggle.

    Field Descriptions

    • Course — The Moodle™ course this price mapping applies to. Each course can have at most one active price mapping.
    • Paddle Price ID — The unique price identifier from your Paddle Dashboard (e.g. pri_01jm5kp7...). This is what Paddle uses to determine the actual charge amount and currency.
    • Amount — A display-only field for your own reference (e.g. 99.00). This does not affect the charge — Paddle always uses the amount configured on the Price ID in your Paddle Dashboard.
    • Currency — A display-only currency code for your reference (e.g. AUD, USD). The actual currency is determined by the Paddle Price ID.
    • Description — An optional note for your records (e.g. "Certificate IV in WHS", "Early bird pricing").

    Important: The Amount and Currency fields on the Price Mapping page are for your own reference only. The actual amount charged to the student is always determined by the Price ID in your Paddle Dashboard. If you change a price amount in Paddle, you do not need to update this page — just make sure the Price ID stays the same. You can also enable/disable a mapping without deleting it using the Enable/Disable button.

    Transaction Reports

    The plugin includes a built-in reporting dashboard. Access it directly at:

    https://yourmoodle.com/payment/gateway/paddle/admin/reports.php

    It has two tabs that give you full visibility into payment activity and webhook processing.

    Tab 1: Transactions

    A paginated table of every payment processed through Paddle. Each row shows the details of a single student payment.

    • Transaction ID — The unique Paddle transaction identifier (e.g. txn_01abc...). Links directly to the transaction in your Paddle Dashboard.
    • User — The Moodle™ student's full name and email address who made the payment.
    • Amount + Currency — The payment amount and currency code (e.g. 99.00 AUD, 49.00 USD).
    • Tax — The tax amount calculated by Paddle (VAT, GST, or sales tax depending on the student's location).
    • Status — Colour-coded badge showing the current state: Completed, Pending, Failed, Refunded, or Chargeback.
    • Date — When the transaction was created, displayed in your Moodle™ site's timezone.

    Search and filter: Use the search box to find transactions by Transaction ID, student email, or student name. Use the status dropdown to filter by Completed, Pending, Failed, Refunded, or Chargeback. Click Export CSV to download all matching transactions as a spreadsheet — includes Transaction ID, Customer ID, User, Email, Amount, Currency, Tax, Status, Component, Item ID, and Date.

    Tab 2: Webhook Events

    An audit trail of every webhook event received from Paddle. This is invaluable for debugging payment issues and confirming that your webhook endpoint is working correctly.

    • Event ID — The unique Paddle event identifier (e.g. evt_01abc...). Each webhook delivery has a unique ID.
    • Transaction ID — The associated Paddle transaction, or a dash if the event is not transaction-specific.
    • Event Type — The Paddle event name, such as transaction.completed, transaction.payment_failed, adjustment.created, or adjustment.updated.
    • Result — Colour-coded badge: Success (processed and applied), Error (processing failed — check your logs), or Skipped (duplicate event already processed — idempotency protection).
    • Date — When the webhook event was received and processed.

    Debugging tip: If a student paid but was not enrolled, check the Webhook Events tab first. Look for a transaction.completed event with a "Success" result. If you see "Error", check your Moodle™ logs for details. If you see no events at all, verify that your webhook URL and signing secret are configured correctly in both your Paddle Dashboard and Moodle™ payment account settings.

    Frequently Asked Questions

    What does "Merchant of Record" mean?

    Paddle is the legal seller of your courses. They appear on your students' credit card statements, handle all tax obligations (VAT, GST, sales tax), issue invoices/receipts, and manage refund/chargeback processes. You receive payouts from Paddle minus their transaction fee. Check paddle.com/pricing for current rates.

    Do I need to register for VAT/GST in every country?

    No. Because Paddle is the Merchant of Record, they handle all tax registration, calculation, collection, and remittance in every jurisdiction. This is the key benefit over payment processors like Stripe or PayPal where you remain the merchant and must handle tax compliance yourself.

    What appears on the student's bank statement?

    "PADDLE.NET" or "PADDLE.COM" will appear on the student's statement, as Paddle is the legal seller. Students automatically receive a tax-compliant receipt/invoice from Paddle with full tax details.

    How are refunds handled?

    Issue refunds through the Paddle Dashboard (or via Paddle's API). The plugin receives an adjustment.created webhook, verifies the signature, updates the transaction status, and automatically unenrols the student from the course. Chargebacks raised by the student's bank are handled the same way via the adjustment.updated webhook.

    Does this work with Moodle™'s "Enrolment on Payment"?

    Yes. This plugin implements Moodle™'s core Payment Gateway API (core_payment\gateway). It works with the standard "Enrolment on payment" enrolment method. You configure it through Payment Accounts, just like any other Moodle™ payment gateway. See the archive-bound Requirements shown near the top for the supported platform versions of this release.

    Where do I configure the API credentials?

    Paddle credentials are configured per Payment Account, not as global plugin settings. Go to Site Administration > Payments > Payment accounts, select or create an account, then click the Paddle gateway to enter your API Key, Client Token, Webhook Secret, Default Price ID, and Environment. This is how all Moodle™ payment gateways work — each payment account can have different credentials (e.g. sandbox account for testing, live account for production).

    Can I have multiple Payment Accounts with Paddle?

    Yes. You can create separate payment accounts (e.g. one for sandbox testing, one for live payments) and assign different payment accounts to different courses. Each account has its own set of Paddle credentials and Default Price ID.

    What currencies are supported?

    30 currencies: USD, EUR, GBP, AUD, CAD, NZD, SGD, HKD, JPY, CHF, SEK, NOK, DKK, PLN, CZK, HUF, BRL, MXN, INR, THB, TWD, KRW, ZAR, ARS, CLP, COP, PEN, CNY, RUB, TRY.

    What payment methods do students see?

    Paddle's checkout automatically shows relevant payment methods based on the student's location. This can include credit/debit cards, Apple Pay, Google Pay, PayPal, iDEAL, Bancontact, and other local payment methods — all handled by Paddle with no extra configuration needed.

    What happens if the webhook fails?

    Paddle automatically retries failed webhooks with exponential backoff. The plugin stores every webhook event in an audit table with idempotency protection, so duplicate deliveries are safely ignored. You can view the webhook event log at Site Administration > Payment Gateways > Paddle Transaction Reports > Webhook Events tab.

    Important Notes

    • One-time payments only — this plugin does not support Paddle subscriptions or recurring billing
    • Webhook endpoint must be publicly accessible — Paddle's servers need to reach /payment/gateway/paddle/webhook.php on your Moodle™ site (no authentication required, signature verification handles security)
    • Your domain must be approved in Paddle Dashboard > Checkout > Checkout settings > Approved domains — without this, the checkout overlay will not load
    • Test with Sandbox first — create a Sandbox account in Paddle, configure a test Payment Account in Moodle™ with sandbox credentials, and verify the full flow before switching to Live
    • Subscribe to all four webhook events — the plugin needs transaction.completed, transaction.payment_failed, adjustment.created, and adjustment.updated to handle the full payment lifecycle