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

# Create Payment Intent

> Create a payment intent that works seamlessly with the Khaime SDK.

## Intro

Create a payment intent for one-time or recurring payments without needing products in Khaime's catalog. Returns a signed `token` that you pass to `<KhaimeCheckout />` — the Khaime SDK handles all payment gateway logic automatically.

<Note>
  **No gateway SDKs required.** You don't need to install Stripe, Paystack, or any other payment SDK. Just use `@khaime/react` and the token handles everything.
</Note>

<Card title="Using Khaime Catalog Products?" icon="cart-shopping" href="/api-reference/commerce/payment-intent">
  If your products are in Khaime's catalog (storefronts), use **Create Product Payment Intent** instead. It supports multi-item carts, shipping calculations, and variant selection.
</Card>

## How It Works

```
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Your Frontend  │────▶│  Your Backend   │────▶│   Khaime API    │
│                 │     │                 │     │                 │
│  Checkout Form  │     │  POST /checkout │     │  POST /payment  │
│  + Customer     │     │  (your route)   │     │  /intent        │
└─────────────────┘     └─────────────────┘     └─────────────────┘
                                                        │
                                                        ▼
                              ┌─────────────────────────────────────┐
                              │  Returns token with gateway config  │
                              │  (gateway selected automatically)   │
                              └─────────────────────────────────────┘
                                                        │
                                                        ▼
                        ┌─────────────────────────────────────────────┐
                        │  <KhaimeCheckout token={token} />           │
                        │  Renders correct payment UI automatically   │
                        └─────────────────────────────────────────────┘
```

**Key points:**

* Your backend calls Khaime API and gets a `token`
* Token contains everything needed for payment (gateway, keys, amount)
* Your code never touches Stripe/Paystack/etc directly
* Gateway is selected automatically based on currency

***

## Quick Start

### 1. Create Payment Intent (Backend)

```bash theme={null}
curl -X POST https://api.khaime.com/api/v1/payment/intent \
  -H "X-API-Key: pk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "charge_amount": 5000,
    "charge_currency": "USD",
    "merchant_amount": 5000,
    "merchant_currency": "USD",
    "description": "Order #123",
    "customer": {
      "email": "jane@example.com",
      "first_name": "Jane",
      "last_name": "Doe"
    }
  }'
```

### 2. Response

```json theme={null}
{
  "status": true,
  "message": "Payment charge created successfully",
  "data": {
    "token": "eyJtZXJjaGFudF9pZCI6MTc5MCwicGF5bWVudF9nYXRld2F5Ijoic3RyaXBlIi...",
    "charge_id": "intent_abc123-def456-789",
    "amount": 5000,
    "currency": "USD",
    "status": "pending",
    "mode": "live",
    "breakdown": {
      "subtotal": 5000,
      "total": 5000,
      "customer_pays_fees": false,
      "is_international": false
    },
    "expires_at": "2024-01-15T10:30:00.000Z"
  }
}
```

### 3. Render Checkout (Frontend)

```tsx theme={null}
import { KhaimeCheckout } from '@khaime/react';

function CheckoutPage({ token }) {
  return (
    <KhaimeCheckout
      token={token}
      onSuccess={(result) => {
        console.log('Payment successful!', result);
        window.location.href = '/success';
      }}
      onError={(error) => {
        console.error('Payment failed:', error);
      }}
      onClose={() => {
        console.log('Checkout closed');
      }}
    />
  );
}
```

That's it. The SDK automatically renders the correct payment UI based on the currency — Stripe card form for USD/EUR/GBP, Paystack popup for NGN, etc.

***

## Request Body

Every payment intent requires **two amounts**:

| Field                                   | Description                                        | Example        |
| --------------------------------------- | -------------------------------------------------- | -------------- |
| `merchant_amount` / `merchant_currency` | What **you** priced the product at (your currency) | `$90 USD`      |
| `charge_amount` / `charge_currency`     | What the **customer** pays (their currency)        | `₦144,000 NGN` |

