Callbacks
When a deposit or withdrawal reaches COMPLETED or FAILED, InsPay POSTs JSON to the callbackUrl you sent on the create call.
{
"transaction_id": "4598b99d-0e13-4536-926b-a662bb34ac42",
"provider_order_id": "routing-ref",
"reference_id": "TXN-20261011-001",
"status": "COMPLETED",
"type": "DEPOSIT",
"amount": 50000,
"actual_amount": 47750,
"currency": "JPY",
"method": "BANK_TRANSFER"
}Field names are snake_case.
| Field | Notes |
|---|---|
transaction_id | InsPay transaction id. Make your handler idempotent on this value. |
provider_order_id | Internal routing reference. Coming soonRemoved from callbacks. |
reference_id | Your order id |
status | COMPLETED or FAILED |
type | DEPOSIT or WITHDRAW |
amount | Integer for JPY. Decimal string for other currencies. |
actual_amount | Same types as amount. null when the transaction failed. Deposit: amount − fee. Withdrawal: amount + fee. |
currency | For example JPY |
method | For example BANK_TRANSFER |
routing-ref above stands in for the routing reference. Do not parse a fixed value.
Acknowledge
Reply HTTP 200 with the body SUCCESS. That is either plain text:
SUCCESSor a JSON string:
"SUCCESS"A JSON object is not the documented reply.
Today a callback is sent once. If you miss it, poll POST /transactions. Coming soonInsPay retries with exponential back-off until you reply SUCCESS. Handle a repeated transaction_id as the same event, so a retry cannot credit the player twice.
Verify the signature
Coming soonCallbacks carry an x-signature header. It is HMAC-SHA256 over the callback body with your secret key, using the same canonical form as requests. Compare it before you move money.
Null fields are dropped. On a failure, actual_amount is null and is not part of the hashed string.
The digest below is for the JSON in the example, secret YOUR_SECRET_KEY, after provider_order_id is included:
75a7fc34c512f934f59e3a58c74dcf2891d14ed6fddc8691e091cc234894487dSECRET_KEY="YOUR_SECRET_KEY"
# Header value InsPay will send. Coming soon.
HEADER_SIG="75a7fc34c512f934f59e3a58c74dcf2891d14ed6fddc8691e091cc234894487d"
CANONICAL='{"actual_amount":"47750","amount":"50000","currency":"JPY","method":"BANK_TRANSFER","provider_order_id":"routing-ref","reference_id":"TXN-20261011-001","status":"COMPLETED","transaction_id":"4598b99d-0e13-4536-926b-a662bb34ac42","type":"DEPOSIT"}'
SIG=$(printf '%s' "$CANONICAL" | openssl dgst -sha256 -hmac "$SECRET_KEY" -hex | awk '{print $NF}')
if [ "$SIG" = "$HEADER_SIG" ]; then
echo "signature ok"
else
echo "reject callback" >&2
exit 1
fiCredit a deposit, or treat a withdrawal as paid, only when status is COMPLETED and, once the header is present, the signature matches.
KYC callback
If you set callbackUrl on POST /kyc-register, InsPay posts the final KYC status there:
{
"event": "kyc.status",
"player_id": "player_abc123",
"gateway": "YOUR_KYC_CHANNEL",
"status": "REGISTERED",
"provider_status": "APPROVED",
"currency": "JPY"
}Today player_id is prefixed with your merchant number, for example 31_player_abc123. Coming soonThe value becomes your own id, with no prefix. Strip a leading run of digits and an underscore so both forms resolve to the same player:
import re
player_id = re.sub(r"^\d+_", "", body["player_id"])const playerId = body.player_id.replace(/^\d+_/, "")Coming soongateway and provider_status become neutral InsPay values. Branch on status (PENDING, REGISTERED, FAILED, EXPIRED), not on the current provider_status text.