Implementation Guide
Detailed instructions for integrating RSCP into your applications using our reference SDKs.
Prerequisites
- Node.js 18+ or Bun 1.0+
- TypeScript 5.0+ (recommended)
- Basic understanding of RSCP concepts
Issuer SDK
The Issuer SDK allows registered companies to create RSCP credentials, manage revocations, and interact with the credential registry.
Installation
npm install @rscp/issuer-sdk
# or
yarn add @rscp/issuer-sdk
# or
pnpm add @rscp/issuer-sdkConfiguration
import { IssuerSDK } from '@rscp/issuer-sdk';
const issuer = new IssuerSDK({
// Your registered issuer DID
issuerId: 'did:rscp:issuer:your-company',
// Ed25519 private key for signing credentials
privateKey: process.env.ISSUER_PRIVATE_KEY!,
// Optional: BBS+ key for selective disclosure
bbsPrivateKey: process.env.ISSUER_BBS_KEY,
// Registry endpoint (defaults to production)
registryUrl: 'https://registry.rscp.org',
});Creating Credentials
Credentials contain both public attributes (stored in registry) and private attributes (stored only by issuer and holder).
// Create a credential after training completion
const credential = await issuer.createCredential({
// Holder information
holder: {
did: 'did:key:z6MkhaXgBZDvotDkL5LTGhgZDvotDkL5LT...',
givenName: 'Rahul',
familyName: 'Kumar',
// Private attributes - NOT sent to registry
email: 'rahul@email.com',
phone: '+91-9876543210',
},
// Certification details
certification: {
level: 'GOLD', // 'BRONZE' | 'SILVER' | 'GOLD'
// Private - only used for predicates
testScore: 92,
hazardScore: 88,
},
// Validity period
validity: {
issuanceDate: new Date(),
// Gold = 2 years, others = 1 year
expirationDate: new Date('2028-01-15'),
},
});
console.log('Credential ID:', credential.id);
// → urn:rscp:credential:your-company:2026G000001:7
console.log('Certificate Number:', credential.certificateNumber);
// → RS-2026-G-IN-YRC-000001-7
console.log('Verification Code:', credential.verificationCode);
// → A3B7K9M2Submitting to Registry
Only public attributes are submitted. The SDK automatically strips private data before transmission.
// Submit to registry (public attributes only)
const result = await issuer.submitToRegistry(credential);
if (result.success) {
console.log('Registry record created');
console.log('Lookup URL:', result.lookupUrl);
// → https://rscp.org/v/A3B7K9M2
} else {
console.error('Submission failed:', result.error);
}Delivering to Wallet
// Deliver the FULL credential to the holder's wallet
const delivery = await issuer.deliverToWallet(credential, {
// Wallet endpoint (from holder's DID document)
endpoint: 'https://wallet.example.com/credentials',
// Or use push notification
pushToken: 'holder-device-token',
});
if (delivery.success) {
console.log('Credential delivered successfully');
}Revocation
Revoke credentials when riders leave your organization or fail to maintain certification requirements.
// Revoke a credential
await issuer.revoke(credential.id, {
reason: 'employment_ended', // | 'violation' | 'expired' | 'superseded'
effectiveDate: new Date(),
});
// Check revocation status
const status = await issuer.checkStatus(credential.id);
console.log(status); // { status: 'revoked', reason: 'employment_ended', ... }Error Handling
import {
IssuerError,
RegistryError,
ValidationError
} from '@rscp/issuer-sdk';
try {
const credential = await issuer.createCredential(data);
await issuer.submitToRegistry(credential);
} catch (error) {
if (error instanceof ValidationError) {
// Invalid input data
console.error('Validation failed:', error.fields);
} else if (error instanceof RegistryError) {
// Registry communication failed
console.error('Registry error:', error.code, error.message);
// Retry with exponential backoff
} else if (error instanceof IssuerError) {
// Signing or key issues
console.error('Issuer error:', error.message);
}
}Best Practices
- Store keys securely
Use HSM or secure key management. Never commit keys to version control.
- Rotate keys regularly
Implement key rotation every 6-12 months.
- Maintain audit logs
Log all credential issuance and revocation events.
Wallet SDK
The Wallet SDK enables mobile apps to store credentials and create selective disclosure presentations. Built for React Native with secure storage integration.
Installation
npm install @rscp/wallet-sdk
# React Native peer dependencies
npm install react-native-keychain react-native-qrcode-svgConfiguration
import { WalletSDK } from '@rscp/wallet-sdk';
import * as Keychain from 'react-native-keychain';
const wallet = new WalletSDK({
// Secure storage adapter (uses device secure enclave)
storage: {
async get(key: string) {
const result = await Keychain.getGenericPassword({ service: key });
return result ? result.password : null;
},
async set(key: string, value: string) {
await Keychain.setGenericPassword(key, value, { service: key });
},
async delete(key: string) {
await Keychain.resetGenericPassword({ service: key });
},
},
// Registry for status checks
registryUrl: 'https://registry.rscp.org',
});Storing Credentials
// Receive credential from issuer
const credential = await wallet.receiveCredential(credentialJwt);
// Store securely
await wallet.storeCredential(credential);
// List all credentials
const credentials = await wallet.listCredentials();
credentials.forEach(cred => {
console.log(cred.certificateNumber, cred.level, cred.validUntil);
});Creating Presentations
Presentations allow holders to selectively disclose attributes and prove predicates without revealing underlying values.
// Create a selective disclosure presentation
const presentation = await wallet.createPresentation({
credential,
// Explicitly disclosed attributes
disclose: ['givenName', 'familyName', 'level', 'validUntil'],
// Prove predicates without revealing values
predicates: [
{
attribute: 'testScore',
op: '>=', // | '>' | '<' | '<=' | '=='
value: 80,
},
{
attribute: 'hazardScore',
op: '>=',
value: 75,
},
],
// Optional: bind to verifier to prevent replay
audience: 'did:web:restaurant.example.com',
// Optional: short-lived presentation
expiresIn: 300, // 5 minutes
});QR Code Generation
import { generateQRCode } from '@rscp/wallet-sdk';
// Generate QR for basic verification (just lookup code)
const basicQR = await generateQRCode({
type: 'basic',
verificationCode: credential.verificationCode,
});
// Returns: "https://rscp.org/v/A3B7K9M2"
// Generate QR with full presentation
const presentationQR = await generateQRCode({
type: 'presentation',
presentation,
});
// Returns: Base64-encoded presentation dataReact Native Component
import { CredentialCard, QRDisplay } from '@rscp/wallet-sdk/react';
function WalletScreen() {
const [credential, setCredential] = useState(null);
const [showQR, setShowQR] = useState(false);
return (
<View>
<CredentialCard
credential={credential}
onPress={() => setShowQR(true)}
/>
<QRDisplay
visible={showQR}
credential={credential}
disclosureOptions={{
disclose: ['givenName', 'level'],
predicates: [{ attribute: 'testScore', op: '>=', value: 80 }],
}}
onClose={() => setShowQR(false)}
/>
</View>
);
}Offline Support
The Wallet SDK supports offline verification for areas with poor connectivity.
// Pre-cache issuer public keys for offline verification
await wallet.cacheIssuerKeys(['did:rscp:issuer:swiggy', 'did:rscp:issuer:zomato']);
// Create offline-capable presentation
const offlinePresentation = await wallet.createPresentation({
credential,
disclose: ['givenName', 'level'],
includeIssuerKey: true, // Embeds issuer public key
});Verifier SDK
The Verifier SDK enables applications to verify RSCP credentials and presentations. Works in browsers, Node.js, and mobile apps.
Installation
npm install @rscp/verifier-sdkConfiguration
import { VerifierSDK } from '@rscp/verifier-sdk';
const verifier = new VerifierSDK({
// Registry endpoint
registryUrl: 'https://registry.rscp.org',
// Trusted issuer registry (optional, uses default if not specified)
trustedIssuers: 'https://rscp.org/issuers',
// Cache settings
cache: {
enabled: true,
ttl: 300, // 5 minutes
},
});Basic Verification
Verify a credential using just the verification code (from QR scan).
// Verify by code (from QR scan)
const result = await verifier.verifyByCode('A3B7K9M2');
if (result.valid) {
console.log('✓ Credential is valid');
console.log('Name:', result.attributes.givenName, result.attributes.familyName);
console.log('Level:', result.attributes.level);
console.log('Valid until:', result.attributes.validUntil);
console.log('Issuer:', result.issuer.name);
} else {
console.log('✗ Verification failed:', result.reason);
// 'expired' | 'revoked' | 'not_found' | 'invalid_signature' | 'issuer_untrusted'
}Presentation Verification
Verify a selective disclosure presentation with predicate proofs.
// Verify a presentation (from advanced QR or direct transfer)
const result = await verifier.verifyPresentation(presentationJwt);
if (result.valid) {
// Disclosed attributes
console.log('Disclosed:', result.disclosed);
// { givenName: 'Rahul', familyName: 'Kumar', level: 'gold' }
// Predicate results
console.log('Predicates:', result.predicates);
// [
// { attribute: 'testScore', op: '>=', value: 80, satisfied: true },
// { attribute: 'hazardScore', op: '>=', value: 75, satisfied: true }
// ]
// What was NOT disclosed
console.log('Hidden:', result.hidden);
// ['email', 'phone', 'testScore', 'hazardScore']
}Revocation Checking
// Check current revocation status
const status = await verifier.checkRevocationStatus(credentialId);
console.log(status);
// {
// status: 'active', // | 'revoked' | 'suspended'
// checkedAt: '2026-01-15T10:30:00Z',
// // If revoked:
// revokedAt: null,
// reason: null,
// }Web Widget Integration
<!-- Add verification widget to your website -->
<script src="https://cdn.rscp.org/verifier-widget.js"></script>
<rscp-verify
registry-url="https://registry.rscp.org"
on-success="handleVerification"
on-error="handleError"
theme="light"
></rscp-verify>
<script>
function handleVerification(result) {
console.log('Verified:', result.attributes);
}
function handleError(error) {
console.error('Verification failed:', error);
}
</script>Error Handling
import {
VerificationError,
NetworkError,
ExpiredCredentialError,
RevokedCredentialError,
} from '@rscp/verifier-sdk';
try {
const result = await verifier.verifyByCode(code);
} catch (error) {
if (error instanceof ExpiredCredentialError) {
showMessage('This credential has expired');
} else if (error instanceof RevokedCredentialError) {
showMessage(`Credential revoked: ${error.reason}`);
} else if (error instanceof NetworkError) {
// Try offline verification if available
const offlineResult = await verifier.verifyOffline(presentation);
} else {
showMessage('Verification failed');
}
}Performance Tips
- Enable caching
Cache issuer public keys and recent verifications to reduce latency.
- Batch verification
Use
verifier.verifyBatch()for multiple credentials. - Don't skip signature verification
Always verify signatures, even if you trust the source.