Skip to main content

NPM SDK Integration

Choose your integration mode

Clive supports both options:

  1. Direct API calls (you control signing/headers)
  2. @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!
});
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 provide statement_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:

  1. Payment method selection
  2. Searchable billing-country selection
  3. checkout-intent-v2 initialization
  4. Built-in processor UIs (Stripe + Revolut card/Revolut Pay/Open Banking)
  5. 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-v2 must go through your backend
StepCallerEndpointNotes
1Widget (browser)POST /api/clive/checkout-intent-v2 (your backend)Uses initializeTransaction callback
2Partner backendPOST /api/v1/transactions/checkout-intent-v2 (Clive)Adds x-api-key, x-clive-hmac, idempotency-key
3Clive APIProcessor initReturns Stripe/Revolut artifact
4Partner backendresponse to widgetReturns init payload JSON to browser
5WidgetProcessor component mountStripe/Revolut/Open Banking UI

Never expose in browser:

  • CLIVE_API_KEY
  • CLIVE_HMAC_SECRET
  • processor secret credentials
// 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_id and stripe_payment_intent_id in your DB.

Direct API alternative (no SDK)

If you prefer direct calls, continue with: