文档 / 概述

商户对接接口文档印度 UPI

面向游戏厂商 / 商户技术对接 · 版本 1.0.4 · 更新 2026-09-20 · 响应字段以网关实际序列化为准 · 最低代收 100 INR(amount = 10000)

商户管理后台

订单、应用、密钥与 IP 白名单

打开 →

签名 / 验签工具

浏览器本地计算,私钥不上传

打开 →

支付网关

统一下单 / 查单 / 余额根地址

打开 →

接口清单

接口方法路径说明
统一下单POST/api/order/pay代收 / 代付
订单查询POST/api/order/query主动查单补偿
余额查询POST/api/account/balance商户钱包
异步通知HOOK商户 notifyUrl入账回调

对接前准备

1. 平台分配

  • 商户后台:https://amipay.net/merchant/
  • merchantNo(商户号),如 C1788073687
  • appId(应用 ID)
  • 登录账号 / 初始密码(开通时下发)
  • 平台公钥(验签响应 / 异步通知)
  • 商户自行生成 RSA2048:私钥自持签名;公钥交给平台写入 appSecret

2. IP 白名单

商户服务器出口公网 IP 需配置到应用白名单,否则返回 ip not in white list。

3. 支付方式 wayCode

请求 wayCode响应 / 回调常见值说明
IND_UPIIndia upi代收
IND_UPI_OUTIndia upi payout代付

下单请求必须用左侧编码;查单/回调里是展示名,不要拿展示名当下单参数。

4. 金额约定

  • 单位:分;1 INR = 100 分
  • 最低金额:100 INR → amount 最小 10000(低于可能拒单)
  • 例:100 INR → 10000;200 INR → 20000
  • 响应 / 回调金额多为小数(如 20000.00),验签按原文保留小数位

通用约定

  • Method:POST(查单同样走签名校验)
  • Content-Type:application/json; charset=UTF-8

请求头(必填)

Header说明示例
Version接口版本1.0.0
Sign-Type签名类型RSA
Req-TimeUnix 秒级时间戳1725000000
Sign请求体 RSA 签名(Base64)(见签名章节)
Content-TypeJSONapplication/json

参与签名的仅为 Body 业务字段(不含 Header、不含 sign)。

统一响应外壳

{
  "code": "0",
  "msg": "SUCCESS",
  "data": { },
  "sign": "..."
}

code=0 仅表示接口调用成功,不等于支付成功。

订单状态

state含义
0订单生成
1处理中 / 支付中
2成功
3失败
4已撤销
5已退款
6已关闭
7拒付
请以「异步通知 + 主动查单」双确认成功。同步下单字段为 orderState,查单/回调用 state。请求 wayCode 传 IND_UPI,响应里常为展示名 India upi。

签名算法(RSA)

  • 算法:SHA256withRSA
  • 请求:商户私钥签名;响应 / 回调:平台公钥验签
  • 签名结果:Base64

验签算法(商户侧,响应 / 异步通知)

密钥用平台公钥(不是商户公钥)。同步响应:对 data 内字段拼串,用顶层 sign;异步通知:对整包扁平 JSON 拼串。
  1. 取 Body JSON 全部字段(同步响应则取 data 对象内字段)
  2. 去掉 sign(以及若有 signature / fileData)
  3. 跳过值为 null 或空字符串 "" 的字段
  4. 按参数名 ASCII 自然序排序(Java TreeMap,区分大小写)
  5. 拼成:key1=value1&key2=value2&...(末尾无 &)
    • value 用字段原始字符串形式(小数保留两位如 20000.00,不能写成 20000)
    • channelExtra 若存在,整段作为 value,不要拆内部字段
  6. 用 平台公钥 对上述字符串做 SHA256withRSA 验签(报文中的 sign 为 Base64);通过后再处理业务,失败则丢弃
推荐先用在线工具校对:https://amipay.net/tools/sign-tool.html(Tab「验签」→ 模式「异步通知 JSON」→ 粘贴回调 Body + 平台公钥)

待签名字符串(商户请求签名时同样按上表 1~5 步拼串,第 6 步改为商户私钥签名)

请求签名与验签拼串规则相同;仅最后一步:请求用商户私钥 sign,响应/回调用平台公钥 verify。

Java 验签示例

