npm package: @keychain-pay/js
Description: Keychain Payment SDK - Accept payments with a beautiful checkout modal
Author: Keychain
Latest version: 1.2.0

# @keychain-pay/js

Accept USDC payments with a secure, simple integration. Multi-currency support included.

## Installation

```bash
npm install @keychain-pay/js
```

Or via CDN:
```html
<script src="https://js.trykeychain.com/v1.js"></script>
```

## Security First

**Your API key never touches the frontend.** The SDK only needs a `sessionId` - an opaque token that reveals nothing sensitive.

```
┌─────────────┐     1. Create session      ┌─────────────┐
│   Backend   │ ────────────────────────── │  Keychain   │
│  (API Key)  │ ◄────────────────────────  │    API      │
└─────────────┘     Returns sessionId      └─────────────┘
       │
       │ 2. Return sessionId
       ▼
┌─────────────┐     3. Open checkout       ┌─────────────┐
│  Frontend   │ ────────────────────────── │  Checkout   │
│ (sessionId) │ ◄────────────────────────  │   Modal     │
└─────────────┘     Payment complete       └─────────────┘
```

## Quick Start

### 1. Backend: Create Session

```javascript
// Your backend - API key stays here
app.post('/api/create-checkout', async (req, res) => {
  const response = await fetch('https://api.trykeychain.com/api/v1/checkout/sessions', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.KEYCHAIN_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amount: 100000,      // ₹1,000.00 in paise (or 2500 cents for $25)
      currency: 'INR',     // Supports: USD, INR, EUR, GBP, CAD, AUD, MXN, PHP
      orderId: 'order_123',
      returnUrl: 'https://yoursite.com/success',
    }),
  });

  const { session } = await response.json();
  res.json({ sessionId: session.id });
});
```

### 2. Frontend: Open Checkout

```javascript
import { Keychain } from '@keychain-pay/js';

// When user clicks "Pay"
async function handlePay() {
  const res = await fetch('/api/create-checkout', { method: 'POST' });
  const { sessionId } = await res.json();

  Keychain.open({
    sessionId,
    onSuccess: (result) => {
      console.log('Paid!', result.transactionId);
      window.location.href = '/success';
    },
    onCancel: () => console.log('Cancelled'),
    onError: (error) => console.error(error.message),
  });
}
```

## Multi-Currency Support

Price in your local currency - customers pay in theirs with automatic conversion.

### Supported Currencies

| Currency | Code | Symbol | Example |
|----------|------|--------|---------|
| US Dollar | USD | $ | $100.00 |
| Indian Rupee | INR | ₹ | ₹8,350.00 |
| Euro | EUR | € | €100,00 |
| British Pound | GBP | £ | £100.00 |
| Canadian Dollar | CAD | C$ | C$100.00 |
| Australian Dollar | AUD | A$ | A$100.00 |
| Mexican Peso | MXN | $ | $100.00 |
| Philippine Peso | PHP | ₱ | ₱100.00 |

### Currency Utilities

Format amounts consistently with built-in utilities:

```javascript
import {
  formatCurrency,
  formatAmount,
  getCurrencyFromPhone,
  getCurrencySymbol,
  formatExchangeRate,
} from '@keychain-pay/js';

// Format with currency symbol and locale
formatCurrency(100000, 'INR');  // "₹1,000.00"
formatCurrency(2500, 'USD');    // "$25.00"
formatCurrency(2500, 'EUR');    // "25,00 €"

// Format number only (no symbol)
formatAmount(100000, 'INR');    // "1,000.00"

// Detect currency from phone number
getCurrencyFromPhone('+91...');  // "INR"
getCurrencyFromPhone('+1...');   // "USD"
getCurrencyFromPhone('+44...');  // "GBP"

// Get currency symbol
getCurrencySymbol('INR');        // "₹"
getCurrencySymbol('EUR');        // "€"

// Format exchange rate display
formatExchangeRate(83.5, 'USD', 'INR');  // "1 USD = 83.5 INR"
```

### Currency Configuration

Access full currency configuration:

```javascript
import { CURRENCY_CONFIG, getCurrencyConfig } from '@keychain-pay/js';

const config = getCurrencyConfig('INR');
// {
//   code: 'INR',
//   symbol: '₹',
//   decimals: 2,
//   locale: 'en-IN',
//   name: 'Indian Rupee'
// }
```

## CDN Usage

```html
<script src="https://js.trykeychain.com/v1.js"></script>
<script>
  document.getElementById('pay-btn').addEventListener('click', async () => {
    const res = await fetch('/api/create-checkout', { method: 'POST' });
    const { sessionId } = await res.json();

    Keychain.open({
      sessionId,
      onSuccess: () => window.location.href = '/success',
    });
  });
</script>
```

## API Reference

### `Keychain.open(options)`

Opens the checkout modal.

| Option | Type | Required | Description |
|--------|------|----------|-------------|
| `sessionId` | string | Yes | Session ID from your backend |
| `onSuccess` | function | No | Called when payment succeeds |
| `onCancel` | function | No | Called when user cancels |
| `onError` | function | No | Called on error |
| `checkoutUrl` | string | No | Custom checkout URL (for testing) |

### `Keychain.close()`

Programmatically close the checkout modal.

### Convenience Exports

```javascript
import { openCheckout, closeCheckout } from '@keychain-pay/js';

openCheckout({ sessionId, onSuccess: () => {} });
closeCheckout();
```

### Browser Global

When loaded via CDN, `Keychain` is available globally:

```javascript
window.Keychain.open({ sessionId });
```

### PaymentResult

```typescript
interface PaymentResult {
  transactionId?: string;
  subscriptionId?: string;
  sessionId?: string;
  reward?: {
    amountCents: number;
  };
}
```

## TypeScript Support

Full TypeScript definitions included:

```typescript
import {
  Keychain,
  CheckoutOptions,
  PaymentResult,
  CurrencyConfig,
  formatCurrency,
  getCurrencyFromPhone,
} from '@keychain-pay/js';
```

## Modal Behavior

- **Desktop**: Centered modal (420px wide)
- **Mobile**: Bottom sheet (full width)
- **Animations**: Smooth open/close transitions
- **Backdrop**: Click outside to cancel

## Test Mode

Use test API keys (`sk_test_...`) for development:

- No real money is transferred
- OTP code is always `123456`
- Session IDs start with `cs_test_`

## Support

- Email: hello@trykeychain.com
- Slack: [Join our community](https://trykeychain.slack.com/archives/C0A3UQ9N9RD)