License Store
Before you open your store
The License Store sells keys for your products using your vendor Stripe account. Create an internal product first, then configure payment credentials, the license issued after checkout, and the details shown to buyers.
Work through a test purchase before sharing the store. Confirm that checkout, payment webhooks, license delivery, invoice details, and cancellation behavior all match your sales model. Use matching Stripe test credentials throughout that test.
For your own PermitCore subscription and account exports, see Account & team. For license lookup and device self-service, see the Customer portal.
License Store
The License Store lets your customers buy licenses directly from a branded public storefront — no coding required on your end. A customer account is required to check out (see "Customer Accounts" below); PermitCore hosts the checkout page and automatically creates a license key when payment succeeds, emailing the buyer a link to sign in and view it from their account rather than the key itself. You connect your own Stripe account, so revenue goes directly to you with 0% PermitCore commission.
A Store 2.0 storefront at /store/{yourSlug} with vendor navigation, hero slides, editorial collections, search, and responsive layouts.
How it works
https://permitcore.dev/store/yourSlug. Add it to your website, email footer, or documentation.Complete setup tutorial
Use this sequence for a new store. It separates payment setup, product fulfillment, presentation, and launch verification so each layer can be tested before customers see it.
1. Prepare the vendor account and Stripe
- Create the software product that the purchased license will belong to.
- Open License Store → Settings and enable the Store.
- Add Stripe publishable, secret, and webhook signing keys from the same Stripe mode: all test or all live.
- Add the vendor legal name, VAT number, default tax rate, and any country overrides used on invoices.
- Create the webhook shown below and enable every required event before accepting an order.
2. Create the product customers will buy
- Open License Store → Products → Add Product and link the correct internal product.
- Write the public name, short tagline, card description, long product-page description, and up to six feature bullets.
- Upload a clear landscape cover image and, where useful, a product gallery. Every active product should have an image before launch.
- Choose one-time, monthly, and/or annual pricing and select the currency.
- Configure the generated license: activation count, node locking, expiry, trial days, and feature flags.
- Add an optional badge, CTA label, featured state, bundle contents, or free trial.
- Save, then use Auto-Setup Stripe Products in Store Settings to create missing Stripe Products and Prices.
The resulting product page combines a gallery, description, features, pricing choices, trial, discount disclosure, cart, and secure checkout.
3. Build the visual identity in Shop Designer
- Open License Store → Shop Designer.
- In Branding, set the store name, logo, primary/accent colors, text colors, page background color, and optional page background image.
- In Visual Style, choose the heading font, card density, hero height, corner style, and desktop product columns.
- Configure the default category-tile color or image. Then edit each visible tile—such as Featured, Special offers, Free trials, Bundles, or a product badge—to override its own background color or image. Select Use default to remove an override.
- Add hero slides with desktop-safe images, headline, supporting copy, CTA label, and a safe internal or HTTPS destination.
- Use the live preview at desktop, tablet, and phone widths before saving.
4. Compose the homepage
Under Homepage Sections, add the blocks that tell the product story. The section list is the published page order.
- Add a Featured spotlight for the strongest product story.
- Add horizontal shelves for offers, trials, bundles, badges, or products under a selected price.
- Add Category tiles for fast discovery and Discovery tabs for compact comparison.
- Keep a Product catalog so every active product remains searchable and sortable.
- Use the up/down arrows to reorder sections and the Visible switch to prepare a block without publishing it.
- Set Space above and Space below independently from 0–160 px. New sections start with a balanced 32 px top gap; reduce a gap when two related blocks should read as one group.
Collections, individually branded category tiles, and discovery tabs make a larger catalog easy to scan.
5. Configure offers without weakening checkout
- Open a Store Product and create a discount campaign with a name, percentage, label color, UTC start, and UTC end.
- Leave Show lowest price in the previous 30 days enabled when the disclosure is useful or required for the product and market.
- Preview the crossed-out regular price, colored percentage badge, campaign label, effective price, and 30-day comparison text.
- Test at least one cart and Stripe test checkout. The API—not browser state—selects the currently valid campaign and calculates the effective price.
6. Run the launch test
- Check the homepage, product page, cart, sign-in, account, invoices, and license view on desktop and a real mobile viewport.
- Buy one one-time item, start one trial, and test a recurring product if the store offers subscriptions.
- Confirm the Stripe webhook creates exactly one order and license, even if Stripe retries the event.
- Verify the issued license has the intended activation and feature settings.
- Cancel a test subscription and confirm the linked license follows the configured lifecycle.
- Review every product image, CTA, currency, tax rule, vendor invoice detail, and external link before switching to live Stripe credentials.
The Create Store Product form before entry, with display fields and a live preview. Pricing and generated-license settings continue below.
A new Store before setup: Products, Orders, Settings, and Shop Designer. Add a product and configure Stripe before accepting purchases.
After creating your store products, click Auto-Setup Stripe Products in Store → Settings. PermitCore automatically creates the matching Stripe Products and Prices in your Stripe account and stores the Price IDs. You never need to copy-paste price_xxx IDs manually. Re-running the setup is safe — prices that haven't changed are skipped.
Store product settings
| Setting | Description |
|---|---|
| Name & Slug | Display name and URL identifier (e.g. /store/yourco/pro-edition) |
| Tagline & Description | Short marketing text shown on the product card |
| Long Description | Full marketing copy shown on the product detail page |
| Feature Bullets | Up to 6 bullet points shown on the card and detail page (e.g. "Unlimited exports") |
| Pricing | One-time, monthly, and/or annual prices. Set at least one. Currency selectable. |
| Stripe Price IDs | Auto-filled by "Auto-Setup Stripe Products" button, or enter manually for recurring subscriptions managed directly in Stripe. |
| Badge | Optional label on the card (e.g. "Most Popular") with custom color |
| Generated license | Max activations, node-locked, trial days, expiry days, feature flags — applied to every license created on purchase |
| Featured | Featured products are shown larger at the top of the store grid |
| Discount campaign | Schedule a 1–90% discount with start/end time, campaign name and color. The server applies the effective price at checkout. |
| Lowest price disclosure | Optional checkbox, enabled by default, showing the lowest applied price during the 30 days before the campaign started. Price history is recorded even when the label is hidden. |
Shop Designer reference
Store → Shop Designer is organized as a visual page builder. Branding controls the shared visual system and full-page background; Visual Style controls typography, hero height, card density and desktop columns; Category Tiles adds per-tile artwork; Homepage Sections controls page order, content, visibility, and spacing.
| Section | Use it for | Available controls |
|---|---|---|
| Featured spotlight | One large editorial product story with a selectable rail. | Title, subtitle, product filter, maximum items, visibility and order. |
| Horizontal shelf | Fast, touch-friendly browsing similar to a marketplace collection. | All/featured/trial/bundle/badge/under-price filter and maximum items. |
| Category tiles | Shortcuts generated from featured items, offers, trials, bundles and product badges. | Title, subtitle, visibility, order, shared fallback style, and a color/image override for each visible tile. |
| Discovery tabs | Compact lists for popular products, offers, trials and bundles. | Title, subtitle, maximum items, visibility and order. |
| Product catalog | The searchable and sortable card grid. | Title, visibility, order and global products-per-row setting. |
Every section also has independent Space above and Space below controls. Use the arrow controls to reorder sections and the Visible switch to prepare a section without publishing it. If the catalog is removed, PermitCore still adds a safe catalog fallback so products never become impossible to browse.
Discounts and the EU 30-day rule
PermitCore stores regular and promotional price events and calculates the comparison period from the 30 days immediately before a campaign starts. The promotional price itself is excluded, so it cannot become its own “lowest price.” Display and checkout use the same percentage and UTC schedule, but checkout always recalculates on the server.
Store security and performance
- Server-authoritative checkout: browser cart prices are display-only. The API reloads the active product, price type, campaign and tax before creating Stripe Checkout.
- Payment boundary: card details are entered on Stripe Checkout. PermitCore stores encrypted vendor credentials and issues a paid license only after verifying the tenant-specific Stripe webhook signature.
- Tenant isolation: store products, customers, orders, designs and price history are protected by tenant filters and PostgreSQL Row-Level Security.
- Safe customization: colors, image URLs and slide links are validated; legacy rows are sanitized again on public read; Enterprise CSS blocks network loads and executable/breakout constructs.
- Traffic resilience: public catalog/product responses use shared Valkey caching and dedicated browse rate limits; checkout and trial endpoints use stricter limits. Cache failure falls back to PostgreSQL.
- Responsive storefront: grids collapse for tablets/phones, shelves remain horizontally scrollable, controls are touch-sized and reduced-motion preferences are respected.
Stripe webhook setup
In your Stripe Dashboard, create a webhook endpoint pointing to:
https://api.permitcore.dev/api/store/webhook/yourSlug
Enable these events: checkout.session.completed, payment_intent.payment_failed, checkout.session.expired, customer.subscription.deleted
Copy the webhook signing secret and paste it into Store → Settings → Webhook Secret.
If you sell a monthly or annual store product, PermitCore listens for customer.subscription.deleted and automatically deactivates the license when the customer's Stripe subscription ends — no manual revoke needed. Make sure this event is enabled on your webhook endpoint, or cancelled customers will keep an active license.
Tax & Pricing
PermitCore supports configurable tax rates at checkout — a default rate that applies to all countries, plus optional per-country overrides for any country in the world (not just EU). Tax is added on top of the product price and shown as a separate line in the cart.
| Setting | Where | Description |
|---|---|---|
| Default tax rate (%) | Store → Settings → Tax | Applied when no country-specific rate matches. Set to 0 for untaxed by default. |
| Per-country rates | Store → Settings → Tax → Country overrides | Enter a comma-separated list of CC:rate pairs, e.g. DE:19, AT:20, US:0. Supports all ISO 3166-1 alpha-2 country codes. |
| Country shown on PDP | Product detail page | When a non-zero tax rate is configured, the product detail page shows a "+X% tax may apply at checkout" notice. |
At checkout, the customer selects their country from a dropdown. The cart immediately recalculates and shows a subtotal / tax / total breakdown. If the buyer later enters a valid EU VAT number, the tax drops to 0% (see B2B exemption below).
EU VAT B2B Exemption
When a business customer provides their EU VAT number at checkout, PermitCore validates it in real time via the VIES SOAP API (the official EU VAT Information Exchange System). If valid, the tax rate is set to 0% under the reverse-charge mechanism — the buyer accounts for VAT in their own country.
How it works at checkout:
- Buyer enters their country (EU member state) in the cart.
- A VAT number field appears for EU countries.
- Buyer enters their VAT number (e.g.
DE123456789). - PermitCore calls VIES in real time — validation result shown instantly.
- If valid: tax drops to 0%. The order is flagged
VatExempt = true. - The generated invoice includes a reverse-charge notice as required by EU law.
B2B Invoicing
PermitCore generates professional HTML invoices for every order that can be printed as PDF from the browser. Invoices include your vendor details, the buyer's details, a line-item breakdown, tax treatment, and a reverse-charge notice when applicable.
Setup: your vendor details
Before invoices are meaningful, add your company details in Store → Settings → Invoicing:
| Field | Description |
|---|---|
| Legal Company Name | Your registered company name — printed as "Sold by" on every invoice. |
| Your VAT Number | Your own VAT registration number — printed on invoices for B2B compliance. |
Cart fields collected from buyer
| Field | Required | Description |
|---|---|---|
| Company Name | Optional | Printed on the invoice as "Buyer company". Leave blank for personal purchases. |
| Country | Optional | Determines the applicable tax rate. Also printed on invoice. |
| VAT Number | Optional (EU only) | VIES-validated. When valid, triggers reverse-charge (0% tax). |
Accessing invoices
| Who | How |
|---|---|
| Admin | Store → Orders → click Invoice next to any order. Opens in a new tab and auto-triggers the print dialog. |
| Customer | My Account → Orders → click Invoice next to any order. Link uses a short-lived access token — no separate login required. |
window.print().
Use your browser's Save as PDF option in the print dialog to get a PDF copy.
Keep a downloaded copy for your accounting records.
Customer Accounts (My Account)
A store account is required to buy anything — including a free trial. Browsing products and adding to cart needs no account, but the checkout/trial button always requires sign-in first: an unauthenticated buyer is prompted to sign in or register (returning straight back to the product/cart afterward) before payment even starts. This is deliberate, not a gap that got closed by accident — a purchase with nowhere to log into afterward had no way to ever see the key/activations again except contacting you directly. Accounts are per-store (each vendor's store has its own account system, distinct from the separate, always-anonymous Customer Portal).
Registration & Login
A Sign In button appears in the store header. Customers can register with name, email, password, and optional address. After registration, a verification email is sent automatically.
| Page | URL | Description |
|---|---|---|
| Login | /store/{slug}/login | Sign in with email + password. Includes "Forgot password?" link and a "Resend verification email" prompt if the account isn't verified yet. |
| Register | /store/{slug}/register | New account: name, email, password, country, address. |
| My Account | /store/{slug}/account | Tabs: My Orders (purchase history, invoices), My Keys (every license linked to the account — purchase date, expiry, activation/device list, "Add a license" to self-link one you hold the key for), Profile, Security. |
Email verification
After registering, customers receive a verification link valid for 48 hours. Until verified, a yellow banner is shown in My Account with a Resend link — also reachable directly from the Login page's error message if a customer tries to sign in before verifying. Verification is not required to buy — sign-in alone is enough; verification is a separate, softer reminder shown after the fact.
/store/yourSlug/verify-email?token=token
Forgot / Reset password
The login page has a Forgot password? link. The customer enters their email and receives a reset link valid for 1 hour. For security, the forgot-password endpoint always returns HTTP 200 regardless of whether the email exists (prevents email enumeration).
/store/yourSlug/reset-password?token=token
What customers can do in My Account
| Tab | Features |
|---|---|
| Orders | Full order history with product name, date, amount. Click Reveal Key to decrypt and display the license key. Click Invoice to open a printable invoice. |
| Profile | Update name, email, country, and address. |
| Security | Change password. Email verification status badge + Resend button if not yet verified. |
End-Customer Expiry Notifications
PermitCore can send expiry reminder emails directly to your end customer — not just to you as the admin. When a Customer Email is set on a license, that address receives branded reminder emails at 30, 7, and 1 day before the license expires, prompting the customer to renew.
The Customer Email field in the Edit Metadata modal — set per license, completely optional.
How to set it up
- Go to Licenses → View → Edit Metadata
- Enter the customer's email in the Customer Email field
- Save — the email will be used for all future expiry notifications for this license
Setting a Customer Email does not replace admin notifications — admins still receive their daily digest. This is an additional, per-license notification that goes directly to the end user.
The customer email
The email sent to the customer:
- Is delivered using the instance's configured mail provider
- Shows your company name as the sender — PermitCore is not mentioned
- Includes the product name, expiry date, and days remaining
- Asks the customer to contact you to renew
Reseller Portal
The Reseller Portal lets you invite partner companies or distributors to issue licenses on your behalf. Each reseller gets their own login and can create licenses only for the products you authorize — with optional caps on how many they can issue.
The reseller invitation form before submission. Choose product access and configure the partner before sending the invitation.
How it works
- Go to Resellers in the sidebar and click Invite Reseller
- Enter the reseller's name, email, and a temporary password
- Select which products they can sell and optionally set a max license count per product
- The reseller logs in with their credentials and creates licenses from their own dashboard
- New live activations of reseller-issued keys count against your monthly activation allowance; creating the key itself does not consume an activation
Reseller permissions
| Can do | Cannot do |
|---|---|
| Create licenses for assigned products | Access products not assigned to them |
| View their own created licenses | See other users or company settings |
| View order/activation counts for their licenses | Access billing or payment settings |
| Generate up to their per-product cap | Exceed the license cap set by you |
Managing resellers
From the Resellers page you can:
- Deactivate a reseller — immediately blocks their login without deleting their account or the licenses they've created
- Adjust product access — add or remove products, change the license cap
- Monitor issuance — see how many licenses each reseller has created per product
Commission tracking
When you invite a reseller, you can set a commission percentage per product (0–100%). Every order placed through a reseller link automatically records the commission amount at the time of sale — so changing the rate later does not retroactively affect existing orders.
- Go to Resellers → Invite Reseller and enter a commission % for each product.
- When a customer purchases through a reseller link, the order records
ResellerId,CommissionPercent, and the computedCommissionAmount. - Open the Commissions tab on the Resellers page to see all pending and paid commissions grouped by reseller.
- Select one or more orders and click Mark as Paid to record payment. The system logs
CommissionPaidAtand updates the reseller's running total.
| Field | Description |
|---|---|
CommissionPercent | Rate set per reseller × product pair (snapshot taken at sale time) |
CommissionAmount | Pre-computed: OrderTotal × CommissionPercent / 100 |
TotalEarned | Running total of all commission amounts ever assigned |
TotalPaid | Running total of commissions marked as paid |
| Unpaid balance | TotalEarned − TotalPaid — shown in the Commissions tab |