Drop a container div and a single script tag on your booking page. The widget reads your booking total and travel date, runs the customer through KYC and 3D Secure, and posts back a confirmed booking reference. No payment-page rebuild required.
Drop these two snippets on your booking page where you want the payment widget to render. The widget reads data-* attributes from the container and calls our API on mount.
<!-- 1. Container — the widget renders into this div -->
<div
id="kalva-widget"
data-amount="3500"
data-currency="USD"
data-travel-date="2026-08-15"
data-merchant-key="YOUR_MERCHANT_KEY"
data-description="Overwater Suite — 5 nights"
></div>
<!-- 2. Script — async, loads from Kalva's CDN -->
<script src="https://widget.kalva.app/v1.js" async></script>That's it. The widget handles installment-plan display, KYC capture, Stripe Elements with 3D Secure, and the booking confirmation lifecycle.
Configure widget behaviour by setting these attributes on the container element.
| Attribute | Required | Description |
|---|---|---|
| data-amount | Yes | Booking total in the currency below. Integer or decimal — e.g. 3500 or 3500.00. |
| data-currency | Yes | ISO 4217 code. USD only at launch. |
| data-travel-date | Yes | ISO 8601 date — the check-in or experience date. Determines available installment plans. |
| data-merchant-key | Yes | Your operator merchant key from the dashboard. |
| data-description | No | Human-readable description shown in the widget header (e.g. "Overwater Suite — 5 nights"). |
| data-success-url | No | URL the widget redirects to after a successful booking. Defaults to your dashboard. |
Use a test merchant key (prefix test_) for sandbox bookings. No real charges; Stripe's test card matrix applies.
| Card | Behaviour |
|---|---|
| 4242 4242 4242 4242 | Default success — auto-authorises |
| 4000 0027 6000 3184 | Forced 3D Secure challenge |
| 4000 0000 0000 9995 | Insufficient funds — failed installment |
| 4000 0000 0000 0002 | Generic decline |
Any CVV. Any future expiry. Any postal code.
Server-side: fetch the available installment plans for a given amount and travel date. Useful if you want to preview plans on your own pricing page before the widget mounts.
{
"merchantKey": "YOUR_MERCHANT_KEY",
"amount": 3500,
"travelDate": "2026-08-15T00:00:00.000Z"
}
// Response
{
"success": true,
"data": [
{ "plan": "PAY_FULL", "count": 1, "installmentAmount": 3500 },
{ "plan": "PAY_IN_2", "count": 2, "installmentAmount": 1750 },
{ "plan": "PAY_IN_3", "count": 3, "installmentAmount": 1167 },
{ "plan": "PAY_IN_4", "count": 4, "installmentAmount": 875 },
{ "plan": "PAY_IN_6", "count": 6, "installmentAmount": 584 }
]
}Configure a webhook endpoint in the operator dashboard. Kalva posts JSON to that URL on every lifecycle event. Each request is HMAC-signed with your webhook secret in the Kalva-Signature header.
{
"event": "booking.confirmed",
"createdAt": "2026-08-29T14:22:01.000Z",
"data": {
"reference": "KLV-7F3A2B1C",
"merchantKey": "merch_abc123",
"amount": 3500,
"currency": "USD",
"commission": 245,
"netPayout": 3255,
"plan": "PAY_IN_4",
"customer": {
"email": "customer@example.com",
"country": "US",
"kycTier": 1
},
"travelDate": "2026-08-15T00:00:00.000Z"
}
}Event types you should handle:
booking.reserved — first installment cleared, booking in Reserved status.booking.confirmed — final installment cleared. Honour the booking.booking.cancelled — customer or auto-cancellation. Free up inventory.payout.sent — Stripe Connect payout initiated to your account.The widget script and the plan API use a public merchant key — safe to embed in page source. Server-side endpoints (cancellations, refund adjustments) require a secret API key, available from your operator dashboard under Settings → API. Never embed secret keys in client code.
Most operators are live the same working day they finish KYB.
Integration questions: hello@kalva.app. Security disclosure: security@kalva.app. General operator help: /help.