<Tip>
  **Same currency?** If customer pays in your currency, both amounts are identical:

  ```json theme={null}
  { "merchant_amount": 5000, "merchant_currency": "USD", "charge_amount": 5000, "charge_currency": "USD" }
  ```

  **Different currency?** Use [/pricing/calculate](/api-reference/products/calculate-pricing) to convert, then pass both:

  ```json theme={null}
  { "merchant_amount": 9000, "merchant_currency": "USD", "charge_amount": 1440000, "charge_currency": "NGN" }
  ```
</Tip>

***

### Customer Payment (What they pay)

<ParamField body="charge_amount" type="integer" required>
  The amount to charge the customer, in smallest currency unit (cents, kobo, etc.).

  * If same currency as merchant: same value as `merchant_amount`
  * If different currency: use the `converted_amount` from [/pricing/calculate](/api-reference/products/calculate-pricing)

  <Note>Alias: `amount`</Note>
</ParamField>

<ParamField body="charge_currency" type="string" required>
  The currency the customer pays in. 3-letter ISO code (e.g., `USD`, `NGN`, `GBP`).

  This determines which payment gateway is used (Stripe for USD/EUR/GBP, Paystack for NGN, etc.).

  <Note>Alias: `currency`</Note>
</ParamField>

### Merchant Settlement (What you receive)

<ParamField body="merchant_amount" type="integer" required>
  The amount you priced the product at, in smallest currency unit. This is your guaranteed settlement amount.

  Khaime guarantees you receive exactly this amount regardless of exchange rate fluctuations between when the customer pays and when you're settled.

  <Note>Alias: `total_amount`</Note>
</ParamField>

<ParamField body="merchant_currency" type="string" required>
  Your settlement currency. 3-letter ISO code (e.g., `USD` for US merchant).

  <Note>Alias: `total_currency`</Note>
</ParamField>

<Warning>
  **Anti-fraud validation:** Khaime recalculates the conversion server-side and rejects if `charge_amount` doesn't match `merchant_amount` at current exchange rates (0.02% tolerance). This prevents partners from overcharging customers.
</Warning>

<ParamField body="description" type="string">
  Human-readable description of the charge. Shown on payment receipts.
</ParamField>

<ParamField body="reference" type="string">
  Your unique reference for this charge. Used for idempotency and reconciliation.
</ParamField>

<ParamField body="callback_url" type="string">
  Redirect URL after payment completes. Used for redirect-based flows.
</ParamField>

<ParamField body="subscription_frequency_key" type="string">
  Set to make this a recurring charge instead of a one-time payment. See [Subscriptions](/payments/subscriptions) for valid keys.
</ParamField>

<ParamField body="customer" type="object" required>
  <Expandable title="Customer fields">
    <ParamField body="email" type="string" required>
      Customer's email address.
    </ParamField>

    <ParamField body="first_name" type="string">
      Customer's first name.
    </ParamField>

    <ParamField body="last_name" type="string">
      Customer's last name.
    </ParamField>

    <ParamField body="country" type="string">
      Customer country code (e.g., `US`, `NG`, `GH`). Recommended for analytics.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="metadata" type="object">
  Custom key-value pairs attached to the charge. Use strings, numbers, or booleans only.

  <Expandable title="Example">
    ```json theme={null}
    {
      "order_id": "123",
      "source": "web_checkout"
    }
    ```
  </Expandable>
</ParamField>

<ParamField body="preview" type="boolean" default="false">
  When `true`, returns a fee breakdown without creating an actual charge. Use this to show customers exactly what they'll pay before confirming.
</ParamField>

***

## Response

```json theme={null}
{
  "status": true,
  "message": "Payment charge created successfully",
  "data": {
    "token": "eyJtZXJjaGFudF9pZCI6MTc5MCwi...",
    "charge_id": "intent_abc123-def456-789",
    "amount": 5320,
    "currency": "USD",
    "status": "pending",
    "mode": "live",
    "breakdown": {
      "subtotal": 5000,
      "transaction_fee": 320,
      "total": 5320,
      "customer_pays_fees": true,
      "is_international": false
    },
    "expires_at": "2024-01-15T10:30:00.000Z"
  }
}
```

### Response Fields

