Back-End Integration

Complete server-side integration guide: query order limits, generate payment orders, lock crypto rates, create on-chain QR codes, and query transaction reconciliation states.

Authentication & credential mapping

Every server-to-server call against Hashpay transaction endpoints requires your Secret Key sent in the api-key header. Never expose your Secret Key in client-side code, mobile bundles, or public repositories:

ParameterHTTP Header / FieldDescription & Source
Secret Keyapi-key: <YOUR_SECRET_KEY>Your confidential merchant secret key. Located in Dashboard → Settings → API Keys.
Merchant IDmerchant_id: "10001"Your public merchant identifier. Located in Dashboard → Settings → API Keys.
Order Numberorder_no / transactionIDYour system's unique alphanumeric reference (e.g., INV-2026-0001). Reused consistently across calls.
Billing Amountamount: "150000"The fiat charge in IDR as an integer numeric string (without decimal points).
Token ContracttokenAddressSmart contract / mint address of the stablecoin, retrieved via GET /listToken.
Blockchain NetworknetworkTarget blockchain network: ETHEREUM, BNB Chain, TRON, or SOLANA.

Step 0 — Check dynamic order limits (Pre-flight)

GET/orderLimits?merchant_id={merchant_id}

Before presenting payment options or creating an order, query your store's configured order limits. Hashpay calculates minimum order thresholds dynamically to ensure transaction amounts cover MDR fees and network gas costs:

Check Order Limits Request
curl -X GET "https://api.hashpay.id/v2/mainnet/orderLimits?merchant_id=10001" \
  -H "api-key: sec_live_9f83b27a8e..."
Check Order Limits Response
{
  "status_code": 200,
  "message": "success",
  "data": {
    "merchant_id": "10001",
    "currency": "IDR",
    "min_order_amount": "10000.00",
    "max_order_amount": "500000000.00",
    "mdr_fee_percent": "0.50"
  }
}

Step 1 — Create an order (Both paths)

POST/merchant/fiat/order

The primary entry point for both Path A (Hosted Page) and Path B (Direct API). Creates an active order record in the Hashpay system and issues an order-scoped checkout credential:

Create Order (cURL)
curl -X POST "https://api.hashpay.id/v2/mainnet/merchant/fiat/order" \
  -H "api-key: sec_live_9f83b27a8e..." \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "10001",
    "order_no": "INV-2026-0001",
    "amount": "150000",
    "name": "Budi Santoso",
    "email": "budi@example.com",
    "phone": "081234567890",
    "products": [
      { "name": "Specialty Coffee Beans 250g", "price": 150000, "quantity": 1 }
    ]
  }'
Create Order (Node.js / TypeScript)
import axios from "axios";

interface CreateOrderResponse {
  status_code: number;
  message: string;
  data: {
    request: {
      order_no: string;
      merchant_id: string;
      amount: string;
      currency: string;
      status: string;
    };
    url: string;
    key: string;
    expired_at: number;
  };
}

async function createHashpayOrder(orderNo: string, amountIdr: number, customerEmail: string) {
  const response = await axios.post<CreateOrderResponse>(
    "https://api.hashpay.id/v2/mainnet/merchant/fiat/order",
    {
      merchant_id: process.env.HASHPAY_MERCHANT_ID,
      order_no: orderNo,
      amount: amountIdr.toString(),
      email: customerEmail,
    },
    {
      headers: {
        "api-key": process.env.HASHPAY_SECRET_KEY,
        "Content-Type": "application/json",
      },
    }
  );

  return response.data.data;
}
Create Order (PHP / Laravel)
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'api-key' => config('services.hashpay.secret_key'),
    'Content-Type' => 'application/json',
])->post('https://api.hashpay.id/v2/mainnet/merchant/fiat/order', [
    'merchant_id' => config('services.hashpay.merchant_id'),
    'order_no'    => 'INV-2026-0001',
    'amount'      => '150000',
    'name'        => 'Budi Santoso',
    'email'       => 'budi@example.com',
]);

if ($response->successful()) {
    $data = $response->json('data');
    // For Path A: redirect customer to $data['url']
    return redirect($data['url']);
}
Create Order Response Payload
{
  "status_code": 201,
  "message": "success",
  "data": {
    "request": {
      "order_no": "INV-2026-0001",
      "merchant_id": "10001",
      "amount": "150000",
      "currency": "IDR",
      "status": "Created"
    },
    "url": "https://webview.hashpay.id/sandbox?key=pl_eyJhbGciOi...",
    "key": "pl_eyJhbGciOi...",
    "expired_at": 1821387856
  }
}

Path B — Direct API (Build your own checkout UI)

If your application renders custom payment UI components instead of opening Hashpay's hosted webview, execute steps 2, 3, and 4 on your server:

Step 2 — Retrieve supported tokens & wallets

GET/listToken?merchant_id={merchant_id}

Returns all active stablecoins (USDT, USDC), blockchain networks (Ethereum, BNB Chain, Tron, Solana), contract addresses, decimals, and supported wallet options configured for your merchant account:

List Token Request
curl -X GET "https://api.hashpay.id/v2/mainnet/listToken?merchant_id=10001" \
  -H "api-key: sec_live_9f83b27a8e..."
