Integration Checklist
Read first: QR Code Loyalty Guide · Phone Number Guide · Kiosk and POS Configuration · Message & Error Codes
This is the normative go-live gate for an In-Store Loyalty API integration. Every item is objectively verifiable — you either did it or you did not — and LoyaltyPlant runs an equivalent checklist during certification. Work through it against the latest protocol version, 1.7.
Items marked (conditional) apply only if you implement that capability; skip them otherwise and note the skip in your test report.
1. Contract conformance
- All requests target versioned paths for the version you certify against —
/one-time-salt/1.7,/qr-code/1.7,/order-closed/1.7,/refund/1.7. - All requests are HTTP
POSTwithContent-Type: application/json. - Request and response payloads validate against the schemas in cloudvalidator-api.yaml — field types, required fields, and enum values match (the spec lint passes).
-
establishmentIdis configurable per outlet on the POS side, not hard-coded.
2. Authentication
- A fresh
one-time-saltis fetched immediately before each authenticated call (qr-code,order-closed,refund) — no salt pooling or caching. -
AuthorizationTokenis computed asSHA256(apiPrivateKey + salt)in lowercase hex (v1.4+); the key comes first and the request body is not part of the hash. -
SaltIdandAuthorizationTokenheaders are sent on every authenticated call;one-time-saltitself is sent with no auth headers. - A salt is never reused; a reused or unknown
SaltIdreturns HTTP 403{"message":"Token expired."}and is handled as an auth error, not a loyalty outcome. - HTTP 403 responses are handled distinctly from business outcomes (recompute token with a fresh salt; surface a clear operator error).
- A request with a wrong/unknown
apiPrivateKeyis verified to fail with HTTP 403 (and surfaces a clear "LP communication error").
3. QR accrual flow
Points are only credited after order-closed; the scan itself just pre-validates. The customer scans their QR code at the self-service kiosk, or the cashier scans the customer's QR code at the cash register / POS terminal. Verify the accrual path end to end, including the cases where the customer changes the order after scanning.
- A QR code (or 5-character short code) read from the LoyaltyPlant app — scanned by the customer at the kiosk, or by the cashier at the cash register / POS terminal — is sent in the
qrCodefield ofqr-code. -
orderIdand the fullposInfo(establishmentId,terminalId,terminalCurrentTimestamp,terminalTimeZone) are sent with everyqr-coderequest. - The bonus card can be read before, during, and after items are added to the order, with correct results in each case.
- Scanning the same card twice into one order credits points only once.
- A repeated identical QR string is rejected (
504/505), and a non-LoyaltyPlant QR is rejected (503); the returned message is displayed in both cases. - If all items are removed after a scan, the order closes as a valid zero/empty transaction without error.
- Loss of connectivity during scan or closure is handled gracefully (operator sees a clear "no connection" state; the order is preserved for retry when connectivity returns), including the case where the POS app is closed and reopened mid-order.
- An anonymous app user (empty profile name/email) scans and accrues points correctly.
4. QR redemption flow
A reward is a menuItems line returned with a 100% discount; redemption only settles correctly if that exact item and discount are echoed back at closure (the "reward echo" rule). Redemption is triggered the same way as accrual — the customer scans their QR code at the self-service kiosk, or the cashier scans the customer's QR code at the cash register / POS terminal. Verify both the redeem and the change-of-mind paths.
- Returned
menuItems(reward items) and theirmodifiersare added to the order. - Item-level and order-level
discounts(type=percent/amount/fixedPrice, withdiscountIdandvalue) are applied. - Reward echo: each reward item and its accompanying 100%-discount (the
discountIdLoyaltyPlant returned) is echoed back, unchanged, in theorder-closedorder contents. - If a returned
paymentis present, it is registered on the order and itstransactionIdis stored for the closure. - Removing a reward before closure is handled: the reward is omitted from
order-closedand LoyaltyPlant returns the reward (verified the points come back). - Two rewards in one order, and rewards for two different customers in one order, settle correctly.
5. Phone-number flow (conditional)
This is the phone number-based flow. The customer enters their number on the self-service kiosk, or the cashier enters it at the POS / cash register.
- After the payment screen, the customer can enter a phone number on the self-service kiosk — or the cashier can enter it at the POS / cash register;
qr-codeis sent withphoneNumber(and optionalclientName,subscribedToPushNotifications) and noqrCode. - The phone-entry screen — on the kiosk and on the POS / cash register alike — is configurable: phone mask (non-mandatory, leading
+non-deletable), marketing texts/banners per language, and legal-consent section. -
509(wrong phone format) and510(flow disabled) are handled — re-prompt or fall back to QR-only — whether the number was entered by the customer at the kiosk or by the cashier at the POS / cash register.
6. order-closed correctness
This is the request that credits points and settles rewards, so its contents and exactly-once delivery are the highest-risk items in the integration.
-
order-closedcarries the complete final order — all paid items, all reward items (with their discount), all order-level discounts, allpayments, andwaiter. -
orderTotalis reported without subtracting the points payment (the points payment is listed inpayments); only reward-item value is netted out. - For a LoyaltyPlant payment,
payments[i].transactionIdequals thetransactionIdfrom theqr-coderesponse, and every points-payment transaction ID is unique. -
order-closedis sent exactly once per order; a duplicate is detected and not resent. A repeatedorder-closedis not a506/ORDER_ALREADY_CLOSED(that is theprocessQrCodescan-time code) — it returns HTTP 200 withaccepted: falseanderrorCode: 0("document not found or not in open state"), which on a retry confirms the first closure landed; branch onaccepted. - The closing call is guaranteed in every code path; an order closed without it auto-annuls (validated-but-unused rewards return automatically after about a day).
- The
order-closedresponse is processed by branching onaccepted(the authoritative success signal — noterrorCode, which is0on both accepted and rejected closures): readpointsReward/pointsSpenton success, anderrorTextfor the rejection reason whenaccepted: false.
7. Refunds
- On a full order cancellation/refund,
refundis called withestablishmentIdandorderId; LoyaltyPlant returns used rewards and writes off accrued points. - Partial refunds are not attempted — only the entire order can be cancelled.
- The
refundresponse is processed by branching onaccepted(the authoritative success signal — noterrorCode, which is0on both accepted and rejected refunds); whenaccepted: false, the reason is inerrorText.
8. Messages & errors
- Every
messages[]entry fromqr-codeis displayed to the cashier / on the kiosk screen — none are swallowed. - HTTP 200 is never treated as unconditional success: a rejection is detected by
accepted: falseonqr-code,order-closed, andrefundalike — not byerrorCode, which stays0on both accepted and rejectedorder-closed/refund(per the golden rule in Message & Error Codes). - Per-code handling follows the Message & Error Codes recommendations (retry vs. show vs. escalate).
- A wrong/non-existent
establishmentIdis handled (operation fails cleanly with a surfaced error, no crash).
9. Configuration (conditional — POS/kiosk config fetch)
- If the integration fetches outlet configuration from LoyaltyPlant, it reads
apiUrl,apiVersion,establishmentId,password,discountId, andflowfrom the config response and applies them per outlet. - The sales-outlet identifier and the 100%-discount ID used for rewards are configured to match the values registered with LoyaltyPlant.
- The outlet is verified to appear correctly in the LP CRM after a successful scan and closure.
10. Onboarding & certification
LoyaltyPlant verifies the integration before it goes live; this section mirrors that verification process so there are no surprises at the gate.
- LoyaltyPlant is notified about a week ahead of the planned completion date, so the verification slot and test plan can be scheduled; if the date slips, a new one is agreed.
- The certification test cases are run on your side and a test report is produced against the LoyaltyPlant test plan.
- Access to your test environment is provided to LoyaltyPlant, which verifies the integration against its test plan; flagged cases are fixed and re-verified until all pass.
- Once every case passes, LoyaltyPlant considers the integration complete and ready for deployment.
- The first deployment to a POS terminal happens with LoyaltyPlant representatives present, or is recorded on video and shared with LoyaltyPlant.
Note: Your LoyaltyPlant integration manager schedules the certification run and shares the test plan, test app, test LP CRM access, and certification test cases at kickoff.
← Back to Overview