WWHITCOMB PAYMENTS DEVELOPER DOCUMENTATION

Developer documentation

Two ways to take payments through Whitcomb: the Process Now plugin for WooCommerce, and the Process Now API for a site you built yourself — any language, any platform. Plus the Stripe Express plugin for merchants on that program.

Which one is for you

WOOCOMMERCE

Process Now plugin

Install, paste your Process Now key, done. The plugin opens Whitcomb’s hosted payment page at checkout, marks the order paid after a server-side check, and keeps your Whitcomb dashboard current on its own.

Set it up →

CUSTOM SITE

Process Now API

Four calls from your own server: activate once, start a payment, confirm it, and (optionally) report your orders. Your buyer pays on Whitcomb’s hosted page and comes back to your site. No card data ever touches your server.

Read the reference →

Both run on your Process Now key — one key per business, issued by your Whitcomb desk and shown in your merchant portal under Set up. Your account takes money only once Whitcomb has approved it (or approved it for a trial); until then both the plugin and the API answer “not armed” and no card page opens.

Process Now — WooCommerce plugin

  1. Download the plugin from your merchant portal (Set up tab) and install it under Plugins → Add New → Upload. WordPress 5.8+, PHP 7.4+, WooCommerce (classic and block checkout).
  2. Activate it with your Process Now key (WooCommerce → Settings → Payments → Process Now by Whitcomb Payments). The plugin exchanges the key for your store’s brand code and store secret and keeps them in your WordPress options — the key is never typed again.
  3. Sell. When the account is armed, “Credit or debit card” (Whitcomb Payments — the title is yours to change) appears at checkout. The buyer is sent to Whitcomb’s payment page and returned to your order-received page; the order is marked paid only after the plugin re-checks the payment with Whitcomb server-side. A buyer who closes the tab is caught by the plugin’s five-minute sweep for a day.
  4. Reporting is automatic: every order (whatever it was paid with) is reported in a signed batch, the whole history is synced once at activation, and a daily heartbeat tells your desk the store is alive and what it holds. Nothing to configure.
  5. Updates arrive on your Plugins screen like any other plugin.
ONE ORDER, ONE CHARGE
An order that is already paid never gets a second card page: asking again returns the paid link, whose page says “payment received” and sends the buyer back to you. The plugin does this for you; if you use the API, keep the same rule (see Start a payment).

Process Now API — custom sites

Base URL https://whitcombpayments.com. Every call is JSON over HTTPS from your server — never from a browser, because your store secret must never leave your backend. The flow:

  1. Activate once with your Process Now key → you receive your brand code and your secret. Store both server-side.
  2. Sign every call after that: base64url the JSON payload, HMAC-SHA256 it with the secret, send both.
  3. Start a payment for an order → you receive a payment page URL and a reference. Redirect the buyer there.
  4. Confirm the payment when the buyer comes back (and again on a timer): mark the order paid only when Whitcomb says paid: true.
  5. Optionally report your orders and send a heartbeat so your Whitcomb dashboard shows the whole store, not just the card payments.

Activation

POST/api/activate

Plain JSON (this is the one unsigned call). Do it once and keep the answer; do not activate on every request.

{
  "k":  "your Process Now key, exactly as it was given to you",
  "u":  "https://yourstore.com",      // your site's address, shown to your desk
  "sn": "Your Store",                  // shown to your desk
  "pv": "1.0"                          // your integration's version, for your desk's eyes
}
200
{
  "ok": true,
  "merchant": "Your Business LLC",
  "brand":  "yourbusiness",           // your brand code — goes into every signed payload as "b"
  "secret": "…",                      // your store secret — signs every call; never leaves your server
  "status": "active",                 // or "pending" while your application is being reviewed
  "armed":  true,                     // may this account take money right now?
  "why":    "",                       // when armed is false: the reason, in words you can show the merchant
  "endpoint": "https://whitcombpayments.com/api/ingest"
}
StatusMeaning
404The key was not recognised (or the account is not switched on for Process Now). Check it with your desk.
429Ten failed attempts from one address in fifteen minutes. Wait, then try again.
503Activation is temporarily unavailable. Try again shortly.

Signing a request

Every call after activation carries the same envelope. Build your payload object, add b (your brand code) and t (the current time in milliseconds since the epoch), JSON-encode it, base64url-encode that string (no padding), and sign the encoded string with HMAC-SHA256 using your secret. Send { "p": encoded, "s": signature } with Content-Type: application/json.

