Whish Collect — integration guide

End-to-end guide to integrating Whish Money's Collect web service into a Node/Express backend.

A practical, end-to-end guide to integrating Whish Money's Collect web service into a Node.js backend — what to put in place before you write a line of code, the API surface, and the payment lifecycle done safely. Whish isn't a card processor in the Stripe sense: it's a Banque-du-Liban–licensed e-wallet with a large cash-in/cash-out network, and Collect is the merchant service on top of it.

LBP · USDHosted redirectBDL-licensedNode / Express
Create collectyour server
Get collect URLfrom Whish
Redirect & paycustomer ↔ Whish
Return callbacka signal only
Verify statussource of truth
The lifecycle. An order is paid when your server confirms with Whish — not when the browser returns.

What you're actually integrating

When a customer pays, Whish hands you a hosted payment URL. You redirect the buyer there; they pay from their Whish wallet balance or by card on Whish's page; funds land in your Whish account; your server confirms the result out-of-band. Because the buyer leaves your checkout, the flow is redirect-based — plan your UX around the hand-off. There are two realistic routes to production:

A · Official Whish Collect API

  • Direct integration against Whish's own web service.
  • You hold the relationship, the fees, and the data.
  • Requires Whish merchant onboarding to receive credentials.
  • Best for a product you control — SaaS, custom checkout.

B · Third-party gateway wrapper

  • Services (e.g. codnloc) and a WooCommerce plugin wrap Whish for you.
  • Faster to switch on; less code.
  • Adds a middleman, extra fees, and a dependency you don't own.
  • Fine for a quick WordPress/Shopify store, weak for a core product.

Reality check

There is no public self-serve API sign-up. Credentials come only after Whish verifies you as a merchant, and the exact base URL, field names, and response codes live in the Whish Collect Web Service Technical Specification PDF they send you. Treat everything here as the shape of the integration — confirm exact strings against that PDF.

Before any code: getting onboarded

The output of this stage is three credentials — a channel, a secret, and a registered websiteurl. Nothing technical works without them.

  1. 1

    Create a Whish account

    Install the app and register at apps.whish.money.
  2. 2

    Request merchant / Whish Pay

    In the app, choose the Whish Pay / merchant service and submit your business details.
  3. 3

    Whish contacts you

    Their team follows up and sends a PDF onboarding form with next steps.
  4. 4

    Submit the form + KYC

    Return the completed form with a valid ID and proof of residency/registration for verification.
  5. 5

    Receive credentials

    Once verified, you're issued your Whish channel and secret.

Identity / KYC documents

  • Lebanese nationals: Lebanese ID, valid passport, recent civil registration (< 6 months), or army ID.
  • Non-Lebanese: valid passport plus residence permit / UN ID / equivalent.
  • Proof of residency and, for a company, business registration.

What you walk away with

  • channel — your merchant channel identifier.
  • secret — the API secret; treat it like a password.
  • websiteurl — the registered site; requests are tied to it.
  • The technical spec PDF + sandbox details.

Ask early

If you need payroll, bulk collection, or a tailored checkout, ask about Whish Corporate Solutions during onboarding — the contract and limits differ from a standard merchant account.

The API surface

Four operations cover everything you need to take a payment and reconcile it.

OperationMethod · pathPurpose
Get BalanceGET /payment/account/balanceReal and void balances. Only LBP is currently retrievable via API.
Get RatePOST /payment/whish/rateThe rate/fee deducted from the invoice amount. Call it to show the net before charging.
CollectPOST /payment/whish/collectCreates a payment and returns the collect URL you redirect to.
Collect StatusPOST /payment/whish/collect/statusStatus of a created collect. Your source of truth — never the redirect alone.

Confirm against the spec

Only GET /payment/account/balance is published verbatim. The other three follow Whish's naming pattern but verify the exact paths and request/response field names in your spec PDF — the kind of detail that silently breaks an integration.

Every response shares a consistent envelope:

response.envelope.json
// present on every Collect response
{
  "status": true,            // true = success, false = failure
  "code":   null,            // operation-specific failure code when status=false
  "dialog": { "title": "…", "message": "…" }, // safe to show the user
  "data":   { /* operation payload */ }
}

Authentication & headers

No OAuth dance. Every request carries the same three header values from onboarding.

HeaderValue
channelYour merchant channel identifier from Whish.
secretYour API secret. Server-side only — never ship it to the browser.
websiteurlThe website registered to the account; must match what Whish has on file.

Non-negotiable

The secret belongs in environment variables on your server. If it ever touches client-side code, a public repo, or a redirect query string, rotate it with Whish immediately.

The payment lifecycle

