Skip to content

πŸ€– Explain with AI

Integrating 4Geeks Payments API into a Node.js Application

Overview

4Geeks Payments is a Merchant of Record (MoR) platform that handles the entire transaction lifecycle β€” from accepting payments to managing subscriptions, tax compliance, and fraud prevention. This tutorial shows you how to integrate the 4Geeks Payments REST API into a Node.js application.

In this tutorial, you will:

  • Set up your 4Geeks Payments account and get API keys
  • Create a charge (one-time payment)
  • Create and manage customers
  • Generate payment links
  • Handle webhooks for payment events
  • Process refunds

Prerequisites

  • An activated 4Geeks Payments account (activate service)
  • Node.js 18+ installed
  • npm or yarn package manager
  • Basic understanding of REST APIs and Express.js

Step 1: Get Your API Keys

  1. Log in to console.4geeks.io
  2. Navigate to Payments β†’ Settings β†’ API Keys
  3. Click “Generate API Key”
  4. Copy and securely store your:
  5. Public Key (for client-side operations)
  6. Secret Key (for server-side operations β€” never expose this)

Important: Use sandbox keys for testing. Switch to production keys when you’re ready to go live.

Step 2: Set Up Your Project

mkdir my-payment-app && cd my-payment-app
npm init -y
npm install express axios dotenv cors

Create your project structure:

my-payment-app/
β”œβ”€β”€ .env
β”œβ”€β”€ server.js
β”œβ”€β”€ routes/
β”‚   └── payments.js
└── package.json

Step 3: Configure Environment Variables

Create a .env file:

FOURGEEKS_PAYMENTS_SECRET_KEY=sk_test_your_secret_key_here
FOURGEEKS_PAYMENTS_PUBLIC_KEY=pk_test_your_public_key_here
FOURGEEKS_PAYMENTS_API_URL=https://api.4geeks.io/v1
PORT=3000

Step 4: Create the Express Server

Create server.js:

require('dotenv').config();
const express = require('express');
const cors = require('cors');
const paymentRoutes = require('./routes/payments');

const app = express();

app.use(cors());
app.use(express.json());
app.use('/api/payments', paymentRoutes);

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Step 5: Create Payment Routes

Create routes/payments.js:

const express = require('express');
const axios = require('axios');
const router = express.Router();

const API_URL = process.env.FOURGEEKS_PAYMENTS_API_URL;
const SECRET_KEY = process.env.FOURGEEKS_PAYMENTS_SECRET_KEY;

const api = axios.create({
  baseURL: API_URL,
  headers: {
    'Authorization': `Bearer ${SECRET_KEY}`,
    'Content-Type': 'application/json',
  },
});

// Create a one-time charge
router.post('/charges', async (req, res) => {
  try {
    const { amount, currency, customer_id, description } = req.body;

    const response = await api.post('/charges', {
      amount,           // Amount in cents (e.g., 1000 = $10.00)
      currency,         // e.g., 'USD', 'EUR', 'CRC'
      customer_id,      // Existing customer ID or create new
      description,
      capture: true,    // Auto-capture the charge
    });

    res.json({ success: true, charge: response.data });
  } catch (error) {
    console.error('Charge error:', error.response?.data || error.message);
    res.status(error.response?.status || 500).json({
      success: false,
      error: error.response?.data || 'Failed to create charge',
    });
  }
});

// Create a customer
router.post('/customers', async (req, res) => {
  try {
    const { email, name, phone, metadata } = req.body;

    const response = await api.post('/customers', {
      email,
      name,
      phone,
      metadata, // Optional: custom key-value pairs
    });

    res.json({ success: true, customer: response.data });
  } catch (error) {
    res.status(error.response?.status || 500).json({
      success: false,
      error: error.response?.data || 'Failed to create customer',
    });
  }
});

// Generate a payment link
router.post('/payment-links', async (req, res) => {
  try {
    const { amount, currency, description, customer_email, success_url, cancel_url } = req.body;

    const response = await api.post('/payment-links', {
      amount,
      currency,
      description,
      customer_email,
      success_url,    // Redirect after successful payment
      cancel_url,     // Redirect if customer cancels
    });

    res.json({ success: true, payment_link: response.data });
  } catch (error) {
    res.status(error.response?.status || 500).json({
      success: false,
      error: error.response?.data || 'Failed to create payment link',
    });
  }
});

