Advanced Usage 🚀
Level up your PAY.ID game! These are the pro patterns for production-grade integrations.
New to PAY.ID? Start with Simple Usage → first.
1️⃣ Complex Rule Patterns
Nested Rules (VIP vs Normal Paths) 🎯
Create different payment paths for different user types:
const VIP_RULE = {
id: 'vip_or_normal',
logic: 'OR' as const,
rules: [
{
// VIP path: high amount + KYC3 + low risk
id: 'vip_path',
logic: 'AND' as const,
rules: [
{ if: { field: 'tx.amount', op: '>=', value: '1000000000' } }, // >= 1000 USDC
{ if: { field: 'oracle.kycLevel', op: '==', value: '3' } },
{ if: { field: 'risk.score', op: '<=', value: '30' } },
],
},
{
// Normal path: 5-500 USDC + ID + KYC1 + moderate risk
id: 'normal_path',
logic: 'AND' as const,
rules: [
{ if: { field: 'tx.amount', op: 'between', value: ['5000000', '500000000'] } }, // 5-500 USDC
{ if: { field: 'oracle.country', op: '==', value: 'ID' } },
{ if: { field: 'oracle.kycLevel', op: '>=', value: '1' } },
{ if: { field: 'risk.score', op: '<=', value: '70' } },
],
},
],
}
What this does: VIP users can send big amounts (1000+ USDC) if they have KYC3 and low risk. Normal users have smaller limits (5-500 USDC) with relaxed requirements. Smart, right?
Time-Based Rules (Business Hours) ⏰
Only allow payments during work hours:
const BUSINESS_HOURS_RULE = {
id: 'business_hours',
if: {
field: 'env.timestamp',
op: 'between',
value: ['9:00', '17:00'],
transform: 'hour',
},
message: 'Payments only allowed during business hours (9 AM - 5 PM)',
}
Weekday-Only Rule 📅
No weekend payments:
const WEEKDAY_RULE = {
id: 'weekday_only',
if: {
field: 'env.timestamp',
op: 'in',
value: [1, 2, 3, 4, 5], // Mon-Fri
transform: 'day',
},
message: 'Payments only allowed on weekdays',
}
Cross-Field Validation (Daily Limit) 💰
Track spending across all payments:
const DAILY_LIMIT_RULE = {
id: 'daily_limit',
if: {
field: 'tx.amount',
op: '<=',
value: '$state.dailyLimit',
},
message: 'Exceeds daily spending limit',
}
2️⃣ Attestation-Gated Payments (EAS) 🔐
Require EAS attestations before payment — perfect for KYC requirements:
// Server-side with Context V2
import { buildContextV2 } from 'payid/context'
const contextV2 = await buildContextV2({
baseContext: {
tx: { sender: payer, receiver: merchant, asset: USDC, amount: '50000000', chainId: 1 },
},
oracle: {
issuer: oracleSigner,
data: {
kycLevel: '2',
attestationUID: '0xabc123...', // EAS attestation UID
},
},
})
// Rule checks for attestation
const ATTESTATION_RULE = {
id: 'kyc_attestation',
if: { field: 'oracle.attestationUID', op: 'exists' },
message: 'KYC attestation required',
}
// Payment requires attestationUIDs
await payid.evaluateAndProve({
context: contextV2,
authorityRule: ATTESTATION_RULE,
attestationUIDs: ['0xabc123...'],
// ... other params
})
On-chain verification happens automatically via PayIDVerifier.requireAllowed() — pretty cool, right?
3️⃣ Context V2 with Server-Signed Data 📝
Complete Context V2 Structure
Here's the full Context V2 structure with all the bells and whistles:
import { buildContextV2 } from 'payid/context'
const contextV2 = await buildContextV2({
baseContext: {
tx: { sender, receiver, asset, amount, chainId },
},
// Environment timestamp signed by server
env: {
issuer: envSigner,
data: { timestamp: Math.floor(Date.now() / 1000) },
},
// State tracking (spending limits)
state: {
issuer: stateSigner,
data: {
spentToday: '150000000', // 150 USDC spent today
dailyLimit: '500000000', // 500 USDC daily limit
period: 'DAY',
},
},
// External oracle data (KYC, country, etc.)
oracle: {
issuer: oracleSigner,
data: {
kycLevel: '2',
country: 'ID',
riskScore: '25',
},
},
// Risk scoring
risk: {
issuer: riskSigner,
data: {
score: 25,
category: 'LOW',
modelHash: '0x123...',
},
},
})
Trusted Issuers Setup 🔑
Tell PAY.ID which signers you trust:
const payid = createPayIDServer({
signer: serverSigner,
trustedIssuers: new Set([
'0xEnvSignerAddress...',
'0xStateSignerAddress...',
'0xOracleSignerAddress...',
'0xRiskSignerAddress...',
]),
})