Authentication

Authentication

Every request to the Indicpay Payments API must include three headers:

HeaderValue
Content-Typeapplication/json
merchant-idYour merchant ID (issued by Indicpay)
AuthorizationBearer <generated_token>

The Authorization token is not static — it's a short-lived, AES-encrypted token that you generate for
every request using the credentials Indicpay issues you:

  • merchant_id
  • secret_key
  • encryption_key
  • encryption_iv
🔒

Keep these credentials secret. Never expose secret_key, encryption_key, or encryption_iv in

client-side code, mobile apps, or public repositories. Token generation should always happen on your server.

How the token works

  1. Build a small JSON payload: { timestamp, secret, reqId }
    • timestamp — current Unix timestamp (seconds)
    • secret — your secret_key
    • reqId — a random unique ID (e.g. a UUID/GUID) per request
  2. Encrypt that JSON payload using AES-256-CBC, with:
    • Key = your encryption_key (first 32 bytes, UTF-8)
    • IV = your encryption_iv (UTF-8)
    • Padding = PKCS7
  3. Base64-encode the ciphertext — that's your token.
  4. Send it as Authorization: Bearer <token>.

Important:

  • Each token is valid for 5 minutes only.
  • Each token can be used for one request only — generate a fresh token every time.

Below are code samples in six languages. Replace the placeholder values with your own credentials.


Node.js

const crypto = require("crypto");
const { v4: uuidv4 } = require("uuid");

const secretKey = "YOUR_SECRET_KEY";
const encryptionKey = "YOUR_ENCRYPTION_KEY"; // first 32 chars are used
const encryptionIv = "YOUR_ENCRYPTION_IV";

function generateAuthToken() {
  const timestamp = Math.floor(Date.now() / 1000);
  const reqId = uuidv4();

  const payload = JSON.stringify({ timestamp, secret: secretKey, reqId });

  const key = Buffer.from(encryptionKey.substring(0, 32), "utf8");
  const iv = Buffer.from(encryptionIv, "utf8");

  const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
  const encrypted = Buffer.concat([cipher.update(payload, "utf8"), cipher.final()]);

  return encrypted.toString("base64");
}

const token = generateAuthToken();
console.log(`Bearer ${token}`);

Python

import base64
import json
import time
import uuid
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

SECRET_KEY = "YOUR_SECRET_KEY"
ENCRYPTION_KEY = "YOUR_ENCRYPTION_KEY"  # first 32 chars are used
ENCRYPTION_IV = "YOUR_ENCRYPTION_IV"

def generate_auth_token():
    payload = json.dumps({
        "timestamp": int(time.time()),
        "secret": SECRET_KEY,
        "reqId": str(uuid.uuid4()),
    })

    key = ENCRYPTION_KEY[:32].encode("utf-8")
    iv = ENCRYPTION_IV.encode("utf-8")

    cipher = AES.new(key, AES.MODE_CBC, iv)
    encrypted = cipher.encrypt(pad(payload.encode("utf-8"), AES.block_size))

    return base64.b64encode(encrypted).decode("utf-8")

token = generate_auth_token()
print(f"Bearer {token}")

PHP

<?php
$secretKey = "YOUR_SECRET_KEY";
$encryptionKey = "YOUR_ENCRYPTION_KEY"; // first 32 chars are used
$encryptionIv = "YOUR_ENCRYPTION_IV";

function generateAuthToken($secretKey, $encryptionKey, $encryptionIv) {
    $payload = json_encode([
        "timestamp" => time(),
        "secret" => $secretKey,
        "reqId" => bin2hex(random_bytes(16)),
    ]);

    $key = substr($encryptionKey, 0, 32);

    $encrypted = openssl_encrypt(
        $payload,
        "AES-256-CBC",
        $key,
        OPENSSL_RAW_DATA,
        $encryptionIv
    );

    return base64_encode($encrypted);
}

$token = generateAuthToken($secretKey, $encryptionKey, $encryptionIv);
echo "Bearer " . $token;

Java

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.UUID;