// signStr:按上文规则拼好的待签名串;signBase64:报文里的 sign;platformPubKeyBase64:平台公钥(无 PEM 头尾)
byte[] keyBytes = Base64.getDecoder().decode(platformPubKeyBase64);
PublicKey pub = KeyFactory.getInstance("RSA")
    .generatePublic(new X509EncodedKeySpec(keyBytes));

Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(pub);
signature.update(signStr.getBytes(StandardCharsets.UTF_8));
boolean ok = signature.verify(Base64.getDecoder().decode(signBase64));
// ok == true 才可入账

Java 签名示例(商户请求)

Signature signature = Signature.getInstance("SHA256withRSA");
  signature.initSign(privateKey);
signature.update(signStr.getBytes("UTF-8"));
String sign = Base64.getEncoder().encodeToString(signature.sign());

更多 Node / PHP / Go 示例见 merchant-api.txt 第三章,或签名工具页 Tab3。

统一下单

POST https://amipay.net/gateway/api/order/pay

Body 字段

字段类型必填说明
merchantNoStringY商户号
appIdStringY应用 ID
orderNoStringY商户订单号(商户侧唯一)
wayCodeStringYIND_UPI / IND_UPI_OUT
amountNumberY商户下单交易金额,单位分(非元)。例:100 INR → 传 10000;最低 10000
currencyStringYINR
descriptionStringY商品 / 订单描述
channelExtraStringY渠道扩展,JSON 字符串
notifyUrlStringN异步通知地址(强烈建议填【贵方】公网回调)
returnUrlStringN支付完成跳转
clientIpStringN玩家端 IP
userIdStringN商户侧用户 ID,≤50
expiredTimeStringN可选,单位秒。可传可不传。多数印度通道会忽略;平台目前不会按此字段自动关单,终态以回调/查单为准
extParamStringN透传字段,回调原样返回

代收 channelExtra(IND_UPI)

字段必填说明
firstNameY名
lastNameY姓
emailY邮箱(合法格式)
phoneNumberY手机号
name / upi / 地址N建议补充

代付 channelExtra(IND_UPI_OUT)

字段必填说明
nameY收款人姓名(玩家)
cardNoY银行账号(玩家账户)
ifscCodeYIFSC
bankNameY银行名
emailY邮箱
phoneNumberY手机号
paymentModeN如 IMPS
accountTypeN如 1=Savings
city / state / postalCode / addressN地址信息
代付 = 商户打款给玩家。卡号等信息由商户在 channelExtra 传入(玩家提现绑卡),平台再转给上游。

代收请求示例(IND_UPI,100 INR)

{
  "merchantNo": "C1788116547",
  "appId": "6a947e438f0872229b1e778c",
  "orderNo": "GAME_20260901120000_001",
  "wayCode": "IND_UPI",
  "amount": 10000,
  "currency": "INR",
  "clientIp": "1.2.3.4",
  "description": "Game diamond recharge",
  "notifyUrl": "https://your-game.com/pay/notify",
  "returnUrl": "https://your-game.com/pay/return",
  "userId": "player_10086",
  "extParam": "optional_biz_tag",
  "channelExtra": "{\"firstName\":\"stephen\",\"lastName\":\"curry\",\"email\":\"[email protected]\",\"phoneNumber\":\"9566778899\",\"name\":\"stephen curry\",\"upi\":\"demo@upi\",\"country\":\"IND\",\"state\":\"Mumbai\",\"city\":\"Bellevue\",\"postalCode\":\"123546\"}"
}

代收成功响应示例

{
  "code": "0",
  "msg": "SUCCESS",
  "data": {
    "payOrderId": "TSPPI08312094417832285474817",
    "mchOrderNo": "GAME_20260901120000_001",
    "orderState": 1,
    "payDataType": "payurl",
    "payData": "https://amipay.net/gateway/api/pay/upi/TSPPI08312094417832285474817",
    "upiUrl": "upi://pay?cu=INR&pa=demo@upi&am=100.00&tn=xxx",
    "channelExtra": {
      "payUrl": "https://pi.example.com/pay/xxxx",
      "upiUrl": "upi://pay?cu=INR&pa=demo@upi&am=100.00&tn=xxx"
    }
  },
  "sign": "Base64..."
}

引导玩家打开 payData / upiUrl 付款。测试即时成功通道可能直接 orderState=2,并带 fee / settleAmount 等。

