Test Mode Licence for a Private Product (Ticket 1540764)

Internal reference page for SureCart ticket #1540764. Everything below was reproduced and verified on this demo store (SureCart 4.7.3) on 15 September 2026.

Short answer

  • There is only one product catalogue. Products and Prices have no live/test flag at all. Only Checkouts, Invoices, Orders, Customers, Purchases, Subscriptions, Payment Intents and Payment Methods are split by mode. So a product can never “belong to the live catalogue” and be invisible to a Test Mode invoice.
  • The price was hidden because it was archived. In SureCart, “Archived” and “Not Purchasable” are the same flag, and a price carries its own archived flag separate from the product.
  • The invoice product picker filters out archived products and archived prices. Un archiving the product alone is not enough if the price is still archived.
  • To get a test mode invoice in WordPress you must use the caret beside Add New on the Invoices screen and pick New Test Invoice. The plain Add New button always makes a live invoice.
  • Product status Draft does not matter. A Draft product is selectable in the invoice picker, so the Suite never has to be published.

1. Why the QA price did not appear

Repro product on this store: “Sentinel Ridge Security Operations Suite (Ticket 1540764 Demo)”, licensing on, activation limit 1, one annual subscription price. Set exactly as the customer describes: Archived, Not Purchasable, Draft.

The Pricing box shows “Set up pricing for your product.” as if there were no price at all. The only clue is the small toggle underneath: Show 1 Archived Prices.

Product editor. Availability “Not Purchaseable”, page Draft, Pricing box appears empty, and a “Show 1 Archived Prices” toggle sits at the bottom.

Flip that toggle and the price is there, tagged Archived.

The same Pricing box after switching on “Show 1 Archived Prices”.

Meanwhile, in a Test Mode invoice, Add Product plus a search for “Sentinel” returns Nothing Found, even with the product un archived and Published.

Test Mode invoice, Add Product, search “Sentinel”: Nothing Found. Product un archived, price still archived.

2. The fix

Open the archived price row menu (the three dots) and choose Un-Archive.

Price row actions: Un-Archive, Delete, Duplicate.

The same search in the same Test Mode invoice now returns the price immediately. The product was still Draft at this point, so nothing had to be published.

Same Test Mode invoice picker after un archiving the price.

3. Supported flow for a Test Mode licence, verified end to end

  • Product: set Availability to Purchasable and un archive the QA price (Pricing box, “Show N Archived Prices”, three dots, Un-Archive). Leave the Product Page on Draft.
  • SureCart, Orders, Invoices. Click the small caret next to Add New and choose New Test Invoice. The new invoice carries a “Test Mode” badge. (The plain Add New button always creates a live invoice, so the caret matters.)
  • Add Product, search the Suite, pick the QA price.
  • Select a Customer, or create a QA customer inline. Test mode customers are separate from live ones.
  • Create Invoice. Turn off “Send an email to the customer” if you do not want the QA address to receive anything.
  • Mark the invoice Paid (invoice actions menu). No processor and no card is involved.
  • SureCart, Licenses. The licence is there with the activation limit from the product.
  • Put the product back to Not Purchasable and re archive the price. The licence keeps working (verified below).
SureCart, Invoices. The caret beside Add New holds “New Test Invoice”. This is the only UI route to a test mode invoice: the plain Add New button is hard coded to live mode.
Test Mode invoice #TEST-0131, status Paid, $1 every year, Test Mode badge.
SureCart, Licenses. The test mode licence sits in the same list as live ones.

4. What was actually verified

QA itemResult on this store
Licence issued from a test mode invoiceYes. Licence created with activation limit 1, on a product that was never published.
One successful activationPOST /v1/public/activations returned 200 with counted: true.
Activation limit 1 enforcedSecond activation on a different fingerprint returned 422 activation.exceeds_activation_limit “exceeds the activation limit of 1 for this license”.
Deactivation and transferDELETE the activation, then activate on a new fingerprint: 200. activations_count back to 1.
Protected current releaseGET /v1/public/licenses/{key}/expose_current_release with a valid activation_id: 200 plus a short lived signed file URL. Without activation_id: 403 “You must pass a valid activation to expose the current release for this license.”
Expiry behaviourRevoking the purchase flipped the licence to revoked and the release call to 403 “This license has been revoked.”
Licence survives re archivingWith the product back to Not Purchasable and the price archived again: licence retrieve 200, release 200, second activation still 422. Nothing breaks.

