Use your gp_live_… API token for API calls and the separate whsec_… project secret for webhook verification.

createInvoice with curl

curl -X POST https://app.grampaybot.com/api/createInvoice \
  -H 'GramPay-API-Token: gp_live_xxx' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order_184' \
  -d '{"amount":"10.00","public_description":"Access for 30 days","payload":"order_184"}'

Node.js

const res = await fetch('https://app.grampaybot.com/api/createInvoice', {
  method: 'POST',
  headers: {'GramPay-API-Token': process.env.GRAMPAY_TOKEN,
    'Content-Type': 'application/json', 'Idempotency-Key': `order_${orderId}`},
  body: JSON.stringify({amount: '10.00', payload: `order_${orderId}`}),
});
const data = await res.json();
if (!data.ok) throw new Error(data.error?.code || data.error);
app.post('/webhooks/grampay', express.raw({type: 'application/json'}), (req, res) => {
  const expected = 'sha256=' + crypto.createHmac('sha256', process.env.GRAMPAY_WEBHOOK_SECRET)
    .update(req.body).digest('hex');
  const given = req.headers['grampay-signature'] || '';
  if (given.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected)))
    return res.sendStatus(401);
  const event = JSON.parse(req.body);
  // Handle idempotently; webhook_test requires no business action.
  res.sendStatus(200);
});

Python

res = requests.post("https://app.grampaybot.com/api/createInvoice",
    headers={"GramPay-API-Token": os.environ["GRAMPAY_TOKEN"],
             "Idempotency-Key": f"order_{order_id}"},
    json={"amount": "10.00", "payload": f"order_{order_id}"}, timeout=10)
res.raise_for_status()
data = res.json()
raw = request.get_data()
expected = "sha256=" + hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(request.headers.get("GramPay-Signature", ""), expected):
    abort(401)

Go

mac := hmac.New(sha256.New, []byte(os.Getenv("GRAMPAY_WEBHOOK_SECRET")))
mac.Write(rawBody)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(r.Header.Get("GramPay-Signature")), []byte(expected)) {
    w.WriteHeader(http.StatusUnauthorized); return
}

PHP

$raw = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $raw, getenv('GRAMPAY_WEBHOOK_SECRET'));
$given = $_SERVER['HTTP_GRAMPAY_SIGNATURE'] ?? '';
if (!hash_equals($expected, $given)) { http_response_code(401); exit; }

Production checklist

  • Send a stable Idempotency-Key with every create request.
  • Verify HMAC over the raw body and reject invalid signatures.
  • Return 2xx for a valid webhook_test without fulfilment.
  • Deduplicate invoice_paid before granting access or shipping goods.
  • Alert on the exceptional 402 account_not_serviceable response.

Useful next step

Your first 50 confirmed payments are on us.

Start with $5 credit ↗