payload = { ...your fields, "b": brand, "t": now_ms }
p = base64url( JSON.stringify(payload) )        // '-' and '_' instead of '+' and '/', no '=' padding
s = hex( HMAC_SHA256( key = secret, message = p ) )
POST { "p": p, "s": s }

Node.js

const crypto = require('crypto');
async function whitcomb(path, fields) {
  const p = Buffer.from(JSON.stringify({ ...fields, b: BRAND, t: Date.now() })).toString('base64url');
  const s = crypto.createHmac('sha256', SECRET).update(p).digest('hex');
  const r = await fetch('https://whitcombpayments.com' + path, {
    method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ p, s })
  });
  return { status: r.status, body: await r.json() };
}

PHP

function whitcomb($path, array $fields) {
  $fields['b'] = BRAND; $fields['t'] = (int) round(microtime(true) * 1000);
  $p = rtrim(strtr(base64_encode(json_encode($fields)), '+/', '-_'), '=');
  $s = hash_hmac('sha256', $p, SECRET);
  $ch = curl_init('https://whitcombpayments.com' . $path);
  curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode(['p' => $p, 's' => $s])]);
  $body = json_decode(curl_exec($ch), true); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  return [$code, $body];
}

Python

import base64, hashlib, hmac, json, time, requests
def whitcomb(path, fields):
    payload = dict(fields, b=BRAND, t=int(time.time() * 1000))
    p = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip('=')
    s = hmac.new(SECRET.encode(), p.encode(), hashlib.sha256).hexdigest()
    r = requests.post('https://whitcombpayments.com' + path, json={'p': p, 's': s}, timeout=25)
    return r.status_code, r.json()

A request is refused when the signature does not match (401, "code":"unlinked"), when it is missing (400 Unsigned request.), or when t is more than an hour from Whitcomb’s clock (400, "code":"skew" — check your server time).

Start a payment

POST/api/pn-checkout  signed · a: "checkout"

Call this when the buyer chooses to pay by card. You get back the address of Whitcomb’s hosted payment page for that order; redirect the buyer there. Save the reference with your order.

{
  "a":   "checkout",
  "oid": "10442",                       // your order number — required, up to 40 characters
  "c":   12999,                         // amount in CENTS (integer): $129.99 — required
  "cur": "USD",                         // USD only
  "em":  "buyer@example.com",           // optional — prefilled on the payment page
  "fn":  "Jane", "ln": "Smith",         // optional
  "ph":  "5125550100",                  // optional
  "ret": "https://yourstore.com/order/10442/thank-you",   // where the buyer is sent after paying
  "u":   "https://yourstore.com"        // your site — "ret" must be on this same host
}
200
{ "ok": true, "url": "https://whitcombpayments.com/pay/?t=ws_…", "reference": "ws_…" }

200 — the same order was already started and is still open at the same amount (the buyer came back):
{ "ok": true, "url": "…", "reference": "ws_…", "reused": true }

200 — the order is ALREADY PAID: no new charge; the page says "payment received" and returns the buyer:
{ "ok": true, "url": "…", "reference": "ws_…", "reused": true, "paid": true }
FieldRules
oidYour order number. One order gets one charge: the same oid at the same amount reuses the open link; a paid oid is never charged again.
cWhole cents, greater than zero, at most 100,000,000 ($1,000,000). Send 12999, never 129.99.
curOnly USD is accepted today.
retMust be an https:// (or http://) address on the same host as u. Anything else is dropped and the buyer stays on Whitcomb’s receipt page.

Refusals (see Errors): 403 code:"off" Process Now is not switched on for this account; 403 code:"not_armed" the account may not take money yet — the error text says why, in words for the merchant, not the buyer; 503 code:"paused" card payments are paused across the platform for the moment; 400 for a missing order number, a bad amount or a currency other than USD.

Confirm a payment

POST/api/pn-checkout  signed · a: "status"

Ask Whitcomb whether a reference was paid. Call it when the buyer lands on your return page before you render the receipt, and again on a timer for orders still pending (the plugin sweeps every five minutes for 24 hours). Mark the order paid only when paid is true — never from the redirect alone.

{ "a": "status", "ref": "ws_…" }
200
{ "ok": true, "reference": "ws_…", "status": "paid", "paid": true, "amount_cents": 12999, "paid_at": "2026-09-16T14:07:31.000Z" }