public class IndicpayAuth {
    static final String SECRET_KEY = "YOUR_SECRET_KEY";
    static final String ENCRYPTION_KEY = "YOUR_ENCRYPTION_KEY"; // first 32 chars are used
    static final String ENCRYPTION_IV = "YOUR_ENCRYPTION_IV";

    public static String generateAuthToken() throws Exception {
        long timestamp = System.currentTimeMillis() / 1000L;
        String reqId = UUID.randomUUID().toString();

        String payload = String.format(
            "{\"timestamp\":%d,\"secret\":\"%s\",\"reqId\":\"%s\"}",
            timestamp, SECRET_KEY, reqId
        );

        SecretKeySpec keySpec = new SecretKeySpec(
            ENCRYPTION_KEY.substring(0, 32).getBytes("UTF-8"), "AES"
        );
        IvParameterSpec ivSpec = new IvParameterSpec(ENCRYPTION_IV.getBytes("UTF-8"));

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
        byte[] encrypted = cipher.doFinal(payload.getBytes("UTF-8"));

        return Base64.getEncoder().encodeToString(encrypted);
    }

    public static void main(String[] args) throws Exception {
        System.out.println("Bearer " + generateAuthToken());
    }
}

C# (.NET)

using System;
using System.Security.Cryptography;
using System.Text;

public class IndicpayAuth
{
    static string SecretKey = "YOUR_SECRET_KEY";
    static string EncryptionKey = "YOUR_ENCRYPTION_KEY"; // first 32 chars are used
    static string EncryptionIv = "YOUR_ENCRYPTION_IV";

    public static string GenerateAuthToken()
    {
        long timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
        string reqId = Guid.NewGuid().ToString();

        string payload = $"{{\"timestamp\":{timestamp},\"secret\":\"{SecretKey}\",\"reqId\":\"{reqId}\"}}";

        using (Aes aes = Aes.Create())
        {
            aes.Key = Encoding.UTF8.GetBytes(EncryptionKey.Substring(0, 32));
            aes.IV = Encoding.UTF8.GetBytes(EncryptionIv);
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            using (var encryptor = aes.CreateEncryptor())
            {
                byte[] payloadBytes = Encoding.UTF8.GetBytes(payload);
                byte[] encrypted = encryptor.TransformFinalBlock(payloadBytes, 0, payloadBytes.Length);
                return Convert.ToBase64String(encrypted);
            }
        }
    }

    public static void Main()
    {
        Console.WriteLine("Bearer " + GenerateAuthToken());
    }
}

Go

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"github.com/google/uuid"
	"time"
)

const (
	secretKey     = "YOUR_SECRET_KEY"
	encryptionKey = "YOUR_ENCRYPTION_KEY" // first 32 chars are used
	encryptionIv  = "YOUR_ENCRYPTION_IV"
)

func pkcs7Pad(data []byte, blockSize int) []byte {
	padding := blockSize - len(data)%blockSize
	padText := make([]byte, padding)
	for i := range padText {
		padText[i] = byte(padding)
	}
	return append(data, padText...)
}

func generateAuthToken() (string, error) {
	timestamp := time.Now().Unix()
	reqId := uuid.New().String()

	payload := fmt.Sprintf(`{"timestamp":%d,"secret":"%s","reqId":"%s"}`, timestamp, secretKey, reqId)

	key := []byte(encryptionKey)[:32]
	iv := []byte(encryptionIv)

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	padded := pkcs7Pad([]byte(payload), aes.BlockSize)
	encrypted := make([]byte, len(padded))

	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(encrypted, padded)

	return base64.StdEncoding.EncodeToString(encrypted), nil
}

func main() {
	token, _ := generateAuthToken()
	fmt.Println("Bearer " + token)
}

Common errors

ResponseCause
401 Unauthorized – Invalid or expired tokenToken is older than 5 minutes, or has already been used once.
401 Unauthorized – Invalid signatureWrong encryption_key / encryption_iv, or key not truncated to 32 bytes.
401 Unauthorized – Merchant not foundmerchant-id header missing or incorrect.

Generate a brand-new token immediately before each API call — do not cache or reuse tokens across requests.


Did this page help you?