The LifterLMS REST API and Webhooks: A Practical Introduction
Create an API key, make your first authenticated request to /wp-json/llms/v1, and set up a signed webhook. Then choose between webhooks, polling and…
Read articleEvery serious LifterLMS site should have a staging copy for testing updates and new features. But a copy of a membership site is also a copy of every active subscription, every scheduled renewal and, often, the live payment gateway keys. Left alone, that copy could try to bill your students a second time.
LifterLMS has a built-in safeguard for this. This guide explains how the LifterLMS staging site detection and URL lock work, how to make staging safe by default with two lines in wp-config.php, what else to switch off on the copy, and how to deploy changes without overwriting live orders. It is for developers and site owners who run subscriptions or payment plans, and it was checked against the LifterLMS 10.3 source.
A LifterLMS recurring payment is a scheduled task. When an order's next payment date arrives, the scheduler runs, LifterLMS asks the payment gateway add-on to charge the saved payment method, and a transaction is recorded.
A typical staging copy is a clone of the live database. It contains the same orders, the same scheduled renewal tasks in the Action Scheduler tables, and the same gateway settings. If the clone ran those renewals with live API keys, the customer would be charged once by the live site and once by staging. Refunds and apologies follow.
When LifterLMS is installed, it stores your site's URL in the llms_site_url option with a marker, _[llms_site_url]_, inserted into the middle of it. The marker exists so that migration and search-replace tools, which rewrite every copy of the old domain, do not recognise and update this value.
On every check, LifterLMS removes the marker and compares the stored URL with the site's current URL. If they differ, the site is treated as a clone. From that moment:
When a renewal falls due on a site in this state, it is skipped and the order gets a note: "Recurring charge skipped because recurring payments are disabled in staging mode." Upcoming payment reminder notifications are suppressed too. You can confirm the current state under LifterLMS > Settings > General, where the Features section reads Automatic Recurring Payments: Enabled or Disabled.
The LifterLMS staging documentation describes the same notice and recommends gateway test mode and disabling email on staging, both covered below.
Relying on URL detection alone has two weak spots: a staging copy that uses the same domain (some hosts swap environments behind one URL), and someone clicking the wrong button in the notice. Remove both risks by declaring the site a clone in its wp-config.php:
// Staging and development copies only. Never on production.
define( 'LLMS_SITE_IS_CLONE', true );
define( 'WP_ENVIRONMENT_TYPE', 'staging' );
LLMS_SITE_IS_CLONE set to true makes LifterLMS treat the site as a clone whatever its URL, and forces automatic recurring payments off. With the constant defined, the staging notice and the reset tool are no longer offered, so nobody can switch payments back on from the dashboard.
WP_ENVIRONMENT_TYPE is a WordPress core setting, read through wp_get_environment_type(). LifterLMS does not use it for payments, but many other plugins, hosts and email tools do, so it is worth setting.
LifterLMS also reads LLMS_SITE_FEATURE_RECURRING_PAYMENTS, which forces recurring payments on or off directly. Avoid setting it to true on production: if wp-config.php is ever copied to a clone, the safeguard goes with it. Keep the explicit constant on the non-production side.
Recurring charges are the most expensive mistake, but not the only one. A cloned site will happily email students, call your CRM and accept payments.
The golden rule: move code and configuration from staging to live, never the database. While you were testing, the live site took new orders, renewals, enrollments, quiz attempts and progress. Pushing the staging database over live deletes all of that.
There is a second, quieter problem. The staging database holds staging's URL lock, or none at all. Push it to production and LifterLMS on the live site may decide it is now the clone, and renewals stop without anyone noticing until revenue drops.
If you truly must merge database changes, do it during a maintenance window with the live site closed to purchases, and exclude orders, transactions, user data and the scheduler tables. That is a job to plan carefully, not to improvise.
The same detection fires when the real site changes address, for example after a rebrand or a move from a temporary domain. LifterLMS cannot tell a new production domain from a staging copy, so it pauses renewals and shows the notice.
If you clicked the wrong button, go to LifterLMS > Status > Tools & Utilities and run Reset Automatic Payments Status. It clears the stored lock and runs the check again, which brings the notice back so you can choose properly. For a larger move, our LifterLMS migration guide covers the full checklist.
LLMS_SITE_IS_CLONE.Yes, when the staging site's URL differs from the stored URL lock, automatic recurring payments are treated as disabled. Manual checkouts still work, which is why gateways should be in test mode as well.
They are skipped and the order gets a note saying the recurring charge was skipped because recurring payments are disabled in staging mode. The live site's copy of the order is unaffected.
Look at the Features section under LifterLMS > Settings > General, which shows Automatic Recurring Payments as Enabled or Disabled. A Staging label on the Orders menu also means they are off.
It is not required, but it removes the risk of a same-domain staging setup or a wrong click. It is a two-line change worth making on every non-production copy.
Add LLMS_SITE_IS_CLONE to your staging wp-config.php today, switch its gateways to test mode and trap its email. Then write down your deployment steps so the next update follows them.
If you want a proper development workflow with version control, automated deployments and a staging environment that refreshes safely, our LifterLMS development team sets these up, and our support plans handle updates for you. Tell us how your site is hosted and we will suggest a setup.
Create an API key, make your first authenticated request to /wp-json/llms/v1, and set up a signed webhook. Then choose between webhooks, polling and…
Read article
LifterLMS development is WordPress development with extra rules: content lives in post types, student activity in custom tables, changes go through…
Read article
Before you commission a custom LifterLMS plugin, rule out a setting, an add-on and a snippet. Here are the signs you have outgrown them and what a…
Read articleSkip the trial and error. Get specialist LifterLMS help and ship faster.