代付请求示例(IND_UPI_OUT,100 INR → 打到玩家银行卡)

{
  "merchantNo": "C1788116547",
  "appId": "6a947e438f0872229b1e778c",
  "orderNo": "WD_20260901120000_001",
  "wayCode": "IND_UPI_OUT",
  "amount": 10000,
  "currency": "INR",
  "clientIp": "1.2.3.4",
  "description": "Player withdrawal",
  "notifyUrl": "https://your-game.com/pay/notify",
  "returnUrl": "https://your-game.com/pay/return",
  "userId": "player_10086",
  "extParam": "optional_biz_tag",
  "channelExtra": "{\"name\":\"stephen curry\",\"cardNo\":\"123456789012\",\"ifscCode\":\"HDFC0001234\",\"bankName\":\"HDFC Bank\",\"email\":\"[email protected]\",\"phoneNumber\":\"9566778899\",\"paymentMode\":\"IMPS\",\"accountType\":1,\"city\":\"Mumbai\",\"state\":\"MH\",\"postalCode\":\"400001\",\"address\":\"Andheri West\"}"
}

代付受理成功响应示例

{
  "code": "0",
  "msg": "SUCCESS",
  "data": {
    "payOrderId": "TSPPO09011200001234567890123",
    "mchOrderNo": "WD_20260901120000_001",
    "orderState": 1,
    "payDataType": "payurl",
    "payData": ""
  },
  "sign": "Base64..."
}
代付一般无收银台 / upiUrl;code=0 只表示受理成功。终态以异步通知 + 查单为准。瞬时成功时可能直接 orderState=2,并带 fee / feeCurrency / settleAmount / settleCurrency / serviceFee / checkDate。

data 字段对照

字段代收 IND_UPI代付 IND_UPI_OUT
payOrderId / mchOrderNo / orderState有有
payData / upiUrl常见,引导玩家付款通常空 / 无,勿依赖
channelExtra.payUrl 等常见通常无
fee / settleAmount 等成功记账后常见成功记账后常见
errCode / errMsg失败时失败时(含余额不足)
  • 同步看 orderState;查单/回调查 state
  • 正式通道多数同步 orderState=1;可能 code=0 但 orderState=3
  • 代付下单前会校验商户余额 ≥ amount + 手续费

订单查询

POST https://amipay.net/gateway/api/order/query

Body

{
  "merchantNo": "C1788073687",
  "appId": "6a93d6d78f08fd1adc3ef1f5",
  "mchOrderNo": "GAME_20260830120000_001",
  "payOrderId": ""
}

mchOrderNo 与 payOrderId 不能同时为空。

data 字段(QueryPayOrderRS)

字段说明
payOrderId / appId / mchOrderNo订单与应用
wayCode展示名,如 India upi(不是 IND_UPI)
amount商户下单交易金额,单位分(与下单请求一致)。例:200 INR → 20000.00。不是元;报文常带两位小数
currency交易币种,如 INR
state订单状态(查单用 state,下单同步用 orderState,数值相同)。含义见上文通用约定 · 订单状态:0 生成 · 1 支付中 · 2 成功 · 3 失败 · 4 撤销 · 5 退款 · 6 关闭 · 7 拒付。仅 2 入账
fee商户手续费,单位分。按费率等计算后入库,库类型 decimal(20,2),允许非整数分(如 9.50、1005.00)。仅支付成功且已记账时常见
feeCurrency手续费币种(通常与 settleCurrency 一致)
settleAmount结算入账金额,单位分。同币种时一般为 记账金额 − fee(例:20000.00 − 1005.00 = 18995.00)。同样可为两位小数非整数分;成功记账后常见
settleCurrency结算 / 记账币种
clientIp / channelOrderNo / extParam有则返回
successTime / createdAt毫秒时间戳
signType如 RSA
walletBrandName / checkDate / marginAmount有则返回
errCode / errMsg失败时
金额三字段关系(代收成功):amount=下单交易额(分),fee=手续费(分),settleAmount=结算额(分)。同币种常见:settleAmount ≈ amount − fee。验签按报文原文小数位拼串,勿自行改成整数或元。
模型含 merchantNo,但当前实现通常不返回,请勿依赖。查单 data 无 channelExtra / reqTime;sign 在外壳顶层。

