> ## Documentation Index
> Fetch the complete documentation index at: https://docs.baanx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Secure PIN Operations

> Token-based PIN management with PCI-compliant image delivery

## Overview

PIN operations use a secure token-based flow that keeps sensitive data completely isolated from your application. Card details and PIN viewing are delivered as secure images, while PIN setting uses hosted pages. You never handle or store sensitive PIN data.

<Warning>
  Never attempt to handle, store, or transmit PIN data directly in your application. Always use the token-based flow.
</Warning>

## Security Architecture

### Token-Based Flow

Sensitive operations follow a simple pattern:

<Steps>
  <Step title="Generate Token">
    Request a single-use, time-limited token from the API
  </Step>

  <Step title="Display Secure Content">
    For viewing: Display secure image. For setting: Use PCI-compliant hosted page
  </Step>

  <Step title="Token Expires">
    Token is invalidated after use or timeout (\~10 minutes)
  </Step>
</Steps>

```mermaid theme={null}
sequenceDiagram
    participant App as Your Application
    participant API as Baanx API
    participant Image as Secure Image
    participant User as User

    App->>API: POST /v1/card/details/token
    API-->>App: {token, imageUrl}
    App->>Image: Display image
    Image->>User: Show card details securely
    Note over API: Token invalidated after access
```

### Benefits

<CardGroup cols={2}>
  <Card title="PCI Compliance" icon="shield-check">
    No sensitive data touches your infrastructure
  </Card>

  <Card title="Zero Liability" icon="scale-balanced">
    Reduces security audit scope
  </Card>

  <Card title="Built-in Security" icon="lock">
    Session management and expiration
  </Card>

  <Card title="User Trust" icon="user-shield">
    Industry-standard secure pages
  </Card>
</CardGroup>

## Viewing Card Details

Display full card information (PAN, CVV, expiry) as a secure image. The card details are never transmitted to or stored by your application.

### Step 1: Generate Details Token

<CodeGroup>
  ```javascript JavaScript/Node.js theme={null}
  const response = await fetch('https://dev.api.baanx.com/v1/card/details/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Client-ID': 'your_client_id',
      'Authorization': `Bearer ${userAccessToken}`
    },
    body: JSON.stringify({
      customCss: {
        cardBackgroundColor: '#000000',
        cardTextColor: '#FFFFFF',
        panBackgroundColor: '#EFEFEF',
        panTextColor: '#000000'
      }
    })
  });

  const { token, imageUrl } = await response.json();
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://dev.api.baanx.com/v1/card/details/token',
      headers={
          'Content-Type': 'application/json',
          'X-Client-ID': 'your_client_id',
          'Authorization': f'Bearer {user_access_token}'
      },
      json={
          'customCss': {
              'cardBackgroundColor': '#000000',
              'cardTextColor': '#FFFFFF',
              'panBackgroundColor': '#EFEFEF',
              'panTextColor': '#000000'
          }
      }
  )

  data = response.json()
  token = data['token']
  image_url = data['imageUrl']
  ```

  ```bash cURL theme={null}
  curl -X POST https://dev.api.baanx.com/v1/card/details/token \
    -H "Content-Type: application/json" \
    -H "X-Client-ID: your_client_id" \
    -H "Authorization: Bearer YOUR_USER_ACCESS_TOKEN" \
    -d '{
      "customCss": {
        "cardBackgroundColor": "#000000",
        "cardTextColor": "#FFFFFF",
        "panBackgroundColor": "#EFEFEF",
        "panTextColor": "#000000"
      }
    }'
  ```
</CodeGroup>

### Request Parameters

