INPAY API 对接文档

1)概览

  • 基础域名https://api.inpark.online
  • 认证方式Authorization: Basic <base64(mid:signature)>
  • 时间戳Request-Time(13 位毫秒时间戳),必须与签名中使用的一致
  • 货币INR
  • 支付方式(pm)NATIVE / INVOKE / QR / WALLET / BANK

2)签名规则

POST 请求签名

signatureString = Request-Time + "." + body
signature = hex(hmac-sha256(signatureString, secret))
Authorization: Basic base64(mid:signature)

GET 请求签名

将查询参数按 key 升序排序,拼接为:key=value,再以 & 连接
signatureString = Request-Time + "." + queryParams
signature = hex(hmac-sha256(signatureString, secret))

回调签名

signatureString = Request-Time + "." + body
signature = hex(hmac-sha256(signatureString, secret))

请求头:
Content-Type: application/json
Signature: <signature>
Request-Time: <13位毫秒时间戳>

验签方式(商户服务端):
1) 读取请求头 Signature、Request-Time
2) 获取原始 body 字符串(不可二次序列化或重排)
3) 本地计算并对比;建议统一小写比较
4) 建议校验时间戳偏差(如 ±300s)
5) 结合 ref/txid 做幂等

3)代收(入金)/ Pay-in

3.1 代收订单提交

接口POST https://api.inpark.online/api/mcht/payment/submit

Headers
Content-Type: application/json
Authorization: Basic <base64(mid:signature)>
Request-Time: <13位毫秒时间戳>
字段类型必填说明
amountnumber金额(两位小数)
pmstring支付方式(NATIVE / INVOKE / QR / WALLET / BANK)
refstring商户订单号,必须唯一
payer.emailstring付款人邮箱(可空值)
payer.namestring付款人姓名(可空值)
payer.phonestring付款人电话(可空值)
redirectstring支付完成后跳转地址
callbackUrlstring异步通知回调地址
Body 示例
{
  "amount": 100.00,
  "pm": "NATIVE",
  "ref": "UNIQUE_ORDER_ID_01",
  "payer": {
    "email": "user@gmail.com",
    "name": "PayerName",
    "phone": "+88100000000"
  },
  "redirect": "https://example.com",
  "callbackUrl": "https://example.com"
}

Python 示例

import time, hmac, hashlib, base64, json, requests

mid = "c70f1719017e290354017d1c101d0cc288d06ceb"
secret = "ME2VRe6tWH6weK/NAUJA5lhmewHkB23rA6CdWlrHrAs+/E/E3j3eG3io/GCHbQKqMMurfTNrBj/R4Yy84UziM5YJheiKFKbsWQc5xRoE46E3/0EYy4ZjbK9jhwGyHS+C"

url = "https://api.inpark.online/api/mcht/payment/submit"

body = {
    "amount": 220.00,
    "pm": "NATIVE",
    "ref": "ORDER_SUBMIT_013",
    "payer": {
        "email": "user@gmail.com",
        "name": "PayerName",
        "phone": "+88100000000"
    },
    "redirect": "https://example.com",
    "callbackUrl": "https://example.com"
}

request_time = str(int(time.time() * 1000))
body_str = json.dumps(body, ensure_ascii=False, separators=(',', ':'))
signature_str = f"{request_time}.{body_str}"

signature_hex = hmac.new(secret.encode(), signature_str.encode(), hashlib.sha256).hexdigest()
auth_header = "Basic " + base64.b64encode(f"{mid}:{signature_hex}".encode()).decode()

headers = {
    "Content-Type": "application/json",
    "Authorization": auth_header,
    "Request-Time": request_time
}

resp = requests.post(url, headers=headers, data=body_str, timeout=30)
print("Status:", resp.status_code)
print("Response:", resp.text)

响应示例

{
  "success": true,
  "code": 200,
  "data": {
    "amount": "220",
    "pm": "NATIVE",
    "ref": "ORDER_SUBMIT_013",
    "redirect": "https://example.com",
    "currency": "INR",
    "txid": "202509081321A6DI4P0V",
    "fee": "2.2",
    "netAmount": "217.8",
    "createTime": 1757308907315,
    "payer": {
      "name": "PayerName",
      "phone": "+88100000000",
      "email": "user@gmail.com"
    },
    "paymentUrl": "/x3v83q7d",
    "callbackUrl": "https://example.com"
  }
}