// still open:        "status": "open",      "paid": false
// buyer cancelled:   "status": "cancelled", "paid": false     → cancel or leave the order unpaid
// paid on another link for the same order (rare): "status": "paid", "paid": true, "twin": true, "paid_reference": "ws_…"

404 { "error": "No such payment." }

A status call always asks the card processor for the freshest reading, so it is safe to trust and cheap to repeat. There are no webhooks to register: polling is how you learn you were paid, and it is the same method the WooCommerce plugin uses.

Heartbeat & order reporting (optional)

POST/api/ingest  signed · a: "ping" or a: "orders"

Ping is your “test connection” and your way of learning when the account becomes armed — a merchant approved this afternoon starts taking cards without touching anything. Send it once a day, and on demand.

{ "a": "ping", "u": "https://yourstore.com", "sn": "Your Store", "pv": "1.0", "cur": "USD", "oc": 1284 }
                                                                   // oc: how many orders your store holds (optional)
200
{ "ok": true, "merchant": "Your Business LLC", "brand": "yourbusiness", "status": "active",
  "armed": true, "why": "", "process_now": true, "orders_on_file": 1280, … }

Orders feed the Payments and reporting views in your merchant portal with every order your store takes, however it was paid. Send up to 250 orders per call; sending an order again updates it (its id is the key), so you can resend freely after a change or a dropped request.

{ "a": "orders", "u": "https://yourstore.com", "pv": "1.0", "oc": 1284,
  "o": [ {
    "id": "10442",              // your permanent order id — required, the idempotency key
    "no": "10442",              // the number you show the customer
    "st": "processing",         // your order status, as text
    "c":  "USD",
    "t":  12999,                // total, cents      "tx": 900  tax      "sh": 599 shipping      "rf": 0 refunded
    "cn": "Jane Smith", "em": "buyer@example.com", "ph": "5125550100",
    "ba": "1 Main St, Austin, TX 78701, US",
    "pm": "whitcomb_process_now", "pt": "Credit or debit card",  // payment method id and title, your own values
    "tid": "ws_…",              // the Whitcomb reference for a card payment
    "ic": 2, "it": [ { "n": "Blue widget", "q": 2, "t": 11000 } ],   // items: name, quantity, line total in cents (up to 100)
    "d":  1758031651000,        // placed, ms since epoch
    "pd": 1758031700000         // paid, ms since epoch, or null
  } ],
  "bf": "done"                  // optional: send once, on the last batch of a full-history sync
}
200 { "ok": true, "written": 1 }

Errors

Every error is { "error": "…" } with a sensible HTTP status, and some carry a short code you can branch on. The error text on the checkout call is written to be shown to a buyer as-is if you have nothing better.

CodeStatusWhat it means
unlinked401Wrong brand code or secret, or the account was closed. Re-activate with your key.
skew400Your t is more than an hour from Whitcomb’s clock. Fix your server time.
off403Process Now is not switched on for this account. Chat with your desk.
not_armed403The account is not yet allowed to take money (still in review, over its monthly limit, or paused by the desk). error says which.
paused503Card payments are paused platform-wide for the moment. Offer the buyer another way to pay and try again later.

Rules of the road

Stripe Express — WooCommerce plugin

Whitcomb Payments — Stripe is a WordPress payment gateway for merchants on the Stripe Express program. Buyers pay on Stripe’s hosted checkout opened on your own Stripe account: the money settles to you, your business name appears on the customer’s card statement, and Whitcomb’s account is not involved.

Security model

Retired: the built-in checkout (Instant Plugin)

Whitcomb’s earlier embedded checkout — the “Instant Plugin”, its plugin.js embed and the original whitcomb-payments WooCommerce gateway — was retired on August 23, 2026 and no longer accepts merchant payments. Stores that used it have been moved to Process Now or Stripe Express; if yours hasn’t been, chat with us now and we’ll switch you across — most accounts are moved the same business day.

Support

Keys, brand codes and store secrets are issued by your Whitcomb desk — there is no self-serve signup, because every account is placed and configured for the specific business. Chat with us now and give your business name; most requests turn around the same business day. Your merchant portal’s Set up tab walks the whole plugin setup end to end.

Questions: Chat with us now · (337) 242-3039 · 660 Madison Avenue, Suite 2349, New York, NY 10065.