Chat Api Port [x] Routes and crm page are registered [x] Unauthenticated requests return json 401 [x] Get cannot send and post requires csrf [x] Origin must match exactly [x] Missing device returns empty contacts without querying messages [x] Message queries require store and device [x] Foreign customer does not fall back to posted phone [x] Contacts preload only current store and keep message delimiters [x] Whatsapp contact contract remains available [x] Crm contacts include customer without messages and search tags [x] Mark read requires csrf and scopes inbound rows [x] Send uses store config and normalized phone with mock sender [x] Provider error string is not mistaken for success [x] Group send is rejected without dispatch [x] Database errors return generic json [x] Crm page renders both configured and missing device Cron Port [x] All five cron routes have aliases [x] Http requires configured cron key [x] Busy lock does not dispatch any worker [x] Queue claims before sending and updates captured ids [x] Queue retries explicit failure until fifth attempt [x] Queue first failure returns to pending [x] Missing per message result is not marked sent from aggregate success [x] Queue exception is uncertain and lock released [x] Scheduled enqueue deduplicates phones and commits marker [x] Schedule already run is skipped under lock [x] Enqueue failure rolls back date marker and log [x] Billing catchup charges days from locked balance [x] Billing already charged does not debit again [x] Insufficient balance downgrades and syncs subscription [x] Subscription failure rolls back downgrade and date [x] Ledger failure rolls back whole store catchup [x] Status report uses store credentials and tracking scope [x] Lock is released on unexpected failure [x] Missing free plan does not advance billing date [x] Queue batch size is limited to two hundred [x] Realtime report uses correct path and does not leak raw response Final Public Port [x] All twelve files have routes [x] Daily quote does not charge hours for timestamp inputs [x] Hourly quote rounds up half hour [x] Invalid dates are rejected [x] Past rental is rejected [x] Cross table overlap rejects insufficient capacity [x] Disjoint existing ranges do not double count capacity [x] Rental confirmation is atomic and does not create pos sale [x] Rental insert failure rolls back without notifications [x] Rental confirmed retry does not insert again [x] Rental wrong otp consumes attempt [x] Rental rate change requires new order [x] Booking returns server price and tracks capacity [x] Booking retry only reuses session owned id [x] Booking api get cannot mutate [x] Booking api post requires csrf [x] Special order cannot be read by numeric id alone [x] Identity verification rejects wrong session proof [x] Reset request stores only otp hash and sends after commit [x] Unknown reset identity does not send or reveal account [x] Reset rate limit rolls back [x] Identity wrong otp persists attempts [x] Identity expired otp cannot verify [x] Reset cannot bypass otp verification [x] Reset writes hash and consumes challenge [x] Registration creates store roles and hashed owner atomically [x] Registration rollback does not leave partial store [x] Table order uses token store and database price [x] Table order retry does not duplicate sale [x] Table order detail failure rolls back [x] Table order post requires csrf [x] Tracking alias uses existing authenticated webhook [x] Rental checkout view renders from validated selection [x] Account and pricing views render [x] Web one view renders store calendar [x] Every legacy entry point resolves to a controller method [x] Special booking renders without losing ownership fields [x] Upgrade uses pending server amount and never marks paid [x] Calendar merges bookings with transactions and uses capacity [x] Payment proof rejects executable mime [x] Payment proof rejects oversized files [x] Consumed reset challenge cannot change password again [x] Five otp failures prevent even correct code [x] Staff qr confirmation locks before stock movement [x] Staff rental creation checks both reservation tables [x] Upgrade view renders without creating subscriptions [x] Table order view escapes cart names and includes idempotency key [x] Rental success renders session owned order [x] Rental otp view renders verification and cancel form [x] Table order rejects unsupported http methods [x] Rental dates accept iso offsets and javascript utc Jaknot Webhook Port [x] Aliases and method routes are registered without browser auth [x] Get returns 405 without queries [x] Missing header cannot use browser session or body token [x] Invalid token returns 401 [x] Token comparison is case sensitive even if database is not [x] Duplicate token mapping is rejected [x] Valid snapshot sets absolute stock and purchase price [x] Body routing cannot select another store or outlet [x] Unknown sku is skipped while matched item commits [x] Ambiguous sku does not choose first product [x] Missing values cannot zero existing data [x] Zero is valid for stock and price [x] Invalid item types and values are reported [x] Malformed json and empty items return 400 [x] Second write failure rolls back the whole batch [x] Revoked token under lock rolls back before product write [x] Repeated snapshot assigns values without accumulating stock [x] Token values are redacted and header is not logged [x] Barcode alias and integer rounding match legacy behavior Qris Api Port [x] Route aliases are registered [x] Unauthenticated request returns json 401 [x] Invalid amounts do not query database [x] Method guard [x] Store is taken from session and must be active [x] Missing configuration returns 422 [x] Valid generation preserves contract and does not write [x] Post json generation [x] Crc known vector and generated checksum [x] Existing amount is replaced once and merchant data preserved [x] Broken tlv duplicate tags and bad crc are rejected [x] Invalid configured payload returns 422 [x] Database failure is generic [x] Every legacy api file has a ci route Shopee Webhook Port [x] Webhook route aliases do not use browser authentication [x] Other methods return 405 without business queries [x] Malformed payload returns 400 [x] Unknown shop returns 404 [x] Secret is required even when configuration is empty [x] Wrong secret cannot use logged in store [x] Duplicate configured shop is rejected [x] New order writes import rows sale details and stock [x] Retry updates status without repeating items or stock [x] Cancellation restores tracked quantity once [x] First cancelled event does not deduct stock [x] Late event cannot reopen cancelled order [x] Paused creation records zero stock movement [x] Cancellation does not credit product paused at creation [x] Partial sku match does not create incomplete sale [x] Detail failure rolls back order writeback too [x] Reseller tier overrides shopee totals [x] Foreign reseller rolls back [x] Webhook secret is redacted in api log [x] Invalid quantity or nested status returns 400 [x] Status only event updates and audits every existing item [x] Ambiguous sku keeps writeback but skips sale [x] Foreign outlet cannot write orders [x] Manual cancellation uses tracked quantity and consumes marker [x] Cancellation before items arrive remains terminal [x] Audit failure rolls back status update Store Api Port [x] Machine routes have legacy aliases and options [x] Preflight returns 204 without authentication or mutations [x] Get cannot create sale [x] Invalid key cannot borrow logged in store [x] Malformed json returns 400 [x] Sale preserves negative stock and snapshots [x] Paused product saves sale without moving stock [x] Duplicate order rolls back before insert [x] Ambiguous sku returns 422 and rolls back [x] Foreign customer returns 422 [x] Detail failure returns 500 and rolls back [x] Invalid date or quantity cannot write sale [x] Discount cannot make negative total [x] Bundle cannot silently change virtual stock [x] Single stock resolves actual sku and remains store scoped [x] Multiple stock response lists missing sku [x] Single missing stock returns 404 [x] Stock rejects nested sku array [x] Invoice lookup is scoped by api key store [x] Missing invoice returns 404 and missing parameter 400 [x] Api log redacts body key [x] Body api key authenticates without header [x] Get query key and sku work without browser session [x] Form input remains supported [x] Shop id takes priority over marketplace id [x] Repeated sku lines move stock for each line [x] Post commit notification failure still returns 201 [x] All stock uses barcode when sku is blank Wablas Webhook Port [x] Both callback aliases have routes without browser authentication [x] Both endpoints require post [x] Missing wrong or unconfigured secret is rejected [x] Query token compatibility remains available [x] Invalid json or missing identity returns 400 [x] Unknown device cannot borrow logged in store [x] Ambiguous device is rejected [x] Inbox persists message and customer under device store [x] Customer lookup uses store and exact phone variants [x] Duplicate inbox does not create contact or message again [x] Echo and group do not create contacts or check rules [x] Message failure rolls back customer creation [x] Autoreply matches once and dispatches after commit [x] Autoreply failure does not roll back accepted message [x] Rule matching supports all legacy modes and invalid regex [x] Tracking scopes update to outgoing store and device [x] Repeated tracking is successful noop [x] Late tracking cannot regress read status [x] Unknown and ambiguous tracking ids are rejected [x] Device mismatch under lock cannot update tracking [x] Tracking failure rolls back [x] Credential fields are redacted in api log [x] Unix milliseconds are normalized