Deploy a zkApp on Mina that handles licensing for your software. Set your price, payment address, and we generate the smart contract.
1
Connect
2
App Info
3
Pricing
4
Settings
5
Deploy
Step 1 of 5
Connect your developer wallet
This wallet will be the zkApp's admin key — it can update pricing and withdraw funds. Keep it safe.
Step 2 of 5
App information
App name is required
App ID is required (lowercase letters, numbers, hyphens only)
⬡
Paste an image URL, or:
Square PNG · min 128×128 px · max 200 KB
PNG with transparency looks best on dark backgrounds.
Step 3 of 5
License pricing
Enter a valid Mina address (starts with B62q)
Duration is enforced on-chain at the time you deploy this tier — once a buyer purchases, the expiry is fixed and cannot be changed by you, the platform, or anyone else. 1 Month = 30 days; 1 Year = 365.25 days; Lifetime = 100 years (the longest interval that fits in the contract's slot counter).
Tier NamePrice (MINA)Duration
MINA
Fetching MINA price…
🏛 Platform fee: zkLicensing takes 2% of each license payment, deducted on-chain via the zkApp. You receive the rest instantly to your payment address.
Step 4 of 5
zkApp settings
Every license deployed on zkLicensing includes the following on-chain guarantees, enforced by the shared zkApp circuit:
7-day grace period after expiry — proofs remain valid so users can renew without interruption.
14-day refund window — buyers can request a full on-chain refund within 14 days of purchase.
These are baked into the verification key; they apply to every deployment and cannot be toggled per-app.
Step 5 of 5
Review & deploy zkApp
App Details
Name—
App ID—
Category—
Website—
Tags—
Pricing
Payment address—
Platform fee2%
zkApp Settings
NetworkMina Devnet
Verification keyPublic
Grace period7 days
Refund window14 days
⛓ Deploying will broadcast a zkApp creation transaction on Mina Devnet. You'll sign it in Auro. Estimated network fee: ~1 MINA. A 100 MINA one-time registration fee is also charged to list your app on the marketplace.
Waiting for Auro signature…
Sign the zkApp creation transaction in your Auro Wallet.
Transaction signed
Broadcasting to Mina network
Compiling ZK circuit
Deploying zkApp contract
✓
zkApp Deployed!
Your licensing contract is live on Mina Devnet. Here's everything you need to integrate it.
Listing review: your app is deployed on-chain, but the public marketplace listing is queued for moderation. We review submissions within 7 business days in most cases, and up to 15 business days at the latest. See the listing requirements, or check status in My Apps.
B62qm4...Xp9N
Mh9xKA...3pRz
https://zklicensing.com/buy.html?app=my-app
Deployed Tiers
Tier
zkApp Address
Link
// npm install zklicensingimport fs from'node:fs';
import { verifyLicense } from'zklicensing';
const proof = JSON.parse(fs.readFileSync('~/.config/MyApp/proof.json', 'utf8'));
const result = awaitverifyLicense({
licenseHash: proof.licenseHash,
productAddress: 'B62q…',
expirySlot: proof.expirySlot,
purchaseSlot: proof.purchaseSlot,
tier: proof.tier,
keeperUrl: 'https://zklicensing.com/api/verify',
});
// After the ~14-day refund window the SDK anchors and goes fully offline.// In Node, pass a `{ storage }` option (file-backed get/setItem) to persist// the anchor between runs. result.source: 'chain' | 'offline'if (result.valid) {
unlockFeatures(result.tier); // tier: "pro" | "basic" | …
}
// Pattern: call the keeper for the ~14-day refund window, then cache// a one-shot anchor in SharedPreferences and verify fully offline after.// The keeper response includes currentSlot + purchaseSlot — anchor only// once currentSlot > purchaseSlot + REFUND_WINDOW_N, where// REFUND_WINDOW_N = 13440 slots (≈ 14 days at 90s/slot post-Mesa).val licenseHash = prefs.getString("licenseHash", "") ?: ""val zkApp = "B62q…"val url = "https://zklicensing.com/api/verify/verify?licenseHash=$licenseHash&zkAppAddress=$zkApp"// OkHttp (or use Retrofit / Ktor)val response = OkHttpClient().newCall(
Request.Builder().url(url).build()
).execute()
val result = JSONObject(response.body?.string() ?: "{}")
if (result.getBoolean("valid")) {
unlockFeatures(result.getString("tier"))
}
// Pattern: call the keeper for the ~14-day refund window, then cache// a one-shot anchor in UserDefaults and verify fully offline after.// The keeper response includes currentSlot + purchaseSlot — anchor only// once currentSlot > purchaseSlot + REFUND_WINDOW_N, where// REFUND_WINDOW_N = 13440 slots (≈ 14 days at 90s/slot post-Mesa).let licenseHash = UserDefaults.standard.string(forKey: "licenseHash") ?? ""let zkApp = "B62q…"let url = URL(string: "https://zklicensing.com/api/verify/verify?licenseHash=\(licenseHash)&zkAppAddress=\(zkApp)")!
let (data, _) = try await URLSession.shared.data(from: url)
let result = try JSONDecoder().decode(VerifyResult.self, from: data)
if result.valid {
unlockFeatures(tier: result.tier) // tier: "pro" | "basic" | …
}
// npm install zklicensingimport { verifyLicense } from'zklicensing';
// proof loaded once from the buyer's proof.json and cached in localStorageconst proof = JSON.parse(localStorage.getItem('proof'));
const result = awaitverifyLicense({
licenseHash: proof.licenseHash,
productAddress: 'B62q…',
expirySlot: proof.expirySlot,
purchaseSlot: proof.purchaseSlot,
tier: proof.tier,
keeperUrl: 'https://zklicensing.com/api/verify',
});
// Calls the keeper for the first ~14 days (refund window), then anchors// to localStorage and verifies fully offline. result.source: 'chain' | 'offline'if (result.valid) {
unlockFeatures(result.tier); // tier: "pro" | "basic" | …
}
The keeper is a self-hosted Node.js service you run alongside your app. It holds the off-chain Merkle state and verifies licenses against the Mina chain.
Replace https://your-keeper.example.com with your keeper's public URL.
The public verify API at
https://zklicensing.com/api/verify
is hosted by zkLicensing — use it directly in your app snippets below without running anything yourself. For full sovereignty over uptime, self-host as described below.
To self-host: run your own lightweight verify service (no ZK prover, starts in ~2 s).
Drop apps.json next to your config.json and start with
npm run verify <deployAlias>.
On first start with an empty licenses.json it will auto-bootstrap all license records from the Mina archive node.