Prerequisites
- The web app is reachable locally.
- You have a target workspace id.
ENABLE_SEPAY_INTEGRATION=trueis set inworkspace_secrets.- The web runtime is configured with:
SEPAY_OAUTH_CLIENT_IDSEPAY_OAUTH_CLIENT_SECRETSEPAY_OAUTH_TOKEN_ENCRYPTION_SECRETSEPAY_WEBHOOK_API_KEYorSEPAY_WEBHOOK_SECRETWEB_APP_URLorNEXT_PUBLIC_WEB_APP_URLorNEXT_PUBLIC_APP_URL
- Optional overrides:
SEPAY_OAUTH_AUTHORIZE_URLSEPAY_OAUTH_BASE_URLSEPAY_API_BASE_URL
Enable The Workspace Flag
Validate OAuth
- Start OAuth from the same browser session that will receive the callback.
- Call:
- Open the returned
authorizeUrl. - Complete SePay consent.
- Confirm the callback succeeds and the workspace receives a SePay connection.
Validate Provisioning
- At least one active endpoint.
token_prefixis present.sepay_webhook_idis present after provisioning.
Validate Webhook Ingestion
- The API returns success, or duplicate-safe success on replay.
- A
sepay_webhook_eventsrow exists forevt_test_001. - A
wallet_transactionsrow exists and is linked throughcreated_transaction_id. transactionDateshould always include an explicit timezone such as+07:00. BareYYYY-MM-DD HH:mm:ssstrings are interpreted as Vietnam time (UTC+7) for SePay compatibility; omitting the timezone in test payloads is a fixture bug, not a supported operator shortcut.
Idempotency
Resend the exact same payload with the sameid.
Expected:
- No duplicate transaction is inserted.
- The event is treated as a duplicate/no-op.
Expense Direction
Send a payload withtransferType: "out" and a positive transferAmount.
Expected:
wallet_transactions.amountis stored as a negative value.- The transaction resolves against an expense category.
Endpoint Lifecycle
- Create an endpoint.
- Rotate the endpoint.
- Delete the endpoint.
- Deleted endpoints have
active = false. - Deleted endpoints have
deleted_at is not null. - Listing routes exclude deleted endpoints.
- Token resolution ignores deleted endpoints.
Disconnect
sepay_connections.status = 'revoked'- Active endpoints are marked inactive and soft-deleted.
Failure Paths
- Invalid webhook auth header is rejected.
- Invalid webhook JSON body returns a validation error.
- Unknown endpoint token is rejected.
- Disabled feature flag blocks SePay workspace APIs.
Troubleshooting
- OAuth start failures usually mean missing OAuth env vars or app-origin config.
- OAuth callback state failures usually mean the callback did not reuse the browser session or cookie jar that started OAuth.
- Provisioning failures usually mean SePay has no usable bank account or the webhook scopes were not granted.
- Missing transactions should be debugged from
sepay_webhook_events.failure_reasonfirst.