Skip to main content

Prerequisites

  • Terminal API key from Xendit In-Person Payment team
  • Node.js or your preferred development environment

For Physical Terminal Testing

  • Terminal Gateway set up (desktop app or Android/iOS SDK)
  • Physical terminal device connected and showing “Ready” status
  • Terminal registered with Xendit for test mode (contact support if needed)
  • Valid terminal_id from your registered device

For Simulation Testing (No Terminal Required)

  • Any terminal_id value (e.g., SIM001) - no registration needed
  • Special test amounts to trigger different payment scenarios
  • Use 4040404 as terminal_id to test invalid terminal error handling
Don’t have a physical terminal yet? No problem! You can start integration immediately using our simulation capabilities. Use special test amounts and terminal IDs to simulate different payment scenarios without needing physical hardware.
Use sandbox/test devices and small amounts while testing. Do not use production keys in development.

1. Configure your environment

Set up your local development environment with the necessary credentials and base URLs.
1

Create environment file

Create a .env file in your project root:
.env
TERMINAL_API_KEY=your_dev_terminal_api_key
TERMINAL_BASE_URL=https://terminal-dev.xendit.co
CALLBACK_URL=http://localhost:3000/terminal/callbacks
TERMINAL_ID=TERM001
Never commit secrets to version control. Add .env to your .gitignore file.
2

Install dependencies

Install required packages for your development environment:
npm install node-fetch express dotenv
3

Set up webhook server

Create a local Express server to receive callbacks:
webhook.js
import express from 'express';
import dotenv from 'dotenv';

dotenv.config();
const app = express();
app.use(express.json());

app.post('/terminal/callbacks', (req, res) => {
  console.log('Event:', req.body?.type, req.body?.data?.status);
  console.log('Full payload:', JSON.stringify(req.body, null, 2));
  res.sendStatus(200);
});

app.listen(3000, () => {
  console.log('Webhook server running on http://localhost:3000');
  console.log('Use ngrok to expose this server for testing');
});
Use ngrok (npm install -g ngrok && ngrok http 3000) to expose your local server publicly for webhook testing.

2. Choose your testing approach

3. Create your first payment session

Now create a payment session and process it. Choose the approach that matches your setup:
  • Simulation Payment
  • Physical Terminal Payment
Perfect for testing without physical terminals
curl -X POST 'https://terminal-dev.xendit.co/v1/terminal/sessions' \
  -u 'YOUR_TERMINAL_API_KEY:' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-key: sim-success-'"$(date +%s)" \
  -d '{
    "session_type": "PAY",
    "mode": "TERMINAL",
    "currency": "IDR",
    "amount": 10000,
    "country": "ID",
    "channel_properties": { "terminal_id": "SIM001" }
  }'
Success: Session created with ACTIVE status, then automatically transitions to COMPLETED
Decline: Session created with ACTIVE status, then transitions to FAILED

4. Monitor callbacks

Your webhook server (started in step 1) will receive real-time payment updates. Monitor the console output to see callback events.
Callback events include PAYMENT_CREATED, PAYMENT_SUCCEEDED, PAYMENT_FAILED, and others. See the Callbacks documentation for complete event types.

5. Verify payment details

Query payment details after receiving a success callback.
curl -X GET 'https://terminal-dev.xendit.co/v1/terminal/payments/PAYMENT_ID' \
  -u 'YOUR_TERMINAL_API_KEY:'
Confirm status is SUCCEEDED and amounts match your session.

6. Optional: Void a payment

If needed, you can void a payment transaction:
curl -X POST 'https://terminal-dev.xendit.co/v1/terminal/payments/PAYMENT_ID/void' \
  -u 'YOUR_TERMINAL_API_KEY:'

Troubleshooting

  • Ensure you include a trailing colon in the Basic Auth username encoding
  • Verify the key is a Terminal API key (not Dashboard API key)
  • Check that you’re using the development base URL for testing
  • Verify you’re using the correct special amounts: 400508 (decline), 400509 (channel unavailable), 400711 (cancellation)
  • Check that you’re in test mode (using development base URL)
  • Any terminal_id works for simulation - you don’t need a real terminal registered
  • Normal amounts (not special test amounts) will always succeed in simulation mode
  • Confirm Terminal Gateway shows device as Connected/Ready
  • Verify terminal_id matches your device configuration exactly
  • Check network connectivity on the device
  • Ensure device has correct time settings
  • For simulation testing: You don’t need a physical terminal - any terminal_id will work
  • Use ngrok to expose your local server publicly
  • Ensure your webhook endpoint returns HTTP 200
  • Check server logs and firewall settings
  • Verify the webhook URL is configured correctly
  • For simulation: Callbacks work the same way as physical terminals
  • Verify .env file is in the correct location
  • Check that you’re calling dotenv.config() before using environment variables
  • Ensure .env file is not committed to version control
  • Install and configure Terminal Gateway app or SDK
  • Register your physical terminal devices with actual Terminal IDs and IP addresses
  • Update your environment variables to use real terminal IDs
  • Test with small amounts on the physical device
  • All your existing integration code will work without changes!

Next steps

Now that you have a working integration, explore these advanced features:

Production deployment

When ready for production:
  1. Switch to live API keys - Use production Terminal API key and base URL https://terminal.xendit.co
  2. Update webhook URLs - Configure production webhook endpoints that are publicly accessible
  3. Test thoroughly - Start with small amounts and verify all payment flows work correctly
  4. Monitor and log - Set up proper logging and monitoring for payment events
Always test thoroughly with small amounts in production before processing real customer payments.