Xendit Sandbox Setup
This project can run against real Xendit test-mode endpoints instead of WireMock for local payment development.
What This App Expects
The current integration:
- sends authenticated server-side requests to Xendit
- creates QR payments through
/qr_codes - keeps subscription billing on a transitional path where renewal orchestration is handled in our backend domain model
- receives payment status updates through
POST /api/v1/payments/webhook - verifies the
x-callback-tokenheader on incoming webhooks
Relevant code:
src/server/outbound/xendit/http.tssrc/server/outbound/xendit/real-client.tssrc/server/payments/use-cases/create-payment.tssrc/server/subscriptions/use-cases/create-renewal-payment.tssrc/server/core/middleware/webhook-auth.ts
1. Create Xendit Test Credentials
- Create a Xendit account and stay in Test Mode.
- In the Xendit dashboard, generate a test Secret API Key.
- Give the key the permissions needed for money-in / payment creation.
- Store the secret key securely.
Official references:
2. Configure a Webhook in Xendit
Xendit must be able to call your app from the public internet, so a pure localhost URL is not enough.
- Start a public tunnel to your local API preview.
- Configure the webhook URL in Xendit as:
https://your-public-url/api/v1/payments/webhook
- Copy the webhook token from the Xendit dashboard.
- Set the same token in this app as
XENDIT_WEBHOOK_TOKEN.
The app verifies the x-callback-token header and can also validate a webhook signature secret if you configure XENDIT_WEBHOOK_SIGNATURE_SECRET.
Official reference:
3. Start the App Against Real Xendit Sandbox
Run the local app without WireMock and point it at real Xendit test endpoints:
APP_BASE_URL=http://localhost:3000 \
API_BASE_URL=https://your-public-url \
NEXT_PUBLIC_API_BASE_URL=http://localhost:8787 \
XENDIT_API_URL=https://api.xendit.co \
XENDIT_SECRET_KEY=your_test_secret_key \
XENDIT_WEBHOOK_TOKEN=your_webhook_token \
./scripts/local.sh start
Notes:
APP_BASE_URLis the browser-facing app URL.API_BASE_URLshould be the public URL Xendit can reach for webhook callbacks.NEXT_PUBLIC_API_BASE_URLremains local so the browser talks to your local preview server.XENDIT_API_URL=https://api.xendit.coswitches off WireMock and uses real Xendit test mode.
4. Recommended Tunnel Setup
Expose the Workers preview port, not the Next.js UI port, because the webhook endpoint is served by the API runtime.
Example with ngrok:
ngrok http 8787
Then use the generated HTTPS URL as API_BASE_URL and as the Xendit webhook base URL.
5. Suggested Local Env File
You can also put the values in .env.local for repeat use:
APP_BASE_URL=http://localhost:3000
API_BASE_URL=https://your-public-url
NEXT_PUBLIC_API_BASE_URL=http://localhost:8787
XENDIT_API_URL=https://api.xendit.co
XENDIT_SECRET_KEY=your_test_secret_key
XENDIT_WEBHOOK_TOKEN=your_webhook_token
Then start the app normally:
./scripts/local.sh start
6. How To Test It
- Open
http://localhost:3000. - Go through the normal photobooth flow until payment creation.
- Confirm the app creates a QR payment successfully.
- Complete the payment in Xendit test mode if supported by your sandbox scenario.
- Verify the webhook reaches:
/api/v1/payments/webhook
- Confirm the app updates payment state and activates the license as expected.
7. Troubleshooting
If payment creation fails:
- verify
XENDIT_SECRET_KEY - verify
XENDIT_API_URL=https://api.xendit.co - check server logs for provider error responses
If webhooks fail:
- verify the public tunnel URL is still active
- verify Xendit points to
/api/v1/payments/webhook - verify
XENDIT_WEBHOOK_TOKENmatches the dashboard token exactly
If the browser cannot talk to the local API:
- verify
NEXT_PUBLIC_API_BASE_URL=http://localhost:8787 - verify the Workers preview runtime is running on port
8787
8. Notes About This Integration
- This repo currently uses the Xendit
/qr_codesintegration path. createMonthlySubscription()is still a transitional integration, not provider-native recurring billing.- The backend now models subscriptions, renewals,
PAST_DUE, and recurring entitlements correctly, but provider-native recurring setup/cancel/resume is still future work. - Xendit documentation emphasizes newer payment APIs for some flows, so revisit the integration if you decide to modernize the payment implementation later.