3.2 代收订单查询

接口GET https://api.inpark.online/api/mcht/payment/retrieve?txid=<平台交易ID>

Headers
Authorization: Basic <base64(mid:signature)>
Request-Time: <13位毫秒时间戳>
字段类型必填说明
txidstring平台交易ID

响应示例

{
  "success": true,
  "code": 200,
  "message": "Error message",
  "data": {
    "txid": "202409061809AE4IK4EZ",
    "amount": "100.00",
    "fee": "1.00",
    "netAmount": "99.00",
    "type": "PAYMENT",
    "ref": "234d5678d345d5d6",
    "currency": "INR",
    "status": "PAID",
    "paidTime": 1725617395000,
    "createTime": 1725617356000,
    "completeTime": 1725617395000,
    "callbackUrl": "",
    "redirectUrl": ""
  }
}

3.3 代收回调数据格式

接口POST <callbackUrl>

Headers
Content-Type: application/json
Signature: <hex(hmac-sha256(Request-Time + "." + body, secret))>
Request-Time: <13位毫秒时间戳>
字段类型说明
amountstring金额
callbackUrlstring异步通知回调地址
createTimenumber创建时间(13 位毫秒时间戳)
currencystring币种(如:INR)
feestring手续费
netAmountstring净金额
refstring商户订单号
statusstring订单状态(PAYMENT: PAID / COMPLETE / FAILED)
txidstring平台交易ID
typestring交易类型(PAYMENT)
paidTimenumber支付时间(可选,状态为 PAID/COMPLETE 时出现)

回调数据示例

POST https://callback.example.com
Content-Type: application/json
Signature: 8f6f1c7a...e2d
Request-Time: 1721872092000

{
  "amount": "56.00",
  "currency": "INR",
  "fee": "0.00",
  "netAmount": "0.00",
  "createTime": 1721872092000,
  "paidTime": 1721872092000,
  "ref": "R1721872058703",
  "txid": "202407250947A87IG0MA",
  "type": "PAYMENT",
  "status": "PAID"
}

4)代付(出金)/ Disbursement

4.1 代付订单提交

接口POST https://api.inpark.online/api/mcht/disbursement/create

Headers
Content-Type: application/json
Authorization: Basic <base64(mid:signature)>
Request-Time: <13位毫秒时间戳>
字段类型必填说明
amountnumber金额(两位小数)
bankAccountNamestring收款人姓名
bankAccountNumberstring收款人账号
bankCodestring收款银行代码(IFSC)
refstring商户订单号,必须唯一
callbackUrlstring异步通知回调地址

Python 示例

import time, hmac, hashlib, base64, json, requests

mid = "c70f1719017e290354017d1c101d0cc288d06ceb"
secret = "ME2VRe6tWH6weK/NAUJA5lhmewHkB23rA6CdWlrHrAs+/E/E3j3eG3io/GCHbQKqMMurfTNrBj/R4Yy84UziM5YJheiKFKbsWQc5xRoE46E3/0EYy4ZjbK9jhwGyHS+C"

url = "https://api.inpark.online/api/mcht/disbursement/create"

body = {
    "amount": 100.00,
    "bankAccountName": "PATANGRAO",
    "bankAccountNumber": "33672747179",
    "bankCode": "SBIN0011132",
    "ref": "WITHDRAW_TEST_002",
    "callbackUrl": "https://example.com"
}

request_time = str(int(time.time() * 1000))
body_str = json.dumps(body, ensure_ascii=False, separators=(',', ':'))
signature_str = f"{request_time}.{body_str}"

signature_hex = hmac.new(secret.encode(), signature_str.encode(), hashlib.sha256).hexdigest()
auth_header = "Basic " + base64.b64encode(f"{mid}:{signature_hex}".encode()).decode()

headers = {
    "Content-Type": "application/json",
    "Authorization": auth_header,
    "Request-Time": request_time
}

resp = requests.post(url, headers=headers, data=body_str, timeout=30)
print("Status:", resp.status_code)
print("Response:", resp.text)

