Scripts_ API Documentation
Everything you need to integrate the Scripts Pay sandbox into your application.
Getting Started
All API requests are made to the following base URL. In sandbox mode, this points to your local or hosted NestJS instance.
BASE_URL = https://api.scriptspay.dev/v1
# Or for local development:
BASE_URL = http://localhost:3001/v1All endpoints return JSON. Amounts are in the smallest currency unit (e.g., VND has no decimal subdivision, so 10000 = 10,000 VND).
Authentication
Authenticate by including your Supabase JWT in the Authorization header. You can obtain a token by signing in through the dashboard.
curl -X POST https://api.scriptspay.dev/v1/payment-intents \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_JWT_TOKEN>" \
-H "Idempotency-Key: unique-request-id-123" \
-d '{
"amount": 10000,
"currency": "VND",
"method": "QR",
"merchantId": "your-merchant-profile-id"
}'Idempotency-Key header. Duplicate keys within 24 hours return the cached response without re-processing.Webhook Verification
Webhook payloads are signed with HMAC-SHA256. Verify the Scripts-Signature header on every delivery.
// Header format: Scripts-Signature: t=<timestamp>,v1=<signature>
const crypto = require('crypto');
function verifyWebhook(body, header, secret) {
const [tPart, v1Part] = header.split(',');
const t = tPart.replace('t=', '');
const v1 = v1Part.replace('v1=', '');
const expected = crypto
.createHmac('sha256', secret)
.update(`${t}.${JSON.stringify(body)}`)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(v1), Buffer.from(expected)
);
}Magic Test Data
In sandbox mode, the amount field on a Payment Intent determines the mock outcome:
| Amount | Behavior | Webhook Event |
|---|---|---|
| 10,000 VND | Instant Success | payment_intent.succeeded |
| 40,000 VND | Insufficient Funds | payment_intent.failed |
| 50,408 VND | Bank Timeout (30s) | Resolves randomly (70% success, 30% fail) |
Any other amount defaults to instant success.
React Integration
Drop the checkout button into your React app to create a payment intent and redirect to the hosted checkout page.
import { useState } from 'react';
function ScriptsCheckoutButton({ amount, merchantId }) {
const [loading, setLoading] = useState(false);
async function handleCheckout() {
setLoading(true);
const res = await fetch('https://api.scriptspay.dev/v1/payment-intents', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer <YOUR_SECRET_KEY>',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
amount,
currency: 'VND',
method: 'QR',
merchantId,
}),
});
const { id } = await res.json();
// Redirect to hosted checkout
window.location.href = `https://app.scriptspay.dev/checkout/${id}`;
}
return (
<button onClick={handleCheckout} disabled={loading}>
{loading ? 'Processing...' : `Pay ${amount.toLocaleString()} VND`}
</button>
);
}
export default ScriptsCheckoutButton;