易支付接口对接文档

泡泡支付完整兼容易支付 V1 协议。已经对接过易支付的系统,改一下网关地址、商户 ID 和密钥即可使用;从零开发也只需看完本页。

  • 易支付 V1 协议
  • MD5 签名
  • PHP / Python / Java 示例
  • 更新于

接入概述

已经对接过易支付的系统,改一下地址和密钥即可使用

泡泡支付完整兼容易支付 V1 协议。如果你的系统(发卡网、AI 站、各类商城插件)已经支持易支付,不需要改代码,只要把后台的网关地址、商户 ID、商户密钥换成泡泡支付提供的即可。

网关地址

用途地址方式
API 下单(后端发起,返回支付链接){网关域名}/mapi.phpPOST
页面下单(浏览器直接跳转){网关域名}/submit.phpGET / POST
综合接口(查单、退款、结算等){网关域名}/api.phpGET / POST

部分系统只要求填一个「网关地址」,此时填到域名即可,系统会自行拼接 /mapi.php 等路径。

商户 ID 与密钥

联系官方客服开通接入应用,网关域名、pid 与 key 由客服直接提供给你:

易支付字段对应泡泡支付说明
pid接入应用的「商户 ID」6 位以上纯数字
key接入应用的「商户密钥」32 位字符,仅用于签名,切勿泄露

若怀疑密钥泄露,可联系官方客服重置。重置后必须同步更新你系统里的配置,否则签名会全部失败。

接口 IP 白名单

/mapi.php 与 /api.php 是服务器对服务器调用,受 IP 白名单限制。需要把你服务器的公网出口 IP 提供给官方客服配置到白名单中,否则会返回「非法请求,请添加接口IP白名单」。

/submit.php 由用户浏览器直接访问,来源是用户 IP,因此不校验白名单,仅靠签名保证请求真实性。

签名算法

对接期绝大多数问题都是签名对不上。本节给出一组可直接比对的完整示例:如果你用相同入参算不出相同的 sign,对照下面四步逐步排查即可定位。

四步规则

  1. 筛选:剔除 sign、sign_type,以及值为空的参数
  2. 排序:按参数名 ASCII 升序(a→z)排列
  3. 拼接:拼成 k1=v1&k2=v2,值不做 URL 编码
  4. 签名:末尾直接追加商户密钥(没有 &key= 分隔),MD5 取 32 位小写

完整示例

按顺序切换下面的标签页,逐步比对。注意 param 为空,不参与签名;第 ③ 步密钥直接拼在末尾,中间没有任何分隔符。

pid          = 100001
type         = alipay
out_trade_no = MCH20260728001
notify_url   = https://shop.com/notify
return_url   = https://shop.com/return
name         = VIP会员
money        = 1.00
clientip     = 1.2.3.4
param        =                  ← 空值,剔除
key(密钥)  = a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

最终请求里再带上 sign=77c46b1a2cb7063f4d01003379863f5e 和 sign_type=MD5 两个参数即可。

容易踩的三个点

现象原因
本地算的 sign 与平台不一致最常见是对参数值做了 URL 编码。中文、://、& 都要用原文参与签名,编码只发生在实际发送 HTTP 请求时
加上某个空参数就签不过空值必须剔除。传了 param= 却把它算进签名串,就会不一致
偶发验签失败,多数订单正常检查商品名是否含尖括号或首尾空格,确保发送的值与参与签名的值完全一致

API 下单

POST/mapi.php

后端发起,返回支付链接或二维码内容,由你自己决定怎么展示给用户。请求体为 application/x-www-form-urlencoded,仅支持 POST,受 IP 白名单限制。

请求参数

参数名必填说明
pid是商户 ID
type是支付方式:alipay / wxpay / qqpay
out_trade_no是商户订单号,同一应用内不可重复(最长 64 字符)
notify_url是异步通知地址,发货请以此为准
name是商品名称
money是金额(元),最多 2 位小数,最小 0.01
clientip是用户 IP
return_url否支付后浏览器跳转地址
device否设备类型,不传自动识别
param否业务扩展参数,通知时原样返回
sign是签名
sign_type是固定 MD5

响应参数

