Create Checkout
Create a new checkout link for a customer to complete payment.
POST /v1/checkoutAuthentication
This endpoint requires API Key + RFC 9421 Signature.
| Header | Required | Description |
|---|---|---|
blox-api-key | Yes | Your API key |
Content-Type | Yes | application/json |
Content-Digest | Yes | SHA-256 hash of request body |
Signature-Input | Yes | RFC 9421 signature parameters |
Signature | Yes | RFC 9421 cryptographic signature |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
addressTo | string | Yes | Your wallet address to receive payment (Ethereum or Solana format) |
amount | integer | Yes | Payment amount in cents (e.g., 15000 = 150.00 MYR) |
tokenId | string (UUID) | Yes | The token ID to receive payment in |
redirectUrl | string (URL) | Yes | URL to redirect customer after payment |
title | string | Yes | Payment title displayed to customer (1–100 characters) |
webhookUrl | string (URL) | No | URL to receive payment status webhooks |
description | string | No | Additional payment details (max 500 characters) |
Amount Limits
| Limit | Value |
|---|---|
| Minimum | 1,000 cents (10.00 MYR) |
| Maximum | 100,000,000 cents (1,000,000.00 MYR) |
Address Formats
The addressTo field accepts:
- Ethereum/Arbitrum:
0xfollowed by 40 hex characters (e.g.,0x742d35Cc6634C0532925a3b844Bc9e7595f8bD21) - Solana: Base58-encoded public key (e.g.,
DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy)
Example Request
curl -X POST "https://api.blox.my/v1/checkout" \
-H "blox-api-key: $BLOX_API_KEY" \
-H "Content-Type: application/json" \
-H "Content-Digest: sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:" \
-H "Signature-Input: sig1=(@method @path content-digest content-type);created=1704067200;keyid=\"your_key_id\";alg=\"ed25519\"" \
-H "Signature: sig1=:MEUCIQDXy2IGhh...=:" \
-d '{
"addressTo": "0x742d35Cc6634C0532925a3b844Bc9e7595f8bD21",
"amount": 15000,
"tokenId": "550e8400-e29b-41d4-a716-446655440000",
"redirectUrl": "https://yoursite.com/order/123/complete",
"webhookUrl": "https://yoursite.com/webhooks/blox",
"title": "Order #123",
"description": "Premium subscription - 1 month"
}'Response
Success (201 Created)
{
"checkoutId": "8f14e45f-ceea-467f-a830-5e3e3c7e2b8a",
"checkoutUrl": "https://checkout.blox.my/8f14e45f-ceea-467f-a830-5e3e3c7e2b8a",
"expiresAt": "2026-02-03T15:20:00.000Z"
}| Field | Type | Description |
|---|---|---|
checkoutId | string (UUID) | Unique identifier for this checkout |
checkoutUrl | string (URL) | The payment link to send to your customer |
expiresAt | string (ISO 8601) | When the checkout link expires |
Important: Checkout links expire 20 minutes after creation. If the customer doesn’t complete payment within this window, the checkout is automatically cancelled.
Errors
400 Bad Request
Invalid request parameters:
{
"error": {
"code": "invalid_request",
"message": "Invalid token ID"
}
}Common causes:
- Invalid
addressToformat amountoutside valid range (1,000–100,000,000)- Invalid
tokenIdUUID titleexceeds 100 charactersdescriptionexceeds 500 characters- Invalid URL format for
redirectUrlorwebhookUrl
401 Unauthorized
Authentication failed:
{
"error": {
"code": "unauthorized",
"message": "Invalid signature"
}
}Common causes:
- Missing or invalid
blox-api-key - Invalid signature construction
- Signature-Input format incorrect
403 Forbidden
Feature not enabled:
{
"error": {
"code": "forbidden",
"message": "Checkout feature not enabled"
}
}Contact BLOX support to enable the Checkout feature for your account.
Code Examples
Node.js
import crypto from "crypto";
import canonicalize from "canonicalize";
async function createCheckout(
apiKey: string,
privateKey: crypto.KeyObject,
checkout: {
addressTo: string;
amount: number;
tokenId: string;
redirectUrl: string;
title: string;
webhookUrl?: string;
description?: string;
}
) {
const body = checkout;
const canonicalBody = canonicalize(body)!;
// Generate Content-Digest
const bodyHash = crypto.createHash("sha256").update(canonicalBody).digest("base64");
const contentDigest = `sha-256=:${bodyHash}:`;
// Generate signature
const created = Math.floor(Date.now() / 1000);
const keyId = apiKey.substring(0, 20); // First 20 chars as keyid
const signatureParams = `(@method @path content-digest content-type);created=${created};keyid="${keyId}";alg="ed25519"`;
const signatureBase = [
`"@method": POST`,
`"@path": /v1/checkout`,
`"content-digest": ${contentDigest}`,
`"content-type": application/json`,
`"@signature-params": ${signatureParams}`,
].join("\n");
const sig = crypto.sign(null, Buffer.from(signatureBase), privateKey);
const signature = `sig1=:${sig.toString("base64")}:`;
const response = await fetch("https://api.blox.my/v1/checkout", {
method: "POST",
headers: {
"blox-api-key": apiKey,
"Content-Type": "application/json",
"Content-Digest": contentDigest,
"Signature-Input": `sig1=${signatureParams}`,
"Signature": signature,
},
body: JSON.stringify(body),
});
return response.json();
}
// Usage
const result = await createCheckout(
process.env.BLOX_API_KEY!,
privateKey,
{
addressTo: "0x742d35Cc6634C0532925a3b844Bc9e7595f8bD21",
amount: 15000, // 150.00 MYR
tokenId: "550e8400-e29b-41d4-a716-446655440000",
redirectUrl: "https://yoursite.com/success",
title: "Order #123",
}
);
console.log("Checkout URL:", result.checkoutUrl);Python
import requests
import hashlib
import base64
import time
import json
from cryptography.hazmat.primitives import serialization
def create_checkout(api_key: str, private_key_path: str, checkout: dict):
# Canonicalize body
def canonicalize(obj):
if isinstance(obj, dict):
return "{" + ",".join(f'"{k}":{canonicalize(v)}' for k, v in sorted(obj.items())) + "}"
elif isinstance(obj, list):
return "[" + ",".join(canonicalize(x) for x in obj) + "]"
return json.dumps(obj, separators=(",", ":"))
canonical_body = canonicalize(checkout)
# Generate Content-Digest
body_hash = base64.b64encode(hashlib.sha256(canonical_body.encode()).digest()).decode()
content_digest = f"sha-256=:{body_hash}:"
# Generate signature
created = int(time.time())
key_id = api_key[:20]
signature_params = f'(@method @path content-digest content-type);created={created};keyid="{key_id}";alg="ed25519"'
signature_base = "\n".join([
'"@method": POST',
'"@path": /v1/checkout',
f'"content-digest": {content_digest}',
'"content-type": application/json',
f'"@signature-params": {signature_params}',
])
# Load private key and sign
with open(private_key_path, "rb") as f:
private_key = serialization.load_pem_private_key(f.read(), password=None)
sig = private_key.sign(signature_base.encode())
signature = f"sig1=:{base64.b64encode(sig).decode()}:"
response = requests.post(
"https://api.blox.my/v1/checkout",
headers={
"blox-api-key": api_key,
"Content-Type": "application/json",
"Content-Digest": content_digest,
"Signature-Input": f"sig1={signature_params}",
"Signature": signature,
},
data=json.dumps(checkout, separators=(",", ":")),
)
return response.json()
# Usage
result = create_checkout(
api_key="your_api_key",
private_key_path="./private_key.pem",
checkout={
"addressTo": "0x742d35Cc6634C0532925a3b844Bc9e7595f8bD21",
"amount": 15000,
"tokenId": "550e8400-e29b-41d4-a716-446655440000",
"redirectUrl": "https://yoursite.com/success",
"title": "Order #123",
}
)
print("Checkout URL:", result["checkoutUrl"])Next Steps
After creating a checkout:
- Send the link — Share
checkoutUrlwith your customer via email, SMS, or in-app - Monitor status — Query checkout status or wait for webhook
- Handle webhook — Process payment notifications
Last updated