Support demo for ticket 1544414. This page is served by a demo plugin (sc-license-gate-demo) that bundles the official SureCart WordPress SDK and locks its premium features until a valid license is activated for this site. Everything below is live: the status table is calculated on every page load.
1. Live license status (from the demo plugin)
| Premium features | ✅ UNLOCKED |
| Reason | license_valid_and_activated |
| Answer came from | cache (no API call this page load) |
| Last checked with API | 2026-10-10 13:32:53 GMT+0000 |
| Cache valid until | 2026-10-11 01:32:53 GMT+0000 |
| Stored license key | 60d4300d…7d84 |
| Stored activation id | 952a642f-1039-49c2-bb2a-7e5f9387a8a7 |
| Last known good | 2026-10-10 13:32:53 GMT+0000 |
| Cache TTL / grace | 12 h / 3 days |
| SDK version | 1.0.1 |
| SDK loaded from | /wp-content/plugins/suremembers/licensing-sdk/src/Client.php |
2. Premium feature (only renders while licensed)
3. Try it yourself (log in as webcare1 first)
- License Gate Demo > Manage License: click Deactivate License, reload this page. Status flips to LOCKED (reason
no_license_activated) with no API call. Re-activate with key60d4300d-a7f7-4c51-8700-c929867a7d84and it unlocks again. While unlocked, a premium submenu “Demo Import (premium)” exists under License Gate Demo; while locked it is not registered at all. - ?sclgd_refresh=1: bypasses the 12 hour cache and asks SureCart right now (“Answer came from: api”).
- ?sclgd_simulate_outage=1&sclgd_refresh=1: points the SDK at an unreachable host for one request. Status stays UNLOCKED with reason
api_unreachable_grace_periodbecause the site had a good answer within the last 3 days. Run ?sclgd_refresh=1 afterwards to go back to a real answer. - SureCart > Customers > Theme Buyer 1544414: open the purchase and click Revoke Access, then load ?sclgd_refresh=1 here. Status becomes LOCKED with
license_revoked_or_missingand the file disappears from the buyer’s dashboard. Restore Access puts both back. - Buyer login for the customer dashboard: user
themebuyer1544414/ThemeDemo!1544414(log out of webcare1 first, or use a private window). Downloads tab and Licenses. - Demo product page (test mode, Test Processor). Product: SC License Gate Demo Theme (Ticket 1544414), Licensing enabled, 1 activation, release ZIP attached as Current Release.
4. Download link security, measured on this store
All results below were produced on this store on 21 Sep 2026 against the file attached to the demo product (Secure Storage). Click the links to see the behaviour for yourself.
| # | What was tested | Link / call | Result |
|---|---|---|---|
| A | Link type used in the Product Access email (expose_for=86400) | 24 hour signed link | Works until 22 Sep 2026 09:31 UTC, then returns HTTP 410. Not tied to the customer: anyone holding it can download during those 24 hours. |
| B | Link type minted by the customer dashboard Download button (expose_for=60) | 60 second link (already expired) | Downloaded the byte-identical ZIP at 0 s. Re-fetched at 79 s: HTTP 410 “This download link has expired or the file has been removed.” |
| C | Same link with 2 characters of the signature changed | tampered link | HTTP 410 immediately. The blob id and expiry are inside the signed token, so nothing can be guessed or extended. |
| D | Guest (not logged in) calling the dashboard endpoint /wp-json/surecart/v1/customers/{id}/expose/{media} | curl, no cookie | HTTP 401. |
| E | Logged-in buyer calling it for their own customer id / for another customer id | curl with the buyer’s login cookie | Own id: 200 and a fresh 60 s link. Other id: HTTP 403 rest_forbidden. |
| F | Purchase revoked (Customers > purchase > Revoke Access) | same endpoint as B | HTTP 404 media.not_found; the Downloads tab lists 0 files; public license status = revoked. Restore Access reverses all three. |
| G | SDK release download: GET /v1/public/licenses/{key}/expose_current_release?activation_id=... | public token + real activation id | 200 with a 15 minute signed URL to the current release ZIP. With a wrong or missing activation id: HTTP 403 “You must pass a valid activation to expose the current release for this license.” |
5. The gate code (PART 2 of the demo plugin, copy into functions.php)
if ( ! defined( 'SCLGD_CACHE_TTL' ) ) {
define( 'SCLGD_CACHE_TTL', 12 * HOUR_IN_SECONDS ); // how long a fresh answer is trusted.
}
if ( ! defined( 'SCLGD_GRACE_PERIOD' ) ) {
define( 'SCLGD_GRACE_PERIOD', 3 * DAY_IN_SECONDS ); // how long an outage may last before locking.
}
/**
* Get the current license state for this site.
*
* @param bool $force Ignore the cache and ask SureCart now.
* @return array{unlocked:bool,reason:string,source:string,checked_at:int}
*/
function sclgd_license_state( $force = false ) {
static $memo = null;
if ( null !== $memo && ! $force ) {
return $memo;
}
$client = $GLOBALS['sclgd_client'] ?? null;
if ( ! $client ) {
return $memo = array( 'unlocked' => false, 'reason' => 'sdk_not_loaded', 'source' => 'local', 'checked_at' => time() );
}
$settings = $client->settings();
$license_key = (string) $settings->license_key;
$activation_id = (string) $settings->activation_id;
// 1. Nothing activated on this site -> locked, no network call.
if ( '' === $license_key || '' === $activation_id ) {
delete_transient( 'sclgd_license_state' );
return $memo = array( 'unlocked' => false, 'reason' => 'no_license_activated', 'source' => 'local', 'checked_at' => time() );
}
// 2. Cached answer, bound to the key + activation so a (de)activation invalidates it at once.
$cached = get_transient( 'sclgd_license_state' );
if ( ! $force && is_array( $cached ) && ( $cached['key'] ?? '' ) === $license_key && ( $cached['activation'] ?? '' ) === $activation_id ) {
$cached['source'] = 'cache';
return $memo = $cached;
}
// 3. Ask SureCart (two small public API calls, authenticated with the store's public token).
$valid = $client->license()->is_valid( $license_key ); // GET /v1/public/licenses/{key} -> true, false (revoked) or WP_Error
$activation = $client->activation()->get( $activation_id ); // GET /v1/public/activations/{id} -> object or WP_Error(not_found)
$active = ! is_wp_error( $activation ) && ! empty( $activation->id ); // same test the SDK's is_active() makes
// not_found = a definite "no" (key or activation really is gone). Any other error = could not reach the API.
$outage = false;
if ( is_wp_error( $valid ) ) {
$outage = 'not_found' !== $valid->get_error_code();
$valid = false;
}
if ( is_wp_error( $activation ) && 'not_found' !== $activation->get_error_code() ) {
$outage = true;
}
$state = array(
'key' => $license_key,
'activation' => $activation_id,
'checked_at' => time(),
'source' => 'api',
);
if ( $outage ) {
$last_good = (int) get_option( 'sclgd_license_last_good', 0 );
if ( $last_good && ( time() - $last_good ) < SCLGD_GRACE_PERIOD ) {
$state['unlocked'] = true;
$state['reason'] = 'api_unreachable_grace_period';
$state['source'] = 'grace';
set_transient( 'sclgd_license_state', $state, HOUR_IN_SECONDS ); // retry sooner during an outage.
return $memo = $state;
}
$state['unlocked'] = false;
$state['reason'] = 'api_unreachable_grace_expired';
set_transient( 'sclgd_license_state', $state, HOUR_IN_SECONDS );
return $memo = $state;
}
if ( true === $valid && $active ) {
$state['unlocked'] = true;
$state['reason'] = 'license_valid_and_activated';
update_option( 'sclgd_license_last_good', time(), false );
} else {
$state['unlocked'] = false;
$state['reason'] = ! $valid ? 'license_revoked_or_missing' : 'activation_removed_for_this_site';
}
set_transient( 'sclgd_license_state', $state, SCLGD_CACHE_TTL );
return $memo = $state;
}
/**
* Convenience helper for templates: true when premium features may run.
*/
function sclgd_is_unlocked() {
return (bool) ( sclgd_license_state()['unlocked'] ?? false );
}
Usage anywhere in the theme: if ( sclgd_is_unlocked() ) { /* premium code */ }. The demo plugin also shows three gates in PART 3: an admin notice while locked, an admin page that is only registered while unlocked, and a shortcode / enqueued script that only load while unlocked. Full source: /wp-content/plugins/sc-license-gate-demo/sc-license-gate-demo.php on this site.
Notes for support (cleanup)
Created for ticket 1544414: plugin sc-license-gate-demo (active), product f9e4f5b3 + price 3b505de8 + media ac46983a + download 4752d25c (current release), test order 3d3fb4fd / purchase 028d6ae4 / license 5bf9be92 (key 60d4300d…), WP user 88 themebuyer1544414 linked to test customer 80e570c4, this page (722). Option sclgd_license_last_good and transient sclgd_license_state.