Skip to main content

Overview

The collectPAN() method securely collects credit card details from users through an isolated iframe. The card data is tokenized via Prava’s PCI DSS vault and returns only safe metadata — your servers never see the raw PAN.

Method Signature

prava.collectPAN(options: CollectPANOptions): Promise<CollectPANResult>

Parameters

options
CollectPANOptions
required
Configuration options for card collection

Return Value

result
CollectPANResult

Example

import { useEffect, useRef, useState } from 'react';
import { PravaSDK } from '@prava-sdk/core';
import type { CollectPANResult } from '@prava-sdk/core';

function CardEnrollment({ session }: { session: { session_token: string; iframe_url: string } }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const sdkRef = useRef<PravaSDK | null>(null);
  const [enrolled, setEnrolled] = useState<CollectPANResult | null>(null);

  useEffect(() => {
    const sdk = new PravaSDK({ publishableKey: 'pk_live_xxx' });
    sdkRef.current = sdk;
    return () => sdk.destroy();
  }, []);

  const handleEnroll = async () => {
    if (!sdkRef.current || !containerRef.current) return;

    const result = await sdkRef.current.collectPAN({
      sessionToken: session.session_token,
      iframeUrl: session.iframe_url,
      container: containerRef.current,
      onReady: () => console.log('Form loaded'),
      onChange: (state) => {
        console.log('Complete:', state.isComplete);
      },
      onSuccess: (data) => {
        console.log(`Enrolled: •••• ${data.last4}`);
        setEnrolled(data);
      },
      onError: (err) => console.error(err.code, err.message),
    });
  };

  return (
    <div>
      <div ref={containerRef} />
      <button onClick={handleEnroll}>Enroll Card</button>
      {enrolled && <p>Card enrolled: {enrolled.brand} •••• {enrolled.last4}</p>}
    </div>
  );
}
<div id="card-form"></div>
<button id="submit-btn" disabled>Enroll Card</button>

Flow Diagram

Under the Hood

When you call collectPAN(), here’s what happens:
1

Iframe Injection

The SDK injects a secure, sandboxed iframe into your specified container. The iframe is served from Prava’s domain — card data never touches your DOM or servers.
2

Session Validation

The iframe validates the session token with Prava’s backend to ensure the request is legitimate and not expired.
3

User Input

The user enters their card details (number, expiry, CVV) in the iframe form with real-time validation.
4

PCI DSS Vaulting

When the user submits, the iframe tokenizes the PAN via Prava’s PCI DSS vault. Your servers never see the raw card number.
5

Result

The enrollment result (with enrollmentId, last4, brand, expMonth, expYear) is returned to your app.

Validation States

The onChange callback receives a CardValidationState object on every keystroke:
interface CardValidationState {
  cardNumber: FieldState;
  expiry: FieldState;
  cvv: FieldState;
  isComplete: boolean; // true when all fields are valid
}

interface FieldState {
  isEmpty: boolean;
  isValid: boolean;
  isFocused: boolean;
  error?: string;
}
Use isComplete to enable/disable your submit button:
onChange: (state) => {
  submitButton.disabled = !state.isComplete;
}

Error Handling

Common Errors

CodeCauseResolution
SDK_ALREADY_ACTIVEcollectPAN called while another session is activeCall destroy() first
INVALID_CONFIGMissing iframeUrl or publishableKeyCheck your config
IFRAME_LOAD_ERRORIframe failed to loadCheck network, verify iframeUrl
CARD_NOT_FOUNDCard ID doesn’t exist or was removedRe-enroll or use a different card

Security Notes

Never attempt to bypass the iframe or collect card data directly. The iframe is sandboxed with allow-scripts allow-same-origin allow-forms allow-popups — minimal permissions. Card data never touches your DOM, JS, or servers.
PostMessage communication is origin-locked. The iframe resolves its backend from its own hostname — merchants cannot inject a fake backend URL.

Next Steps