先说句实在话:NewAPI 对接 epay 这件事,网上能搜到的教程,九成是「打开充值配置 → 填三个参数 → 保存」就没了。照着做,下单能跳收银台,但回调收不到、配额不加、或者报签名错误,教程里一个字没提。这篇不干那种事,我把每一步该填什么、为什么这么填、填错了会看到什么报错,从头到尾讲一遍,最后给你一段不用改就能跑的签名代码。

先把三样东西拿到手,顺序别反

对接之前,你手上得有三样东西,少一样都别急着去后台点保存:

  • 网关地址:epay 服务商给你的接口地址,形如 https://xxkkfz.cnhttps://xxkkfz.cn/。注意末尾的斜杠,服务商文档写没写、你就照着抄,别自作主张加一个或删一个。
  • 商户 PID:纯数字的那串,一般是 4 到 6 位,比如 10001
  • 商户 KEY:32 位小写字母数字混合的字符串,是用来算 MD5 签名的密钥。

这里有个 90% 的人都会踩的坑:复制 PID 和 KEY 的时候,从网页上直接拖选,很容易把首尾的空格、换行符一起带进来。KEY 里藏一个看不见的空格,签名就是错的,下单立刻报「签名错误」。拿到参数先手动敲一遍到记事本里,确认首尾干净,再往后台贴。

如果服务商提供沙箱参数,先用沙箱那套。沙箱下的订单不结算真钱,你可以随便测,测通了再换生产参数。

NewAPI 后台到底填哪儿

登录 NewAPI 管理后台,左侧找「系统设置」里的「充值」或「支付」相关页面(不同版本叫法略有差异,但都在系统设置下)。里面会有一块「易支付」配置区,就三个输入框加几个开关:

  • 网关地址:填上面说的那个地址。
  • 商户 PID:填数字编号。
  • 商户密钥:填 32 位 KEY。

下面一般还有「启用支付宝」「启用微信」之类的勾选项,把你要用的勾上就行。页面里其它海外的支付配置,比如 Stripe、Creep 之类,全部留空,别手贱去填,填了前端充值页可能出现多余的入口。

保存之后,NewAPI 会自动拼出两个地址,一个是异步通知地址 notify_url,一个是支付完成跳转地址 return_url。异步通知地址是整个链路里最要命的一环,格式一般是:

https://你的域名/api/pay/notify

用户付完钱,epay 网关会往这个地址发一个 POST 请求,带上订单号、金额、签名这些数据。NewAPI 收到后验签通过,才给对应用户加配额。这个地址必须是公网能访问的,后面单独说怎么验。

NewAPI 自己会算签名,你其实不用写

很多人以为对接 epay 得自己写一堆签名代码,其实不对。NewAPI 原生内置了 epay 的整套逻辑:你填完 PID 和 KEY,它自己就负责拼参数、算 MD5、发下单请求、处理回调。所以「对接」这一步,在 NewAPI 上就是填参数,不需要你写任何代码。

但为什么我还要给你一段代码?因为有两个场景你必须自己会算签名:

  1. 你想先验证一下「我这份 KEY 到底对不对」,不想每次都要点一次充值按钮去试;
  2. 你后面要自建回调转发、或者做自动充值脚本,脱离 NewAPI 自己下单。

下面这段 Python 就是 epay 标准下单签名,参数名和算法都是标准协议,直接能跑:

import time
import hashlib

EPAY_PID = "10001"           # 你的商户 PID
EPAY_KEY = "改成你的32位密钥"
EPAY_GATEWAY = "https://xxkkfz.cn"   # 网关地址,末尾斜杠按文档来

def create_order(out_trade_no, total_fee, subject, notify_url, return_url):
    params = {
        "pid": EPAY_PID,
        "out_trade_no": out_trade_no,
        "total_fee": total_fee,
        "subject": subject,
        "notify_url": notify_url,
        "return_url": return_url,
    }
    # 规则一:剔掉空值,一个都不能留
    params = {k: v for k, v in params.items() if v not in ("", None)}
    # 规则二:按键名 ASCII 升序排序
    items = sorted(params.items(), key=lambda x: x[0])
    # 规则三:拼成 k=v&k=v&... 再接上 KEY
    sign_str = "".join(f"{k}={v}&" for k, v in items) + EPAY_KEY
    # 规则四:UTF-8 编码,MD5 转小写
    sign = hashlib.md5(sign_str.encode("utf-8")).hexdigest().lower()
    params["sign"] = sign
    params["sign_type"] = "MD5"
    return params