<ParamField path="customCss" type="object">
  Optional styling to match your brand. If omitted, default styling is applied.

  <Expandable title="properties">
    <ParamField path="cardBackgroundColor" type="string" default="#000000">
      Card background color (hex format)
    </ParamField>

    <ParamField path="cardTextColor" type="string" default="#FFFFFF">
      Text color on card background (hex format). Must contrast with cardBackgroundColor.
    </ParamField>

    <ParamField path="panBackgroundColor" type="string" default="#EFEFEF">
      PAN number background color (hex format)
    </ParamField>

    <ParamField path="panTextColor" type="string" default="#000000">
      PAN number text color (hex format). Must contrast with panBackgroundColor.
    </ParamField>
  </Expandable>
</ParamField>

### Response

```json theme={null}
{
  "token": "100a99cf-f4d3-4fa1-9be9-2e9828b20ebb",
  "imageUrl": "https://cards.baanx.com/details-image?token=100a99cf-f4d3-4fa1-9be9-2e9828b20ebb"
}
```

<ResponseField name="token" type="string">
  Single-use token valid for \~10 minutes
</ResponseField>

<ResponseField name="imageUrl" type="string">
  URL that renders card details as a secure image. Use as `src` attribute of an `<img>` tag.
</ResponseField>

<Warning>
  **Security**: Treat `imageUrl` as highly sensitive. Never log or store it. Use HTTPS only and display in secure contexts.
</Warning>

### Step 2: Display Card Image

```javascript theme={null}
function showCardDetails(imageUrl) {
  const img = document.createElement('img');
  img.src = imageUrl;
  img.alt = 'Card Details';
  img.style.cssText = `
    max-width: 100%;
    border-radius: 12px;
    box-shadow: 0 4px 6px rgba(0,0,0,0.1);
  `;

  document.getElementById('card-details-container').appendChild(img);
}
```

### Complete React Implementation

<CodeGroup>
  ```javascript React Component theme={null}
  import { useState } from 'react';

  export function CardDetailsViewer({ clientId, accessToken }) {
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);
    const [imageUrl, setImageUrl] = useState(null);

    async function viewCardDetails() {
      setLoading(true);
      setError(null);

      try {
        const response = await fetch('https://dev.api.baanx.com/v1/card/details/token', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'X-Client-ID': clientId,
            'Authorization': `Bearer ${accessToken}`
          },
          body: JSON.stringify({
            customCss: {
              cardBackgroundColor: '#000000',
              cardTextColor: '#FFFFFF',
              panBackgroundColor: '#EFEFEF',
              panTextColor: '#000000'
            }
          })
        });

        if (!response.ok) {
          throw new Error('Failed to generate details token');
        }

        const data = await response.json();
        setImageUrl(data.imageUrl);

      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    function hideCardDetails() {
      setImageUrl(null);
    }

    if (imageUrl) {
      return (
        <div className="card-details-viewer">
          <div className="overlay" onClick={hideCardDetails} />
          <div className="modal">
            <button className="close-btn" onClick={hideCardDetails}>
              ✕
            </button>
            <img
              src={imageUrl}
              alt="Card Details"
              style={{
                maxWidth: '100%',
                borderRadius: '12px'
              }}
            />
          </div>
        </div>
      );
    }

    return (
      <div>
        <button
          onClick={viewCardDetails}
          disabled={loading}
          className="btn-view-details"
        >
          {loading ? 'Loading...' : '👁️ View Card Details'}
        </button>

        {error && (
          <div className="error-message">{error}</div>
        )}
      </div>
    );
  }
  ```
</CodeGroup>

## Viewing PIN

Display the card PIN as a secure image. The PIN is never transmitted to or stored by your application.

### Step 1: Generate PIN Token