参数名说明
code1 为成功,-1 为失败
msg失败原因,成功时为空串
trade_no平台订单号
out_trade_no商户订单号
money订单金额
payurl支付跳转链接(http/https)
qrcode二维码内容(weixin://、alipays:// 等)
urlscheme小程序跳转链接

payurl、qrcode、urlscheme三者只返回其中一个,取决于实际出码的通道类型。你的代码要三个都做兼容判断。

{
  "code": 1,
  "msg": "",
  "trade_no": "PN20260728120000123",
  "out_trade_no": "MCH20260728001",
  "money": "1.00",
  "payurl": "https://pay.example.com/xxxx"
}

页面下单

GET/submit.php

把用户浏览器直接引导到本地址(表单 POST 或拼 query 串 GET 都可以),平台下单后自动跳转到支付页面,适合不想自己处理支付页展示的场景。本接口不校验 IP 白名单(访问者是终端用户),仅靠签名保证真实性。

请求参数

参数名必填说明
pid是商户 ID
out_trade_no是商户订单号
notify_url是异步通知地址
return_url是支付后跳转地址
name是商品名称
money是金额(元)
type否支付方式,不传则跳转平台收银台由用户自选
param否业务扩展参数
sign是签名
sign_type是固定 MD5

响应是一个自动跳转的 HTML 页面。若参数或签名有误,返回的是可读的错误提示页,页面上会写明具体原因,可据此排查。

异步通知

GETnotify_url

支付成功后,平台以 GET 方式请求你的 notify_url,参数拼在 query 上。发货必须以异步通知为准,不要依赖同步跳转。

通知参数

参数名说明
pid商户 ID(与你下单时用的一致)
trade_no平台订单号
out_trade_no商户订单号
type支付方式,回传你下单时传的原值
name商品名称
money订单金额
trade_status只有 TRADE_SUCCESS 表示成功
param下单时传的业务扩展参数(下单未传则不出现)
sign签名,请务必验签
sign_typeMD5

你需要返回什么

处理成功后,页面只输出 success 七个字符(ok 也可接受),不要包含 HTML 标签或其他内容。

若未收到 success,平台会自动重试,间隔逐次递增。因此你的处理逻辑必须做幂等:同一 out_trade_no 收到多次通知只能发货一次。

GET https://shop.com/notify?money=1.00&name=VIP%E4%BC%9A%E5%91%98&out_trade_no=MCH20260728001
    &pid=100001&sign=xxxxxxxx&sign_type=MD5&trade_no=PN20260728120000123&trade_status=TRADE_SUCCESS

验签时用的是 URL 解码后的参数值。多数框架(PHP 的 $_GET、Java 的 getParameter)已自动解码,直接取用即可。

补发通知

若你的服务当时不可用错过了通知,可调用综合接口 /api.php?act=notify 主动要求平台重发,无需联系客服。

同步跳转

GETreturn_url

用户支付完成后跳回你的 return_url,参数与异步通知一致,可用于展示「支付成功」页面。submit.php 下单时该参数必填,mapi.php 下单时可选。

同步跳转不保证一定发生,且不可作为发货依据。是否跳回、以及跳回时带哪些参数,取决于实际出码的上游通道,部分通道支付完成后用户会停留在通道页面,这与易支付原版的情况一致。你的成功页应当再查一次订单状态(act=order),而不是直接相信跳转本身。

综合接口

GET/api.php?act={操作}

查单、退款、结算等操作都走这个地址,用 act 区分。本系列接口不用签名,而是直接传 key(商户密钥)校验,这是易支付协议的约定。

因为 key 会出现在 URL 上,请仅在服务端调用,不要放到前端代码里。本接口受 IP 白名单限制。

公共参数

参数名必填说明
act是操作类型,见下文
pid是商户 ID
key是商户密钥(明文)

act=order 查单

额外参数:trade_no(平台单号)或 out_trade_no(商户单号),二选一,同时传则 trade_no 优先。

返回字段说明
status1 = 支付成功,0 = 其他状态(未付、失败、已关闭)
trade_no / out_trade_no平台单号 / 商户单号
api_trade_no上游通道订单号
type支付方式(你下单时传的原值)
money订单金额
name商品名称
addtime / endtime创建时间 / 支付完成时间
param业务扩展参数
GET /api.php?act=order&pid=100001&key=你的密钥&out_trade_no=MCH20260728001

act=orders 批量查单

额外参数:limit(每页条数,最大 50,默认 20)、page(页码,从 1 开始)。返回 data 数组,每项字段与 act=order 一致,按创建时间倒序。

act=query 查账户信息

返回字段说明
money账户余额
active1 = 账户正常,0 = 已停用
order_today / order_lastday今日 / 昨日订单数
key原样回显请求时传入的商户密钥(与 V1 一致),便于部分系统的「测试连接」比对配置
type / account / username协议中为结算方式与结算账户信息。泡泡支付结算由官方统一处理,不通过接口暴露,返回空串占位
orders协议中为历史累计订单数,泡泡支付未维护该口径,返回 0 占位,需要统计请用 act=orders

act=settle 查结算记录

返回 data 数组(最近 50 条),字段:id、money(结算金额)、addtime、remark。结算在账户维度进行,同账户下多个应用共享结算记录。

act=refund 申请退款(POST)

参数名必填说明
trade_no二选一平台订单号
out_trade_no二选一商户订单号
money是退款金额,少数通道需与原订单金额一致

trade_no 与 out_trade_no 不能同时为空,都传则以 trade_no 为准。

本接口 code=0 表示成功,与其他所有 act 的 code=1 相反。这是易支付 V1 协议的定义,泡泡支付保持一致以兼容既有客户端,请勿按 code=1 判断。

act=notify 补发通知(扩展)

额外参数:trade_no 或 out_trade_no(二选一)。仅支付成功的订单可补发。返回 code=1 表示补发任务已提交,通知为异步发送,不代表你已成功接收。该 act 不属于 V1 标准接口,是泡泡支付的扩展,用于商户自助补发错过的通知。

常见系统配置对照

各家系统的字段叫法不同,下表把常见系统后台的配置项对应到泡泡支付的值,照着填完就能跑,不需要读上面的协议细节。开始前先联系官方客服开通接入应用,拿到 pid 和 key。

通用对应关系

对方系统里的叫法填什么
支付接口地址 / 网关地址 / API 地址 / 接口 URL客服提供的网关域名,如 https://网关域名
商户 ID / 商户号 / PID / 合作者身份接入应用的商户 ID(pid)
商户密钥 / 通信密钥 / KEY / Token接入应用的商户密钥(key)
签名方式 / 加密方式MD5
支付方式编码 / 通道编码alipay(支付宝)、wxpay(微信)
接口版本 / 协议版本选「易支付」或「彩虹易支付」;若区分版本选 V1

发卡网系统

系统配置位置要点
异次元发卡后台 → 支付接口 → 新增,类型选「彩虹易支付 / 易支付」「接口地址」填到域名即可,程序自动拼 /submit.php;商户 ID 填 pid,密钥填 key
独角数卡管理后台 → 支付配置 → 添加支付方式,选 Epay / 易支付需分别为支付宝和微信各加一条,两条的 pid / key 相同,只有「支付类型」分别填 alipay / wxpay
彩虹发卡 / 各类彩虹二开后台 → 支付设置 → 易支付原生同源协议,通常只需替换网关地址、PID、KEY 三项

AI 站 / API 中转站

系统配置位置要点
One-API / New-API / 各类二开系统设置 → 支付设置(或支付宝 / 易支付相关配置项)「易支付接口地址」填网关域名,「易支付商户ID」填 pid,「易支付商户密钥」填 key。部分版本要求地址以 / 结尾,按其占位提示填
自建站 / 其他商城插件凡是支持「易支付」的插件按上面「通用对应关系」逐项填即可

别忘了 IP 白名单:把你服务器的公网出口 IP 提供给官方客服配置进应用中,否则 /mapi.php 和 /api.php 会被拒绝。纯用 /submit.php 跳转的系统不受此限制。

代码示例

下面是签名、下单、验签三段核心代码。把 GATEWAY、PID、KEY 换成你自己的即可运行。

epay.php
<?php
const GATEWAY = 'https://pay.example.com'; // 网关地址,开户后由官方客服提供
const PID     = '100001';
const KEY     = '你的商户密钥';

/** 生成签名:剔除空值与 sign/sign_type,按键名排序,拼接后追加密钥取 MD5 */
function epaySign(array $params, string $key): string {
    unset($params['sign'], $params['sign_type']);
    $params = array_filter($params, fn($v) => $v !== '' && $v !== null);
    ksort($params);
    $parts = [];
    foreach ($params as $k => $v) {
        $parts[] = "$k=$v"; // 注意:值不做 urlencode
    }
    return md5(implode('&', $parts) . $key);
}

/** 下单,返回支付地址 */
function epayCreate(array $biz): array {
    $params = array_merge($biz, ['pid' => PID]);
    $params['sign'] = epaySign($params, KEY);
    $params['sign_type'] = 'MD5';

    $ch = curl_init(GATEWAY . '/mapi.php');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => http_build_query($params),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 30,
    ]);
    $resp = curl_exec($ch);
    curl_close($ch);

    $data = json_decode($resp, true);
    if (!$data || ($data['code'] ?? -1) != 1) {
        throw new Exception('下单失败:' . ($data['msg'] ?? $resp));
    }
    // payurl / qrcode / urlscheme 三者只返回其一,都要兼容
    $data['pay_target'] = $data['payurl'] ?? $data['qrcode'] ?? $data['urlscheme'] ?? '';
    return $data;
}

