POS integration with
Two API contracts and a step-by-step guide. The POS receives orders via HMAC-signed webhooks, confirms them via REST, and advances the status until delivery.
API contracts
Languages supported
HMAC-SHA bits
Three resources, one integration
Integrating with BipBip is made up of two complementary API contracts and a step-by-step guide that ties them together.
Webhook spec
BipBip sends every new order to the POS via POST /v1/order/{remoteId}. The HMAC signature is verified and the response is 200 OK.
REST API reference
The POS calls BipBip to accept, reject, advance the status and query orders. Authentication uses X-Bipbip-Api-Key and every mutation includes an Idempotency-Key.
Integration guide
Step-by-step quickstart with real code in 4 languages, HMAC verification with tested samples, troubleshooting and a complete glossary of contract terms.
Read the guide →Designed for reliable integrations
HMAC-SHA256 signature
Every webhook is signed with HMAC-SHA256 over {timestamp}.{rawBody}. Requests older than 5 minutes are rejected to block replays.
At-least-once delivery
Every attempt carries the same X-Bipbip-Delivery-Id. That UUID enables dedup and safe retries without creating duplicate orders.
Idempotency-Key required
Every REST mutation includes a unique UUID per logical attempt. 24h cache, safe retries without side effects.
Samples in 4 languages
Node.js, Python, C# and PHP. All use stdlib only — no external dependencies. Ready to copy-paste.
Clear state machine
pending → preparing (atomic cascade on accept) → ready → handed_over. rejected and cancelled are terminal. No ambiguity.
Path-based versioning
Everything runs under /v1/. When v2 lands both versions can run in parallel during migration — no surprise breaking changes.
HMAC verification on every webhook
BipBip signs every request with HMAC-SHA256 over {timestamp}.{rawBody}. The raw bytes are captured, the timestamp is validated (max. 300s / 5 min skew) and the comparison runs in constant time.
- Standard library only — no external dependencies
timingSafeEqual/compare_digest/hash_equals- The raw body is captured before
JSON.parse
// HMAC-SHA256 verification — Node.js (built-in crypto) const crypto = require('crypto'); function verifyBipBipSignature(secret, timestamp, rawBody, signature) { // Reject requests older than 5 minutes (replay protection) const now = Math.floor(Date.now() / 1000); if (Math.abs(now - parseInt(timestamp, 10)) > 300) return false; // Sign the RAW body — never JSON.parse + re-serialize const message = `${timestamp}.${rawBody}`; const computed = crypto .createHmac('sha256', secret) .update(message, 'utf8') .digest('hex'); const expected = `sha256=${computed}`; // Timing-safe comparison (never use === on signatures) const a = Buffer.from(expected, 'utf8'); const b = Buffer.from(signature, 'utf8'); if (a.length !== b.length) return false; return crypto.timingSafeEqual(a, b); }
# HMAC-SHA256 verification — Python (stdlib only) import hmac, hashlib, time def verify_bipbip_signature(secret: str, timestamp: str, raw_body: bytes, signature: str) -> bool: # Reject requests older than 5 minutes if abs(int(time.time()) - int(timestamp)) > 300: return False # Build the exact message BipBip signed: "{ts}.{rawBody}" message = f"{timestamp}.".encode("utf-8") + raw_body computed = hmac.new(secret.encode("utf-8"), message, hashlib.sha256).hexdigest() expected = f"sha256={computed}" # Timing-safe comparison return hmac.compare_digest(expected, signature)
// HMAC-SHA256 verification — C# (.NET 6+) using System.Security.Cryptography; using System.Text; public static bool VerifyBipBipSignature(string secret, string timestamp, byte[] rawBody, string signature) { // Reject requests older than 5 minutes var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); if (Math.Abs(now - long.Parse(timestamp)) > 300) return false; var prefix = Encoding.UTF8.GetBytes($"{timestamp}."); var message = prefix.Concat(rawBody).ToArray(); using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); var computed = Convert.ToHexString(hmac.ComputeHash(message)).ToLowerInvariant(); var expected = Encoding.UTF8.GetBytes($"sha256={computed}"); var received = Encoding.UTF8.GetBytes(signature); return CryptographicOperations.FixedTimeEquals(expected, received); }
// HMAC-SHA256 verification — PHP 7.2+ function verify_bipbip_signature(string $secret, string $timestamp, string $rawBody, string $signature): bool { // Reject requests older than 5 minutes if (abs(time() - (int)$timestamp) > 300) return false; // Sign the raw body — never re-serialize the parsed JSON $message = $timestamp . '.' . $rawBody; $computed = hash_hmac('sha256', $message, $secret); $expected = 'sha256=' . $computed; // Constant-time comparison to avoid timing oracles return hash_equals($expected, $signature); }
The 4 steps to go live
From "credentials in hand" to "first order received and confirmed" without help from the BipBip team.
Implementing the webhook
The POS exposes POST /v1/order/{remoteId}. The raw body is captured before parsing the JSON.
Verifying the HMAC signature
HMAC-SHA256 is recomputed over {timestamp}.{rawBody} and compared in constant time.
Responding with remoteOrderId
The response is HTTP 200 with { "remoteOrderId": "..." }. Without that field BipBip will retry.
Accepting via REST API
POST /api/v1/Orders/{orderKey}/accept with X-Bipbip-Api-Key and Idempotency-Key.
Before starting
BipBip provides these during onboarding
- HMAC Secret (one per account)
- API Key (
X-Bipbip-Api-Key) - remoteId (one per store)
- Registered base URL (public)
POS integration with BipBip — ready to start
The BipBip team provides credentials and enables the production pilot during onboarding.