🧪 Testing in Sandbox

Complete guide to testing all payment scenarios

Testing in Sandbox

The Koywe sandbox environment allows you to test all payment flows with simulated transactions before going live.

Sandbox Environment

Base URL

All sandbox API requests use:

https://api-sandbox.koywe.com

Getting Test Credentials

Contact soporte@koywe.com to receive:

  • Sandbox API Key
  • Sandbox Secret
  • Organization ID
  • Merchant ID

Sandbox vs Production: Sandbox credentials are completely separate from production. No real money is involved in sandbox testing.


Test Payment Methods by Country

Colombia (COP) 🇨🇴

PSE (Pagos Seguros en Línea)

Test Banks:

  • BANCOLOMBIA
  • DAVIVIENDA
  • BOGOTA
  • OCCIDENTE

Usage:

1{
2 "method": "PSE",
3 "extra": "BANCOLOMBIA" // Any test bank
4}

Behavior:

  • Payment link is fully functional in sandbox
  • Follow the simulated payment process
  • You can choose to succeed or fail the payment during the flow
  • Allows testing of complete user experience

Nequi

Usage:

1{
2 "method": "NEQUI"
3}

Behavior:

  • Generates a test QR code
  • After scanning the QR code, you’ll be given options to succeed or fail the payment
  • Simulates the complete Nequi payment experience

Brazil (BRL) 🇧🇷

PIX Static

Usage:

1{
2 "method": "PIX_STATIC"
3}

Behavior:

  • Generates a test QR code
  • After scanning the QR code, you’ll be given options to succeed or fail the payment
  • Simulates the complete PIX payment experience

PIX Dynamic

Usage:

1{
2 "method": "PIX_DYNAMIC"
3}

Behavior:

  • Generates a test QR code
  • After scanning the QR code, you’ll be given options to succeed or fail the payment
  • Similar to PIX_STATIC with interactive testing options

Mexico (MXN) 🇲🇽

SPEI (Instant Settlement ⚡)

Usage:

1{
2 "method": "SPEI"
3}

Behavior:

  • Provides test bank account details
  • Auto-completes after order creation
  • Simulates bank transfer confirmation

Cards

Test Card Numbers:

  • Success: 4242424242424242
  • Decline: 4000000000000002
  • Insufficient Funds: 4000000000009995

Usage:

1{
2 "method": "CARD"
3}

Chile (CLP) 🇨🇱

Khipu

Usage:

1{
2 "method": "KHIPU"
3}

Behavior:

  • Payment link is fully functional in sandbox
  • Follow the simulated payment process
  • You can choose to succeed or fail the payment during the flow
  • Allows testing of complete user experience

Argentina (ARS) 🇦🇷

Multiple Local Methods

Usage:

1{
2 "method": "KHIPU" // Also works for Argentina
3}

Behavior:

  • Payment link is fully functional in sandbox
  • You can choose to succeed or fail the payment during the flow

Test Scenarios

Interactive Testing: Most payment methods (PSE, Khipu) provide functional payment links where you can follow the simulated payment process and choose to succeed or fail. QR-based methods (PIX, Nequi) give you options after scanning the code.

Successful Payment Flow

Test a successful end-to-end payment:

Node.js
1async function testSuccessfulPayment() {
2 // Any amount will succeed in sandbox
3 const order = await createPayinOrder(token, orgId, merchantId, {
4 amount: 50000,
5 currency: 'COP',
6 paymentMethod: 'PSE',
7 bank: 'BANCOLOMBIA'
8 });
9
10 console.log('Order created:', order.id);
11 console.log('Status:', order.status); // "PENDING"
12 console.log('Payment URL:', order.paymentUrl);
13
14 // In sandbox, order auto-completes
15 // Wait a few seconds and check status
16 await sleep(5000);
17
18 const updated = await getOrderStatus(token, orgId, merchantId, order.id);
19 console.log('Updated status:', updated.status); // "COMPLETED"
20}

Expected flow:

PENDING → PROCESSING → PAID → COMPLETED

Failed Payment Scenario

Test payment failure handling:

Use the special test amount 666 to simulate a failed payment:

Node.js
1async function testFailedPayment() {
2 const order = await createPayinOrder(token, orgId, merchantId, {
3 amount: 666, // Special test amount for failure
4 currency: 'COP',
5 paymentMethod: 'PSE',
6 bank: 'BANCOLOMBIA'
7 });
8
9 console.log('Order created:', order.id);
10
11 // Wait for processing
12 await sleep(3000);
13
14 const updated = await getOrderStatus(token, orgId, merchantId, order.id);
15 console.log('Status:', updated.status); // "FAILED"
16 console.log('Error:', updated.errorMessage);
17}

Expected flow:

PENDING → PROCESSING → FAILED

Expired Payment

Test order expiration:

Node.js
1async function testExpiredPayment() {
2 // Set dueDate in the past
3 const pastDate = new Date();
4 pastDate.setHours(pastDate.getHours() - 1);
5
6 const order = await createPayinOrder(token, orgId, merchantId, {
7 amount: 50000,
8 currency: 'COP',
9 paymentMethod: 'PSE',
10 bank: 'BANCOLOMBIA',
11 dueDate: pastDate.toISOString()
12 });
13
14 console.log('Status:', order.status); // "EXPIRED"
15}

Test the complete Koywe-branded checkout experience:

PAYMENT_LINK is perfect for e-commerce! No contact or payment method needed - just create a link and share it. The customer enters their own data and selects their payment method in the Koywe checkout.

1async function testPaymentLink() {
2 // Create payment link - no contact or payment method needed!
3 const order = await axios.post(
4 `https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders`,
5 {
6 type: 'PAYMENT_LINK',
7 originCurrencySymbol: 'COP',
8 destinationCurrencySymbol: 'COP',
9 amountIn: 50000,
10 description: 'Test Invoice #123 - Web Design',
11 externalId: `test-invoice-${Date.now()}`
12 },
13 { headers: { 'Authorization': `Bearer ${token}` } }
14 );
15
16 console.log('✓ Payment link created:', order.data.id);
17 console.log('📧 Share this link:', order.data.paymentUrl);
18 console.log('');
19 console.log('Customer will:');
20 console.log(' 1. Open the link');
21 console.log(' 2. See Koywe-branded checkout');
22 console.log(' 3. Enter their personal information');
23 console.log(' 4. Select payment method (PSE, PIX, Nequi, etc.)');
24 console.log(' 5. Complete payment');
25
26 // Simulate sharing via email, WhatsApp, or QR code
27 return order.data;
28}
29
30// Usage
31const link = await testPaymentLink();
32
33// Open the payment URL in a browser to test the full checkout flow
34console.log('\n🌐 Open this URL to test:', link.paymentUrl);

Testing the checkout flow:

  1. Create the payment link using the code above
  2. Copy the paymentUrl
  3. Open it in your browser
  4. You’ll see the Koywe-branded checkout page
  5. Fill in test customer information
  6. Select a payment method
  7. Complete the simulated payment (choose success or fail)
  8. Verify webhook notification is received

Use Case: PAYMENT_LINK is ideal for:

  • E-commerce checkout pages
  • Invoice payment requests
  • Email/WhatsApp payment links
  • QR code payments
  • Quick payment collections without storing customer data

Testing Webhooks

Setup Webhook Endpoint

Use webhook testing tools:

  1. webhook.site - Instant webhook URL
  2. ngrok - Tunnel to localhost
  3. RequestBin - Webhook inspector

Using webhook.site

1

Get webhook URL

Visit webhook.site and copy your unique URL

2

Create order with webhook

1const order = await createPayinOrder(token, orgId, merchantId, {
2 amount: 50000,
3 webhookUrl: 'https://webhook.site/your-unique-id'
4});
3

View webhooks

Return to webhook.site to see webhook events in real-time

Expected Webhook Events

For a successful PAYIN:

  1. order.created - Order is created
  2. order.pending - Waiting for payment
  3. order.processing - Payment being processed
  4. order.paid - Payment confirmed
  5. order.completed - Funds credited

Webhook Payload Example

1{
2 "id": "evt_abc123",
3 "type": "order.completed",
4 "version": "v1",
5 "occurred_at": "2025-11-13T15:30:00Z",
6 "source": "koywe.api",
7 "environment": "sandbox",
8 "organization_id": "org_xyz",
9 "merchant_id": "mrc_abc",
10 "data": {
11 "orderId": "ord_123456",
12 "type": "PAYIN",
13 "status": "COMPLETED",
14 "amountIn": 50000,
15 "currencySymbol": "COP"
16 }
17}

Test Data

Test Contacts

Use these test document numbers (all valid in sandbox):

Colombia

1{
2 "documentType": "CC",
3 "documentNumber": "1234567890",
4 "fullName": "Juan Pérez Test"
5}

Brazil

1{
2 "documentType": "CPF",
3 "documentNumber": "12345678900",
4 "fullName": "Maria Silva Test"
5}

Mexico

1{
2 "documentType": "RFC",
3 "documentNumber": "XAXX010101000",
4 "fullName": "Pedro García Test"
5}

Chile

1{
2 "documentType": "RUT",
3 "documentNumber": "11111111-1",
4 "fullName": "Ana López Test"
5}

Test Bank Accounts

For PAYOUT testing:

Colombia

1{
2 "bankCode": "BANCOLOMBIA",
3 "accountNumber": "1234567890",
4 "accountType": "savings",
5 "currencySymbol": "COP"
6}

Brazil

1{
2 "bankCode": "BANCO_DO_BRASIL",
3 "accountNumber": "12345678",
4 "accountType": "checking",
5 "currencySymbol": "BRL"
6}

Mexico

1{
2 "bankCode": "BBVA_MEXICO",
3 "accountNumber": "012345678901234567",
4 "accountType": "checking",
5 "currencySymbol": "MXN"
6}

Chile

1{
2 "bankCode": "BANCO_CHILE",
3 "accountNumber": "12345678",
4 "accountType": "checking",
5 "currencySymbol": "CLP"
6}

Testing Crypto Operations

Automatic Test Network Selection

Production Network Names in Sandbox: When using production network names like ETHEREUM, POLYGON, or BSC in sandbox, the system automatically routes to test networks (Sepolia, Amoy, BSC Testnet respectively). You don’t need to specify testnet names explicitly.

Test Networks

Sandbox automatically uses these testnets:

Production NetworkSandbox TestnetSupported Assets
ETHEREUMSepoliaETH, USDC, USDT
POLYGONAmoyMATIC, USDC
BSCBSC TestnetBNB, USDC

Example: Specify "network": "POLYGON" in your request, and sandbox will use Amoy automatically.

Test Wallet Address

Use this address for receiving test crypto:

0x0000000000000000000000000000000000000000

Never use production addresses in sandbox - Always use test/burn addresses like the zero address above.

ONRAMP Test

Node.js
1async function testOnramp() {
2 // Create crypto wallet (or use existing)
3 const wallet = await createCryptoWallet(token, orgId, merchantId, {
4 address: '0x0000000000000000000000000000000000000000',
5 network: 'ETHEREUM' // Automatically uses Sepolia in sandbox
6 });
7
8 // Buy USDC with COP
9 const order = await createOrder(token, orgId, merchantId, {
10 type: 'ONRAMP',
11 originCurrencySymbol: 'COP',
12 destinationCurrencySymbol: 'USDC',
13 amountIn: 50000,
14 destinationAccountId: wallet.id
15 });
16
17 console.log('ONRAMP order:', order.id);
18 // In sandbox, crypto is "sent" to test address
19}

OFFRAMP Test

Node.js
1async function testOfframp() {
2 // Sell USDC for COP
3 const order = await createOrder(token, orgId, merchantId, {
4 type: 'OFFRAMP',
5 originCurrencySymbol: 'USDC',
6 destinationCurrencySymbol: 'COP',
7 amountIn: 10 // 10 USDC
8 });
9
10 console.log('OFFRAMP order:', order.id);
11 // In sandbox, order auto-completes
12 // Fiat is credited to virtual account
13}

Rate Limits

Sandbox environment has the following limits:

Limit TypeValue
Requests per minute100
Requests per hour1,000
Orders per day10,000

Rate limit headers are included in all responses:

  • X-RateLimit-Limit: Total requests allowed
  • X-RateLimit-Remaining: Requests remaining
  • X-RateLimit-Reset: Unix timestamp when limit resets

Testing Best Practices

Test All Order Types

Test each order type:

  • ✅ PAYIN with different payment methods
  • ✅ PAYOUT to different countries
  • ✅ BALANCE_TRANSFER between currencies
  • ✅ ONRAMP for crypto purchases
  • ✅ OFFRAMP for crypto sales
  • ✅ PAYMENT_LINK with expiry

Test Error Scenarios

Test error handling:

  • ✅ Invalid API credentials
  • ✅ Insufficient balance for PAYOUT
  • ✅ Invalid bank account details
  • ✅ Expired quotes
  • ✅ Failed payments (amount: 666)
  • ✅ Webhook signature verification

Test Idempotency

Node.js
1async function testIdempotency() {
2 const externalId = `test-order-${Date.now()}`;
3
4 // Create order
5 const order1 = await createPayinOrder(token, orgId, merchantId, {
6 amount: 50000,
7 externalId: externalId
8 });
9
10 // Try to create same order again
11 const order2 = await createPayinOrder(token, orgId, merchantId, {
12 amount: 50000,
13 externalId: externalId // Same externalId
14 });
15
16 // Should return same order
17 console.log(order1.id === order2.id); // true
18}

Common Test Workflows

E-Commerce Checkout Flow

Node.js
1async function testCheckoutFlow() {
2 console.log('1. Customer adds items to cart...');
3 const cartTotal = 50000; // 50,000 COP
4
5 console.log('2. Customer proceeds to checkout...');
6 const contact = await createContact(token, orgId, merchantId, {
7 email: 'test@example.com',
8 fullName: 'Test Customer',
9 country: 'CO',
10 documentType: 'CC',
11 documentNumber: '1234567890'
12 });
13
14 console.log('3. Get payment methods...');
15 const methods = await getPaymentMethods('CO', 'COP');
16
17 console.log('4. Create payment order...');
18 const order = await createPayinOrder(token, orgId, merchantId, {
19 amount: cartTotal,
20 currency: 'COP',
21 contactId: contact.id,
22 paymentMethod: 'PSE',
23 bank: 'BANCOLOMBIA',
24 externalId: `cart-${Date.now()}`
25 });
26
27 console.log('5. Redirect customer to payment...');
28 console.log('Payment URL:', order.paymentUrl);
29
30 console.log('6. Wait for webhook confirmation...');
31 // In production, webhook handler processes this
32 await sleep(5000);
33
34 console.log('7. Verify order completed...');
35 const completed = await getOrderStatus(token, orgId, merchantId, order.id);
36 console.log('Final status:', completed.status); // "COMPLETED"
37
38 console.log('8. Fulfill order...');
39 console.log('✅ Test checkout flow complete!');
40}

Provider Payout Flow

Node.js
1async function testPayoutFlow() {
2 console.log('1. Create provider contact...');
3 const provider = await createContact(token, orgId, merchantId, {
4 email: 'provider@example.com',
5 fullName: 'Test Provider',
6 country: 'CO',
7 documentType: 'NIT',
8 documentNumber: '900123456-1',
9 businessType: 'COMPANY'
10 });
11
12 console.log('2. Add provider bank account...');
13 const bankAccount = await addBankAccountToContact(token, orgId, merchantId, provider.id, {
14 country: 'CO',
15 currency: 'COP',
16 bankCode: 'BANCOLOMBIA',
17 accountNumber: '1234567890',
18 accountType: 'checking'
19 });
20
21 console.log('3. Check virtual balance...');
22 const balance = await getBalance(token, orgId, merchantId, 'COP');
23 console.log('Available:', balance.availableBalance, 'COP');
24
25 console.log('4. Create payout order...');
26 const payout = await createPayoutOrder(token, orgId, merchantId, {
27 amount: 100000,
28 currency: 'COP',
29 contactId: provider.id,
30 destinationAccountId: bankAccount.id,
31 externalId: `payout-${Date.now()}`
32 });
33
34 console.log('5. Monitor payout status...');
35 await sleep(3000);
36
37 const completed = await getOrderStatus(token, orgId, merchantId, payout.id);
38 console.log('Final status:', completed.status); // "COMPLETED"
39 console.log('✅ Test payout flow complete!');
40}

Moving to Production

When you’re ready to go live:

1

Request Production Credentials

Contact soporte@koywe.com for production API key and secret

2

Update Base URL

Change from sandbox to production:

1// Sandbox
2const BASE_URL = 'https://api-sandbox.koywe.com';
3
4// Production
5const BASE_URL = 'https://api.koywe.com';
3

Update Credentials

Replace sandbox credentials with production credentials in your environment variables

4

Update Webhook URLs

Change webhook endpoints from test URLs to production URLs

5

Test with Small Amounts

Start with small real transactions to verify everything works

6

Monitor Closely

Watch your first production transactions carefully

7

Setup Monitoring

Implement logging, alerts, and monitoring for production

Production Checklist

  • Production API credentials obtained
  • Base URL updated to production
  • Webhook URLs pointing to production servers
  • Webhook signature verification implemented
  • Error handling tested
  • Logging and monitoring setup
  • Small test transaction successful
  • Team trained on production processes

Troubleshooting

Order Stuck in PENDING

Issue: Order created but stays in PENDING status

Solution:

  • In sandbox, wait 30-60 seconds for auto-completion
  • Check if you used the failure test amount (666)
  • Verify payment method is correct for country

Webhooks Not Received

Issue: No webhook events arriving

Solution:

  • Verify webhook URL is publicly accessible
  • Check firewall/security settings
  • Use webhook.site to test
  • Verify webhook endpoint is configured

Insufficient Balance Error

Issue: PAYOUT fails with insufficient balance

Solution:

  • Check virtual account balance
  • Create PAYIN orders to add funds to sandbox account
  • Verify you’re checking the correct currency balance

Need Help?