响应示例:支付中(state=1)

代收:玩家尚未完成付款;代付:出款处理中。勿入账,继续等回调或退避查单。

{
  "code": "0",
  "msg": "SUCCESS",
  "data": {
    "payOrderId": "TSPPI08312094417832285474817",
    "appId": "6a947e438f0872229b1e778c",
    "mchOrderNo": "GAME_20260830120000_001",
    "wayCode": "India upi",
    "amount": 10000.00,
    "currency": "INR",
    "state": 1,
    "clientIp": "1.2.3.4",
    "extParam": "optional_biz_tag",
    "createdAt": 1788177513995,
    "signType": "RSA"
  },
  "sign": "..."
}

支付中通常无 successTime、fee、settleAmount;errCode / errMsg 一般也不出现(非错误)。

响应示例:支付成功(state=2)

{
  "code": "0",
  "msg": "SUCCESS",
  "data": {
    "payOrderId": "TSPPI08312094417832285474817",
    "appId": "6a947e438f0872229b1e778c",
    "mchOrderNo": "P20944178244636221461788183096",
    "wayCode": "India upi",
    "amount": 20000.00,
    "currency": "INR",
    "state": 2,
    "clientIp": "127.0.0.1",
    "channelOrderNo": "fHGoc",
    "extParam": "P20944178244636221461788183096",
    "successTime": 1788177514000,
    "createdAt": 1788177513995,
    "signType": "RSA",
    "fee": 1005.00,
    "feeCurrency": "INR",
    "settleAmount": 18995.00,
    "settleCurrency": "INR",
    "walletBrandName": "India upi",
    "checkDate": "20260831"
  },
  "sign": "..."
}

响应示例:支付失败(state=3)

终态失败,勿入账。请读取 errCode / errMsg 记录或展示给用户。

{
  "code": "0",
  "msg": "SUCCESS",
  "data": {
    "payOrderId": "TSPPI08312094417832285474817",
    "appId": "6a947e438f0872229b1e778c",
    "mchOrderNo": "GAME_20260830120000_001",
    "wayCode": "India upi",
    "amount": 10000.00,
    "currency": "INR",
    "state": 3,
    "errCode": "100007",
    "errMsg": "Channel payment failed",
    "createdAt": 1788177513995,
    "signType": "RSA"
  },
  "sign": "..."
}
长时间无回调时按 5s / 15s / 30s / 60s 退避查单;仅 state=2 入账并做幂等。state=1 继续等待;state=3 按失败处理。

余额查询

POST https://amipay.net/gateway/api/account/balance

请求

{
  "merchantNo": "C1788073687",
  "appId": "6a93d6d78f08fd1adc3ef1f5"
}

完整响应示例

{
  "code": "0",
  "msg": "SUCCESS",
  "data": {
    "walltes": [
      {
        "currency": "INR",
        "balance": 0.00,
        "freezeBalance": 0.00,
        "waitSettleBalance": 18995.00,
        "marginAmount": 0.00,
        "preBalance": 0,
        "specialBalance": 0.00
      }
    ]
  },
  "sign": "..."
}

data.walltes[](历史拼写,不是 wallets):currency / balance / freezeBalance / waitSettleBalance / marginAmount / preBalance / specialBalance。金额单位:分。

异步通知(商户 notifyUrl)

HOOK 由下单 Body 的 notifyUrl 指定(贵方游戏服)
notifyUrl = 贵方接收入账通知的地址。平台内部「上游 → 平台」回调与商户无关。通知 Body 为扁平 JSON(不是 {code,data,sign} 外壳)。

验签算法(商户侧,必读)

密钥:用平台公钥验签(不是商户公钥)。
  1. 取 Body JSON 全部字段
  2. 去掉 sign(以及若有 signature / fileData)
  3. 跳过值为 null 或空字符串 "" 的字段
  4. 按参数名 ASCII 自然序排序(Java TreeMap,区分大小写)
  5. 拼成:key1=value1&key2=value2&...(末尾无 &)
    • value 用字段原始字符串形式(小数保留两位如 20000.00)
  6. 用 平台公钥 对上述字符串做 SHA256withRSA 验签(签名为 Base64);通过后再入账

