pop402
Cryptographic receipts for purchases and access — sell licenses & subscriptions over HTTP, without middlemen.
╭─────────────────────╮
│ │
╱│ │╲
╱ │ │ ╲
╱ │ │ ╲
│ │ ┌─────┐ │ │
│ │ │ ● │ │ │
│ │ └─────┘ │ │
│ ├─────────────────────┤ │
│ │ │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────┘
Facilitator API
https://facilitator.pop402.com
1. Get Challenge
// Request authentication challenge
const challenge = await fetch('https://facilitator.pop402.com/challenge', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
walletAddress: wallet.publicKey.toBase58(),
ttl: 3600 // 1 hour session
})
}).then(r => r.json());
2. Sign Challenge
// User signs challenge message const message = new TextEncoder().encode(challenge.challenge.message); const signature = await wallet.signMessage(message); const signatureB58 = bs58.encode(signature);
3. Verify Access
// Verify with signed challenge
const result = await fetch('https://facilitator.pop402.com/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
paymentPayload: {
x402Meta: {
challengeId: challenge.challenge.id,
signature: signatureB58,
walletAddress: wallet.publicKey.toBase58(),
sku: 'premium-content-xyz'
}
},
paymentRequirements: {
network: 'solana'
}
})
}).then(r => r.json());
FAQ
How does challenge-response authentication work?
1. Request challenge from /challenge with wallet address 2. Sign the challenge message with your wallet 3. Send challengeId + signature to /verify Server verifies signature and checks license. Reusable until expiration.
How long is a challenge valid?
Default 5 minutes, configurable up to hours via ttl parameter. Challenge can be reused for multiple requests until expiration. No wallet popup needed after initial signature.
Does pop402 change x402 endpoints?
No. Keep /verify and /settle. Point them to pop402 and add X-PAYMENT-META.
What about revocation?
Deferred. Receipts include jti for future revocation lists.
Why receipts and licenses?
Payments prove transfer; receipts prove rights. pop402 issues cryptographic receipts with license terms so apps can grant time/usage‑based access and verify it later.
Use cases include gated downloads, timed subscriptions, single‑use content, and verifiable returns — receipts are the portable proof that an app can trust.
Quickstart
Base64url‑encode your Payment Meta and send it in the X-PAYMENT-META header.
QUICKSTART • X-PAYMENT-META header
// base64url helper
function toBase64Url(json) {
const b64 = btoa(JSON.stringify(json));
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/,'');
}
const paymentMeta = {
sku: 'premium-content-xyz',
payerPubkey: 'Hh1k...s9eZ',
signature: '3hF...',
message: 'premium-content-xyz:2025-10-30T00:00:00.000Z'
};
const headerValue = toBase64Url(paymentMeta);
// fetch to an x402-protected route
await fetch('/api/protected', { headers: { 'X-PAYMENT-META': headerValue } });
# curl (base64url)
# HEADER=$(node -e "const m={sku:'premium-content-xyz'};const b=Buffer.from(JSON.stringify(m),'utf8').toString('base64').replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,'');console.log(b)")
# curl -H "X-PAYMENT-META: $HEADER" https://your.app/api/protected
Tear‑off to start
pop402 • facilitator‑first receipts on‑chain