响应示例

{
  "success": true,
  "code": 200,
  "data": {
    "amount": "100",
    "bankAccountName": "PATANGRAO",
    "bankAccountNumber": "33672747179",
    "bankCode": "SBIN0011132",
    "ref": "WITHDRAW_TEST_002",
    "currency": "INR",
    "callbackUrl": "https://example.com",
    "txid": "PO202509081405A6DIK5PK",
    "netAmount": "100",
    "fee": "0",
    "createTime": 1757311533901
  }
}

4.2 代付订单查询

接口GET https://api.inpark.online/api/mcht/disbursement/retrieve?txid=<平台交易ID>

Headers
Authorization: Basic <base64(mid:signature)>
Request-Time: <13位毫秒时间戳>
字段类型必填说明
txidstring平台交易ID

响应示例

{
  "success": true,
  "code": 200,
  "message": "Error message",
  "data": {
    "txid": "PO202408231658A87IKRYL",
    "amount": "100.00",
    "fee": "1.00",
    "netAmount": "99.00",
    "type": "DISBURSEMENT",
    "ref": "234d5678d345d5d6",
    "currency": "INR",
    "status": "PAID",
    "paidTime": 1725617395000,
    "createTime": 1725617356000,
    "callbackUrl": "",
    "redirectUrl": ""
  }
}

4.3 代付回调数据格式

接口POST <callbackUrl>

Headers
Content-Type: application/json
Signature: <hex(hmac-sha256(Request-Time + "." + body, secret))>
Request-Time: <13位毫秒时间戳>
字段类型说明
amountstring金额
callbackUrlstring异步通知回调地址
createTimenumber创建时间(13 位毫秒时间戳)
currencystring币种(如:INR)
feestring手续费
netAmountstring净金额
paidTimenumber支付时间(13 位毫秒时间戳)
refstring商户订单号
statusstring订单状态(DISBURSEMENT: PAID / FAILED)
txidstring平台交易ID
typestring交易类型(DISBURSEMENT)

回调数据示例

Content-Type: application/json
Signature: 5a3c9b...f12
Request-Time: 1757910583000

{
  "amount": "521.30",
  "callbackUrl": "https://yourcallbackurl.app/webhooks/payout",
  "createTime": 1757910583000,
  "currency": "INR",
  "fee": "0.00",
  "netAmount": "533.30",
  "paidTime": 1757910583000,
  "ref": "REF460128f04955612de",
  "status": "PAID",
  "txid": "PO202509151229664IDW3M83",
  "type": "DISBURSEMENT"
}

5)查询余额 / Wallet Balance

接口GET https://api.inpark.online/api/mcht/wallet?currency=INR

Headers
Authorization: Basic <base64(mid:signature)>
Request-Time: <13位毫秒时间戳>
字段类型必填说明
currencystring币种(如:INR)

Python 示例

import time, hmac, hashlib, base64, requests

mid = "c70f1719017e290354017d1c101d0cc288d06ceb"
secret = "ME2VRe6tWH6weK/NAUJA5lhmewHkB23rA6CdWlrHrAs+/E/E3j3eG3io/GCHbQKqMMurfTNrBj/R4Yy84UziM5YJheiKFKbsWQc5xRoE46E3/0EYy4ZjbK9jhwGyHS+C"

url = "https://api.inpark.online/api/mcht/wallet"
params = {"currency": "INR"}

request_time = str(int(time.time() * 1000))
query_str = "&".join([f"{k}={v}" for k, v in sorted(params.items())])
signature_str = f"{request_time}.{query_str}"

signature_hex = hmac.new(secret.encode(), signature_str.encode(), hashlib.sha256).hexdigest()
auth_header = "Basic " + base64.b64encode(f"{mid}:{signature_hex}".encode()).decode()

headers = {
    "Authorization": auth_header,
    "Request-Time": request_time
}

resp = requests.get(url, headers=headers, params=params, timeout=30)
print("Status:", resp.status_code)
print("Response:", resp.text)

响应示例

{
  "success": true,
  "code": 200,
  "data": [
    {
      "currency": "INR",
      "balance": "1000",
      "freeze": "0",
      "total": "1000"
    }
  ]
}