5. Three things that will bite the QA plan

a. Staging and local hostnames do not consume the activation limit

SureCart classifies a set of host patterns as development sites. Those activations are recorded with counted: false and are ignored by the limit. Verified live: with the limit already used up, an activation for https://staging.qa-sentinel.com was accepted with counted: false.

Patterns include localhost, 127.0.0.1, the .dev .test .local .staging .example .invalid endings, the dev. local. stage. staging. staging1. test. testing. prefixes, and many managed hosting staging domains (wpengine, kinsta, cloudways, pantheon, flywheel, instawp, tastewp, zipwp and more).

So test “activation limit 1” from ordinary production style hostnames, otherwise the limit will never trip and the test will look broken.

b. Expiry on a subscription price is not cleanly reversible

Revoking the purchase also cancels the subscription behind it. Re invoking the purchase restores the subscription, but on a manually paid test subscription it comes back as past_due, which revokes the purchase again. Verified on this store. Use a throwaway test purchase for the expiry test, or test expiry with “Revoke access after N days” on a one time price instead.

c. The licensing SDK has no offline grace period

The SureCart WordPress licensing SDK calls https://api.surecart.com on every validation. There is no cached verdict and no grace window in the SDK itself. If continuity during a provider outage matters, the vendor plugin has to implement its own tolerance around is_valid() and treat a transport error differently from a “revoked” verdict.

6. Artefacts left on this demo store

Product     21257dcd-0e64-4cbe-aae8-f182462c5b87   Sentinel Ridge Security Operations Suite (Ticket 1540764 Demo)
                                                   left ARCHIVED + DRAFT, licensing on, activation limit 1
Price       7817d48c-e367-40c2-b307-588afd59626b   QA TEST, Annual Subscription $1/year, left ARCHIVED
Download    8bd48962-4a00-4986-a754-90808a90fb98   set as Current Release
Customer    698cc12d-de52-459b-9c0a-ff8fa5c95632   Sentinel QA Tester, qa.sentinel.1540764@example.com (test mode)
Invoice 1   741a6922-847e-4a76-827c-fa860e64e650   TEST-0131 paid  -> licence d3237ef3... now REVOKED (expiry demo)
Invoice 2   22e9f98d-d8ee-4c47-b46c-334028e71066   TEST-0133 paid  -> licence e98c1c74-4ffb-4b08-a805-e98ca5144232 ACTIVE 1/1
Public token pt_75QUM1whXf5oKRRk1NfLoKnj

Re run the checks yourself:

curl -H "Authorization: Bearer pt_75QUM1whXf5oKRRk1NfLoKnj" \
  https://api.surecart.com/v1/public/licenses/e98c1c74-4ffb-4b08-a805-e98ca5144232

# second activation, expect 422 exceeds_activation_limit
curl -X POST -H "Authorization: Bearer pt_75QUM1whXf5oKRRk1NfLoKnj" \
  -H "Content-Type: application/json" \
  -d '{"activation":{"fingerprint":"https://qa-sentinel-two.com","name":"QA Two","license":"bf885405-9d41-473e-ad4a-6f720c91f015"}}' \
  https://api.surecart.com/v1/public/activations

Admin links: Licenses · Invoices · The repro product

7. Source of truth

  • Picker query and filters: packages/admin/invoices/modules/NewPrice.js (sends archived: false), packages/admin/components/PriceSelector.js, packages/admin/components/SelectPrice.js (drops every price where price.archived is true).
  • Archived equals Not Purchasable: packages/admin/products/components/Availability/index.js.
  • Archived prices hidden behind the toggle: packages/admin/products/hooks/useSelectPrices.js and packages/admin/products/modules/Prices/ShowArchivedToggle.js.
  • No live/test flag on Product or Price: only checkout, customer, invoice, order, payment_intent, payment_method, payout, payout_group, processor, processor_invitation, purchase, referral, shipment, shipping_provider, subscription, subscription_event include LiveModeModelConcern on the platform.
  • Development host patterns: config/activation_not_counted_host_patterns.yml on the platform.
  • Docs: API reference, “Live & Test Mode”.
Scroll to Top