> ## 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.

# SDK Quick Reference

> Concise reference for Khaime SDK integration

# Khaime SDK Quick Reference

## Critical Rules

<CardGroup cols={2}>
  <Card title="Backend calculates, frontend displays" icon="server">
    **NEVER** calculate prices on frontend. Display only what the API returns for subtotal, shipping, and total.
  </Card>

  <Card title="All amounts in CENTS" icon="dollar-sign">
    `$4.50 = 450 cents`
    `$45.00 = 4500 cents`
    `$450.00 = 45000 cents`
  </Card>

  <Card title="Shipping is server-side" icon="truck">
    Call `validateCart` with delivery address to get shipping cost. Cannot calculate client-side.
  </Card>

  <Card title="Wait for DOM" icon="clock">
    In React, use `useEffect` to initialize payment AFTER container renders.
  </Card>
</CardGroup>

***

## SDK Methods

### Load SDK

```html theme={null}
<script src="https://js.khaime.com/v1/"></script>
```

### Confirm Payment

```typescript theme={null}
await Khaime.confirmPayment({
  // Required
  secret: 'pi_xxx_secret_yyy',
  publishable_key: 'pk_live_xxx',

  // Optional but recommended
  stripe_account_id: 'acct_xxx',  // For Connect
  amount: 4500,                    // Display amount in CENTS
  currency: 'USD',

  // Display mode
  display: 'inline',               // 'modal' | 'inline'
  container: element,              // Required for inline mode

  // Callbacks
  onReady: () => {},
  onSuccess: (result) => {},
  onError: (error) => {},
});
```

***

## Cart Item Schema

```typescript theme={null}
// Products WITHOUT variations
{
  product_id: 123,              // number, not string
  quantity: 2,
  price: 18500,                 // CENTS
  has_variation: false,
  product_image: 'https://...',
  product_variant_data: 'none', // Must be 'none'
  // DO NOT include: main_variant, least_sub_variant_id
}

// Products WITH variations
{
  product_id: 123,
  quantity: 1,
  price: 2500,
  has_variation: true,
  product_image: 'https://...',
  product_variant_data: 'Large / Blue',
  main_variant: 'Large / Blue',      // Include for variants
  least_sub_variant_id: 'var_xxx',   // Include for variants
}
```

***

## Checkout Flow

```
1. SHIPPING STEP
   └─> User enters address
   └─> Click "Continue"
   └─> Call validateCart(cart, deliveryDetails)
   └─> Response: { shipping_fee: 450, cart_unique_id: 'xxx' }
   └─> Update UI: setShippingFee(450)

2. PAYMENT STEP
   └─> User clicks "Pay"
   └─> Call createPaymentIntent(cart_unique_id, ...)
   └─> Response: { secret, publishable_key, amount_to_pay }
   └─> setCredentials(response) + setShowForm(true)

3. PAYMENT FORM (useEffect)
   └─> Wait for container to exist
   └─> Call Khaime.confirmPayment({ container, ... })
   └─> onSuccess: clearCart + redirect
```

***

## React Pattern

```tsx theme={null}
const [credentials, setCredentials] = useState(null);
const [showForm, setShowForm] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);

// Get credentials, then show form
const handlePay = async () => {
  const intent = await createPaymentIntent(...);
  setCredentials(intent.data);
  setShowForm(true);  // Container renders
};

// Initialize AFTER container exists
useEffect(() => {
  if (!showForm || !credentials || !containerRef.current) return;

  Khaime.confirmPayment({
    container: containerRef.current,  // Pass element, not ID
    secret: credentials.secret,
    publishable_key: credentials.publishable_key,
    amount: credentials.amount_to_pay,
    display: 'inline',
    onSuccess: () => router.push('/success'),
  });
}, [showForm, credentials]);

return showForm && <div ref={containerRef} />;
```

***

## Validation Errors

| Error                                  | Cause                                       | Fix                                    |
| -------------------------------------- | ------------------------------------------- | -------------------------------------- |
| `main_variant not allowed to be empty` | Sent `main_variant` for non-variant product | Only include for `has_variation: true` |
| `product_variant_data is required`     | Missing field                               | Use `'none'` for non-variant products  |
| `least_sub_variant_id not allowed`     | Sent variant ID for non-variant product     | Only include for `has_variation: true` |
| `Container element not found`          | DOM not ready                               | Use `useEffect`, pass element not ID   |

***

## Multicurrency (Critical)

<Warning>
  **Double-Conversion Bug**: Backend returns `shipping_fee` and `amount_to_pay` ALREADY CONVERTED. Do NOT apply exchange rate again!
</Warning>

### What to convert vs skip

```typescript theme={null}
// Frontend converts these (in base currency)
formatPrice(product.price)     // Convert
formatPrice(subtotal)          // Convert

// Backend already converted these (in target currency)
formatPrice(shippingFee, true) // SKIP conversion
formatPrice(orderTotal, true)  // SKIP conversion
```

### formatPrice with skipConversion

```typescript theme={null}
const formatPrice = (cents: number, skipConversion = false): string => {
  const amount = (currency === baseCurrency || skipConversion)
    ? cents
    : Math.round(cents * exchangeRate);

  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency,
  }).format(amount / 100);
};
```

### Combining different currencies

```typescript theme={null}
// When API doesn't provide total, calculate manually:
const convertedSubtotal = Math.round(subtotal * exchangeRate); // USD → target
const total = convertedSubtotal + shippingFee;                 // Both in target now
formatPrice(total, true);                                       // Skip - already converted
```

***

## Display Currency (Single Currency)

```typescript theme={null}
function formatCurrency(cents: number, currency = 'USD'): string {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency,
  }).format(cents / 100);
}

// Usage
formatCurrency(4500);  // "$45.00"
formatCurrency(45000); // "$450.00"
```

***

## Shipping Rules

4 destination types (priority order):

| Type                 | Priority     | Meaning                             |
| -------------------- | ------------ | ----------------------------------- |
| `specific_locations` | 1 (highest)  | Specific cities/areas within states |
| `specific_states`    | 2            | One or more configured states       |
| `nationwide`         | 3            | Any domestic destination            |
| `international`      | 4 (fallback) | Outside merchant's country          |

```typescript theme={null}
// Location in pricing_summary.shipping_details
const { matched_rules, available_destinations } = response.data.pricing_summary.shipping_details;

// Build state/location selectors from available_destinations
const locationsForState = available_destinations
  .filter(opt => opt.destination_type === 'specific_locations' && opt.destination_states.includes(selectedState))
  .flatMap(opt => opt.destination_locations);
```

***

## API Response Fields

### validateCart Response

```typescript theme={null}
{
  status: true,
  data: {
    cart_unique_id: 'cart_xxx',  // Use in payment intent
    shipping_fee: 450,           // CENTS (from matched_rules)
    shipping_details: {
      matched_rules: [{ rule_id: 58, rule_name: 'Nationwide', destination_type: 'nationwide' }],
      available_destinations: [
        { rule_id: 56, rule_name: 'Dallas Rate', destination_type: 'specific_locations', shipping_fee: 350 },
        { rule_id: 58, rule_name: 'Nationwide', destination_type: 'nationwide', shipping_fee: 450 }
      ]
    }
  }
}
```

### createPaymentIntent Response

```typescript theme={null}
{
  status: true,
  data: {
    secret: 'pi_xxx_secret_yyy',
    publishable_key: 'pk_xxx',
    amount_to_pay: 5000,         // Final amount in CENTS
    currency: 'USD',
    data: {
      stripe_account_id: 'acct_xxx',
    }
  }
}
```
