Pay-in notification
Pay-in notification. Sent once when the order reaches a final state. Reply HTTP 200 {"status":"SUCCESS"} within 5 s.
See How callbacks work for retries and notify-URL rules.
Headers
| Header | Required | Value |
|---|---|---|
Content-Type | required | application/json |
lintaspay-signature | required | Base64 SHA256withRSA signature — verify it |
lintaspay-timestamp | required | Unix time in milliseconds |
lintaspay-nonce | required | Random string |
Body
| Name | Type | Description |
|---|---|---|
amount | string | Example: 10000 |
completionTime | string | Example: 2026-10-14T10:35:12+07:00 |
fee | string | Example: 4500.00 |
mchOrderNo | string | Example: M202610140001 |
merchantId | integer | Example: 100001 |
nonceStr | string | Example: a1b2c3d4e5f60718 |
orderNo | string | Example: LPIN2026101410300512AB34CD56EF |
orderStatus | integer | 2 success, 3 failed.Example: 2 |
payCode | string | Example: 8808123456789012 |
productCode | integer | Example: 11 |
settleDate | string | Example: 2026-10-15 |
additionalInfo | object | null | Payer details for a successful pay-in (when the channel provides them). |
paymentNtb | string | Example: NTB20261014103512 |
senderName | string | Example: BUDI SANTOSO |
sourceAccountNo | string | Example: 0123456789 |
sourceBankName | string | Example: BNI |
Example
{
"amount": "10000",
"completionTime": "2026-10-14T10:35:12+07:00",
"fee": "4500.00",
"mchOrderNo": "M202610140001",
"merchantId": 100001,
"nonceStr": "a1b2c3d4e5f60718",
"orderNo": "LPIN2026101410300512AB34CD56EF",
"orderStatus": 2,
"payCode": "8808123456789012",
"productCode": 11,
"settleDate": "2026-10-15",
"additionalInfo": {
"paymentNtb": "NTB20261014103512",
"senderName": "BUDI SANTOSO",
"sourceAccountNo": "0123456789",
"sourceBankName": "BNI"
}
}Your response
Reply HTTP 200 within 5 seconds with exactly:
{"status":"SUCCESS"}Verifying the notification
// Express — keep the raw body for signature verification
import crypto from 'node:crypto';
import express from 'express';
import fs from 'node:fs';
const PLATFORM_PUBLIC_KEY = fs.readFileSync('lintaspay_public_key.pem', 'utf8');
const app = express();
app.post('/notify/payout', express.raw({ type: '*/*' }), (req, res) => {
const raw = req.body.toString('utf8');
const signed = `${req.get('lintaspay-timestamp')}\n${req.get('lintaspay-nonce')}\n${raw}\n`;
const valid = crypto.verify('RSA-SHA256', Buffer.from(signed), PLATFORM_PUBLIC_KEY,
Buffer.from(req.get('lintaspay-signature') ?? '', 'base64'));
if (!valid) return res.status(401).end();
const notice = JSON.parse(raw);
queueForProcessing(notice); // process asynchronously, idempotently (by orderNo)
res.json({ status: 'SUCCESS' }); // reply within 5 seconds
});# Flask
import base64, json
from flask import Flask, request, jsonify
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
with open("lintaspay_public_key.pem", "rb") as f:
PLATFORM_PUBLIC_KEY = serialization.load_pem_public_key(f.read())
app = Flask(__name__)
@app.post("/notify/payout")
def notify_payout():
raw = request.get_data(as_text=True)
signed = f"{request.headers['lintaspay-timestamp']}\n{request.headers['lintaspay-nonce']}\n{raw}\n".encode()
try:
PLATFORM_PUBLIC_KEY.verify(base64.b64decode(request.headers["lintaspay-signature"]), signed,
padding.PKCS1v15(), hashes.SHA256())
except (InvalidSignature, KeyError):
return "", 401
queue_for_processing(json.loads(raw)) # process asynchronously, idempotently (by orderNo)
return jsonify(status="SUCCESS") # reply within 5 seconds<?php
$raw = file_get_contents('php://input');
$signed = $_SERVER['HTTP_LINTASPAY_TIMESTAMP'] . "\n" . $_SERVER['HTTP_LINTASPAY_NONCE'] . "\n" . $raw . "\n";
$platformKey = openssl_pkey_get_public(file_get_contents('lintaspay_public_key.pem'));
$ok = openssl_verify($signed, base64_decode($_SERVER['HTTP_LINTASPAY_SIGNATURE'] ?? ''), $platformKey, OPENSSL_ALGO_SHA256);
if ($ok !== 1) {
http_response_code(401);
exit;
}
$notice = json_decode($raw, true);
queue_for_processing($notice); // process asynchronously, idempotently (by orderNo)
header('Content-Type: application/json');
echo '{"status":"SUCCESS"}'; // reply within 5 seconds// Spring Boot
@PostMapping(value = "/notify/payout", consumes = "*/*")
public ResponseEntity<String> notifyPayout(@RequestBody String raw,
@RequestHeader("lintaspay-signature") String signature,
@RequestHeader("lintaspay-timestamp") String timestamp,
@RequestHeader("lintaspay-nonce") String nonce) throws Exception {
Signature v = Signature.getInstance("SHA256withRSA");
v.initVerify(platformKey); // PublicKey loaded from lintaspay_public_key.pem
v.update((timestamp + "\n" + nonce + "\n" + raw + "\n").getBytes(StandardCharsets.UTF_8));
if (!v.verify(Base64.getDecoder().decode(signature))) {
return ResponseEntity.status(401).build();
}
queueForProcessing(raw); // process asynchronously, idempotently (by orderNo)
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body("{\"status\":\"SUCCESS\"}");
}