| Field        | Description                                                                                       |
| ------------ | ------------------------------------------------------------------------------------------------- |
| `token`      | Signed token for `<KhaimeCheckout />`. Contains all gateway configuration. Expires in 15 minutes. |
| `charge_id`  | Unique charge identifier for tracking                                                             |
| `amount`     | Final amount to charge in smallest currency unit                                                  |
| `currency`   | Currency code                                                                                     |
| `status`     | Payment status (`pending`)                                                                        |
| `mode`       | Environment (`live` or `sandbox`)                                                                 |
| `breakdown`  | Fee breakdown with subtotal, transaction\_fee, total, customer\_pays\_fees, is\_international     |
| `expires_at` | Token expiration timestamp                                                                        |

<Warning>
  **Token Expiration:** Tokens expire after 15 minutes. If a customer waits too long, create a new payment intent.
</Warning>

***

## Accepting Payment

### Option 1: Embedded Checkout (Recommended)

Use `@khaime/react` to embed checkout directly in your app:

<Steps>
  <Step title="Install the SDK">
    <CodeGroup>
      ```bash npm theme={null}
      npm install @khaime/react
      ```

      ```bash yarn theme={null}
      yarn add @khaime/react
      ```

      ```bash pnpm theme={null}
      pnpm add @khaime/react
      ```
    </CodeGroup>
  </Step>

  <Step title="Render the Checkout">
    ```tsx theme={null}
    import { KhaimeCheckout } from '@khaime/react';

    function PaymentPage({ paymentToken }) {
      return (
        <KhaimeCheckout
          token={paymentToken}
          onSuccess={(result) => {
            // Payment successful - redirect to confirmation
            window.location.href = `/order/${result.transaction_id}`;
          }}
          onError={(error) => {
            console.error('Payment failed:', error.message);
          }}
          onClose={() => {
            // User closed checkout without completing
          }}
        />
      );
    }
    ```
  </Step>
</Steps>

The SDK automatically:

* Detects the payment gateway from the token
* Renders the appropriate payment UI (card form, mobile money, etc.)
* Handles 3D Secure authentication
* Manages loading states and errors

### Option 2: Redirect Checkout

Redirect the customer to Khaime's hosted checkout:

```javascript theme={null}
// After getting the response
window.location.href = response.data.payment_url;
```

The customer completes payment on Khaime's hosted page and returns to your `callback_url`.

***

## Currency & Gateway Routing

Khaime automatically selects the optimal payment gateway based on currency:

| Currency                | Gateway                   | Payment UI         |
| ----------------------- | ------------------------- | ------------------ |
| USD, EUR, GBP, CAD, AUD | Stripe                    | Embedded card form |
| NGN                     | Paystack                  | Popup / redirect   |
| GHS, KES, ZAR, TZS, UGX | Flutterwave / StartButton | Redirect           |

**You don't need to know or care about this.** Just pass the currency and the SDK handles everything.

***

## Examples

### Same Currency (USD → USD)

```bash theme={null}
curl -X POST https://api.khaime.com/api/v1/payment/intent \
  -H "X-API-Key: pk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "charge_amount": 5000,
    "charge_currency": "USD",
    "merchant_amount": 5000,
    "merchant_currency": "USD",
    "description": "Order #123",
    "reference": "order_123",
    "callback_url": "https://yourstore.com/order-complete",
    "customer": {
      "email": "customer@example.com",
      "first_name": "John",
      "last_name": "Doe",
      "country": "US"
    }
  }'
```

### Multicurrency (Merchant prices USD, Customer pays NGN)

When the customer pays in a different currency than the merchant's settlement currency:

1. **Get converted amount** from `/pricing/calculate`
2. **Pass both amounts** — `merchant_amount/merchant_currency` (what you receive) and `charge_amount/charge_currency` (what customer pays)
3. **Khaime validates** the conversion matches within 0.02% tolerance

