# TRON Pay sandbox integration This release is SANDBOX ONLY. It cannot receive or confirm real money. - /openapi.json: Versioned HTTP API specification, OpenAPI 3.0.3. - /integration.md: English integration guide, boundaries and webhook verification. - /integration.md?lang=zh: Chinese integration guide. - /docs: Interactive API explorer. Merchant secret keys are server-side only. Never include them in a browser, Electron installer, URL or source control. Use Idempotency-Key for order creation and persist merchantOrderNo before the first request. Webhook delivery is at least once; verify the raw body and deduplicate event.id before fulfillment. Only call sandbox simulation routes in this explicit sandbox environment. The existing mainnet indexer is used by separate GET-only shadow ingestion with durable scan/repair cursors. It never marks sandbox orders paid. Console onboarding is available in the sandbox: the owner creates applications, issues least-privilege keys, configures notifications, archives/restores applications and explicitly revokes keys. Console cookie endpoints are not third-party API authentication. Third parties continue using application-scoped Bearer keys from the workspace owner. API key rotation uses overlap followed by explicit revocation, without scope escalation. Archived applications reject new orders but preserve historical processing. Every notification references immutable URL/signing configuration. Normal retries retain it. Optional pay-key-version is a signing-key hint only; URL configuration versions are separate. Retain old signing secrets during normal rotation and always verify raw bytes. No-endpoint history is not automatically redirected by later configuration. TypeScript verifySignatureWithSecrets and Python/PHP helpers accept retained secrets. Node example accepts WEBHOOK_SECRETS_JSON and durably records a deduplicated SQLite inbox before HTTP200. Accepted is not fulfilled; business effects still require transactional event deduplication. Emergency signing-version revocation and explicit audited historical rerouting are available to the reauthenticated console owner. They preserve event IDs/history; ordinary API retries do not reroute. Remove compromised secrets from receiver configuration as well as disabling platform signing versions. This release remains sandbox only. ## Downloadable examples (public fixed allowlist, no API key needed) - /examples/node/create-order.mjs: Node24 order creation, persisted caller-supplied idempotency identity, private result file. - /examples/node/webhook.mjs: loopback Node24 receiver with durable SQLite inbox. - /examples/python/integration.py: Python3.9+ standard-library create_order, verify_webhook_rotating and accept_webhook. - /examples/python/webhook.py: loopback HTTP adapter; keep integration.py next to it. - /examples/php/integration.php: PHP8 cURL order creation and PDO SQLite inbox functions. - /examples/php/webhook.php: local PHP router; keep integration.php next to it. - /examples/typescript/index.ts: standalone Node TypeScript helper; not published to npm. Includes typed order responses, opaque pagination, cancel/retry, PaymentApiError and raw-byte signature verification. Secrets come from private server environment configuration only. Never print full order responses containing checkout tokens. For Node create-order, set PAYMENT_BASE_URL, PAYMENT_API_KEY, MERCHANT_ORDER_NO and IDEMPOTENCY_KEY after durably storing the business request. For callback adapters, set WEBHOOK_SECRET or version-to-secret WEBHOOK_SECRETS_JSON and a persistent private WEBHOOK_DB_PATH. Choose one adapter at a time. Python/PHP require Content-Length; all accept POST /hook and only the configured PAYMENT_ENV (sandbox by default; set live explicitly and require nested data.environment=live/data.network=tron:mainnet). Production needs HTTPS and a proper application server, not these development servers. Receiver acceptance statuses: 200 after durable inbox commit (identical duplicate also200), 401 for bad/stale signature, 400 for invalid JSON/event, 409 for same event ID with different signed bytes, 413 for body over64KiB, 503 for unavailable storage. Verify pay-timestamp rather than event.createdAt freshness; historical events get a fresh signed delivery timestamp. HTTP200 means accepted, not fulfilled. In a separate consumer verify application/environment, your merchant order, asset, exact string amount and payment.paid/data.status=paid. Atomically insert a unique(application,event) receipt plus the business effect in your business database; also prevent double fulfillment per business order. Commit before marking the example inbox processed. A separate SQLite inbox alone is not an atomic business effect in another database. Finalized/review/extra/expired events and resolved manual reviews cannot create another fulfillment. Do not fulfill real goods from sandbox events. Bind each receiver/inbox and retained version-secret set to one application. Do not mix unrelated applications in a single secret set; validate the order with that application's server key and your preexisting business mapping. Public scopes: orders:read for order queries, orders:write for create/cancel/original-delivery retry, events:read for events/delivery lists, sandbox:simulate only for explicit sandbox observation. List results are data/hasMore/nextCursor; pass nextCursor unchanged as after. Retry creation only with the same persisted identity/body. On409 refresh business state; on401/403 repair credentials/scopes; on429 or transient failures use bounded backoff. Never invent a force-paid or refund endpoint. The console also provides append-only reconciliation, exact JSON/CSV differences and application-scoped sending-service health. These cookie endpoints are not the third-party API. Live address ownership, mainnet claiming/correction, identity recovery/MFA, global platform operations, deployment/backup acceptance and controlled real payments remain incomplete. Live compatibility is preparatory, not activation. The current deployment is sandbox. Live order admission defaults disabled (live_collection_unavailable, HTTP503); an accepted live deployment can enable LIVE_PAYMENTS_ENABLED with authenticated existing-indexer wiring. Address ownership, real-payment acceptance and deployment remain required. Live environment/network must pair as live/tron:mainnet. Payable amount/amountRaw is authoritative; requestedAmount/requestedAmountRaw is the original request. Preserve every decimal as strings/atomic integers. Address QR encodes the address only, not asset or amount. Never simulate, manually confirm or mark a live invoice paid. Do not describe a schema/type change as a completed real-payment acceptance. HTTP402 subscription_required_or_exhausted means the merchant must renew their platform plan in the console. Only newly created payment orders consume allowance; idempotent replays do not. Expiry does not stop existing payment settlement or notification. Never simulate a live subscription payment or invent an entitlement grant endpoint.