# Getting Started

## Prerequisites

Before using the SDK, you'll need:

1. **API Credentials** - Client ID and Client Secret from the Partner Dashboard (for your backend)
2. **A backend endpoint** - To generate checkout tokens for your frontend
3. **Invoice ID** - The invoice the customer will be paying
4. **A container element** - An HTML element where the SDK will render

{% hint style="warning" %}
**Important:** API credentials (Client ID and Secret) should only be used on your backend server. The frontend SDK uses access tokens generated by your backend to keep credentials secure.
{% endhint %}

***

## Step-by-Step Setup

{% stepper %}
{% step %}

#### Install the SDK

```bash
npm install @getalternative/partner-sdk
```

{% endstep %}

{% step %}

#### Create a Backend Token Endpoint

Your backend generates checkout tokens using your API credentials:

```typescript
// backend/routes/checkout.ts (example with Express)
import express from 'express';

const router = express.Router();

router.post('/api/checkout-token', async (req, res) => {
  const { customerId, invoiceId } = req.body;

  // 1. Get OAuth token using client credentials
  const oauthResponse = await fetch('https://public-api.alternativepayments.io/oauth/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Authorization': `Basic ${Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64')}`,
    },
    body: 'grant_type=client_credentials',
  });
  const { access_token } = await oauthResponse.json();

  // 2. Generate checkout token
  const checkoutResponse = await fetch('https://public-api.alternativepayments.io/v1/checkout-auth/init', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${access_token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ customer_id: customerId, invoice_id: invoiceId }),
  });
  const { token, expires_at } = await checkoutResponse.json();

  res.json({ token, expiresAt: expires_at });
});
```

{% endstep %}

{% step %}

#### Add a Container Element

```html
<div id="payment-container"></div>
```

{% endstep %}

{% step %}

#### Initialize the SDK

```typescript
import { AlternativeClient } from '@getalternative/partner-sdk';

// Fetch token from your backend
const { token } = await fetch('/api/checkout-token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ customerId: 'cus_xxx', invoiceId: 'inv_xxx' }),
}).then(res => res.json());

// Initialize SDK
const client = await AlternativeClient.create({
  accessToken: token,
  environment: 'production',
  onAccessTokenExpired: async () => {
    // Re-fetch token when expired
    const response = await fetch('/api/checkout-token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ customerId: 'cus_xxx', invoiceId: 'inv_xxx' }),
    });
    const { token } = await response.json();
    return token;
  },
});
```

{% endstep %}

{% step %}

#### Create the Payment Flow

```typescript
const flow = client.createPaymentFlow({
  containerId: 'payment-container',
  onPaymentSuccess: (payment) => {
    console.log('Payment completed:', payment.id);
  },
  onPaymentError: (error) => {
    console.error('Payment failed:', error.message);
  },
});
```

{% endstep %}
{% endstepper %}

***

## Configuration Options

### AlternativeClientConfig

| Property               | Type                                  | Required | Description                                 |
| ---------------------- | ------------------------------------- | -------- | ------------------------------------------- |
| `accessToken`          | `string`                              | Yes      | JWT access token from your backend          |
| `environment`          | `'production' \| 'staging' \| 'demo'` | No       | API environment (default: production)       |
| `baseUrl`              | `string`                              | No       | Custom API base URL (overrides environment) |
| `timeout`              | `number`                              | No       | Request timeout in ms (default: 30000)      |
| `retries`              | `number`                              | No       | Number of retry attempts (default: 3)       |
| `onAccessTokenExpired` | `() => Promise<string>`               | No       | Callback to fetch new token when expired    |
| `theme`                | `ThemeConfig`                         | No       | Default theme for all components            |

***

## Environments

<table><thead><tr><th width="200">Environment</th><th>Description</th></tr></thead><tbody><tr><td><code>production</code></td><td>Live environment for real payments</td></tr><tr><td><code>staging</code></td><td>Test environment for development</td></tr><tr><td><code>demo</code></td><td>Demo environment for showcasing and testing</td></tr></tbody></table>

{% hint style="danger" %}
**Important:** Use separate API credentials for each environment. Production credentials will not work in staging and vice versa.
{% endhint %}

***

## Full Example

```html
<!DOCTYPE html>
<html>
<head>
  <title>Payment Page</title>
</head>
<body>
  <h1>Complete Your Payment</h1>
  <div id="payment-container"></div>

  <script type="module">
    import { AlternativeClient } from '@getalternative/partner-sdk';

    // Fetch token from your backend
    const { token } = await fetch('/api/checkout-token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        customerId: 'cus_abc123',
        invoiceId: 'inv_abc123'
      }),
    }).then(res => res.json());

    const client = await AlternativeClient.create({
      accessToken: token,
      environment: 'staging',
      onAccessTokenExpired: async () => {
        const response = await fetch('/api/checkout-token', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            customerId: 'cus_abc123',
            invoiceId: 'inv_abc123'
          }),
        });
        const { token } = await response.json();
        return token;
      },
    });

    const flow = client.createPaymentFlow({
      containerId: 'payment-container',
      onPaymentSuccess: (payment) => {
        alert('Payment successful!');
        window.location.href = '/thank-you';
      },
      onPaymentError: (error) => {
        alert('Payment failed: ' + error.message);
      },
      onClose: () => {
        console.log('Payment flow closed');
      },
    });

    // Cleanup when navigating away
    window.addEventListener('beforeunload', () => {
      flow.unmount();
    });
  </script>
</body>
</html>
```

***

## React Integration

```tsx
import { useEffect, useRef } from 'react';
import { AlternativeClient } from '@getalternative/partner-sdk';

interface PaymentWidgetProps {
  customerId: string;
  invoiceId: string;
}

function PaymentWidget({ customerId, invoiceId }: PaymentWidgetProps) {
  const flowRef = useRef<ReturnType<typeof AlternativeClient.prototype.createPaymentFlow> | null>(null);

  useEffect(() => {
    let mounted = true;

    async function initPayment() {
      // Fetch token from your backend
      const { token } = await fetch('/api/checkout-token', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ customerId, invoiceId }),
      }).then(res => res.json());

      if (!mounted) return;

      const client = await AlternativeClient.create({
        accessToken: token,
        environment: 'staging',
        onAccessTokenExpired: async () => {
          const response = await fetch('/api/checkout-token', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ customerId, invoiceId }),
          });
          const { token } = await response.json();
          return token;
        },
      });

      if (!mounted) return;

      flowRef.current = client.createPaymentFlow({
        containerId: 'payment-container',
        onPaymentSuccess: (payment) => {
          console.log('Success:', payment);
        },
      });
    }

    initPayment();

    return () => {
      mounted = false;
      flowRef.current?.unmount();
    };
  }, [customerId, invoiceId]);

  return <div id="payment-container" />;
}
```

***

## Next Steps

{% content-ref url="authentication" %}
[authentication](https://docs.alternativepayments.io/web-sdk/authentication)
{% endcontent-ref %}

{% content-ref url="payment-flow" %}
[payment-flow](https://docs.alternativepayments.io/web-sdk/payment-flow)
{% endcontent-ref %}

{% content-ref url="theming" %}
[theming](https://docs.alternativepayments.io/web-sdk/theming)
{% endcontent-ref %}