<CodeGroup>
  ```javascript JavaScript/Node.js theme={null}
  const response = await fetch('https://dev.api.baanx.com/v1/card/pin/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Client-ID': 'your_client_id',
      'Authorization': `Bearer ${userAccessToken}`
    },
    body: JSON.stringify({
      customCss: {
        backgroundColor: '#EFEFEF',
        textColor: '#000000'
      }
    })
  });

  const { token, imageUrl } = await response.json();
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://dev.api.baanx.com/v1/card/pin/token',
      headers={
          'Content-Type': 'application/json',
          'X-Client-ID': 'your_client_id',
          'Authorization': f'Bearer {user_access_token}'
      },
      json={
          'customCss': {
              'backgroundColor': '#EFEFEF',
              'textColor': '#000000'
          }
      }
  )

  data = response.json()
  token = data['token']
  image_url = data['imageUrl']
  ```

  ```bash cURL theme={null}
  curl -X POST https://dev.api.baanx.com/v1/card/pin/token \
    -H "Content-Type: application/json" \
    -H "X-Client-ID: your_client_id" \
    -H "Authorization: Bearer YOUR_USER_ACCESS_TOKEN" \
    -d '{
      "customCss": {
        "backgroundColor": "#EFEFEF",
        "textColor": "#000000"
      }
    }'
  ```
</CodeGroup>

### Request Parameters

<ParamField path="customCss" type="object">
  Optional styling to match your brand. If omitted, default styling is applied.

  <Expandable title="properties">
    <ParamField path="backgroundColor" type="string" default="#EFEFEF">
      Background color of the PIN image (hex format)
    </ParamField>

    <ParamField path="textColor" type="string" default="#000000">
      Text color for the PIN digits (hex format). Must contrast with backgroundColor.
    </ParamField>
  </Expandable>
</ParamField>

### Response

```json theme={null}
{
  "token": "100a99cf-f4d3-4fa1-9be9-2e9828b20ebb",
  "imageUrl": "https://cards.baanx.com/details-image?token=100a99cf-f4d3-4fa1-9be9-2e9828b20ebb"
}
```

<ResponseField name="token" type="string">
  Single-use token valid for \~10 minutes
</ResponseField>

<ResponseField name="imageUrl" type="string">
  URL that renders PIN as a secure image. Use as `src` attribute of an `<img>` tag.
</ResponseField>

<Warning>
  **Security**: Treat `imageUrl` as highly sensitive. Never log or store it. Display only in authenticated, secure contexts.
</Warning>

### Step 2: Display PIN Image

```javascript theme={null}
function showPIN(imageUrl) {
  const img = document.createElement('img');
  img.src = imageUrl;
  img.alt = 'Card PIN';
  img.style.cssText = `
    max-width: 100%;
    border-radius: 8px;
  `;

  document.getElementById('pin-container').appendChild(img);
}
```

### Complete React Implementation

<CodeGroup>
  ```javascript React Component theme={null}
  import { useState } from 'react';

  export function PINViewer({ clientId, accessToken }) {
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);
    const [imageUrl, setImageUrl] = useState(null);

    async function viewPIN() {
      setLoading(true);
      setError(null);

      try {
        const response = await fetch('https://dev.api.baanx.com/v1/card/pin/token', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'X-Client-ID': clientId,
            'Authorization': `Bearer ${accessToken}`
          },
          body: JSON.stringify({
            customCss: {
              backgroundColor: '#EFEFEF',
              textColor: '#000000'
            }
          })
        });

        if (!response.ok) {
          throw new Error('Failed to generate PIN token');
        }

        const data = await response.json();
        setImageUrl(data.imageUrl);

      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    function hidePIN() {
      setImageUrl(null);
    }

    if (imageUrl) {
      return (
        <div className="pin-viewer">
          <div className="overlay" onClick={hidePIN} />
          <div className="modal">
            <button className="close-btn" onClick={hidePIN}>
              ✕
            </button>
            <img
              src={imageUrl}
              alt="Card PIN"
              style={{
                maxWidth: '100%',
                borderRadius: '8px'
              }}
            />
          </div>
        </div>
      );
    }

    return (
      <div>
        <button
          onClick={viewPIN}
          disabled={loading}
          className="btn-view-pin"
        >
          {loading ? 'Loading...' : '👁️ View PIN'}
        </button>

        {error && (
          <div className="error-message">{error}</div>
        )}
      </div>
    );
  }
  ```
</CodeGroup>

## Setting/Changing PIN

