Widget checkout integration
Embed ArosaPay's protected checkout directly into your store. Buyers select a funding source, authorization completes in seconds, and you receive webhooks as the order progresses.
How It Works
The complete widget checkout lifecycle — from session creation to payout.
1. Create Session
Merchant calls POST /widget-checkout with amount, items, delivery SLA, and buyer contact.
2. Receive Credentials
ArosaPay returns transaction_id + payment_session for the widget.
3. Buyer Pays
Widget renders in your page. Buyer selects M-Pesa, Card, or Wallet and authorizes.
4. Funds Secured
Authorization completes in <10s. Funds are secured with buyer protection.
5. Webhooks Fire
Merchant receives funds.secured, delivery.marked, buyer.approved, and payout.sent events.
⚡ Top-Team Rule
Your "order confirmed" logic should only trigger on the funds.secured webhook — never earlier. This ensures the buyer's payment is fully authorized before you start fulfillment.
Quick Start
Copy-paste integration snippets for your stack.
// server.js — Create a widget session (Node.js / Express)
const express = require("express");
const app = express();
app.post("/create-checkout", async (req, res) => {
const { amount, items, buyerPhone } = req.body;
const response = await fetch("https://api.arosapay.com/v1/widget-checkout", {
method: "POST",
headers: {
"X-API-Key": process.env.AROSAPAY_SECRET_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
amount,
title: items[0]?.name || "Order",
vertical: "general",
satisfaction_days: 3,
buyer_phone: buyerPhone,
items,
}),
});
const session = await response.json();
res.json(session); // { sessionId, transactionId }
});// CheckoutButton.tsx — React component
import { useEffect, useRef } from "react";
export function CheckoutButton({ amount, items, buyerPhone }) {
const containerRef = useRef(null);
const handleCheckout = async () => {
// 1. Create session on your server
const res = await fetch("/create-checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ amount, items, buyerPhone }),
});
const { sessionId } = await res.json();
// 2. Render the ArosaPay widget
ArosaPay.renderCheckout("#arosapay-widget", {
sessionId,
onSuccess: (result) => {
console.log("Funds secured:", result.transactionId);
// Redirect to order confirmation
window.location.href = "/order-confirmed";
},
onError: (error) => {
console.error("Payment failed:", error.message);
},
});
};
return (
<>
<button onClick={handleCheckout}>
Pay with ArosaPay (Protected)
</button>
<div id="arosapay-widget" ref={containerRef} />
</>
);
}Try it live
Experience the checkout widget flow without writing code. Configure an order, pick a funding source, and watch the authorization complete — no real money moves.
Order Configuration
Widget preview will appear here
Press "Launch Widget" to start
Webhook Events
Four canonical events drive the entire order lifecycle.
Sample Payload
{
"id": "evt_wh_abc123",
"event": "funds.secured",
"created_at": "2025-02-20T14:30:00Z",
"data": {
"transaction_id": "txn_def456",
"reference": "AP-2602-VKDR-7NQM",
"amount": 15000,
"currency": "KES",
"funding_source": "mpesa",
"buyer_phone": "+254712345678"
}
}HMAC-SHA256 Signature Verification
Every webhook includes an X-Arosapay-Signature header. Always verify before processing.
const crypto = require("crypto");
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(payload, "utf8")
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express.js example
app.post("/webhooks/arosapay", (req, res) => {
const sig = req.headers["x-arosapay-signature"];
if (!verifySignature(JSON.stringify(req.body), sig, WEBHOOK_SECRET)) {
return res.status(401).json({ error: "Invalid signature" });
}
const { event, data } = req.body;
if (event === "funds.secured") {
confirmOrder(data.transaction_id);
}
res.json({ received: true });
});For the complete guide with 5 language implementations, replay protection, and debugging tools, see the Webhook Security Guide →
Retry Policy
Failed deliveries are retried 5 times with increasing delays: 1m → 5m → 30m → 2h → 24h. After all attempts fail, the endpoint is flagged and you receive an email alert.
Idempotency
Each event has a unique id. Store processed IDs and skip duplicates to ensure safe retries.
API Reference
Widget checkout endpoints. All requests require an X-API-Key header.
https://api.arosapay.com/v1/widget-checkoutCreate a widget session for embedded checkout.
{
"amount": "number — Transaction amount in KES",
"title": "string — Item/order title",
"vertical": "string — Category (electronics, general, etc.)",
"satisfaction_days": "number — Inspection window in days",
"buyer_phone": "string — Buyer's phone number",
"items?": "array — Line items [{name, qty, price}]"
}{
"sessionId": "string — Session ID for the widget",
"transactionId": "string — ArosaPay transaction reference"
}https://api.arosapay.com/v1/checkout-payInitiate M-Pesa STK push payment for a session.
{
"sessionId": "string — Widget session ID",
"phoneNumber": "string — M-Pesa number (+254...)"
}{
"paymentId": "string — Payment attempt ID",
"status": "string — 'initiated'",
"pollUrl": "string — URL to poll for status"
}https://api.arosapay.com/v1/checkout-card-payInitiate card payment via Flutterwave hosted checkout.
{
"sessionId": "string — Widget session ID",
"buyerEmail": "string — Buyer's email address",
"buyerName": "string — Buyer's full name"
}{
"paymentId": "string — Payment attempt ID",
"status": "string — 'initiated'",
"redirectUrl": "string — Flutterwave hosted page URL",
"pollUrl": "string — URL to poll for status"
}https://api.arosapay.com/v1/checkout-wallet-payPay using the buyer's ArosaPay wallet balance.
{
"sessionId": "string — Widget session ID",
"phoneNumber": "string — Wallet owner's phone"
}{
"paymentId": "string — Payment attempt ID",
"status": "string — 'completed' | 'failed'"
}https://api.arosapay.com/v1/checkout-status?sessionId=xxxPoll payment status for a widget session.
{
"status": "string — pending | funds_securing | funds_secured | failed",
"transactionId": "string — ArosaPay transaction ID"
}Go-Live Checklist
0/15 completed