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.
USD, EUR, GBP, AUD, CAD, JPY, NZD, SGD & more
VAT, GST, sales tax calculated per buyer location
SHA-256 signature verification on every event
Auto-unenrol on refund or chargeback
Transaction history + CSV export
Duplicate event protection built-in
transaction.completed webhook to your Moodle™ site at /payment/gateway/paddle/webhook.phpdeliver_order()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.
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.
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.
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).
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.
paddle folder sits at /payment/gateway/paddle/ in your Moodle™ directory. You can either:payment/gateway/paddle/version.phpAfter 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.
pri_) — this will be your Default Price ID in Moodle™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.
https://yourmoodle.com/payment/gateway/paddle/webhook.phptransaction.completed — triggers student enrolmenttransaction.payment_failed — logs failed paymentsadjustment.created — handles refundsadjustment.updated — handles chargebacksmoodle.yoursite.com) to the Approved domains listNow switch to your Moodle™ site. You need to create a Payment Account and configure Paddle's credentials inside it.
pri_01jm5kp7...). All courses will use this price unless you set up per-course Price MappingMultiple 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.
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.
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.
Before going live, always test the complete payment flow using Paddle's Sandbox environment.
4242 4242 4242 4242 with any future expiry and any CVCIf your Price ID doesn't appear to be working, check these three issues first — they account for the vast majority of problems:
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.
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.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.
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
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.
If all your courses cost the same amount, you do not need price mapping — the Default Price ID covers everything.
pri_ (e.g. pri_01jm5kp7abc123xyz)pri_... value you copied from PaddleThis price will be used for every course that does not have its own specific price mapping.
https://yourmoodle.com/payment/gateway/paddle/admin/pricemap.phppri_...) for that course into the Paddle Price ID fieldWhen 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.
pri_01jm5kp7...). This is what Paddle uses to determine the actual charge amount and currency.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.
The plugin includes a built-in reporting dashboard. Access it directly at:
https://yourmoodle.com/payment/gateway/paddle/admin/reports.phpIt has two tabs that give you full visibility into payment activity and webhook processing.
A paginated table of every payment processed through Paddle. Each row shows the details of a single student payment.
txn_01abc...). Links directly to the transaction in your Paddle Dashboard.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.
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.
evt_01abc...). Each webhook delivery has a unique ID.transaction.completed, transaction.payment_failed, adjustment.created, or adjustment.updated.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.
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.
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.
"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.
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.
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.
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).
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.
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.
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.
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.
/payment/gateway/paddle/webhook.php on your Moodle™ site (no authentication required, signature verification handles security)transaction.completed, transaction.payment_failed, adjustment.created, and adjustment.updated to handle the full payment lifecycle