Allow users to create or change their PIN through a secure hosted page.

### Step 1: Generate Set PIN Token

<CodeGroup>
  ```javascript JavaScript/Node.js theme={null}
  const response = await fetch('https://dev.api.baanx.com/v1/card/set-pin/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Client-ID': 'your_client_id',
      'Authorization': `Bearer ${userAccessToken}`
    },
    body: JSON.stringify({
      isEmbedded: true,
      customCss: {
        backgroundColor: '#EFEFEF',
        textColor: '#000000',
        backgroundColorPrimary: '#000000',
        textColorPrimary: '#FFFFFF',
        buttonBorderRadius: 8,
        pinBorderRadius: 4
      }
    })
  });

  const { token, hostedPageUrl } = await response.json();
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://dev.api.baanx.com/v1/card/set-pin/token',
      headers={
          'Content-Type': 'application/json',
          'X-Client-ID': 'your_client_id',
          'Authorization': f'Bearer {user_access_token}'
      },
      json={
          'isEmbedded': True,
          'customCss': {
              'backgroundColor': '#EFEFEF',
              'textColor': '#000000',
              'backgroundColorPrimary': '#000000',
              'textColorPrimary': '#FFFFFF',
              'buttonBorderRadius': 8,
              'pinBorderRadius': 4
          }
      }
  )

  data = response.json()
  token = data['token']
  hosted_page_url = data['hostedPageUrl']
  ```

  ```bash cURL theme={null}
  curl -X POST https://dev.api.baanx.com/v1/card/set-pin/token \
    -H "Content-Type: application/json" \
    -H "X-Client-ID: your_client_id" \
    -H "Authorization: Bearer YOUR_USER_ACCESS_TOKEN" \
    -d '{
      "isEmbedded": true,
      "customCss": {
        "backgroundColor": "#EFEFEF",
        "textColor": "#000000",
        "backgroundColorPrimary": "#000000",
        "textColorPrimary": "#FFFFFF"
      }
    }'
  ```
</CodeGroup>

### Request Parameters

Same as PIN viewing, with identical `customCss` options.

### Response

```json theme={null}
{
  "token": "100a99cf-f4d3-4fa1-9be9-2e9828b20ebb",
  "hostedPageUrl": "https://cards.baanx.com/pin-direct/set?token=100a99cf-f4d3-4fa1-9be9-2e9828b20ebb"
}
```

<Note>
  Set PIN hosted page includes built-in validation for PIN requirements (4 digits, no sequential patterns, etc.)
</Note>

### Step 2: Display Set PIN Page

```javascript theme={null}
function showSetPIN(hostedPageUrl) {
  const iframe = document.createElement('iframe');
  iframe.src = hostedPageUrl;
  iframe.style.cssText = `
    width: 100%;
    height: 450px;
    border: none;
    border-radius: 8px;
  `;

  document.getElementById('pin-container').appendChild(iframe);

  window.addEventListener('message', (event) => {
    if (event.origin !== 'https://cards.baanx.com') return;

    switch (event.data) {
      case 'success':
        console.log('PIN set successfully');
        iframe.remove();
        showSuccessMessage('PIN updated successfully');
        break;
      case 'abort':
        console.log('User cancelled PIN change');
        iframe.remove();
        break;
      case 'error':
        console.error('Error setting PIN');
        iframe.remove();
        showErrorMessage('Failed to set PIN');
        break;
    }
  });
}
```

## Complete PIN Management Component

