Integrate Kunog in an afternoon.
Clean primitives, predictable webhooks, and examples that match real apps.
Quickstart
Create a payment intent, send your customer to hosted checkout, then verify a webhook when the payment is confirmed.
curl https://api.kunog.com/v1/payment_intents \
-H "Authorization: Bearer kng_test_••••" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 3b2d9a1a-9f1d-4c6f-bade-2bf0b3d0b12f" \
-d '{
"amount": 4900,
"currency": "USD",
"settlement": "USDC",
"success_url": "https://kunog.com/success",
"cancel_url": "https://kunog.com/cancel"
}'
// Node/Express example
app.post("/create-checkout", async (req, res) => {
const r = await fetch("https://api.kunog.com/v1/payment_intents", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KUNOG_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID()
},
body: JSON.stringify({
amount: 4900,
currency: "USD",
settlement: "USDC",
metadata: { order_id: "KNG-2048" }
})
});
const intent = await r.json();
res.redirect(303, intent.checkout_url);
});
SDK pattern
Keep your app thin: wrap Kunog calls in a small client so you can swap environments and add retries.
export class KunogClient {
constructor({ apiKey, baseUrl = "https://api.kunog.com" }) {
this.apiKey = apiKey;
this.baseUrl = baseUrl;
}
async createPaymentIntent(payload, { idempotencyKey }) {
const r = await fetch(`${this.baseUrl}/v1/payment_intents`, {
method: "POST",
headers: {
"Authorization": `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey
},
body: JSON.stringify(payload)
});
if (!r.ok) throw new Error(`Kunog error: ${r.status}`);
return r.json();
}
}
Webhooks
Kunog sends signed webhooks for state changes. Always verify the signature and use idempotent handlers.
Events
payment_intent.createdpayment_intent.pendingpayment_intent.confirmedpayment_intent.expiredrefund.created
Headers
Kunog-Signature(HMAC)Kunog-Timestamp(ms)Kunog-Event-Id
import crypto from "crypto";
function timingSafeEqual(a, b) {
const aa = Buffer.from(a);
const bb = Buffer.from(b);
return aa.length === bb.length && crypto.timingSafeEqual(aa, bb);
}
export function verifyKunogWebhook({ rawBody, signature, timestamp, secret }) {
const payload = `${timestamp}.${rawBody}`;
const expected = crypto.createHmac("sha256", secret).update(payload).digest("hex");
if (!timingSafeEqual(signature, expected)) throw new Error("Invalid signature");
return true;
}
Idempotency
Retry safely by sending an Idempotency-Key header. Treat keys as unique per operation (e.g., intent creation).
Developer FAQ
For digital goods, many merchants treat pending as soft-paid and upgrade to fully-paid on confirmed. For high-risk goods, wait for confirmed.
On-chain transfers are irreversible, but disputes can still exist at the business layer. Document your refund and dispute policies clearly.