Feature: specs/011-newebpay-payment/spec.md Date: 2026-06-22
本文定义本期对外与内部接口契约。Controller 位于 ruoyi-admin;Service/Domain 位于 ruoyi-system;加密工具位于 ruoyi-admin/.../app/utils/newebpay/。
蓝新在交易完成/失败后以 Form Post 通知本接口。本接口解密验签并更新订单。
POST /pay/newebpay/notify@Anonymous(蓝新无登录态,经 PermitAllUrlProperties 放行)application/x-www-form-urlencoded| 参数 | 说明 |
|---|---|
| MerchantID | 商店代号 |
| Status | SUCCESS 或错误代码 |
| Version | 串接版本 |
| TradeInfo | AES-256-CBC 加密的交易结果(hex) |
| TradeSha | SHA256 检查码(大写 hex) |
| EncryptType | 加密模式(若发起时设 0/不传,可能不回传) |
IpnLog)。pos_store_newebpay 取该门店 HashKey/HashIV;无凭证 → 记录并返回。SHA256("HashKey={key}&{TradeInfo}&HashIV={iv}") 大写 == TradeSha;不符 → 拒绝、记录。pos_order_payment;已存在且 pay_status=1 → 直接返回成功应答,不重复处理。pos_order_payment 写入/更新 trade_no、pay_type、auth_code、pay_time、pay_status=1、callback_raw。pos_order.payStatus = 1,pos_order.state 保持不变(沿用 payipn 语义)。orderLogHelper.logSync(...) 记订单日志。{"Status":"SUCCESS","Message":"OK"}(HTTP 200);蓝新收到非成功应答会重试。蓝新在用户付款后将浏览器 Form Post 回本接口(携带与 NotifyURL 相同的加密参数)。
GET/POST /pay/newebpay/return@AnonymousPOST /pay/newebpay/create@Anonymous + @Auth(C 端用户 token,参照 /pay/VNPay)| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| orderid | String | V | 系统订单号 ddId |
pos_store_newebpay;校验已开通(status=2)且启用(is_enabled=1),否则报错「该门店暂不支持在线支付」。pos_order_payment(merchant_order_no、store_id、merchant_id、amount、pay_status=0、create_time)。pos_order.payType="6"、pos_order.payUrl=gatewayUrl。响应 AjaxResult.success:
{
"code": 200,
"msg": "...",
"data": {
"gatewayUrl": "https://ccore.newebpay.com/MPG/mpg_gateway",
"MerchantID": "MS12345678",
"TradeInfo": "<hex>",
"TradeSha": "<UPPER hex>",
"Version": "2.3",
"EncryptType": "0"
}
}
前端用隐藏 form 自动 submit 到 gatewayUrl,字段名对应 data 内 key。
POST /pay/newebpay/query@Auth(商家/运营 token)orderid(ddId)SHA256("IV={iv}&Amt=&MerchantID=&MerchantOrderNo= 排序&Key={key}")。POST https://(c)core.newebpay.com/API/QueryTradeInfo(MerchantID/Version=1.3/RespondType=JSON/CheckValue/TimeStamp/MerchantOrderNo/Amt)。AjaxResult.success 含 TradeStatus、PaymentType、PayTime、订单当前 payStatus。复用 009 PosStoreEzpayController 模式,路径 /system/storeNewebpay,@PreAuthorize("@ss.hasPermi('chanting:storeNewebpay:xxx')")。Service 纯 DB,联网验证在 Controller 层。
| 方法 | 路径 | 权限后缀 | 说明 |
|---|---|---|---|
| GET | /system/storeNewebpay/list |
:list |
开通管理列表(分页+筛选) |
| GET | /system/storeNewebpay/{storeId} |
:query |
门店蓝新详情 |
| PUT | /system/storeNewebpay/apply/{storeId} |
:apply |
发起申请 0→1 |
| PUT | /system/storeNewebpay/saveCredentials |
:saveCredentials |
录入凭证+验证(调 QueryTradeInfo 探测)→2 |
| PUT | /system/storeNewebpay/toggleEnable/{storeId} |
:toggleEnable |
停用/恢复 |
| PUT | /system/storeNewebpay/reset/{storeId} |
:query |
重置状态(凭证作废) |
| PUT | /system/storeNewebpay/enabledPayments/{storeId} |
:toggleEnable |
设置启用的支付方式 CREDIT/LINEPAY/APPLEPAY |
saveCredentials 请求体 StoreNewebpayCredentialDto:
{ "storeId": 1, "merchantId": "MS...", "hashKey": "...", "hashIv": "...", "enabledPayments": "CREDIT,LINEPAY,APPLEPAY" }
验证:Controller 调 NewebPay QueryTradeInfo(查虚构订单);返回金钥/商店错误 → 视为无效(拒绝、状态不变);否则通过置 status=2、is_enabled=1。
位于 ruoyi-admin/.../app/utils/newebpay/,仿 ezPay 结构:
NewebPayEncryptUtil: encrypt(query,key,iv)→hex(AES-256-CBC/PKCS7)、decrypt(hex,key,iv)→明文、genTradeSha(tradeInfoHex,key,iv)→大写、genCheckValue(amt,mid,orderNo,key,iv)→大写、genCheckCode(fields,key,iv)→大写。含 main 自测(用 NDNF §4.1 示例 Key=Fs5cX1TGqYM2PpdbE14a9H83YQSQF5jn/IV=C6AcmfqJILwgnhIP/MID=MS127874575 验证加解密 round-trip 与 TradeSha 规则)。NewebPay: 端点常量(URL_MPG_GATEWAY、URL_QUERY_TRADE_INFO)、createMpgForm(baseUrl,cfg,params) 返回加密后的 form 字段、queryTrade(baseUrl,cfg,amt,orderNo) 调查询。NewebPayConfig: merchantId/hashKey/hashIV(运行时由 Service 从 pos_store_newebpay 构造)。newebpay:
base-url: https://ccore.newebpay.com # 测试 ccore / 正式 core
notify-url: https://<公网域名>/pay/newebpay/notify
return-url: https://<公网域名>/pay/newebpay/return
mpg-version: "2.3"