DocsImplementation Guide

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

bash
npm install @rscp/issuer-sdk
# or
yarn add @rscp/issuer-sdk
# or
pnpm add @rscp/issuer-sdk

Configuration

typescript
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).

typescript
// 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);
// → A3B7K9M2

Submitting to Registry

Only public attributes are submitted. The SDK automatically strips private data before transmission.

typescript
// 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

typescript
// 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.

typescript
// 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

typescript
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

bash
npm install @rscp/wallet-sdk
# React Native peer dependencies
npm install react-native-keychain react-native-qrcode-svg

Configuration

typescript
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

typescript
// 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.

typescript
// 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

typescript
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 data

React Native Component

tsx
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.

typescript
// 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

bash
npm install @rscp/verifier-sdk

Configuration

typescript
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).

typescript
// 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.

typescript
// 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

typescript
// 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

html
<!-- 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

typescript
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.