Complete this once.
Activate
Activate your Merchant, MID, TID and at least one payment method.
Copy secrets
Store the live API secret and webhook signing secret on your server.
Add URLs
Register your checkout website origin and HTTPS webhook URL.
Create one server route.
This example uses Node and Express. Load the order from your database; never accept its amount from browser code.
Colored values come from your store or Yeah Connect account.
app.post("/api/create-checkout-session", async (req, res) => {
const order = await loadVerifiedOrder(req); // Your database
const response = await fetch(
"https://yeahconnect.com/v1/checkout/sessions",
{
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.YEAHCONNECT_SECRET}`,
"Idempotency-Key": order.id,
"Content-Type": "application/json"
},
body: JSON.stringify({
amount: order.amountInCents,
currency: "SGD",
reference: order.id,
successUrl: "https://shop.example.com/payment/complete",
cancelUrl: "https://shop.example.com/checkout"
})
}
);
const data = await response.json();
if (!response.ok) return res.status(response.status).json(data);
res.json({ clientToken: data.session.clientToken });
});YEAHCONNECT_SECRET must never appear in HTML or browser JavaScript. Amounts use cents: 4990 means S$49.90.Request rules
amount must be an integer from 1 to 100000000, currency is currently SGD, reference is required and at most 120 characters, and Idempotency-Key must be 8 to 128 characters using letters, numbers, dots, underscores, colons or hyphens. Reuse the same key only for the same amount, reference and URLs. Accounts with more than one checkout scope may also send midCode and tidCode; otherwise the key's configured defaults are used.
Preload checkout, then mount it.
yeahco.js starts downloading from the document head, calls your same-origin server route and immediately loads the default payment provider with the short-lived client token. No checkout option is needed.
<head>
<link rel="preconnect" href="https://yeahconnect.com" crossorigin>
<link rel="modulepreload" href="https://yeahconnect.com/js/yeahco.js" crossorigin>
<link rel="preload" as="style" href="https://yeahconnect.com/js/yeahco.css" crossorigin>
<link rel="stylesheet" href="https://yeahconnect.com/js/yeahco.css" data-yeahco-style crossorigin>
</head>
<div id="yeahco-checkout"></div>
<script
type="module"
src="https://yeahconnect.com/js/yeahco.js"
crossorigin="anonymous"
data-session-url="/api/create-checkout-session"
data-auto-mount>
</script>Manual JavaScript
Use the manual API only when your checkout needs custom application logic.
import { Yeahco } from "https://yeahconnect.com/js/yeahco.js";
const response = await fetch("/api/create-checkout-session", {
method: "POST"
});
const { clientToken } = await response.json();
await Yeahco({ clientToken })
.mount("#yeahco-checkout");Style the checkout.
The SDK works without custom CSS. To match your store, style these supported elements after mounting it.
#yeahco-checkoutYour mount container and maximum width.yeahco-payment-elementThe complete generated payment element.yeahco-payment-optionsThe payment-method list.yeahco-payment-optionOne selectable payment method.yeahco-payment-frameThe secure hosted payment frame.is-card-fieldsCard forms: number, expiry, CVV and Pay.is-content-flowTaller non-card provider flows#yeahco-checkout {
width: 100%;
max-width: 640px;
margin-inline: auto;
}
#yeahco-checkout .yeahco-payment-element {
display: grid;
gap: 16px;
color: #073d3a;
font: 600 14px/1.45 system-ui, sans-serif;
}
#yeahco-checkout .yeahco-payment-options {
display: grid;
gap: 8px;
margin: 0;
padding: 0;
border: 0;
}
#yeahco-checkout .yeahco-payment-option {
display: flex;
align-items: center;
gap: 10px;
min-height: 46px;
padding: 11px 13px;
border: 1px solid #c7dcda;
border-radius: 10px;
background: #fff;
cursor: pointer;
}
#yeahco-checkout .yeahco-payment-option:has(input:checked) {
border-color: #0f766e;
background: #edf9f7;
box-shadow: 0 0 0 2px rgb(15 118 110 / 9%);
}
#yeahco-checkout .yeahco-payment-option input {
width: 16px;
height: 16px;
accent-color: #0f766e;
}
#yeahco-checkout .yeahco-payment-frame {
display: block;
inline-size: 100%;
max-inline-size: 100%;
block-size: clamp(600px, 92dvh, 1040px);
border: 1px solid #c7dcda;
border-radius: 12px;
background: #fff;
box-shadow: 0 12px 30px rgb(7 61 58 / 8%);
}
@media (max-width: 650px) {
#yeahco-checkout .yeahco-payment-frame {
block-size: clamp(600px, 92dvh, 1040px);
}
}Trust the webhook.
Webhook verification
Register this route before any JSON body parser so signature verification receives the unchanged request bytes. The signature is v1= followed by an unpadded base64url HMAC-SHA256 digest.
import crypto from "node:crypto";
app.post(
"/webhooks/yeahconnect",
express.raw({ type: "application/json" }),
async (req, res) => {
const timestamp = req.get("x-yeahco-timestamp") || "";
const signature = req.get("x-yeahco-signature") || "";
const eventId = req.get("x-yeahco-event-id") || "";
if (!/^\d+$/.test(timestamp)
|| Math.abs(Date.now() / 1000 - Number(timestamp)) > 300
|| !eventId) return res.sendStatus(400);
const expected = "v1=" + crypto
.createHmac("sha256", process.env.YEAHCONNECT_WEBHOOK_SECRET)
.update(`${timestamp}.${req.body.toString("utf8")}`)
.digest("base64url");
const valid = signature.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!valid) return res.sendStatus(401);
const event = JSON.parse(req.body.toString("utf8"));
if (await hasProcessedEvent(eventId)) return res.sendStatus(200);
const payment = event.data?.payment;
const order = payment && await loadOrderByReference(payment.reference);
if (!order
|| payment.amount !== order.amountInCents
|| payment.currency !== "SGD"
|| payment.status !== "succeeded") return res.sendStatus(400);
await markOrderPaidOnce({ order, eventId, paymentId: payment.id });
return res.sendStatus(200);
}
);Persist x-yeahco-event-id and the paid-order transition atomically. Return a successful response for an already processed event so retries stop safely.
The demo uses the same auto-mount.
/demo loads the same stable module declaratively. It uses the public YEAH01 test setup, or automatically reads an optional test TID from ?tid=YOUR_TEST_TID. This test shortcut is not used for production merchant orders.
<div id="yeahco-checkout"></div>
<script
type="module"
src="/js/yeahco.js"
data-key="YEAH01"
data-auto-mount>
</script>