Developer Docs

API Reference

Everything you need to integrate Prilana Pay into your site. Register at /sites to get your API key.

Quick Start

1. Register your site

Go to /sitesand fill out the registration form. You'll receive a merchantId and apiKeyimmediately. Save your API key — it's shown once.

2. Embed the payment widget

<script src="https://prilana.com/widget.js"></script> <prilana-pay merchant-id="YOUR_MERCHANT_ID" api-key="YOUR_API_KEY" amount="9.99" label="Premium Access" ></prilana-pay>

3. Handle the payment event

document.querySelector('prilana-pay') .addEventListener('prilana:success', (e) => { const { txSignature, amount, orderId } = e.detail; // Verify server-side, then unlock content fetch('/your-api/verify', { method: 'POST', body: JSON.stringify({ txSignature }) }); });

4. Verify payment server-side

curl -X POST https://prilana.com/api/merchant/verify-payment \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"txSignature":"5UfDuX...","merchantId":"YOUR_MERCHANT_ID"}'

5. Configure webhooks (optional)

Set your webhook URL in the dashboard settings. We'll POST payment confirmations to your endpoint in real time.

Authentication

All authenticated endpoints accept your API key via the Authorization header or merchant-specific headers.

Bearer Token (payment verification)
Authorization: Bearer YOUR_API_KEY
Header Auth (verification check)
X-API-Key: YOUR_API_KEY X-Merchant-Id: YOUR_MERCHANT_ID

Your API key is shown once at registration. If you lose it, contact support@prilana.comto rotate it. Never expose your API key in client-side code (the widget's api-keyattribute is safe — it only enables pre-flight verification checks, not payment operations).

Payment Widget

The widget is a Web Component that handles wallet connection, age verification pre-check, payment UI, and transaction confirmation. Works on any HTML page.

Attributes

AttributeRequiredDescription
merchant-idYesYour Prilana merchant ID (prln_m_...)
amountYesPayment amount in token units (e.g. "9.99")
api-keyRecommendedYour API key. Enables pre-flight verification check before payment.
labelNoDescription shown to the buyer in the payment modal
order-idNoYour order reference. Included in webhooks and events.
themeNo"dark" (default) or "light"

Events

EventDetail
prilana:success{ txSignature, amount, orderId, merchantAmount, fee }
prilana:error{ error }
prilana:cancelBuyer closed the payment modal

Full Example

<script src="https://prilana.com/widget.js"></script> <prilana-pay merchant-id="prln_m_abc123" api-key="pk_live_..." amount="19.99" label="Premium Membership" order-id="order_456" ></prilana-pay> <script> const widget = document.querySelector('prilana-pay'); widget.addEventListener('prilana:success', (e) => { console.log('TX:', e.detail.txSignature); console.log('Net:', e.detail.merchantAmount); // Unlock content or redirect }); widget.addEventListener('prilana:error', (e) => { console.error('Payment failed:', e.detail.error); }); widget.addEventListener('prilana:cancel', () => { console.log('Buyer cancelled'); }); </script>

React / Next.js

For React and Next.js apps, use the @prilana/react package instead of the raw widget. It handles script loading, TypeScript types, and wires events to React callbacks.

Install

Terminal
npm install @prilana/react

Payment Button

components/BuyButton.tsx
import { PrilanaPay } from '@prilana/react'; export default function BuyButton() { return ( <PrilanaPay merchantId="prln_m_abc123" apiKey="pk_live_..." amount="9.99" label="Premium Access" orderId="order_456" onSuccess={(e) => { console.log('Paid!', e.txSignature); fetch('/api/unlock', { method: 'POST', body: JSON.stringify({ tx: e.txSignature }), }); }} onError={(e) => console.error(e.error)} onCancel={() => console.log('Cancelled')} /> ); }

Server-Side Verification

Always verify payments on your backend before unlocking content. Never trust the client event alone.

app/api/unlock/route.ts (Next.js)
import { verifyPayment } from '@prilana/react'; export async function POST(req: Request) { const { tx } = await req.json(); const result = await verifyPayment({ txSignature: tx, merchantId: 'prln_m_abc123', apiKey: process.env.PRILANA_API_KEY!, }); if (!result.valid) { return Response.json({ error: 'Invalid payment' }, { status: 400 }); } // Unlock content for this user return Response.json({ unlocked: true }); }

Webhook Handler

app/api/webhooks/prilana/route.ts
import { verifyWebhookSignature } from '@prilana/react'; export async function POST(req: Request) { const body = await req.text(); const sig = req.headers.get('x-prilana-signature')!; const ts = req.headers.get('x-prilana-timestamp')!; if (!verifyWebhookSignature(body, sig, ts, process.env.WEBHOOK_SECRET!)) { return Response.json({ error: 'Bad signature' }, { status: 401 }); } const event = JSON.parse(body); // event.txSignature, event.amount, event.buyerWallet... return Response.json({ ok: true }); }

Payments API

POST/api/pay/createBuild an unsigned payment transaction

Called by the widget automatically. Returns a serialized Solana transaction for the buyer to sign.

Request Body
{ "merchantId": "prln_m_abc123", "amount": "9.99", "buyerWallet": "7nYBm...", "orderId": "order_456" // optional }
Response (200)
{ "transaction": "base64-encoded-transaction...", "blockhash": "...", "lastValidBlockHeight": 123456, "orderIdHex": "abc123...", "details": { "amount": "9990000", "fee": "299700", "merchantAmount": "9690300", "feeBps": 300, "tokenMint": "CSi5eSLSkkbLHqABicuejQ393KAvPKiWjEEX4o8LzGvc", "decimals": 6 } }
Error Response (403 — not verified)
{ "error": "Age verification required", "verifyUrl": "https://prilana.com/verify" }
POST/api/pay/confirmConfirm a signed payment on-chain

Called by the widget after the buyer signs. Parses the on-chain transaction, stores the payment record, and triggers your webhook.

Request Body
{ "txSignature": "5UfDuX...", "merchantId": "prln_m_abc123" }
Response (200)
{ "success": true, "payment": { "txSignature": "5UfDuX...", "amount": 9990000, "fee": 299700, "merchantAmount": 9690300, "buyerWallet": "7nYBm..." } }
POST/api/merchant/verify-paymentAPI KeyServer-side payment verification

Use this to verify a payment on your backend before unlocking content. Authenticate with your API key.

Request
curl -X POST https://prilana.com/api/merchant/verify-payment \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "txSignature": "5UfDuX...", "merchantId": "prln_m_abc123", "expectedAmount": 9.99 }'
Response (200)
{ "valid": true, "payment": { "txSignature": "5UfDuX...", "amount": 9990000, "fee": 299700, "merchantAmount": 9690300, "buyerWallet": "7nYBm...", "createdAt": "2026-03-19T12:00:00Z" } }

Verification API

GET/api/verify/check?wallet=...API KeyCheck a wallet's age verification status

Query whether a buyer is age-verified before allowing a purchase. Requires your merchant API key. The widget calls this automatically if you provide the api-key attribute.

Request
curl https://prilana.com/api/verify/check?wallet=7nYBm... \ -H "X-API-Key: YOUR_API_KEY" \ -H "X-Merchant-Id: prln_m_abc123"
Response — Verified
{ "verified": true, "level": 1, "method": "rekognition_selfie", "verifiedAt": "2026-03-19T10:30:00Z", "expiresAt": "2027-03-19T10:30:00Z" }
Response — Not Verified
{ "verified": false, "level": 0 }

Use case: Gate content or purchases behind age verification. If verified is false, redirect the user to https://prilana.com/verify to complete verification. Verification is a one-time process per wallet.

Webhooks

Configure your webhook URL in the dashboard settings. Prilana sends a POST request to your endpoint for every confirmed payment.

Payload

POST to your webhook URL
{ "event": "payment.confirmed", "data": { "txSignature": "5UfDuX...", "merchantId": "prln_m_abc123", "amount": 9990000, "fee": 299700, "merchantAmount": 9690300, "buyerWallet": "7nYBm...", "orderId": "order_456", "timestamp": "2026-03-19T12:00:00Z" } }

Headers

HeaderDescription
X-Prilana-SignatureHMAC-SHA256 hex digest of `${timestamp}.${body}`
X-Prilana-TimestampUnix timestamp (ms) when the webhook was sent. Reject if older than 5 minutes.

Signature Verification

The signature covers `${timestamp}.${body}`to prevent replay attacks. Always verify the timestamp is recent (< 5 minutes).

Node.js verification example
import crypto from 'crypto'; function verifyWebhook(body, signature, timestamp, webhookSecret) { // Reject old webhooks (replay protection) if (Math.abs(Date.now() - parseInt(timestamp)) > 300000) return false; // Signature covers timestamp.body const expected = crypto .createHmac('sha256', webhookSecret) .update(timestamp + '.' + body) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex') ); } // Express handler: app.post('/webhooks/prilana', (req, res) => { const sig = req.headers['x-prilana-signature']; const ts = req.headers['x-prilana-timestamp']; if (!verifyWebhook(JSON.stringify(req.body), sig, ts, WEBHOOK_SECRET)) { return res.status(401).send('Invalid signature'); } const { txSignature, amount, buyerWallet } = req.body; // Process the payment... res.status(200).send('OK'); });

Retry Policy

We retry aggressively to ensure delivery:

Immediate retries: 4 attempts (0s, 1s, 5s, 30s)

Persistent queue: If all immediate retries fail, the webhook is queued in our database with exponential backoff: 1min, 5min, 30min, 2hr, 4hr, 8hr, 12hr

Max attempts: 10 total over ~40 hours

After exhaustion: Marked as failed. You can always verify payments manually via the API.

Error Codes

CodeMeaningAction
400Bad request — missing or invalid parametersCheck the request body matches the API spec
401Unauthorized — invalid or missing API keyVerify your API key and merchant ID
403Buyer not age-verifiedRedirect buyer to https://prilana.com/verify
404Merchant or payment not foundVerify the merchant ID or transaction signature
409Wallet already verifiedUser has already completed verification
429Rate limit exceededWait and retry. Limits: 60 req/min for verify/check, 30 req/min for verify-payment
500Server errorRetry after a moment. Contact support if persistent.

Need help?

Email support@prilana.com or check your merchant dashboard for integration status.