Union Pay 印尼支付 API
Union Pay 为商户提供印尼盾代收、银行及钱包代付、订单查询与账户余额查询。商户通过本站 API 创建订单,本站返回收银台地址并向商户发送订单结果通知。
接口域名:https://inpay.happypay99.com。所有金额以 IDR 计价,不进行换汇。商户号由 Union Pay 分配;商户登记自己的 RSA 公钥,使用商户私钥签请求,并使用平台回调公钥验证通知。
| 接口 | 方法与路径 | 用途 |
|---|---|---|
| 代收下单 | POST /api/payment/create | 创建订单并返回收银台地址 |
| 代收查单 | POST /api/payment/query | 查询本站订单支付状态 |
| 代付下单 | POST /api/payout/create | 提交银行或钱包代付 |
| 代付查单 | POST /api/payout/query | 查询本站代付订单状态 |
| 商户余额 | POST /api/balance/query | 查询商户可用余额及冻结金额 |
接入流程
- 在商户后台「API 对接」登记至少2048位的商户 RSA 公钥,保管匹配私钥。
- 配置商户 API IP 白名单及代付 IP 白名单,联系运营开通业务、确认银行/钱包范围及商户限额。
- 按本页字段准备 JSON,使用商户私钥生成 RSA-SHA256 签名,再调用本站接口。
- 代收打开本站响应的 order_data 收银台地址支付;代付下单受理后等待订单结果。
- 对通知进行平台 RSA 验签,按订单号幂等处理,并结合查单确认最终状态。
通用约定
币种 ccy_no=IDR。当前代收单笔金额为 10,000–50,000,000 IDR,代付为 10,000–50,000 IDR,且必须为整数印尼盾;商户已设置的业务开关、单笔/当日限额和余额条件仍须满足。允许以 "10000"、"10000.0" 或 "10000.00" 表示同一整数金额,签名必须保留原字符串,不接受含非零小数部分的金额。
API 响应通常使用 HTTP 200,必须同时读取 JSON 中的 code 和订单业务状态。请求正文最大16 KiB。订单号最多64字符,仅支持英文字母、数字、下划线、点、冒号和连字符。单商户内订单号保持唯一,不用大小写差异创建新订单。
请求为 POST application/json,所有请求字段均为字符串;未知字段、旧字段、重复字段、null 和多余 JSON 均返回参数错误。金额字符串及手机号前导零原样参与签名。
响应保留本站 {code,message,data} 外层,code=0 表示接口调用成功,业务状态在 data 中。错误示例:
{
"code": 400,
"message": "参数错误",
"data": null
}代付金额加手续费必须不超过商户可用余额,失败只退款一次;不确定的下单结果保留待核验,不自动重复付款。
RSA 签名规则
所有商户请求使用 RSA-SHA256 / PKCS#1 v1.5 / Base64。至少2048位商户私钥签名,后台登记对应公钥。
- 所有参数为字符串,金额 order_amount 如 "10000.00",按传入的原始字节参与签名,不改成数字或重新格式化。
- 排除 sign、未传字段和空字符串,其他非空字段全部参与,包括姓名、手机、邮箱、币种、业务编码、通知地址和摘要。
- 按字段名 ASCII 升序拼接 key=value,以 & 连接;不 URL 编码,不追加共享密钥,不 trim 值,不签整段 JSON。
- 对拼接串 UTF-8 字节直接做 RSA-SHA256,使用标准 Base64 编码签名字节。
客户端不得发送未定义字段。完整示例见 RSA 对接说明;回调协议见 回调通知。
用 OpenSSL 生成 RSA 公钥
首次接入时在商户自己的机器生成一对 RSA 密钥,再从私钥导出公钥。ssh-ed25519 AAAA... 是 SSH 的 Ed25519 公钥,算法和格式均不符合此接口要求,不能转换成 RSA 密钥;请另外生成下列密钥对。
- OpenSSL 官方网站与官方下载页面:提供源码发行包。安装/编译步骤见官方 INSTALL;Windows 另见 Windows 安装说明。
- 命令官方手册:genpkey(生成密钥)、pkey(导出与检查公钥)。
在终端先执行 openssl version 确认可用。Windows 可在已安装 OpenSSL 的 PowerShell 或 Git Bash 中执行;若提示找不到命令,将 OpenSSL 的 bin 目录加入 PATH,或使用 openssl.exe 的完整路径。官方下载页是源码,第三方 Windows 安装包不属于 OpenSSL 官方发行包。
在专门保存密钥的目录执行以下命令。第一条生成2048位 RSA 私钥,第二条导出对应的 PEM 公钥,第三条检查公钥:
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out merchant-private.pem
openssl pkey -in merchant-private.pem -pubout -out merchant-public.pem
openssl pkey -pubin -in merchant-public.pem -pubcheck -noout
检查成功会显示 Key is valid。读取公钥:Windows PowerShell 用 Get-Content -Raw ./merchant-public.pem;Linux、macOS 或 Git Bash 用 cat merchant-public.pem。
-----BEGIN PUBLIC KEY-----
这里是 merchant-public.pem 中实际生成的 Base64 内容,请完整复制
-----END PUBLIC KEY-----
把 merchant-public.pem 的完整内容(包括 BEGIN/END 行)粘贴到商户后台「API 对接 → 机器 RSA 公钥」并保存。merchant-private.pem 是未加密的 PKCS#8 私钥,只保留在商户服务器,用于生成 sign,不要上传到后台或提交到 Git。示意文字不能直接当作公钥保存。
商户已配置有效密钥时继续使用原密钥;重新运行生成命令会覆盖同名文件。已有私钥但丢失公钥时,只执行第二、三条命令即可,无需重新生成私钥。
商户接口 · RSA 测试工具
所有计算在本机浏览器完成。密钥不上传、不写入浏览器存储。工具只计算签名,不发起订单请求。
mer_no 从上方补入。若 JSON 中也填写了 mer_no,两处必须一致。sign 字段会排除后重新生成或验签。
支持 PUBLIC KEY / RSA PUBLIC KEY。生成模式下可选填,用来检查生成的签名与公钥是否匹配。
支持未加密 PKCS#8 PRIVATE KEY / PKCS#1 RSA PRIVATE KEY。可先用下方演示密钥验证 SDK。
1. 实际参与签名的字段
| 字段(ASCII 顺序) | 参与签名的值 |
|---|
创建代收订单
/api/payment/create| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mer_no | string | 是 | 本站分配的商户号 |
| mer_order_no | string | 是 | 商户订单号,单商户内唯一,最长64字符 |
| order_amount | string | 是 | 整数 IDR,10,000–50,000,000;例如 "10000.00",签名保留原字符串 |
| ccy_no | string | 是 | 固定 IDR,不执行汇率换算 |
| notifyUrl | string | 是 | 商户接收本站终态通知的 HTTP/HTTPS 地址 |
| sign | string | 是 | 商户 RSA-SHA256 / PKCS#1 v1.5 / Base64 签名 |
| pname | string | 是 | 付款人英文姓名,仅英文字母和空格,最长64字符 |
| pemail | string | 是 | 有效邮箱,最长128字符 |
| phone | string | 是 | 纯数字手机号,不加区号前缀 |
| busi_code | string | 是 | 固定 101202 |
| pageUrl | string | 否 | 支付完成后的 HTTP/HTTPS 跳转地址 |
{
"mer_no": "YOUR_MERCHANT_ID",
"mer_order_no": "IDR_IN_20261102_001",
"pname": "Budi Santoso",
"pemail": "buyer@example.com",
"phone": "081234567890",
"order_amount": "10000.00",
"ccy_no": "IDR",
"busi_code": "101202",
"notifyUrl": "https://merchant.example/notify",
"pageUrl": "https://merchant.example/paid",
"sign": "BASE64_RSA_SIGNATURE"
}成功响应
{
"code": 0,
"message": "success",
"data": {
"mer_no": "YOUR_MERCHANT_ID",
"mer_order_no": "IDR_IN_20261102_001",
"order_no": "STABLEPAYIN...",
"order_amount": "10000.00",
"ccy_no": "IDR",
"status": "SUCCESS",
"order_status": "UNPAY",
"order_data": "https://cashier.example/pay"
}
}
order_data 是本站下单响应返回的完整收银台地址,示例地址仅表示字段形状,实际请原样使用每笔订单的返回值,不自行拼接地址。本站代收采用收银台支付,商户按上述字段下单即可。
pageUrl 仅为付款后的可选页面跳转;页面打开、关闭或跳回商户站点均不能作为到账证据。notifyUrl 是商户接收本站结果通知的地址,两者用途不同。
status=SUCCESS 仅表示受理成功,order_status=UNPAY 表示尚未到账。缺少姓名、邮箱或手机号时拒绝创建订单。出现网络超时、接口错误或无法确定结果时,先用原商户订单号查单;不更换订单号重复创建付款。
查询代收订单
/api/payment/query| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mer_no | string | 是 | 本站商户号 |
| mer_order_no | string | 二选一 | 商户订单号 |
| order_no | string | 二选一 | 本站平台订单号;同时传时优先使用此项 |
| request_no | string | 是 | 商户生成的查询流水号,最长64字符,建议每次使用不同值 |
| request_time | string | 是 | UTC+7 时间,yyyyMMddHHmmss |
| sign | string | 是 | RSA-SHA256 签名 |
{
"mer_no": "YOUR_MERCHANT_ID",
"mer_order_no": "IDR_IN_20261102_001",
"request_no": "QUERY_20261102_001",
"request_time": "20261102120000",
"sign": "BASE64_RSA_SIGNATURE"
}{
"code": 0,
"message": "success",
"data": {
"mer_no": "YOUR_MERCHANT_ID",
"mer_order_no": "IDR_IN_20261102_001",
"order_no": "STABLEPAYIN...",
"order_amount": "10000.00",
"ccy_no": "IDR",
"query_status": "SUCCESS",
"request_no": "QUERY_20261102_001",
"request_time": "20261102120000",
"utr": "",
"order_status": "UNPAY",
"pay_amount": "0.00",
"order_data": "https://cashier.example/pay"
}
}query_status 只表示查单成功;最终业务状态看 order_status:UNPAY 待支付、SUCCESS 成功、FAIL 失败。商户只可查询自己的订单。order_time / pay_time 在有对应事件时间时返回,格式 yyyyMMddHHmmss(UTC+7)。
创建代付订单
/api/payout/create印尼银行与钱包代付
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mer_no | string | 是 | 本站分配的商户号 |
| mer_order_no | string | 是 | 商户订单号,单商户内唯一,最长64字符 |
| order_amount | string | 是 | 整数 IDR,10,000–50,000;例如 "10000.00",签名保留原字符串 |
| ccy_no | string | 是 | 固定 IDR,不执行汇率换算 |
| notifyUrl | string | 是 | 商户接收本站终态通知的 HTTP/HTTPS 地址 |
| sign | string | 是 | 商户 RSA-SHA256 / PKCS#1 v1.5 / Base64 签名 |
| acc_no | string | 是 | 银行账号或钱包绑定手机号,保留前导零,最长64字符 |
| acc_name | string | 是 | 真实收款姓名,最长64字符 |
| bank_code | string | 是 | 已开通的印尼银行/钱包数字编码,保留前导零;例如 BCA 为 014,范围须与商户开通配置一致 |
| mobile_no | string | 是 | 纯数字收款手机号 |
| string | 是 | 有效收款邮箱,最长128字符 | |
| summary | string | 是 | 摘要,最长256字符 |
{
"mer_no": "YOUR_MERCHANT_ID",
"mer_order_no": "IDR_OUT_20261102_001",
"acc_no": "081234567890",
"acc_name": "Budi Santoso",
"ccy_no": "IDR",
"order_amount": "10000.00",
"bank_code": "014",
"mobile_no": "081234567890",
"email": "payee@example.com",
"notifyUrl": "https://merchant.example/notify",
"summary": "Payout IDR_OUT_20261102_001",
"sign": "BASE64_RSA_SIGNATURE"
}受理响应
{
"code": 0,
"message": "success",
"data": {
"mer_no": "YOUR_MERCHANT_ID",
"mer_order_no": "IDR_OUT_20261102_001",
"order_no": "STABLEPAYOUT...",
"order_amount": "10000.00",
"ccy_no": "IDR",
"fee": "0.00",
"status": "SUCCESS"
}
}下单响应 status=SUCCESS 表示已受理,不表示已打款;status=FAIL 表示明确失败并退款。下单使用的 notifyUrl 是商户接收本站订单结果通知的地址。
查询代付订单
/api/payout/query| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mer_no | string | 是 | 本站商户号 |
| mer_order_no | string | 二选一 | 商户订单号 |
| order_no | string | 二选一 | 本站平台订单号;同时传时优先使用此项 |
| request_no | string | 是 | 商户生成的查询流水号,最长64字符,建议每次使用不同值 |
| request_time | string | 是 | UTC+7 时间,yyyyMMddHHmmss |
| sign | string | 是 | RSA-SHA256 签名 |
{
"mer_no": "YOUR_MERCHANT_ID",
"mer_order_no": "IDR_OUT_20261102_001",
"request_no": "QUERY_20261102_001",
"request_time": "20261102120000",
"sign": "BASE64_RSA_SIGNATURE"
}{
"code": 0,
"message": "success",
"data": {
"mer_no": "YOUR_MERCHANT_ID",
"mer_order_no": "IDR_OUT_20261102_001",
"order_no": "STABLEPAYOUT...",
"order_amount": "10000.00",
"ccy_no": "IDR",
"query_status": "SUCCESS",
"request_no": "QUERY_20261102_001",
"request_time": "20261102120000",
"utr": "",
"status": "UNKNOW",
"fee": "0.00",
"acc_no": "081234567890",
"acc_name": "Budi Santoso",
"bank_code": "014",
"mobile_no": "081234567890",
"email": "payee@example.com",
"summary": "Payout"
}
}query_status 只表示查单成功;最终业务状态看 status:UNKNOW 处理中、SUCCESS 成功、FAIL 失败。商户只可查询自己的订单。order_time / pay_time 在有对应事件时间时返回,格式 yyyyMMddHHmmss(UTC+7)。
银行与钱包编码
代付 bank_code 使用已开通的印尼数字编码,必须用字符串传递并保留前导零。以下为填写参考,实际支持范围和限额以商户已开通配置为准。
| 银行/钱包 | bank_code | acc_no |
|---|---|---|
| BCA | 014 | 真实银行账号 |
| BNI | 009 | 真实银行账号 |
| BRI | 002 | 真实银行账号 |
| Mandiri | 008 | 真实银行账号 |
| DANA | 10002 | 钱包绑定手机号 |
| OVO | 10001 | 钱包绑定手机号 |
| GOPAY | 10003 | 钱包绑定手机号 |
查询商户余额
/api/balance/query| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mer_no | string | 是 | 本站商户号 |
| request_no | string | 是 | 商户生成的查询流水号,最长64字符,建议每次使用不同值 |
| request_time | string | 是 | UTC+7 时间,yyyyMMddHHmmss |
| sign | string | 是 | RSA-SHA256 签名 |
{
"mer_no": "YOUR_MERCHANT_ID",
"request_no": "QUERY_20261102_001",
"request_time": "20261102120000",
"sign": "BASE64_RSA_SIGNATURE"
}{
"code": 0,
"message": "success",
"data": {
"mer_no": "YOUR_MERCHANT_ID",
"request_no": "QUERY_20261102_001",
"request_time": "20261102120000",
"query_status": "SUCCESS",
"list": [
{
"account_no": "YOUR_MERCHANT_ID",
"ccy_no": "IDR",
"balance": "10000.25",
"frozen_balance": "500.00"
}
]
}
}查询本站商户余额。balance 是可用余额,已扣冻结金额,不再减 frozen_balance;金额为十进制字符串。查询不改变余额和业务数据。
回调通知
订单结果通知使用以下字段,所有值均为字符串;代收增加 pay_amount,代付增加 fee。status=SUCCESS 表示到账/打款成功,FAIL 表示最终失败;失败增加 err_code 和 err_msg。代付失败先退款,再通知。
{
"mer_no": "YOUR_MERCHANT_ID",
"mer_order_no": "IDR_OUT_20261102_001",
"order_no": "STABLEPAYOUT...",
"order_amount": "10000.00",
"ccy_no": "IDR",
"status": "SUCCESS",
"fee": "0.00",
"utr": "1234567890",
"sign_type": "RSA-SHA256",
"sign_key_id": "平台公钥标识",
"sign": "BASE64_RSA_SIGNATURE"
}通知发往下单 notifyUrl,来源 IP:167.172.74.33。以订单号幂等处理;历史订单保留原字段名,所有后续通知和重试统一使用平台 RSA 签名。
回调验签
在商户后台「商户信息」或「API 对接」复制平台回调 RSA 公钥,用于验证本站通知。平台私钥签名,算法为 RSA-SHA256 / PKCS#1 v1.5 / 标准 Base64。排除 sign 和空串,将其余非空字段(包括 sign_type、sign_key_id)按 ASCII 升序拼接 key=value,以 & 连接,按 UTF-8 字节验签,不追加 key、不 URL 编码、不修改字段原值。使用平台公钥验通知;商户自己的公钥用于平台验证商户请求。商户请求本站使用商户私钥签名,已登记的商户公钥只显示配置状态、仅可替换。Python 回调验签示例。
应答与重试
建议在完成验签和幂等处理后返回 HTTP 200、纯文本 success;重复通知也返回 success。投递失败每隔60秒重试,自动投递总共最多3次;运营可在回调失败列表重新发送。平台签名未配置或无效时暂停发送,不消耗投递次数;配置有效密钥后等待中的回调自动继续。更换平台密钥前向商户提供新公钥,商户可按 sign_key_id 选择可信公钥。
订单状态
下单 status=SUCCESS 是受理成功。代收查单看 order_status:UNPAY / SUCCESS / FAIL;代付查单看 status:UNKNOW / SUCCESS / FAIL。查询本身成功由 query_status=SUCCESS 表示。以成功终态或有效成功回调入账,不根据下单受理结果入账。
错误码
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| 0 | number | 响应 | 调用成功,继续核对业务状态 |
| 400 | number | 响应 | 字段、类型、格式错误;旧字段不再接受 |
| 401 | number | 响应 | 签名无效或缺少签名 |
| 403 | number | 响应 | 商户、公钥、IP 白名单或权限校验失败 |
| 500 | number | 响应 | 业务异常、余额不足、重复订单或订单结果待核验 |
不确定结果请先按原商户订单号查单或联系运营核验,不更换订单号重复付款。
cURL 示例
先按 RSA 规则生成 sign,再发送请求;示例资料须替换为真实商户和收款资料。
curl -X POST https://inpay.happypay99.com/api/payout/create \
-H "Content-Type: application/json" \
-d '{"mer_no": "YOUR_MERCHANT_ID", "mer_order_no": "IDR_OUT_20261102_001", "acc_no": "081234567890", "acc_name": "Budi Santoso", "ccy_no": "IDR", "order_amount": "10000.00", "bank_code": "014", "mobile_no": "081234567890", "email": "payee@example.com", "notifyUrl": "https://merchant.example/notify", "summary": "Payout IDR_OUT_20261102_001", "sign": "BASE64_RSA_SIGNATURE"}'
Python 示例
完整代收、代付 Python RSA-SHA256 示例及 OpenSSL 公钥步骤见 RSA 对接说明。所有请求参数均为字符串,order_amount 不做二次格式化。