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