Java 验签代码见下方「签名算法」同页示例,或点左侧 签名算法。也可直接用工具:sign-tool.html → Tab「验签」→ 模式「异步通知 JSON」。

其他说明

  • 平台 → 商户:HTTP POST,Content-Type: application/json
  • 商户应答 Body 必须是纯文本 SUCCESS(大小写不敏感);否则约最多重试 6 次
  • 务必幂等:同一订单可能多次通知

平台推送 Body 示例(代收成功,真实字段)

{
  "payOrderId": "TSPPI08312094394410113363970",
  "appId": "6a947e438f0872229b1e778c",
  "mchOrderNo": "P20943944073414287371788177513",
  "wayCode": "India upi",
  "amount": 20000.00,
  "currency": "INR",
  "state": 2,
  "clientIp": "127.0.0.1",
  "channelOrderNo": "fHGoc",
  "extParam": "P20943944073414287371788177513",
  "successTime": 1788177514000,
  "createdAt": 1788177513995,
  "signType": "RSA",
  "fee": 1005.00,
  "feeCurrency": "INR",
  "settleAmount": 18995.00,
  "settleCurrency": "INR",
  "walletBrandName": "India upi",
  "checkDate": "20260831",
  "reqTime": 1788177514646,
  "channelExtra": { "utr": "422253713113" },
  "sign": "Base64..."
}
字段说明
state终态通知;成功为 2
amount商户下单交易金额,单位分(与下单一致)。例:200 INR → 20000.00。不是元
fee商户手续费,单位分。可为两位小数非整数分(如 9.50)。成功记账后常见
feeCurrency手续费币种
settleAmount结算入账金额,单位分。同币种常见 amount − fee;成功记账后常见
settleCurrency结算 / 记账币种
wayCode展示名 India upi(不是 IND_UPI)
reqTime本次通知毫秒时间戳(回调特有)
sign平台签名;验签时剔除
channelExtra如 {"utr":"..."};无则常省略
金额含义与查单相同:amount(交易额)/ fee(手续费)/ settleAmount(结算额)均为分,不是元;同币种常见 settleAmount ≈ amount − fee。
merchantNo 通常不出现在回调 JSON,请勿依赖。

商户应答(必须)

SUCCESS
整包响应体只能是纯文本 SUCCESS,不要返回 JSON / OK / success。

处理流程

  1. 读取 POST JSON
  2. 按上方「验签算法」用平台公钥验签(失败则丢弃,勿返回 SUCCESS)
  3. 校验 appId / amount / mchOrderNo(及本地订单)
  4. 若 state==2 且本地未入账 → 入账(幂等)
  5. 返回纯文本 SUCCESS

错误与联调注意

现象处理
ip not in white list把出口 IP 加入应用白名单
验签失败核对平台公钥、跳过 null/""、TreeMap 区分大小写排序、金额小数位(如 20000.00)、channelExtra 整段参与
Bean ValidationHeader Version / Sign-Type;代收必填 firstName/lastName/email/phoneNumber
amount 过小amount ≥ 10000
code=0 且 orderState/state=3看 data.errCode / errMsg
notifyUrl 填错填游戏服地址,不要填上游通道地址
字段对不上文档同步看 orderState;查单/回调查 state;wayCode 请求用编码、响应用展示名;勿依赖 merchantNo

联调检查清单

  • RSA 密钥对已生成,公钥已交给平台
  • IP 白名单已配置
  • 代收下单 IND_UPI,amount ≥ 10000
  • 能解析完整外壳 {code,msg,data,sign},并从 data 取 payData / upiUrl
  • notifyUrl 公网可达,成功返回 SUCCESS
  • 回调验签通过,幂等入账(按真实字段,勿依赖 merchantNo)
  • 查单接口可用,作为回调补偿
  • (如需)代付 IND_UPI_OUT 银行字段正确

附录

结算提现

  • 申请:POST /api/account/cashout — 请求 merchantNo / appId / currency / mchOrderNo / amount(分)/ orderType(可选) / remarks(可选);响应 data:{ "state": <Byte> }
  • 查询:POST /api/account/cashoutQuery — 请求 merchantNo / appId / mchOrderNo;响应 data:state / fee / feeCurrency / actualAmount / fxRate

常用地址

纯文本源:商户接口.txt · 切勿把私钥提交到代码仓库或发给无关第三方。