<CodeGroup>
  ```javascript React Component theme={null}
  import { useState } from 'react';

  export function PINManagement({ clientId, accessToken, cardStatus }) {
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);
    const [activeView, setActiveView] = useState(null);
    const [imageUrl, setImageUrl] = useState(null);
    const [hostedPageUrl, setHostedPageUrl] = useState(null);

    async function generateToken(endpoint) {
      setLoading(true);
      setError(null);

      try {
        const customCss = endpoint === 'pin'
          ? {
              backgroundColor: '#f5f5f5',
              textColor: '#1a1a1a'
            }
          : {
              backgroundColor: '#f5f5f5',
              textColor: '#1a1a1a',
              backgroundColorPrimary: '#2563eb',
              textColorPrimary: '#ffffff',
              buttonBorderRadius: 8,
              pinBorderRadius: 4
            };

        const requestBody = endpoint === 'pin'
          ? { customCss }
          : { isEmbedded: true, customCss };

        const response = await fetch(`https://dev.api.baanx.com/v1/card/${endpoint}/token`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'X-Client-ID': clientId,
            'Authorization': `Bearer ${accessToken}`
          },
          body: JSON.stringify(requestBody)
        });

        if (!response.ok) {
          throw new Error('Failed to generate token');
        }

        const data = await response.json();

        if (endpoint === 'pin') {
          setImageUrl(data.imageUrl);
        } else {
          setHostedPageUrl(data.hostedPageUrl);
          window.addEventListener('message', handleMessage);
        }

        setActiveView(endpoint);

      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    function handleMessage(event) {
      if (event.origin !== 'https://cards.baanx.com') return;

      switch (event.data) {
        case 'success':
          closeView();
          break;
        case 'abort':
          closeView();
          break;
        case 'error':
          setError('Operation failed');
          closeView();
          break;
      }
    }

    function closeView() {
      setActiveView(null);
      setImageUrl(null);
      setHostedPageUrl(null);
      window.removeEventListener('message', handleMessage);
    }

    if (activeView === 'pin' && imageUrl) {
      return (
        <div className="pin-modal">
          <div className="overlay" onClick={closeView} />
          <div className="modal-content">
            <button className="close-btn" onClick={closeView}>✕</button>
            <img
              src={imageUrl}
              alt="Card PIN"
              style={{
                maxWidth: '100%',
                borderRadius: '8px'
              }}
            />
          </div>
        </div>
      );
    }

    if (activeView === 'set-pin' && hostedPageUrl) {
      return (
        <div className="pin-modal">
          <div className="overlay" onClick={closeView} />
          <div className="modal-content">
            <button className="close-btn" onClick={closeView}>✕</button>
            <iframe
              src={hostedPageUrl}
              style={{
                width: '100%',
                height: '450px',
                border: 'none',
                borderRadius: '8px'
              }}
            />
          </div>
        </div>
      );
    }

    const isFrozen = cardStatus === 'FROZEN';
    const isBlocked = cardStatus === 'BLOCKED';

    return (
      <div className="pin-management">
        <h3>PIN Management</h3>

        {error && <div className="error-message">{error}</div>}

        <div className="pin-actions">
          <button
            onClick={() => generateToken('pin')}
            disabled={loading || isBlocked}
            className="btn-view-pin"
          >
            {loading ? 'Loading...' : '👁️ View PIN'}
          </button>

          <button
            onClick={() => generateToken('set-pin')}
            disabled={loading || isFrozen || isBlocked}
            className="btn-set-pin"
          >
            {loading ? 'Loading...' : '🔐 Change PIN'}
          </button>
        </div>

        {isFrozen && (
          <p className="info-text">
            Unfreeze your card to change PIN
          </p>
        )}

        {isBlocked && (
          <p className="error-text">
            Card is blocked - contact support
          </p>
        )}
      </div>
    );
  }
  ```
</CodeGroup>

## Sequence Diagrams

### View Card Details Flow

```mermaid theme={null}
sequenceDiagram
    participant User
    participant App as Your App
    participant API as Baanx API
    participant Image as Secure Image

    User->>App: Click "View Card Details"
    App->>API: POST /v1/card/details/token
    API-->>App: {token, imageUrl}
    App->>Image: Display image
    Image->>User: Show card details securely
    User->>App: Click "Close"
    App->>App: Remove image from DOM
    Note over API: Token invalidated after access
