NPM SDK Integration
Choose your integration mode
Clive supports both options:
- Direct API calls (you control signing/headers)
@clive-alliance/partner-sdk(Clive SDK handles signing, idempotency defaults, and request wiring)
If your team prefers speed and fewer auth mistakes, use the SDK.
Install
npm install @clive-alliance/partner-sdk
Initialize the SDK client
import { CliveClient } from "@clive-alliance/partner-sdk";
const clive = new CliveClient({
baseUrl: process.env.CLIVE_API_BASE_URL,
apiKey: process.env.CLIVE_API_KEY!,
hmacSecret: process.env.CLIVE_HMAC_SECRET!
});
Recommended one-call checkout flow (SDK)
const checkout = await clive.createCheckoutIntent({
partner_id: "ECHEZONA001",
client_id: "AIRPEACE001",
amount: 750,
email: "buyer@example.com",
currency: "USD",
statement_descriptor_suffix: "AIRPEACE",
echezona_reference: "EZ12345"
});
// Send these to your frontend checkout page
return {
clive_tx_id: checkout.clive_tx_id,
stripe_payment_intent_id: checkout.stripe_payment_intent_id,
client_secret: checkout.client_secret,
stripe_publishable_key: checkout.stripe_publishable_key
};
The SDK signs and sends Clive API requests.
In the recommended flow, Clive creates Stripe PaymentIntents; partners still providestatement_descriptor_suffix.
Frontend checkout page (Stripe Elements)
After your backend returns client_secret, stripe_publishable_key, and clive_tx_id, mount Stripe Elements in your checkout page:
import { Elements, PaymentElement, useElements, useStripe } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";
const stripePromise = loadStripe(stripePublishableKey);
function CheckoutForm() {
const stripe = useStripe();
const elements = useElements();
const onSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!stripe || !elements) return;
const submitResult = await elements.submit();
if (submitResult.error) return;
await stripe.confirmPayment({
elements,
confirmParams: { return_url: window.location.href },
redirect: "if_required"
});
};
return (
<form onSubmit={onSubmit}>
<PaymentElement />
<button type="submit">Pay now</button>
</form>
);
}
<Elements stripe={stripePromise} options={{ clientSecret }}>
<CheckoutForm />
</Elements>
Advanced option: Clive widget v2 (multi-processor UI)
Keep the Stripe Elements flow above if that fits your team.
If you want a richer hosted-like checkout, use the browser widget from @clive-alliance/partner-sdk.
The advanced widget includes:
- Payment method selection
- Searchable billing-country selection
checkout-intent-v2initialization- Built-in processor UIs (Stripe + Revolut card/Revolut Pay/Open Banking)
- In-widget success/failure screens with external callbacks (
onSuccess,onError,onStateChange)
Who calls what (critical)
For secure integration, follow this boundary:
- Browser widget can call Clive directly only for branding
GET /branding/packet
- Browser widget must NOT call Clive transaction-ingestion endpoints directly
POST /transactions/checkout-intent-v2must go through your backend
| Step | Caller | Endpoint | Notes |
|---|---|---|---|
| 1 | Widget (browser) | POST /api/clive/checkout-intent-v2 (your backend) | Uses initializeTransaction callback |
| 2 | Partner backend | POST /api/v1/transactions/checkout-intent-v2 (Clive) | Adds x-api-key, x-clive-hmac, idempotency-key |
| 3 | Clive API | Processor init | Returns Stripe/Revolut artifact |
| 4 | Partner backend | response to widget | Returns init payload JSON to browser |
| 5 | Widget | Processor component mount | Stripe/Revolut/Open Banking UI |
Never expose in browser:
CLIVE_API_KEYCLIVE_HMAC_SECRET- processor secret credentials
Backend adapter route (recommended)
// server route called by browser widget
app.post("/api/clive/checkout-intent-v2", async (req, res) => {
const payload = req.body;
const checkout = await clive.createCheckoutIntentV2({
...payload
});
res.json(checkout);
});
Frontend widget usage
import { createClivePaymentsWidget } from "@clive-alliance/partner-sdk";
createClivePaymentsWidget({
mount: "#clive-pay-widget",
partnerId: "ECHEZONA001",
clientId: "AIRPEACE001",
brandingBaseUrl: "https://api.clivepayments.com/api/v1",
amount: 75,
currency: "GBP",
email: "buyer@merchant.com",
echezonaReference: "EZ-2026-001",
initializeTransaction: async (payload) => {
const response = await fetch("/api/clive/checkout-intent-v2", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
if (!response.ok) throw new Error("Widget init failed");
return await response.json();
},
onSuccess: ({ payload, response, result }) => {
console.log("Payment success", { payload, response, result });
},
onError: (error) => {
console.error("Payment failed", error);
},
onStateChange: (state) => {
console.log("Widget state", state);
}
});
Sandbox-web style server route
Use one backend route to return both client_secret and clive_tx_id to your frontend:
app.post("/checkout-intent", async (req, res) => {
const { amount, email } = req.body;
const checkout = await clive.createCheckoutIntent({
partner_id: "ECHEZONA001",
client_id: "AIRPEACE001",
amount,
email,
currency: "USD",
statement_descriptor_suffix: "AIRPEACE",
echezona_reference: `EZ-${Date.now()}`
});
res.json({
clive_tx_id: checkout.clive_tx_id,
stripe_payment_intent_id: checkout.stripe_payment_intent_id,
client_secret: checkout.client_secret,
stripe_publishable_key: checkout.stripe_publishable_key
});
});
Helpful SDK utilities
CliveClient.createIdempotencyKey(prefix, reference)CliveClient.createRandomIdempotencyKey(prefix)CliveClient.createCheckoutIdempotencyKey(reference)CliveClient.signPayload(rawBody, hmacSecret)createSignedRequestHeaders(...)for teams that still send raw fetch requests
Suffix compliance checklist
- Keep suffix deterministic by merchant/client identity.
- Keep suffix short and human-readable (Stripe statement constraints apply).
- Store the suffix with
clive_tx_idandstripe_payment_intent_idin your DB.
Direct API alternative (no SDK)
If you prefer direct calls, continue with: