Skip to main content

Your First Transaction in 3 Steps

This guide walks you through creating a test payment using the Sandbox environment. You’ll learn the complete flow from authentication to receiving webhooks.
Sandbox mode: No real money involved. Perfect for testing and development.

Before You Start

1

Create Account

2

Get Approved

Upload required documents in Account Details (usually approved within 24h)
3

Get Credentials

Navigate to Settings → API Credentials and copy:
  • API Key (starts with sk_test_)
  • Merchant ID (format: MCH-COL-XXX)
Keep your credentials secure. Never commit them to version control.

Step 1: Generate Token

Exchange your credentials for a short-lived access token.
curl -X POST "https://api-sandbox.88pay.io/api/auth/token" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "x-merchant-id: YOUR_MERCHANT_ID"
Response:
{
  "status": "Success",
  "code": 200,
  "message": "Token generated successfully",
  "data": {
    "access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expires_in": 60,
    "session_id": "sess_53a2cfc4-441e-4554-b560-4e008784d98e"
  }
}
Token expires in 60 seconds. Generate a new one for each operation or implement caching (see Authentication Guide).

Step 2: Create Payment

Create a card payment using the token from Step 1.
curl -X POST "https://api-sandbox.88pay.io/api/transactions/charges" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "x-session-id: YOUR_SESSION_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "flow": "PAYIN",
    "method_code": "CARDS",
    "method_category": "CARD",
    "amount": 50000,
    "country": "COL",
    "currency": "COP",
    "description": "Test Purchase #001",
    "customer_id": "customer_12345",
    "notification_url": "https://yoursite.com/webhook",
    "return_url": "https://yoursite.com/thank-you"
  }'
Response:
{
  "status": "Success",
  "code": 200,
  "message": "Method processed",
  "data": {
    "urlCheckout": "https://link.88pay.io/GSW4WAR",
    "reference": "IP9BD7CJES6",
    "customer_id": "customer_12345",
    "currency": "COP",
    "amount": 50000
  }
}
Key Parameters:
ParameterDescription
urlCheckoutRedirect your customer here to complete payment
referenceUnique transaction ID for tracking
notification_urlWhere we’ll send webhook notifications
return_urlWhere we redirect customers after payment

Step 3: Complete Payment Flow

3.1 Redirect Customer

Send your customer to the checkout URL:
// Option 1: Full redirect
window.location.href = data.urlCheckout;

// Option 2: Open in new tab
window.open(data.urlCheckout, '_blank');

// Option 3: Embed in iframe (not recommended for cards)
<iframe src={data.urlCheckout} />

3.2 Handle Webhook

After payment completion, you’ll receive a POST request to your notification_url:
{
  "transaction_date": "2025-01-17 14:30:45",
  "transaction_status": "COMPLETED",
  "transaction_id": "sess_4e91a5c1-b7f2-4c64-91b1-3d204a5738b4",
  "transaction_amount": "50000",
  "transaction_payment_method": "CREDIT_CARD",
  "transaction_country": "COL",
  "transaction_currency": "COP",
  "transaction_reference": "IP9BD7CJES6",
  "transaction_customer_id": "customer_12345"
}
Webhook Handler Example:
app.post('/webhook', express.json(), (req, res) => {
  const { transaction_status, transaction_reference } = req.body;
  
  if (transaction_status === 'COMPLETED') {
    // Update order status in your database
    console.log(`Payment ${transaction_reference} completed`);
  }
  
  // Always respond with 200 OK
  res.status(200).json({ received: true });
});
Critical: Your webhook endpoint MUST return 200 OK. Otherwise, we’ll retry the notification (up to 10 times with exponential backoff).

Testing Your Integration

Test Cards (Sandbox Only)

Card NumberCVVExpiryResult
4111111111111111Any 3 digitsAny future date✅ Approved
4000000000000002Any 3 digitsAny future date❌ Declined
4000000000000069Any 3 digitsAny future date⏳ Pending

Possible Webhook Statuses

StatusDescription
COMPLETEDPayment successful
PENDINGAwaiting confirmation (e.g., bank transfer)
REJECTEDPayment failed, declined, or expired

Complete Example

Full end-to-end implementation:
async function processPayment() {
  try {
    // 1. Generate Token
    const tokenRes = await fetch('https://api-sandbox.88pay.io/api/auth/token', {
      method: 'POST',
      headers: {
        'x-api-key': process.env.EIGHTY_EIGHT_PAY_API_KEY,
        'x-merchant-id': process.env.EIGHTY_EIGHT_PAY_MERCHANT_ID
      }
    });
    
    const { data: { access_token, session_id } } = await tokenRes.json();
    
    // 2. Create Payment
    const paymentRes = await fetch('https://api-sandbox.88pay.io/api/transactions/charges', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${access_token}`,
        'x-session-id': session_id,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        flow: "PAYIN",
        method_code: "CARDS",
        method_category: "CARD",
        amount: 50000,
        country: "COL",
        currency: "COP",
        description: "Test Purchase #001",
        customer_id: "customer_12345",
        notification_url: "https://yoursite.com/webhook",
        return_url: "https://yoursite.com/thank-you"
      })
    });
    
    const payment = await paymentRes.json();
    
    // 3. Redirect to checkout
    window.location.href = payment.data.urlCheckout;
    
  } catch (error) {
    console.error('Payment error:', error);
    // Handle error (show message to user, log to monitoring, etc.)
  }
}

What’s Next?

Webhooks Deep Dive

Secure webhook validation and retry handling

Other Payment Methods

Cash, bank transfers, wallets, and crypto

Production Checklist

Move to live environment safely

Error Handling

Handle failures gracefully

Troubleshooting

Cause: Invalid credentials or expired tokenSolution:
  • Verify API Key and Merchant ID in dashboard
  • Check token hasn’t expired (60s lifetime)
  • Ensure both Authorization and x-session-id headers are present
Cause: Invalid URL or server not responding with 200 OKSolution:
  • Test webhook URL with tools like webhook.site
  • Ensure your server is publicly accessible (no localhost in production)
  • Return 200 OK immediately, process webhook async
Cause: Invalid parameters or insufficient merchant balanceSolution:
  • Verify all required fields are present
  • Check amount is within allowed limits
  • Ensure currency matches country (e.g., COP for Colombia)

Still Need Help?

Join our Discord community for real-time support