```

### View PIN Flow

```mermaid theme={null}
sequenceDiagram
    participant User
    participant App as Your App
    participant API as Baanx API
    participant Image as Secure Image

    User->>App: Click "View PIN"
    App->>API: POST /v1/card/pin/token
    API-->>App: {token, imageUrl}
    App->>Image: Display image
    Image->>User: Show PIN securely
    User->>App: Click "Close"
    App->>App: Remove image from DOM
    Note over API: Token invalidated after access
```

### Set PIN Flow

```mermaid theme={null}
sequenceDiagram
    participant User
    participant App as Your App
    participant API as Baanx API
    participant Hosted as Hosted Page

    User->>App: Click "Change PIN"
    App->>API: POST /v1/card/set-pin/token
    API-->>App: {token, hostedPageUrl}
    App->>Hosted: Display iframe with token
    Hosted->>User: Show PIN entry form
    User->>Hosted: Enter new PIN
    Hosted->>Hosted: Validate PIN
    Hosted->>API: Submit PIN update
    API-->>Hosted: Success
    Hosted-->>App: postMessage("success")
    App->>App: Remove iframe & show success
    Note over API: Token invalidated
```

## Customization Examples

### Dark Theme

```javascript theme={null}
// Card details viewing
const darkThemeCardDetails = {
  cardBackgroundColor: '#1a1a1a',
  cardTextColor: '#ffffff',
  panBackgroundColor: '#2a2a2a',
  panTextColor: '#ffffff'
};

// PIN viewing
const darkThemePIN = {
  backgroundColor: '#1a1a1a',
  textColor: '#ffffff'
};

// PIN setting (hosted page)
const darkThemeSetPIN = {
  backgroundColor: '#1a1a1a',
  textColor: '#ffffff',
  backgroundColorPrimary: '#3b82f6',
  textColorPrimary: '#ffffff',
  buttonBorderRadius: 8,
  pinBorderRadius: 4
};
```

### Light Theme

```javascript theme={null}
// Card details viewing
const lightThemeCardDetails = {
  cardBackgroundColor: '#ffffff',
  cardTextColor: '#1a1a1a',
  panBackgroundColor: '#f5f5f5',
  panTextColor: '#1a1a1a'
};

// PIN viewing
const lightThemePIN = {
  backgroundColor: '#ffffff',
  textColor: '#1a1a1a'
};

// PIN setting (hosted page)
const lightThemeSetPIN = {
  backgroundColor: '#ffffff',
  textColor: '#1a1a1a',
  backgroundColorPrimary: '#2563eb',
  textColorPrimary: '#ffffff',
  buttonBorderRadius: 8,
  pinBorderRadius: 4
};
```

### Brand Theme

```javascript theme={null}
// Card details viewing
const brandThemeCardDetails = {
  cardBackgroundColor: '#7c3aed', // Brand purple
  cardTextColor: '#ffffff',
  panBackgroundColor: '#f5f5f5',
  panTextColor: '#1a1a1a'
};

// PIN viewing
const brandThemePIN = {
  backgroundColor: '#fafafa',
  textColor: '#7c3aed'
};

// PIN setting (hosted page)
const brandThemeSetPIN = {
  backgroundColor: '#fafafa',
  textColor: '#1a1a1a',
  backgroundColorPrimary: '#7c3aed', // Brand purple
  textColorPrimary: '#ffffff',
  buttonBorderRadius: 12,
  pinBorderRadius: 8
};
```

## Security Best Practices

### Token Handling

<Check>Never store tokens in localStorage or sessionStorage</Check>
<Check>Use tokens immediately after generation</Check>
<Check>Don't reuse tokens - generate new ones for each operation</Check>
<Check>Implement token expiration handling</Check>

### Image URL Security

<Warning>
  Image URLs contain sensitive data. Never log, cache, or store them. Use HTTPS only and display only in authenticated contexts.
</Warning>

```javascript theme={null}
// ✅ Good: Use imageUrl immediately and clear after viewing
const { imageUrl } = await response.json();
displayImage(imageUrl);