// Process a refund
router.post('/charges/:charge_id/refund', async (req, res) => {
  try {
    const { charge_id } = req.params;
    const { amount, reason } = req.body;

    const response = await api.post(`/charges/${charge_id}/refunds`, {
      amount,  // Optional: partial refund (in cents). Omit for full refund
      reason,  // e.g., 'duplicate', 'fraudulent', 'requested_by_customer'
    });

    res.json({ success: true, refund: response.data });
  } catch (error) {
    res.status(error.response?.status || 500).json({
      success: false,
      error: error.response?.data || 'Failed to process refund',
    });
  }
});

// Get charge details
router.get('/charges/:charge_id', async (req, res) => {
  try {
    const { charge_id } = req.params;
    const response = await api.get(`/charges/${charge_id}`);
    res.json({ success: true, charge: response.data });
  } catch (error) {
    res.status(error.response?.status || 500).json({
      success: false,
      error: error.response?.data || 'Failed to get charge',
    });
  }
});

module.exports = router;

Step 6: Handle Webhooks

Webhooks notify your server about payment events (successful charges, failed payments, refunds, etc.).

Add to server.js:

// Webhook endpoint
app.post('/webhooks/payments', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-4geeks-signature'];
  const payload = req.body;

  // Verify webhook signature (implement verification logic)
  // const isValid = verifyWebhookSignature(payload, signature);
  // if (!isValid) return res.status(401).send('Invalid signature');

  const event = JSON.parse(payload);

  switch (event.type) {
    case 'charge.succeeded':
      console.log('Payment successful:', event.data.charge_id);
      // Update order status, send confirmation email, etc.
      break;
    case 'charge.failed':
      console.log('Payment failed:', event.data.charge_id);
      // Notify customer, retry logic, etc.
      break;
    case 'refund.completed':
      console.log('Refund processed:', event.data.refund_id);
      // Update order status, notify customer, etc.
      break;
    case 'subscription.created':
      console.log('New subscription:', event.data.subscription_id);
      // Activate subscription, send welcome email, etc.
      break;
    default:
      console.log('Unhandled event type:', event.type);
  }

  res.json({ received: true });
});

Step 7: Test Your Integration

Start the server:

node server.js

Test with curl:

# Create a customer
curl -X POST http://localhost:3000/api/payments/customers \
  -H "Content-Type: application/json" \
  -d '{"email": "test@example.com", "name": "John Doe"}'

# Create a charge
curl -X POST http://localhost:3000/api/payments/charges \
  -H "Content-Type: application/json" \
  -d '{"amount": 1000, "currency": "USD", "customer_id": "cus_xxx", "description": "Test charge"}'

# Generate a payment link
curl -X POST http://localhost:3000/api/payments/payment-links \
  -H "Content-Type: application/json" \
  -d '{"amount": 2500, "currency": "USD", "description": "Premium Plan", "success_url": "https://yoursite.com/success", "cancel_url": "https://yoursite.com/cancel"}'

Testing Cards

Use these test cards in sandbox mode:

Card Number Result
4242 4242 4242 4242 Successful payment
4000 0000 0000 9995 Declined (insufficient funds)
4000 0000 0000 0002 Requires 3D Secure authentication
4000 0000 0000 0010 Requires CVC verification

Best Practices

Security

  • Never expose your secret key in client-side code
  • Use environment variables for all credentials
  • Verify webhook signatures to prevent spoofed events
  • Implement idempotency keys for charge creation to prevent duplicates

Error Handling

  • Always check error.response?.data for detailed error messages
  • Implement retry logic for transient failures
  • Log all payment events for auditing
  • Handle 3D Secure redirects gracefully

Compliance

  • 4Geeks Payments is PCI DSS compliant β€” card data never touches your servers
  • Tax compliance is handled automatically (VAT, sales tax, GST)
  • As Merchant of Record, 4Geeks handles chargeback liability

What’s Next?

Need Help?


Still questions? Ask the community.