if __name__ == "__main__":
    order = create_order(
        out_trade_no="test" + str(int(time.time())),
        total_fee="1.00",
        subject="测试订单",
        notify_url="https://你的域名/api/pay/notify",
        return_url="https://你的域名/",
    )
    print(order)

这段代码里四个规则,是 epay 签名出错最常见的原因,一条都不能错:空值不剔、排序乱了、没接 KEY、MD5 写了大写。你拿它算出来的 sign,跟服务商文档里的示例对一遍,对得上说明你 KEY 没复制错。

四个最容易翻车的细节

填完参数,真正开始测的时候,下面这四个坑会挨个冒出来,提前知道能省你一下午。

一、回调地址公网不通

NewAPI 的回调地址是 /api/pay/notify,它必须能被 epay 网关从外面 POST 到。先在你自己服务器上 curl 一下这个地址,看是不是 200:

curl -I https://你的域名/api/pay/notify

如果返回 404 或 502,先查 Nginx 的反向代理是不是把 NewAPI 的端口转发对了。NewAPI 默认跑在某个端口上(比如 3000),你得有 Nginx 把 /api/ 这个路径转发到那个端口,否则回调根本进不来。

二、服务器时间不准

epay 网关会校验请求的时间戳,服务器时间跟标准时间差超过几分钟,下单可能直接被拒。上线前跑一句 date 看看,或者用 ntp 同步一下。这个小问题特别隐蔽,报错还看不出来。

三、金额和配额的比例没对上

NewAPI 里一般有个「充值档位」或「配额比例」的配置,比如 1 元 = 多少 token。这里填错,用户付了 10 块钱,到账的配额跟你预期对不上,后面全是工单。上线前一定要用沙箱,充一笔最小的,核对到账配额是不是你设的那个数。

四、回调重复到账

epay 网关在没收到 success 的时候会隔一段时间重试回调。NewAPI 内部有做幂等,正常不会重复加配额。但如果你后面自己写回调转发、或者接了第三方插件,一定要按订单号去重,别每次都加一次余额。

上线前必须跑一遍的完整流程

别直接切生产参数,按这个顺序走,每一步都确认没问题再进下一步:

  1. 拿到沙箱参数,填进 NewAPI,保存;
  2. curl 一下回调地址,确认 200;
  3. 前台点充值,选最小金额,确认能跳转到 epay 收银台;
  4. 在沙箱里模拟付款(或用沙箱提供的测试支付),观察 NewAPI 日志,确认回调进来了、验签过了;
  5. 回到用户账户页,确认配额确实增加了,且只加了一次;
  6. 全通了,再换生产参数,正式开卖。

第 4 步和第 5 步是大多数人跳过的,结果一上线就是「钱收了、配额没到」,用户骂、你挨个退。沙箱阶段把这两个卡点堵死,后面省心。

回调验签那段,NewAPI 帮你挡了什么

你填完 KEY 之后,NewAPI 收到回调会做这么几件事:先用回调里的参数重算一遍 MD5,跟 epay 传过来的 sign 对比,对不上直接丢弃;再核对回调里的订单号和金额,跟本地订单一致才入账;最后才给用户加配额,并且按订单号去重,防重复回调。

这三件事,就是「验签 + 金额比对 + 幂等」,是支付回调安全的三道闸。NewAPI 原生都做了,所以你自己用 NewAPI 对接,不用重复造轮子。但如果你哪天想脱离 NewAPI 自己写回调,这三道闸一道都不能省,省了就是给低价充值、重复到账留后门。

顺带说一句金额比对为什么重要:如果回调不核对金额,有人能伪造一个「金额 0.01 元」的支付成功回调,把签名不算错的情况下套你的配额。签名保证数据没被篡改,金额比对保证业务没被钻空子,两件事不是一回事。

排错速查:看到这些报错,往哪查

把常见的现象和原因对应起来,你遇到了直接对号入座:

  • 下单报「签名错误」:先查 KEY 有没有复制到空格、换行;再查网关地址末尾斜杠;最后用上面那段代码单独算一次 sign 跟文档示例对。
  • 能跳收银台,但付完钱配额没加:回调没进来,去 curl 回调地址,查 Nginx 转发和服务器时间。
  • 回调反复重试、日志一直报验签失败:NewAPI 里填的 KEY 和 epay 后台实际 KEY 不一致,多半是复制时多了字符。
  • 前端充值页跳转地址 404:return_url 配的域名和你实际访问的域名不一致,或者 Nginx 没把根路径转发对。

这几个现象覆盖了大部分情况,先从「签名」和「回调」两个方向排查,比瞎找快得多。