List Token Response (Excerpt)
{
  "status_code": 200,
  "message": "success",
  "data": [
    {
      "walletType": "METAMASK",
      "walletIcon": "https://assets.hashpay.id/wallets/metamask.png",
      "tokenDetails": [
        {
          "name": "USDT",
          "network": "BNB Chain",
          "tokenAddress": "0x55d398326f99059fF775485246999027B3197955",
          "icon": "https://assets.hashpay.id/tokens/usdt.png",
          "decimals": 18,
          "chainId": 56
        },
        {
          "name": "USDT",
          "network": "TRON",
          "tokenAddress": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
          "icon": "https://assets.hashpay.id/tokens/usdt.png",
          "decimals": 6,
          "chainId": 728126428
        }
      ]
    }
  ]
}

Step 3 — Lock exchange rate & compute fees

POST/merchant/fiat/checkOrderRate

Locks the real-time cryptocurrency exchange rate for 15 minutes and calculates the exact crypto amount required. Use the tokenAddress and network obtained from Step 2, and supply your order_no as transactionID:

Check Order Rate Request
curl -X POST "https://api.hashpay.id/v2/mainnet/merchant/fiat/checkOrderRate" \
  -H "api-key: sec_live_9f83b27a8e..." \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "10001",
    "transactionID": "INV-2026-0001",
    "tokenAddress": "0x55d398326f99059fF775485246999027B3197955",
    "network": "BNB Chain"
  }'
Check Order Rate Response
{
  "status_code": 200,
  "message": "success",
  "data": {
    "order_no": "INV-2026-0001",
    "merchant_id": "10001",
    "status": "Created",
    "currency": "IDR",
    "amount": "150000",
    "amount_crypto": "9.200000",
    "crypto_rate": "16304.34",
    "is_direct_deposit": false,
    "merchant_fees": [
      {
        "name": "MDR Platform Fee",
        "fee_type": "PERCENT",
        "fee_value": "0.50",
        "fee_amount": "750.00",
        "fee_amount_crypto": "0.046000",
        "is_included": true
      }
    ],
    "total_fee_amount_merchant": "750.00",
    "total_fee_amount_crypto_merchant": "0.046000",
    "merchant_received_amount": "149250.00",
    "merchant_received_amount_crypto": "9.154000",
    "fees": [],
    "total_fee_amount": "0.00",
    "total_fee_amount_crypto": "0.000000",
    "total_amount": "150000.00",
    "total_amount_crypto": "9.200000"
  }
}

Step 4 — Generate on-chain QR & destination address

POST/pgqr/fiat/create

Generates the dynamic, payment-ready QR code data. Returns an EIP-681 standard wallet URI (qrValue) and the exact destination blockchain deposit address:

Create Dynamic QR Request
curl -X POST "https://api.hashpay.id/v2/mainnet/pgqr/fiat/create" \
  -H "api-key: sec_live_9f83b27a8e..." \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "10001",
    "transactionID": "INV-2026-0001",
    "tokenAddress": "0x55d398326f99059fF775485246999027B3197955",
    "network": "BNB Chain",
    "walletType": "METAMASK"
  }'
Create Dynamic QR Response
{
  "status_code": 200,
  "message": "success",
  "data": {
    "qrValue": "ethereum:0x55d398326f99059fF775485246999027B3197955@56/transfer?address=0x2AC2223419f32a6Aab57D9d4cb3210C97Bc62d51&uint256=9200000000000000000",
    "request": {
      "order_no": "INV-2026-0001",
      "merchantID": "10001",
      "tokenName": "USDT",
      "network": "BNB Chain",
      "walletType": "METAMASK",
      "destinationAddress": "0x2AC2223419f32a6Aab57D9d4cb3210C97Bc62d51",
      "amount": "9.200000",
      "totalAmount": "9.200000",
      "totalAmountFiat": "150000.00",
      "uniqueCode": "0",
      "expiredAt": 1821388756,
      "is_direct_deposit": false
    }
  }
}

Step 5 — Query order status & reconciliation (Fallback)

POST/merchant/fiat/getOrder

While webhooks are the primary delivery channel for payment events, use getOrder as an on-demand reconciliation fallback (for example, if your webhook endpoint experienced downtime or when verifying an invoice status during customer support inquiries):

Get Order Request
curl -X POST "https://api.hashpay.id/v2/mainnet/merchant/fiat/getOrder" \
  -H "api-key: sec_live_9f83b27a8e..." \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "10001",
    "order_no": "INV-2026-0001"
  }'
Get Order Response
{
  "status_code": 200,
  "message": "success",
  "data": {
    "order_no": "INV-2026-0001",
    "merchant_id": "10001",
    "merchant_name": "Toko Sejahtera Bersama",
    "currency": "IDR",
    "amount": "150000",
    "status": "Success",
    "paid_amount": "9.200000",
    "total_amount": "9.200000",
    "total_amount_fiat": "150000.00",
    "tx_hash": "0x3a7e28b14c995f088127394c8b66da982341209b0a1d48293c834017834bcdef",
    "link_explorer": "https://bscscan.com/tx/0x3a7e28b14c995f088127394c8b66da982341209b0a1d48293c834017834bcdef",
    "expired_at": 1821388756,
    "createdAt": "2026-10-23T10:15:00Z",
    "updatedAt": "2026-10-23T10:16:42Z"
  }
}