// ---------- 下单 ----------
$order = epayCreate([
    'type'         => 'alipay',
    'out_trade_no' => 'MCH' . date('YmdHis'),
    'notify_url'   => 'https://shop.com/notify.php',
    'return_url'   => 'https://shop.com/return.php',
    'name'         => 'VIP会员',
    'money'        => '1.00',
    'clientip'     => $_SERVER['REMOTE_ADDR'],
]);
header('Location: ' . $order['pay_target']);

// ---------- 接收异步通知(notify.php) ----------
$params = $_GET; // 框架已自动 urldecode
if (epaySign($params, KEY) !== ($params['sign'] ?? '')) {
    exit('sign error'); // 验签失败,不可发货
}
if (($params['trade_status'] ?? '') !== 'TRADE_SUCCESS') {
    exit('success'); // 非成功状态,应答避免重试
}
// 幂等:同一订单号可能收到多次通知,必须保证只发货一次
if (!alreadyShipped($params['out_trade_no'])) {
    ship($params['out_trade_no'], $params['money']);
}
exit('success'); // 只输出 success,不要带 HTML

排错指引

返回信息原因与处理
验签失败最常见是对参数值做了 URL 编码后再签名,中文、:// 要用原文参与签名。其次检查是否把空值参数算进了签名串。请对照「签名算法」一节的完整示例逐步比对
非法请求,请添加接口IP白名单把你服务器的公网出口 IP 提供给官方客服配置到白名单中。注意是出口 IP,不是内网 IP,也不是用户 IP
商户不存在,请刷新pid 填错,或填的是别人的 pid,请核对客服提供的 pid
接入应用已停用联系官方客服把该应用状态改回「启用」
接入应用未配置支付方式 xxx 对应的产品该应用没有为这个支付方式配置产品,请联系官方客服配置
商户订单号重复同一应用内 out_trade_no 不可重复。若是重试导致,先用 act=order 查原单状态,不要换号重下
参数 money 格式错误 / 不能小于 0.01 / 最多保留 2 位小数按提示修正金额,注意单位是元不是分
出码失败,暂无任何匹配支付链接可用当前金额下没有可用通道。可换个金额再试,或联系官方客服确认通道状态与金额区间
订单不存在单号填错,或该订单属于同账户下的另一个应用。查单时请用下单时那个应用的 pid / key
收不到异步通知确认 notify_url 是公网可访问的 http/https 地址、无需登录,且返回内容只有 success。确认无误后用 act=notify 补发
支付完成后没跳回网站同步跳转取决于上游通道是否支持,部分通道不会跳回。这属于正常情况,请以异步通知为发货依据

上线前自查清单

  • 已请官方客服为要用的支付方式各配置了一个产品
  • 已把服务器公网出口 IP 加入白名单
  • 签名串里没有 URL 编码、没有空值参数、末尾直接拼了密钥
  • 下单响应对 payurl / qrcode / urlscheme 三个字段都做了兼容
  • 异步通知做了验签,且返回内容只有 success
  • 发货逻辑做了幂等,同一订单号重复通知只发一次
  • 发货依据是异步通知,而不是同步跳转

获取商户 ID 与密钥

联系官方客服开通接入应用,网关地址、pid 与 key 由客服直接提供,专人协助联调。

联系官方客服