Five stops. The discipline that matters most is the last one — you decide an order is paid on your server, after asking Whish, not when the browser comes back.

  1. 1

    Create the collect — your server → Whish

    POST amount, currency (LBP or USD), your own orderId, and success/failure callback URLs. Optionally call Get Rate first to show the net of fees.
  2. 2

    Receive the collect URL — Whish → your server

    The response carries a hosted payment URL. Persist the Whish reference against your order so you can reconcile later.
  3. 3

    Redirect & pay — customer ↔ Whish

    Send the buyer to the collect URL. They pay from their Whish balance or with a card on Whish's page. You don't see card data — that's the point.
  4. 4

    Return to your callback — Whish → customer → you

    The buyer is redirected back to your success/failure URL. Treat this purely as a signal to go verify — not proof of payment.
  5. 5

    Verify with Collect Status — your server → Whish

    Call Collect Status server-side with your stored reference. Only on success do you fulfil, mark paid, and run idempotency so a refresh can't double-fulfil.

Implementation (Node / Express)

A thin client plus two routes. Field names are placeholders to align with your spec PDF; the structure and the safety model carry over unchanged.

1 — A small Whish client. One place that owns the headers, base URL, and response envelope.

lib/whish.js
import axios from "axios";

const api = axios.create({
  baseURL: process.env.WHISH_BASE_URL,   // sandbox vs prod, from the spec
  headers: {
    channel:    process.env.WHISH_CHANNEL,
    secret:     process.env.WHISH_SECRET,
    websiteurl: process.env.WHISH_WEBSITE_URL,
    "Content-Type": "application/json",
  },
  timeout: 15000,
});

// Unwrap Whish's { status, code, dialog, data } envelope
async function call(path, body) {
  const { data: res } = await api.post(path, body ?? {});
  if (!res.status) {
    const err = new Error(res?.dialog?.message ?? "Whish request failed");
    err.code = res.code;
    throw err;
  }
  return res.data;
}

export const whish = {
  getRate: (amount, currency) => call("/payment/whish/rate", { amount, currency }),

  collect: (p) => call("/payment/whish/collect", {
    amount: p.amount,
    currency: p.currency,                 // "LBP" | "USD"
    invoice: p.invoice,                   // description shown to payer
    externalId: p.orderId,                // YOUR id, for reconciliation
    successCallbackUrl: p.successUrl,
    failureCallbackUrl: p.failureUrl,
  }),

  status: (orderId) => call("/payment/whish/collect/status", { externalId: orderId }),
};

2 — Start a payment. Create the collect, store the reference, redirect.

routes/checkout.js
router.post("/pay", async (req, res) => {
  const order = await Orders.create({ amount: 25, currency: "USD", state: "pending" });

  const data = await whish.collect({
    amount: order.amount,
    currency: order.currency,
    invoice: `Order #${order.id}`,
    orderId: order.id,
    successUrl: `${BASE}/checkout/return?order=${order.id}`,
    failureUrl: `${BASE}/checkout/return?order=${order.id}&failed=1`,
  });

  await order.update({ whishRef: data.collectId });   // confirm field name
  res.redirect(data.collectUrl);                       // hosted Whish page
});

3 — Verify on return. The redirect only triggers a check; the order is paid only if Whish says so.

routes/checkout.js
router.get("/return", async (req, res) => {
  const order = await Orders.find(req.query.order);

  // Idempotent: never fulfil the same order twice
  if (order.state === "paid") return res.redirect("/thank-you");

  const result = await whish.status(order.id);   // ask the source of truth

  if (result.collectStatus === "success") {       // confirm enum in spec
    await order.update({ state: "paid" });
    await fulfil(order);
    return res.redirect("/thank-you");
  }
  res.redirect("/checkout?status=unpaid");
});

Belt & braces

Browsers close, networks drop. Run a reconciliation job that re-checks Collect Status for any order stuck in pending past a timeout, so a payment is never lost just because the customer never made it back to your callback.

Gotchas worth pre-empting

  • Sandbox first. Test the full lifecycle in sandbox and only flip the base URL to production once it passes. Sandbox and prod credentials are separate.
  • Two currencies, real consequences. LBP and USD are both supported, but amounts, balances, and fees differ per currency. Be explicit about currency on every call and store it on the order.
  • Fees come out of the invoice. Get Rate tells you what Whish deducts. If you need to net a specific amount, gross it up before calling Collect.
  • Redirect ≠ receipt. Anyone can hit your success URL. Fulfilment must hang off a server-side Collect Status check, never the query string.
  • Idempotency. A refresh on the return page must not charge or fulfil twice. Guard on order state.
  • Field names vary. Exact request/response keys and status enums live in your spec PDF. Wire them against the document, not this guide's placeholders.
  • UX for the hand-off. Customers leave your site to pay. Set expectations on checkout and design the return clearly so the redirect doesn't read as an error.

Sources

Built from Whish Money's public materials and the Collect Web Service Technical Specification structure. Endpoint paths and field names marked for confirmation are inferred from Whish's documented naming and must be checked against the spec PDF you receive at onboarding. Get credentials and the authoritative spec from whish.money/corporate-solutions — there is no public API sign-up.