Create Payment Intent
curl --request POST \
--url https://api.khaime.com/api/v1/payment/intent \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"charge_amount": 123,
"charge_currency": "<string>",
"merchant_amount": 123,
"merchant_currency": "<string>",
"description": "<string>",
"reference": "<string>",
"callback_url": "<string>",
"subscription_frequency_key": "<string>",
"customer": {
"email": "<string>",
"first_name": "<string>",
"last_name": "<string>",
"country": "<string>"
},
"metadata": {},
"sub_merchant_id": 123,
"preview": true
}
'import requests
url = "https://api.khaime.com/api/v1/payment/intent"
payload = {
"charge_amount": 123,
"charge_currency": "<string>",
"merchant_amount": 123,
"merchant_currency": "<string>",
"description": "<string>",
"reference": "<string>",
"callback_url": "<string>",
"subscription_frequency_key": "<string>",
"customer": {
"email": "<string>",
"first_name": "<string>",
"last_name": "<string>",
"country": "<string>"
},
"metadata": {},
"sub_merchant_id": 123,
"preview": True
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
charge_amount: 123,
charge_currency: '<string>',
merchant_amount: 123,
merchant_currency: '<string>',
description: '<string>',
reference: '<string>',
callback_url: '<string>',
subscription_frequency_key: '<string>',
customer: {
email: '<string>',
first_name: '<string>',
last_name: '<string>',
country: '<string>'
},
metadata: {},
sub_merchant_id: 123,
preview: true
})
};
fetch('https://api.khaime.com/api/v1/payment/intent', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.khaime.com/api/v1/payment/intent",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'charge_amount' => 123,
'charge_currency' => '<string>',
'merchant_amount' => 123,
'merchant_currency' => '<string>',
'description' => '<string>',
'reference' => '<string>',
'callback_url' => '<string>',
'subscription_frequency_key' => '<string>',
'customer' => [
'email' => '<string>',
'first_name' => '<string>',
'last_name' => '<string>',
'country' => '<string>'
],
'metadata' => [
],
'sub_merchant_id' => 123,
'preview' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.khaime.com/api/v1/payment/intent"
payload := strings.NewReader("{\n \"charge_amount\": 123,\n \"charge_currency\": \"<string>\",\n \"merchant_amount\": 123,\n \"merchant_currency\": \"<string>\",\n \"description\": \"<string>\",\n \"reference\": \"<string>\",\n \"callback_url\": \"<string>\",\n \"subscription_frequency_key\": \"<string>\",\n \"customer\": {\n \"email\": \"<string>\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"country\": \"<string>\"\n },\n \"metadata\": {},\n \"sub_merchant_id\": 123,\n \"preview\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.khaime.com/api/v1/payment/intent")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"charge_amount\": 123,\n \"charge_currency\": \"<string>\",\n \"merchant_amount\": 123,\n \"merchant_currency\": \"<string>\",\n \"description\": \"<string>\",\n \"reference\": \"<string>\",\n \"callback_url\": \"<string>\",\n \"subscription_frequency_key\": \"<string>\",\n \"customer\": {\n \"email\": \"<string>\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"country\": \"<string>\"\n },\n \"metadata\": {},\n \"sub_merchant_id\": 123,\n \"preview\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.khaime.com/api/v1/payment/intent")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"charge_amount\": 123,\n \"charge_currency\": \"<string>\",\n \"merchant_amount\": 123,\n \"merchant_currency\": \"<string>\",\n \"description\": \"<string>\",\n \"reference\": \"<string>\",\n \"callback_url\": \"<string>\",\n \"subscription_frequency_key\": \"<string>\",\n \"customer\": {\n \"email\": \"<string>\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"country\": \"<string>\"\n },\n \"metadata\": {},\n \"sub_merchant_id\": 123,\n \"preview\": true\n}"
response = http.request(request)
puts response.read_bodyPayments
Create Payment Intent
Create a payment intent that works seamlessly with the Khaime SDK.
POST
/
payment
/
intent
Create Payment Intent
curl --request POST \
--url https://api.khaime.com/api/v1/payment/intent \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"charge_amount": 123,
"charge_currency": "<string>",
"merchant_amount": 123,
"merchant_currency": "<string>",
"description": "<string>",
"reference": "<string>",
"callback_url": "<string>",
"subscription_frequency_key": "<string>",
"customer": {
"email": "<string>",
"first_name": "<string>",
"last_name": "<string>",
"country": "<string>"
},
"metadata": {},
"sub_merchant_id": 123,
"preview": true
}
'import requests
url = "https://api.khaime.com/api/v1/payment/intent"
payload = {
"charge_amount": 123,
"charge_currency": "<string>",
"merchant_amount": 123,
"merchant_currency": "<string>",
"description": "<string>",
"reference": "<string>",
"callback_url": "<string>",
"subscription_frequency_key": "<string>",
"customer": {
"email": "<string>",
"first_name": "<string>",
"last_name": "<string>",
"country": "<string>"
},
"metadata": {},
"sub_merchant_id": 123,
"preview": True
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
charge_amount: 123,
charge_currency: '<string>',
merchant_amount: 123,
merchant_currency: '<string>',
description: '<string>',
reference: '<string>',
callback_url: '<string>',
subscription_frequency_key: '<string>',
customer: {
email: '<string>',
first_name: '<string>',
last_name: '<string>',
country: '<string>'
},
metadata: {},
sub_merchant_id: 123,
preview: true
})
};
fetch('https://api.khaime.com/api/v1/payment/intent', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.khaime.com/api/v1/payment/intent",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'charge_amount' => 123,
'charge_currency' => '<string>',
'merchant_amount' => 123,
'merchant_currency' => '<string>',
'description' => '<string>',
'reference' => '<string>',
'callback_url' => '<string>',
'subscription_frequency_key' => '<string>',
'customer' => [
'email' => '<string>',
'first_name' => '<string>',
'last_name' => '<string>',
'country' => '<string>'
],
'metadata' => [
],
'sub_merchant_id' => 123,
'preview' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.khaime.com/api/v1/payment/intent"
payload := strings.NewReader("{\n \"charge_amount\": 123,\n \"charge_currency\": \"<string>\",\n \"merchant_amount\": 123,\n \"merchant_currency\": \"<string>\",\n \"description\": \"<string>\",\n \"reference\": \"<string>\",\n \"callback_url\": \"<string>\",\n \"subscription_frequency_key\": \"<string>\",\n \"customer\": {\n \"email\": \"<string>\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"country\": \"<string>\"\n },\n \"metadata\": {},\n \"sub_merchant_id\": 123,\n \"preview\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.khaime.com/api/v1/payment/intent")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"charge_amount\": 123,\n \"charge_currency\": \"<string>\",\n \"merchant_amount\": 123,\n \"merchant_currency\": \"<string>\",\n \"description\": \"<string>\",\n \"reference\": \"<string>\",\n \"callback_url\": \"<string>\",\n \"subscription_frequency_key\": \"<string>\",\n \"customer\": {\n \"email\": \"<string>\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"country\": \"<string>\"\n },\n \"metadata\": {},\n \"sub_merchant_id\": 123,\n \"preview\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.khaime.com/api/v1/payment/intent")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"charge_amount\": 123,\n \"charge_currency\": \"<string>\",\n \"merchant_amount\": 123,\n \"merchant_currency\": \"<string>\",\n \"description\": \"<string>\",\n \"reference\": \"<string>\",\n \"callback_url\": \"<string>\",\n \"subscription_frequency_key\": \"<string>\",\n \"customer\": {\n \"email\": \"<string>\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"country\": \"<string>\"\n },\n \"metadata\": {},\n \"sub_merchant_id\": 123,\n \"preview\": true\n}"
response = http.request(request)
puts response.read_bodyIntro
Create a payment intent for one-time or recurring payments without needing products in Khaime’s catalog. It returns a signedtoken that you pass to the frontend SDK for your stack. Use the Khaime SDK that matches your app: @khaime/react for React apps and khaime_flutter from pub.dev for Flutter apps. The token handles all payment gateway logic automatically.
No gateway SDKs required. You don’t need to install Stripe, Paystack, or any other payment SDK. Use
@khaime/react in React, or khaime_flutter in Flutter; the token handles everything.Selling in installments? This endpoint has no catalog product, so it can’t take installments. Use Installment Checkout.
Using Khaime Catalog Products?
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.
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 │
└─────────────────────────────────────────────┘
- 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)
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
{
"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)
React
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');
}}
/>
);
}
Flutter
import 'package:khaime_flutter/khaime_flutter.dart';
KhaimeCheckout(
token: token,
onSuccess: (result) {
print('Payment successful: $result');
},
onError: (error) {
print('Payment failed: $error');
},
onClose: () {
print('Checkout closed');
},
);
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 |
Same currency? If customer pays in your currency, both amounts are identical:Different currency? Use /pricing/calculate to convert, then pass both:
{ "merchant_amount": 5000, "merchant_currency": "USD", "charge_amount": 5000, "charge_currency": "USD" }
{ "merchant_amount": 9000, "merchant_currency": "USD", "charge_amount": 1440000, "charge_currency": "NGN" }
Customer Payment (What they pay)
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_amountfrom /pricing/calculate
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.).Merchant Settlement (What you receive)
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.
string
required
Your settlement currency. 3-letter ISO code (e.g.,
USD for US merchant).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.string
Human-readable description of the charge. Shown on payment receipts.
string
Your unique reference for this charge. Used for idempotency and reconciliation.
string
Redirect URL after payment completes. Used for redirect-based flows.
string
Set to make this a recurring charge instead of a one-time payment. See Subscriptions for valid keys.
object
required
object
Custom key-value pairs attached to the charge. Use strings, numbers, or booleans only.
Show Example
Show Example
{
"order_id": "123",
"source": "web_checkout"
}
integer
The Khaime business ID of the sub-merchant this charge is being collected for.
Must have To sell a product that lives in the sub-merchant’s Khaime catalogue rather
than on your own platform, use
Create Product Payment Intent
instead — there you send the sub-merchant’s
active status in your marketplace.When present, the webhook additionally credits the sub-merchant’s wallet for
the merchant amount minus your marketplace commission. Your marketplace wallet
is still credited as normal.POST /payment/intent and POST /payments/charge
are the same endpoint behind two paths — same validation, same handler, same
behaviour. The marketplace rules are documented once, on
Create Charge, including how the
commission is resolved and what happens when a sub-merchant is not active.product_id and ownership is
resolved server-side, with no sub_merchant_id in the body at all.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.Response
{
"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 |
Token Expiration: Tokens expire after 15 minutes. If a customer waits too long, create a new payment intent.
Showing the fee to the customer
Whenbreakdown.customer_pays_fees is true, the transaction fee has been added to what the customer pays. Show it as a separate line in your order summary before they pay — it’s best practice for transparency, and a total that’s higher than the advertised price with no explanation erodes trust and invites disputes. See Fees.
Who pays is the business’s customer_pays_transaction_fee setting. For a marketplace charge (sub_merchant_id), it’s the operator’s setting.
| Field | Show as |
|---|---|
breakdown.subtotal | The price of what they’re buying |
breakdown.transaction_fee | ”Transaction fee” — only when customer_pays_fees is true |
breakdown.total | Total to pay. Equals amount. |
transaction_fee is returned even when the merchant absorbs the fee. Decide whether to show the line from customer_pays_fees, not from whether transaction_fee is present — otherwise you’ll show customers a fee they aren’t paying.const { amount, currency, breakdown } = intent.data;
const money = (minor) =>
new Intl.NumberFormat(undefined, { style: "currency", currency }).format(minor / 100);
const lines = [{ label: "Subtotal", value: money(breakdown.subtotal) }];
if (breakdown.customer_pays_fees && breakdown.transaction_fee > 0) {
lines.push({ label: "Transaction fee", value: money(breakdown.transaction_fee) });
}
lines.push({ label: "Total", value: money(amount) });
preview: true first — see Preview Mode. Use the currency’s real exponent when formatting; minor / 100 is only right for 2-decimal currencies.
Accepting Payment
Option 1: Embedded Checkout (Recommended)
Use the Khaime SDK for your frontend stack.React
1
Install the SDK
npm install @khaime/react
yarn add @khaime/react
pnpm add @khaime/react
2
Render the Checkout
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
}}
/>
);
}
Flutter
1
Install the SDK
flutter pub add khaime_flutter
2
Render the Checkout
import 'package:khaime_flutter/khaime_flutter.dart';
KhaimeCheckout(
token: paymentToken,
onSuccess: (result) {
print('Payment successful: $result');
},
onError: (error) {
print('Payment failed: $error');
},
onClose: () {
print('Checkout closed');
},
);
- 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:// After getting the response
window.location.href = response.data.payment_url;
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 |
Examples
Same Currency (USD → USD)
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:- Get converted amount from
/pricing/calculate - Pass both amounts —
merchant_amount/merchant_currency(what you receive) andcharge_amount/charge_currency(what customer pays) - Khaime validates the conversion matches within 0.02% tolerance
# 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"
}
}'
Settlement guarantee: The merchant receives
merchant_amount in merchant_currency regardless of exchange rate fluctuations. Khaime absorbs the FX risk.Preview Mode (Fee Breakdown)
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"
}
}'
{
"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)
// 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
Always verify payments via webhooks before fulfilling orders. Frontend callbacks are for UI purposes only — a malicious user could fake them.
payment.succeeded webhook events to confirm payment and fulfill orders.
Related
React SDK
Full
<KhaimeCheckout /> documentationWebhooks
Listen for payment events
Gateway Routing
How currency determines gateway
Subscriptions
Recurring payments