// Clean up when user closes
onClose(() => {
  removeImageFromDOM();
  // imageUrl is garbage collected
});

// ❌ Bad: Don't store or log
localStorage.setItem('pinImage', imageUrl); // NEVER
console.log('Image URL:', imageUrl); // NEVER
```

### iframe Security (Set PIN Only)

For PIN setting operations that use hosted pages:

```html theme={null}
<iframe
  src="{hostedPageUrl}"
  sandbox="allow-scripts allow-same-origin allow-forms"
  allow="clipboard-write"
  referrerpolicy="no-referrer"
  style="border: none;"
/>
```

### Origin Verification (Set PIN Only)

Only needed for PIN setting hosted pages:

```javascript theme={null}
window.addEventListener('message', (event) => {
  // Always verify origin
  if (event.origin !== 'https://cards.baanx.com') {
    console.warn('Rejected message from unauthorized origin:', event.origin);
    return;
  }

  // Process message
  handleMessage(event.data);
});
```

### Content Security Policy

```html theme={null}
<meta http-equiv="Content-Security-Policy" content="
  frame-src https://cards.baanx.com;
  img-src https://cards.baanx.com;
  script-src 'self' 'unsafe-inline';
  connect-src https://dev.api.baanx.com;
">
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="Token expires before user views">
    **Cause**: Token has \~10 minute lifetime

    **Solution**: Generate token only when user clicks action button

    ```javascript theme={null}
    // ❌ Bad: Generate token on page load
    useEffect(() => {
      generateToken(); // May expire before use
    }, []);

    // ✅ Good: Generate on user action
    <button onClick={generateToken}>View PIN</button>
    ```
  </Accordion>

  <Accordion title="Image fails to load">
    **Cause**: Token expired, already used, or network issue

    **Solution**: Implement error handling and retry logic

    ```javascript theme={null}
    const img = document.createElement('img');

    img.onerror = () => {
      console.error('Failed to load image');
      alert('Unable to display. Please try again.');
    };

    img.onload = () => {
      console.log('Image loaded successfully');
    };

    img.src = imageUrl;
    ```
  </Accordion>

  <Accordion title="Image styling doesn't match">
    **Cause**: Color contrast issues or invalid hex codes

    **Solution**: Ensure valid hex codes and sufficient contrast

    ```javascript theme={null}
    // ❌ Bad: Same colors for background and text
    {
      backgroundColor: '#000000',
      textColor: '#000000' // Can't read!
    }

    // ✅ Good: High contrast
    {
      backgroundColor: '#000000',
      textColor: '#ffffff'
    }
    ```
  </Accordion>

  <Accordion title="PostMessage events not received (Set PIN)">
    **Cause**: Origin verification failing or listener not attached

    **Solution**: Verify origin and ensure listener is active (only applies to set-PIN hosted page)

    ```javascript theme={null}
    window.addEventListener('message', (event) => {
      console.log('Received message:', event.data, 'from:', event.origin);

      if (event.origin !== 'https://cards.baanx.com') {
        console.warn('Wrong origin');
        return;
      }

      handleMessage(event.data);
    });
    ```
  </Accordion>

  <Accordion title="Can't set PIN on frozen card">
    **Cause**: PIN changes not allowed on frozen cards

    **Solution**: Check card status and disable PIN change button

    ```javascript theme={null}
    <button
      onClick={changePIN}
      disabled={cardStatus === 'FROZEN' || cardStatus === 'BLOCKED'}
    >
      Change PIN
    </button>
    ```
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Transaction History" icon="receipt" href="/guides/card/transactions">
    View and manage card transactions
  </Card>

  <Card title="Card Management" icon="toggle-on" href="/guides/card/management">
    Freeze, unfreeze, and control cards
  </Card>

  <Card title="Order Cards" icon="credit-card" href="/guides/card/ordering">
    Issue new virtual cards
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/card/details-token">
    Complete API documentation
  </Card>
</CardGroup>