```bash theme={null}
# Step 1: Get converted amount
# GET /pricing/calculate?amount=9000&source_currency=USD&target_currency=NGN
# Response: { "converted_amount": 1440000, "rate": 1600 }

# Step 2: Create payment intent with both amounts
curl -X POST https://api.khaime.com/api/v1/payment/intent \
  -H "X-API-Key: pk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "charge_amount": 1440000,
    "charge_currency": "NGN",
    "merchant_amount": 9000,
    "merchant_currency": "USD",
    "description": "Design consultation",
    "customer": {
      "email": "client@example.ng",
      "first_name": "Emeka",
      "last_name": "Eze",
      "country": "NG"
    }
  }'
```

<Info>
  **Settlement guarantee:** The merchant receives `merchant_amount` in `merchant_currency` regardless of exchange rate fluctuations. Khaime absorbs the FX risk.
</Info>

### Preview Mode (Fee Breakdown)

```bash theme={null}
curl -X POST https://api.khaime.com/api/v1/payment/intent \
  -H "X-API-Key: pk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "preview": true,
    "charge_amount": 5000,
    "charge_currency": "USD",
    "merchant_amount": 5000,
    "merchant_currency": "USD",
    "customer": {
      "email": "customer@example.com"
    }
  }'
```

Returns fee breakdown without creating a charge:

```json theme={null}
{
  "status": true,
  "message": "Preview calculated",
  "data": {
    "preview": true,
    "amount": 5320,
    "currency": "USD",
    "breakdown": {
      "subtotal": 5000,
      "transaction_fee": 320,
      "total": 5320,
      "customer_pays_fees": true,
      "is_international": false
    }
  }
}
```

***

## Full Backend Example (Next.js)

```typescript theme={null}
// app/api/checkout/route.ts
import { NextResponse } from 'next/server';

const KHAIME_API_KEY = process.env.KHAIME_API_KEY!;
const KHAIME_API_URL = 'https://api.khaime.com/api/v1';

export async function POST(request: Request) {
  const { amount, currency, customer, orderId } = await request.json();

  const response = await fetch(`${KHAIME_API_URL}/payment/intent`, {
    method: 'POST',
    headers: {
      'X-API-Key': KHAIME_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      // Customer pays this amount in this currency
      charge_amount: amount,
      charge_currency: currency,
      // Merchant receives this amount in this currency
      merchant_amount: amount,
      merchant_currency: currency,
      description: `Order #${orderId}`,
      reference: `order_${orderId}`,
      callback_url: `${process.env.NEXT_PUBLIC_URL}/order/${orderId}`,
      customer,
    }),
  });

  const data = await response.json();

  if (!data.status) {
    return NextResponse.json({ error: data.message }, { status: 400 });
  }

  // Return only the token to the frontend
  return NextResponse.json({
    token: data.data.token,
    amount: data.data.amount,
    currency: data.data.currency,
  });
}
```

***

## Error Codes

| Status | Error Code                     | Fix                                                             |
| ------ | ------------------------------ | --------------------------------------------------------------- |
| `400`  | `VALIDATION_MISSING_FIELD`     | Include required fields: `amount`, `currency`, `customer.email` |
| `400`  | `PAYMENT_AMOUNT_MISMATCH`      | Use `/pricing/calculate` for currency conversion                |
| `400`  | `PAYMENT_CURRENCY_UNSUPPORTED` | Use a supported currency                                        |
| `401`  | —                              | Check your `X-API-Key` header                                   |

***

## Confirming Payment

<Warning>
  **Always verify payments via [webhooks](/webhooks/overview)** before fulfilling orders. Frontend callbacks are for UI purposes only — a malicious user could fake them.
</Warning>

Listen for `payment.succeeded` webhook events to confirm payment and fulfill orders.

***

## Related

<CardGroup cols={2}>
  <Card title="React SDK" icon="react" href="/sdks/react/overview">
    Full `<KhaimeCheckout />` documentation
  </Card>

  <Card title="Webhooks" icon="webhook" href="/webhooks/overview">
    Listen for payment events
  </Card>

  <Card title="Gateway Routing" icon="route" href="/payments/gateway-routing">
    How currency determines gateway
  </Card>

  <Card title="Subscriptions" icon="repeat" href="/payments/subscriptions">
    Recurring payments
  </Card>
</CardGroup>
