Back to home
Developers/SDK Docs

Hadawi Web SDK

Install @hadawi/sdk to add group-contribution checkout. Works with Node backends and any modern browser app — plain HTML, React, Vue, Next.js, and more. Not a native iOS/Android SDK.

1 — Install

Terminal
npm install @hadawi/sdk
# or
pnpm add @hadawi/sdk
# or
yarn add @hadawi/sdk
# or
bun add @hadawi/sdk

What platforms are supported?

  • Server @hadawi/sdk on Node.js ≥ 18 (Express, Next.js API routes, Nest, Fastify, …).
  • Browser @hadawi/sdk/browser in any modern browser. There is no separate React/Vue package: mount into a DOM node with mountButton().
  • Works with plain HTML, React, Next.js, Vue, Svelte, Angular, and similar web frameworks.
  • Not yet native iOS / Android, React Native, or Flutter SDKs — checkout is the hosted web flow (popup or redirect).

2 — Browser: mount the checkout button

Start on the storefront. Use mountButton() so shoppers always see official Hadawi styling (not a custom button). On click, call your server for an intentId (next step). Try the demos, then pick the example that matches your stack.

All variants

Primary

Dark

Light

Outline

Interactive demo

Variant

Label

Locale

Badge

Live preview

Your selection
checkout.mountButton({
  container: '#hadawi-checkout',
  variant: 'primary',
  label: 'split',
  locale: 'en',
  showBadge: true,
  getIntentId: async () => {
    const res = await fetch('/api/create-contribution', { method: 'POST' });
    const data = await res.json();
    return data.intentId;
  },
  open: { mode: 'popup', onSuccess: () => { /* … */ } },
});

React / Next.js

Import from @hadawi/sdk/browser, mount into a ref, and destroy on unmount. Same idea in Vue/Svelte with a template ref.

React / Next.js (client)
'use client'; // Next.js App Router only

import { useEffect, useRef } from 'react';
import { HadawiCheckout } from '@hadawi/sdk/browser';

export function HadawiPayButton({ productId }: { productId: string }) {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!ref.current) return;

    const checkout = new HadawiCheckout({ mode: 'live' }); // or 'dev'
    const button = checkout.mountButton({
      container: ref.current,
      variant: 'primary',
      label: 'split',
      getIntentId: async () => {
        const res = await fetch('/api/create-contribution', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ productId }),
        });
        const data = await res.json();
        return data.intentId;
      },
      open: {
        mode: 'popup',
        onSuccess: () => { window.location.href = '/thank-you'; },
      },
    });

    return () => button.destroy();
  }, [productId]);

  return <div ref={ref} />;
}

Vue 3

Vue 3
<script setup>
import { onMounted, onBeforeUnmount, ref } from 'vue';
import { HadawiCheckout } from '@hadawi/sdk/browser';

const el = ref(null);
let button;

onMounted(() => {
  const checkout = new HadawiCheckout({ mode: 'live' });
  button = checkout.mountButton({
    container: el.value,
    variant: 'primary',
    label: 'split',
    getIntentId: async () => {
      const res = await fetch('/api/create-contribution', { method: 'POST' });
      const data = await res.json();
      return data.intentId;
    },
    open: { mode: 'popup', onSuccess: () => { location.href = '/thank-you'; } },
  });
});

onBeforeUnmount(() => button?.destroy());
</script>

<template>
  <div ref="el" />
</template>

Plain HTML (no bundler)

HTML + script tag
<div id="hadawi-checkout"></div>
<script src="https://cdn.jsdelivr.net/npm/@hadawi/sdk/dist/browser.iife.js"></script>
<script>
  const checkout = new Hadawi.HadawiCheckout({ mode: 'live' });

  checkout.mountButton({
    container: '#hadawi-checkout',
    variant: 'primary',
    label: 'split',
    getIntentId: async () => {
      const res = await fetch('/api/create-contribution', { method: 'POST' });
      const data = await res.json();
      return data.intentId;
    },
    open: {
      mode: 'popup',
      onSuccess: () => { location.href = '/thank-you'; },
    },
  });
</script>

3 — Server: create a checkout intent

Power the button's getIntentId call. Use your API key from Developer → API Keys. Use a dev API key (sandbox payments) while integrating. live keys require Hadawi approval and charge the production gateway. SDK mode picks the Hadawi host (dev / live).

Node / Express
import { HadawiClient } from '@hadawi/sdk';

const hadawi = new HadawiClient({
  apiKey: process.env.HADAWI_API_KEY!,
  mode: 'live', // or 'dev'
  webhookSecret: process.env.HADAWI_WEBHOOK_SECRET,
});

// e.g. POST /api/create-contribution
const { intent, redirectUrl } = await hadawi.intents.create({
  itemSnapshot: [{
    name: 'Luxury Watch Gift Set',
    price: 150000, // halalas (1500.00 SAR)
    image: 'https://example.com/watch.jpg',
    reference: 'watch-001',
  }],
  totalAmount: 150000,
  currency: 'SAR',
  metadata: { orderId: 'ord_abc123' },
});

// Return intentId to the browser button
res.json({ intentId: intent.id, redirectUrl });

4 — Webhooks

Set your webhook URL in Developer → Webhook, then reveal the signing secret and verify events with the SDK:

Express
app.post(
  '/webhooks/hadawi',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['x-hadawi-signature'];
    const event = hadawi.webhooks.constructEvent(req.body, signature);
    // event.type → 'order.funded' | …
    res.sendStatus(200);
  },
);

Need more detail?

Full README and TypeScript types are on npm. Browse the generated API reference at /docs/sdk/api.