6)订单状态说明

代收(入金)订单状态

状态说明
PAYING订单已创建,正在处理中,等待付款人完成支付
PAID付款人已完成支付,资金已到达平台账户
COMPLETE订单已完成,资金已结算至商户账户
FAILED订单失败,可能由于付款人未支付、支付超时或异常等原因

代付(出金)订单状态

状态说明
PAYING订单已创建,正在处理中,等待银行处理
PAID资金已成功转账至收款人账户
FAILED订单失败,可能由于银行账户信息错误、余额不足或其他异常

7)注意事项

  1. 幂等性ref 必须唯一,可用毫秒时间戳拼接生成。
  2. JSON 序列化:签名用的 JSON 必须与实际请求体完全一致。
  3. 时间戳Request-Time 使用 13 位毫秒时间戳,并与签名保持一致。
  4. 回调callbackUrl 必须公网可访问,平台会在交易状态变更时异步通知。
  5. 回调签名验证:商户需验证 SignatureRequest-Time
  6. 安全secret 仅在服务端保存,严禁下发至前端或客户端。
  7. 异常处理:请对 4xx/5xx 错误做好重试与告警,记录签名串以便排查。

8)多语言验签示例(服务端)

Python (Flask)

import hmac, hashlib
from flask import Flask, request, abort

SECRET = b"YOUR_SECRET"

app = Flask(__name__)

@app.route("/callback", methods=["POST"])
def callback():
    sig = (request.headers.get("Signature") or "").lower()
    ts  = request.headers.get("Request-Time") or ""
    raw = request.get_data(as_text=True)  # 原始body字符串

    calc = hmac.new(SECRET, f"{ts}.{raw}".encode(), hashlib.sha256).hexdigest()
    if sig != calc:
        abort(401, "invalid signature")

    # 可选:校验时间戳 ±300s;根据 txid/ref 做幂等
    return "ok"

Node.js (Express)

const crypto = require("crypto");
const express = require("express");
const app = express();

// 关键:保留原始body字符串(不进行 JSON.parse)
app.use(express.raw({ type: "application/json" }));

const SECRET = Buffer.from("YOUR_SECRET");

app.post("/callback", (req, res) => {
  const sig = (req.header("Signature") || "").toLowerCase();
  const ts  = req.header("Request-Time") || "";
  const raw = req.body.toString("utf8");

  const calc = crypto
    .createHmac("sha256", SECRET)
    .update(`${ts}.${raw}`)
    .digest("hex");

  if (sig !== calc) return res.status(401).send("invalid signature");
  res.send("ok");
});

app.listen(3000);

PHP (原生)

<?php
$secret = "YOUR_SECRET";
$sig = strtolower($_SERVER['HTTP_SIGNATURE'] ?? '');
$ts  = $_SERVER['HTTP_REQUEST_TIME'] ?? ''; // 自定义头“Request-Time”
$raw = file_get_contents('php://input');    // 原始body

$calc = hash_hmac('sha256', $ts . "." . $raw, $secret);
if ($sig !== $calc) {
    http_response_code(401);
    echo "invalid signature";
    exit;
}

echo "ok";

Java (Spring Boot)

@PostMapping(value="/callback", consumes=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> callback(HttpServletRequest request) throws IOException {
    String sig = Optional.ofNullable(request.getHeader("Signature")).orElse("").toLowerCase();
    String ts  = Optional.ofNullable(request.getHeader("Request-Time")).orElse("");

    // 使用 ContentCachingRequestWrapper 在过滤器中缓存原始body
    String raw = new String(((ContentCachingRequestWrapper)request)
                   .getContentAsByteArray(), StandardCharsets.UTF_8);

    String calc = hmacHex(ts + "." + raw, "YOUR_SECRET");
    if (!sig.equals(calc)) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("invalid signature");
    }
    return ResponseEntity.ok("ok");
}

private String hmacHex(String data, String secret) {
    try {
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        byte[] d = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
        StringBuilder sb = new StringBuilder();
        for (byte b : d) sb.append(String.format("%02x", b));
        return sb.toString();
    } catch(Exception e){ throw new RuntimeException(e); }
}