1. 交易通知及对账
星驿支付文档中心
  • 文档说明
    • 阅读对象
    • 版本说明
  • 术语
    • 支付模式
    • 名词解释
    • 支付产品
      • 付款码支付
      • 聚合码支付
      • PC支付
      • 小程序支付
        • 微信小程序支付
        • 支付宝小程序支付
      • APP支付
        • 微信支付
        • 支付宝支付
      • H5支付
        • 微信H5支付
        • 支付宝H5支付
        • 云闪付H5支付
        • 手机浏览器H5支付
      • Native支付
        • 支付宝支付
        • 云闪付支付
      • 终端支付
        • 智能云MIS
        • 智能APP
  • 接口规则
    • 协议规则
    • 安全规范
  • 支付交易
    • 交易前指引
      • 微信认证操作流程
      • 支付宝认证操作流程
      • H5页面微信支付对接前准备
      • H5页面支付宝支付对接前准备
      • 支付流程
      • 支付接入注意事项
      • 消费者IP获取指引
    • 基础支付
      • 付款码支付
      • JSAPI支付
      • 订单查询
      • 扫码支付-星驿码
      • 扫码支付-官方码
      • 扫码支付-场景码
      • 关闭订单
      • 撤销订单
      • 退款
      • 退款查询
      • 授权码查询openid
      • 获取银联用户标识
      • 获取微信刷脸凭证
      • 成品油(现金/储值)开票
    • 小程序支付
      • 使用说明
      • 小程序半屏-星驿付
      • 小程序半屏-场景
    • 扫码预授权
      • 扫码预授权
      • 预授权查询
      • 预授权撤销
      • 预授权撤销查询
      • 预授权完成
      • 预授权完成查询
      • 预授权完成撤销
      • 预授权完成撤销查询
    • 订单推送
      • 推送终端交易
      • 关闭推送终端交易
      • 碰一下推单状态查询
      • 碰一下订单取消
      • 碰一下订单推送
    • 智能终端SDK
      • 智能终端SDK
    • 交易通知及对账
      • 交易对账文件-场景应用
        GET
      • 交易对账文件-ISV
        GET
      • 交易和退款结果通知
        POST
      • 交易手续费通知
        POST
    • 刷卡预授权
      • 预授权撤销
      • 预授权完成撤销
  • 商户管理
    • 前件前指引
      • 进件说明
      • 图片示例
      • 微信认证操作流程
      • 支付宝认证操作流程
      • 附件下载
    • 商户入网
      • 商户进件上传图片
      • 普通商户进件
      • 普通商户信息修改
      • 小微商户进件
      • 小微商户信息修改
      • 审核结果查询
      • 商户审核结果通知
      • 待审核商户撤回
      • 商户信息查询
      • 结算卡修改
      • 商户快捷修改
      • 受益人信息查询
      • 商户报备查询
      • 商户报备结果通知
      • 商户状态修改
      • 终端报备查询
      • 商户阶梯费率配置
      • 商户阶梯费率查询
    • 商户协议签约
      • 入网协议签约接口调用流程
      • 普通商户用户开户
      • 小微商户用户开户
      • 发短信验证码
      • 获取签约链接
      • 校验验证码
      • 校验验证码-补签
      • 签约结果通知
    • 微信支付宝认证
      • 微信申请单提交
      • 微信申请单撤销
      • 微信申请单状态查询
      • 微信商户授权状态查询
      • 支付宝申请单提交
      • 支付宝申请单撤销
      • 支付宝申请单状态查询
      • 支付宝商户授权状态查询
    • 微信开发配置
      • 微信APPID配置
      • 微信支付目录配置
      • 微信公众号查询
      • 微信开发配置查询
      • 指定渠道报备
      • 微信小程序冻结状态查询
      • 微信小程序冻结通知(非接口)
    • 商户限额调整
      • 商户限额查询
      • 商户提额申请
      • 商户提额记录查询
    • 商户风险处置
      • 商户风险处置通知
      • 风险商户图片上传
      • 微信官方风险商户文件
      • 支付宝官方风险商户文件
      • 风险商户异常流水查询
      • 风险商户调查列表
      • 风险商户调查详情
      • 风险调查单历史查询
      • 风险调查材料下载
      • 风险调查单处理
    • 商户手续费开票
      • 开具发票
      • 开票详情
      • 待开发票信息查询
      • 合并开票商户信息查询
      • 电子普票重发邮箱
      • 开票历史查询
    • 无界收款
      • 无界收款图片上传
      • 无界收款开通
      • 无界收款审核查询
      • 无界收款审核结果通知
  • 资金结算
    • D0结算业务
      • 产品介绍
      • 获取人脸识别链接
      • 人脸认证结果查询
      • D0开通
      • D0信息修改
      • D0状态查询
      • D0开通失败申诉
      • D0申诉结果查询
      • D0申诉结果通知
    • 特殊结算业务
      • 产品介绍
      • 结算业务申请
      • 结算业务变更
      • 结算业务结果查询
      • 结算业务结果通知
    • 提现查账
      • 手工提现
      • 到账记录查询
      • 到账记录批量查询
      • 秒到提现记录查询
      • 账户余额查询
      • 挂账记录查询
      • 补付申请
      • 提现结果通知
      • 账户余额变动通知
      • 提现对账文件
      • 批量结算提现通知
      • 到账附言配置
      • 查询到账附言
  • 星账云管家
    • 星账云管家
    • 功能开通
      • 功能开通(XZY001)
      • 编辑(XZY003)
      • 编辑上传付款凭证(XZY002)
      • 修改生效状态(XZY004)
      • 查询详情信息(XZY005)
      • 产品开通审核通知(XZY006)
    • 公共接口
      • 上传文件(XZY013)
    • 分账
      • 交易
        • 分账结算电子回单下载(XZY037)
        • 余额查询接口(XZY041)
        • 分账合并入账电子回单下载(XZY039)
        • 订单查询(XZY015)
        • 分账(XZY014)
        • 分账撤销退回(XZY017)
        • 分账结果回调(XZY016)
      • 客户管理
        • 新增客户(XZY018)
        • 分账授权申请接口-收单统一结算(XZY043)
        • 批量分账授权申请接口-收单统一结算(XZY044)
        • 合作状态调整(XZY020)
        • 解约/重启签约(XZY-001-002)
        • 编辑客户(XZY023)
        • 替换客户结算卡(XZY022)
        • 审核通知(XZY019)
        • 查询分账客户信息(XZY021)
    • 归集
      • 开户/授权
        • 归集授权申请接口-收单统一结算(XZY037)
        • 审核通知(XZY025)
        • 合作状态调整(XZY026)
        • 查询归集客户信息(XZY027)
        • 替换结算卡(XZY028)
        • 编辑信息(XZY029)
      • 交易
        • 归集合并入账电子回单下载(XZY040)
        • 归集结算电子回单下载(XZY038)
        • 余额查询接口(XZY042)
        • 订单查询(XZY031)
        • 归集-支持自定义服务费(XZY030)
        • 归集(XZY030)
        • 归集结果回调(XZY032)
        • 归集撤销退回(XZY033)
    • 提现
      • 提现订单查询(XZY035)
      • 结算回调通知(XZY050)
      • 提现电子回单下载(XZY040)
      • 提现(XZY036)
    • 账户
      • 结算资金流水查询(XZY047)
      • 结算卡资金流水查询(XZY049)
  • 设备管理与推送
    • 设备管理
      • 收款设备绑定
      • 收款设备解绑
      • 收款设备绑定查询
      • 设备绑定/解绑结果通知
      • 音箱绑定
      • 音箱绑定查询
      • 音箱解绑
    • 设备推送
      • 音箱播报
      • 交易打印
  • 营销活动
    • 银行活动
      • 产品介绍
      • 图片上传
      • 商户补贴列表查询
      • 银行活动报名
      • 银行活动报名结果查询
      • 银行活动报名结果通知
      • 银行活动列表查询
      • 银行活动退出
    • 特殊行业活动
      • 产品介绍
      • 特殊行业活动报名申请
      • 特殊活动报名结果通知
      • 特殊行业商户活动查询
      • 特殊行业活动图片上传
    • 综合账户
      • 营销增资-订单
      • 营销增资-金额
      • 营销增资查询
      • 综合账户余额查询
      • 综合账户流水查询
    • 增值服务
      • 获取增值营销产品列表
      • 获取增值营销产品明细
      • 获取分享链接
      • 推广结果列表查询
      • 推广结果详情
  • 慧徕店开放平台
    • 慧+SPI
      • 对接前准备
      • 商户同步
        • 开发配置
          • 请求域名
          • 加签&验签
        • 商户同步
        • 商户同步查询
        • 商户同步回调
      • 应用模块
        • 产品介绍
        • 开发配置
          • 请求域名
          • 加签&验签
        • 应用及增值服务查询
        • 商户应用注册及增值服务状态查询
        • 应用注册
        • 应用注册状态查询
        • 应用注册回调通知
        • 增值服务激活
        • 增值服务激活状态查询
        • 增值服务激活回调通知
      • 聚合支付SPI
        • 产品介绍
        • 开发配置
          • 签名方式
          • 验签方式
          • 公共参数
        • 支付API
        • 付款码支付
        • 扫码支付-官方码
        • JSAPI支付
        • 扫码支付-聚合码
        • 获取小程序支付信息
        • 订单查询
        • 交易结果通知
        • 退款
        • 退款查询
        • 关闭订单
        • 获取银联用户标识
    • 应用开发API
      • 验签说明
      • 应用支付
      • 门店信息同步
      • 门店增值服务激活
  • 支付广告
    • 微信小程序广告
    • 支付宝小程序广告
    • 支付宝H5广告
    • SDK资源文件
  • 数据字典
    • MCC
      • 搜索查询二级mcc
      • 获取一级mcc目录
      • 获取二级mcc目录
    • 地区码
      • 获取省
      • 获取市
      • 获取区县
    • 落地银行
      • 落地银行查询
      • 落地银行下级机构查询
    • 银行信息
      • 获取银行省信息-联行号
      • 获取银行市信息-联行号
      • 银行信息查询
      • 查询联行号信息
      • 根据银行卡号获取银行信息
    • 连锁品牌
      • 获取连锁品牌信息
  • 分账服务
    • 订单分账
      • 订单分账
      • 订单分账结果查询
      • 订单分账撤销
      • 订单分账对帐文件
    • 余额分账
      • 余额分账
      • 余额分账结果查询
      • 可分账余额查询
    • 分账配置
      • 分账图片上传
      • 分账商户配置
      • 分账商户配置结果查询
      • 分账商户配置结果通知
      • 分账商户移除
      • 快捷创建资金接收方
      • 修改分账比例与金额
      • 商圈信息查询
  • 数据模型
    • FileHeader
    • FileBody
    • StandardResponse
  1. 交易通知及对账

交易和退款结果通知

测试环境
https://xyf-server-test.postar.cn
测试环境
https://xyf-server-test.postar.cn
POST
/服务商的异步通知地址
使用场景:商户、ISV、应用开发者

简介#

请注意:邮驿付&星驿付后期会对返回参数保留扩展的权力,扩展方式为新增参数但不会删除参数,请商户在解析邮驿付&星驿付返回参数时要支持邮驿付&星驿付可能扩展参数这种情况。
返回值格式:所有异步通知返回参数均为 JSON 对象,且必须包含 "rspCod" 和 "rspMsg" 字段。
注意:返回的是对象,非字符串,rspMsg必须是一级参数。示例:{"rspCod":"","rspMsg":"success"}。
通知范围:正向交易成功、反向退款成功均可触发异步通知(早期机构如需反向退款通知,可联系运营后台配置)。
重试机制:若首次通知失败,平台将按照以下间隔重复推送,共推送 10 次:
10s → 15s → 30s → 1m → 5m → 10m → 30m → 1h → 1h → 3h
重要:商户在成功接收通知后,必须在响应中返回 "rspMsg":"success",否则平台会持续重试直至耗尽 10 次机会。
由于网络异常、系统波动等原因,可能会存在用户支付成功、但是商户侧未能成功接收到支付结果异步通知的情况。这将会导致商户获取不到支付结果、用户订单显示为未支付,用户体验差且易造成客诉。
为杜绝以上问题,需要商户同时接入支付结果异步通知与订单查询接口。当商户侧未收到支付结果异步通知时,必须调用 /yyfsevr/order/orderQuery(订单查询接口)查询订单的支付结果。

使用说明#

一、基本规则#

1.
报备要求:所有异步通知地址必须提前向运营人员报备,否则无效。
2.
运营后台配置(机构层的配置,只对当个机构生效):
默认异步通知地址:必配项,只有配置了该地址才会触发异步通知。
多异步通知地址列表:可选配置,可配置多个通知地址,用于根据业务规则推送不同地址。
3.
推送地址规则:
接口未上传异步通知地址/官品扫码刷卡 → 推送到默认异步通知地址。
接口上传了异步通知地址 → 将该地址与多异步通知地址列表进行路径匹配:
若匹配成功(地址已报备),则推送到接口上传的地址。
若匹配失败,则推送到默认异步通知地址。

二、环境差异说明#

测试环境#

服务商提供异步通知地址给运营报备后,可直接使用。
接口可不传地址(走默认)或显式上传自己的地址(需符合推送规则)。
后台配置的地址为服务商提供的原始地址。

生产环境#

服务商提供原始异步通知地址给运营报备后,运营会返回一个映射后的内网地址(仅星驿付服务器可访问,但对服务商而言等价于原始地址)。
后台配置的地址均为映射后的地址。
接口若显式上传异步通知地址,必须上传映射后的地址,否则无法匹配多地址列表,最终会走默认地址。
推送时,星驿付会将请求发往映射后的内网地址,但服务商收到的是对应原始地址的请求(由内部映射机制保证)。

三、配置与推送示例#

测试环境示例#

假设服务商有以下三个原始地址:
https://www.postar.cn/notify
https://pay.postar.cn/notify1
https://pay.postar.cn/notify2

场景1:默认地址为空,多地址列表为空#

无论接口是否上传地址,均不触发通知。

场景2:默认地址为 https://www.postar.cn/notify,多地址列表为空#

接口不上传地址 → 推送至 https://www.postar.cn/notify
接口上传 https://www.postar.cn/notify → 推送至 https://www.postar.cn/notify
接口上传其他地址(如 https://pay.postar.cn/notify1)→ 仍推送至 https://www.postar.cn/notify

场景3:默认地址为 https://www.postar.cn/notify,多地址列表包含 https://pay.postar.cn/notify1、https://pay.postar.cn/notify2#

接口不上传地址 → 推送至 https://www.postar.cn/notify
接口上传 https://www.postar.cn/notify → 推送至 https://www.postar.cn/notify
接口上传 https://pay.postar.cn/notify1 → 推送至 https://pay.postar.cn/notify1
接口上传 https://pay.postar.cn/notify2 → 推送至 https://pay.postar.cn/notify2
接口上传未报备地址(如 https://pay.postar.cn/notify3)→ 推送至 https://www.postar.cn/notify

生产环境示例(普通地址)#

原始地址与映射后地址对应关系:
https://www.postar.cn/notify → http://192.168.130.163:8009/yyfToIsvcs/notify
https://pay.postar.cn/notify1 → http://192.168.130.163:8009/yyfToIsvcspay/notify1
https://pay.postar.cn/notify2 → http://192.168.130.163:8009/yyfToIsvcspay/notify2

场景1:默认地址为空,多地址列表为空#

不触发通知。

场景2:默认地址为 http://192.168.130.163:8009/yyfToIsvcs/notify,多地址列表为空#

无论接口上传何种地址(或不上传),最终推送至原始地址 https://www.postar.cn/notify。

场景3:默认地址为 http://192.168.130.163:8009/yyfToIsvcs/notify,多地址列表包含 http://192.168.130.163:8009/yyfToIsvcspay/notify1、http://192.168.130.163:8009/yyfToIsvcspay/notify2#

接口不上传地址 → 推送至 https://www.postar.cn/notify
接口上传原始地址(如 https://www.postar.cn/notify、https://pay.postar.cn/notify1 等)→ 因与多地址列表不匹配,推送至 https://www.postar.cn/notify
接口上传映射后地址:
上传 http://192.168.130.163:8009/yyfToIsvcspay/notify1 → 推送至 https://pay.postar.cn/notify1
上传 http://192.168.130.163:8009/yyfToIsvcspay/notify2 → 推送至 https://pay.postar.cn/notify2
上传未在多列表中的映射地址 → 推送至默认地址

四、动态异步通知地址配置#

星驿付支持动态地址,即路径中包含可变部分(如订单号),例如:
https://www.postar.cn/notify/20260318101800
https://www.postar.cn/notify/20260318101801
注意事项:
如需使用动态地址,报备时必须告知运营,否则运营不会配置支持。
动态地址的匹配规则:只要接口上传的地址前缀与多地址列表中某条记录匹配(即域名+固定路径部分一致),则视为匹配成功,后续动态部分可任意变化。

生产环境动态地址示例#

原始地址与映射后地址对应关系:
https://www.postar.cn/notify → http://192.168.130.163:8009/yyfToIsvcs/notify(固定地址)
https://pay.postar.cn/ → http://192.168.130.163:8009/yyfToIsvcspay/(动态地址,前缀匹配)
https://cust.postar.cn/notify → http://192.168.130.163:8009/yyfToIsvcscust/notify(固定地址)

场景1:默认地址为空,多地址列表为空#

不触发通知。

场景2:默认地址为 http://192.168.130.163:8009/yyfToIsvcs/notify,多地址列表为空#

所有通知推送至 https://www.postar.cn/notify。

场景3:默认地址为 http://192.168.130.163:8009/yyfToIsvcs/notify,多地址列表包含 http://192.168.130.163:8009/yyfToIsvcspay/、http://192.168.130.163:8009/yyfToIsvcscust/notify#

接口不上传地址 → 推送至 https://www.postar.cn/notify
接口上传原始地址(如 https://www.postar.cn/notify、https://pay.postar.cn/notify1、https://cust.postar.cn/notify/xxx 等)→ 因与多地址列表(映射后地址)不匹配,推送至默认地址
接口上传映射后地址:
上传 http://192.168.130.163:8009/yyfToIsvcspay/notify1 → 推送至 https://pay.postar.cn/notify1(匹配前缀 http://192.168.130.163:8009/yyfToIsvcspay/)
上传 http://192.168.130.163:8009/yyfToIsvcspay/notify2 → 推送至 https://pay.postar.cn/notify2
上传 http://192.168.130.163:8009/yyfToIsvcspay/notify/20260318101800 → 推送至 https://pay.postar.cn/notify/20260318101800(动态部分任意)
上传 http://192.168.130.163:8009/yyfToIsvcscust/notify → 推送至 https://cust.postar.cn/notify
上传未匹配任何列表前缀的映射地址 → 推送至默认地址

五、总结#

核心原则:所有通知地址必须报备;生产环境使用映射地址进行配置和上传;动态地址需提前声明。
推送优先级:接口上传地址若匹配多地址列表 → 使用该地址;否则使用默认地址;默认地址未配置则无通知。
环境差异:测试环境使用原始地址,生产环境使用映射地址,但最终服务商收到的请求均为原始地址格式。

异步返回结果验签#

假设接收到通知的示例如下:
注意:以下示例报文仅供参考,实际返回的详细报文请以实际返回为准。
{"WX_AGET_SUBSIDY":"0","CUST_ID":"60000012628620","AGET_CUST_ID":"61000000582957","MEDIATYPE":"","POS_VOUCHERNO":"hi9njr","THREE_ORDER_NO":"2511042491723721","sign":"YZBvY7CN18RPi6wX/UeAhOO+/5F/zjgVRGbgfUoShnUui3xweD8TxPwT07+rYoz8q9XpsdQ7jTLBibK2rIevb6ojaq5FJcgbZke/IGOIJjLiEviQ7dGiU9xBQ3SUVyPEibxVHlMfzbo7PlxORjF2hvNlSXIB8o8vp0pvXVN99+PGZqyyksi1PDfPDiVJZdZX4uGy+S7//NXGaJTE2669g8FMSRadsc2wdaW/flMjb0kvI8BZhjvZVN8vohaBu1aoCufyUJqwcDtpvR9o5qA9txJQfJdwaddZdvzymJAZkWLmllXr4mzgMyU5bZefGTf8Fu2ydN7h00/PyMC32HxWXg==","CARD_TYPE":"01","OLD_FEE":"0","ORDER_NO":"20251104CChi9njr","ACTUAL_PAY_AMT":"2","T_ORDER_NO":"20251104CChi9njr","AGET_ID":"61000000582957","WX_CUST_SUBSIDY":"0","TERM_FEE":"0","ORDER_STATUS":"1","POS_PAY_WAY":"","CODE_AGE_AMT":"0","DISCOUNT_FEE":"0","BANK_CODE":"CCB_DEBIT","ORD_TYPE":"","SREF_NO":"","PAY_CHANNEL":"2","REFUND_DIS_AMT":"","DISCOUNT_FLAG":"0","LIMIT_FEE":"0","DEBT_STATE":"20251104","PAY_WAY":"8","OLD_TXAMT":"2","TRAN_TYPE_SER":"01","CODE_AMT":"0","TXAMT":"2","SUB_OPEN_ID":"oBY7i5WShNImAoHaKuY6zmu-gAc8","NETR_AMT":"2","OPEN_ID":"oBY7i5WShNImAoHaKuY6zmu-gAc8","MERCH_CONTRIBUTE":"0","CUST_AMT":"0","TRADING_IP":"39.144.92.50","MERCID":"856342004348074","CUST_FEE":"0","USTLDAT":"20251104","T_PAY_NO":"4200002861202511042403075191","ORDER_TIME":"20251104155010","OLD_ORDER_NO":"","BANK_NAME":"建设银行(借记卡)"}
第一步:在通知返回参数列表中,除去 sign 参数外,通知返回的参数均为待验签的参数。
第二步:将剩下参数进行字典排序,组成字符串,得到拼接串:
ACTUAL_PAY_AMT=2&AGET_CUST_ID=61000000582957&AGET_ID=61000000582957&BANK_CODE=CCB_DEBIT&BANK_NAME=建设银行(借记卡)&CARD_TYPE=01&CODE_AGE_AMT=0&CODE_AMT=0&CUST_AMT=0&CUST_FEE=0&CUST_ID=60000012628620&DEBT_STATE=20251104&DISCOUNT_FEE=0&DISCOUNT_FLAG=0&LIMIT_FEE=0&MEDIATYPE=&MERCH_CONTRIBUTE=0&MERCID=856342004348074&NETR_AMT=2&OLD_FEE=0&OLD_ORDER_NO=&OLD_TXAMT=2&OPEN_ID=oBY7i5WShNImAoHaKuY6zmu-gAc8&ORDER_NO=20251104CChi9njr&ORDER_STATUS=1&ORDER_TIME=20251104155010&ORD_TYPE=&PAY_CHANNEL=2&PAY_WAY=8&POS_PAY_WAY=&POS_VOUCHERNO=hi9njr&REFUND_DIS_AMT=&SREF_NO=&SUB_OPEN_ID=oBY7i5WShNImAoHaKuY6zmu-gAc8&TERM_FEE=0&THREE_ORDER_NO=2511042491723721&TRADING_IP=39.144.92.50&TRAN_TYPE_SER=01&TXAMT=2&T_ORDER_NO=20251104CChi9njr&T_PAY_NO=4200002861202511042403075191&USTLDAT=20251104&WX_AGET_SUBSIDY=0&WX_CUST_SUBSIDY=0
第三步:将拼接串使用 SHA256 编码,生成字符串1:
d8727d96bbb4bae07f5caef4d56c6c79b1d3e079c00bb55df8284cadd0351829
第四步:将签名参数(sign)使用 RSA 公钥解密为字符串2。
第五步:将字符串1和字符串2进行比较,相同即为验签通过,反之验签失败。

验签demo#

1.
JAVA
展开查看完整demo
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.ArrayUtils;
import sun.misc.BASE64Decoder;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.TreeMap;


/**
 * 公钥解签
 */
@Log4j2
public class RUtil {
    /**     
     *  验证sign
     *  plainText 返回数据报文
     *  publicKey 公钥
     * @return
     */
    public static boolean checkSign(String plainText,String publicKey) throws Exception {
        TreeMap<String ,Object> map = JSONObject.parseObject(plainText, new TypeReference<TreeMap<String, Object>>(){});
        System.out.println(JSONObject.toJSONString(map));
        StringBuilder sb = new StringBuilder();
        String sign = "";
        for (String key : map.keySet()){
            if("sign".equals(key)){
                sign =(String) map.get(key);
                continue;
            }
            if (map.get(key) != null) {
                sb.append(key).append("=").append(map.get(key)).append("&");
            }  
        }
        String res = sb.substring(0,sb.lastIndexOf("&"));
        log.info("拼接字符串:"+res);
        String sha256 = SHA256.getSHA256(res);
        log.info("拼接串的SHA256:"+sha256);
        String decodeSha256 = decrypt(publicKey,sign);
        log.info("sign解密的SHA256:"+decodeSha256);

        return sha256.equals(decodeSha256);
    }

    /**
     * 公钥解密过程
     *
     * @param key  公钥
     * @param data 密文数据
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    public static String decrypt(String key, String data) throws Exception {
        if (key == null) {
            throw new Exception("解密公钥为空, 请设置");
        }
        PublicKey publicKey = getPublicKey(key);
        byte[] cipherData = Base64Util.decode(data);
        Cipher cipher = null;
        try {
            // 使用默认RSA
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, publicKey);

            byte[] buf = (byte[]) null;
            for (int i = 0; i < cipherData.length; i += 256) {
                byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(cipherData, i, i + 256));
                if(buf == null) {
                    buf = doFinal;
                } else {
                    buf = addBytes(buf,doFinal);
                }
            }
            return new String(buf);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此解密算法");
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("解密公钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("密文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("密文数据已损坏");
        }
    }

    /**
     * 数组拼接
     * @param data1 数组1
     * @param data2 数组2
     * @return
     */
    public static byte[] addBytes(byte[] data1, byte[] data2) {
        byte[] data3 = new byte[data1.length + data2.length];
        System.arraycopy(data1, 0, data3, 0, data1.length);
        System.arraycopy(data2, 0, data3, data1.length, data2.length);
        return data3;
    }

    /**
     * 得到公钥
     *
     * @param key 密钥字符串(经过base64编码)
     * @throws Exception
     */
    public static PublicKey getPublicKey(String key) throws Exception {
        byte[] keyBytes;
        keyBytes = new BASE64Decoder().decodeBuffer(key);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }
}
其中用到的工具类Base64Util:

public class Base64Util {
    private static final char[] base64EncodeChars = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
    private static byte[] base64DecodeChars = new byte[] { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 };
    private Base64Util() {}

    /**
     * 将字节数组编码为字符串
     *
     * @param data
     */
    public static String encode(byte[] data) {
        StringBuffer sb = new StringBuffer();
        int len = data.length;
        int i = 0;
        int b1, b2, b3;
        while (i < len) {
            b1 = data[i++] & 0xff;
            if (i == len) {
                sb.append(base64EncodeChars[b1 >>> 2]);
                sb.append(base64EncodeChars[(b1 & 0x3) << 4]);
                sb.append("==");
                break;
            }
            b2 = data[i++] & 0xff;
            if (i == len) {
                sb.append(base64EncodeChars[b1 >>> 2]);
                sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
                sb.append(base64EncodeChars[(b2 & 0x0f) << 2]);
                sb.append("=");
                break;
            }
            b3 = data[i++] & 0xff;
            sb.append(base64EncodeChars[b1 >>> 2]);
            sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
            sb.append(base64EncodeChars[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]);
            sb.append(base64EncodeChars[b3 & 0x3f]);
        }
        return sb.toString();
    }


    /**
     * 将base64字符串解码为字节数组
     *
     * @param str
     */
    public static byte[] decode(String str) throws Exception{
        byte[] data = str.getBytes("GBK");
        int len = data.length;
        ByteArrayOutputStream buf = new ByteArrayOutputStream(len);
        int i = 0;
        int b1, b2, b3, b4;
        while (i < len) {


            /* b1 */
            do {
                b1 = base64DecodeChars[data[i++]];
            } while (i < len && b1 == -1);
            if (b1 == -1) {
                break;
            }


            /* b2 */
            do {
                b2 = base64DecodeChars[data[i++]];
            } while (i < len && b2 == -1);
            if (b2 == -1) {
                break;
            }
            buf.write((int) ((b1 << 2) | ((b2 & 0x30) >>> 4)));


            /* b3 */
            do {
                b3 = data[i++];
                if (b3 == 61) {
                    return buf.toByteArray();
                }
                b3 = base64DecodeChars[b3];
            } while (i < len && b3 == -1);
            if (b3 == -1) {
                break;
            }
            buf.write((int) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2)));


            /* b4 */
            do {
                b4 = data[i++];
                if (b4 == 61) {
                    return buf.toByteArray();
                }
                b4 = base64DecodeChars[b4];
            } while (i < len && b4 == -1);
            if (b4 == -1) {
                break;
            }
            buf.write((int) (((b3 & 0x03) << 6) | b4));
        }
        return buf.toByteArray();
    }
}
SHA256.java

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class SHA256 {
	
	/**
	 * 利用java原生的类实现SHA256加密
	 * 
	 * @param str 加密后的报文
	 * @return
	 */
	public static String getSHA256(String str) {
		MessageDigest messageDigest;
		String encodestr = "";
		try {
			messageDigest = MessageDigest.getInstance("SHA-256");
			messageDigest.update(str.getBytes("UTF-8"));
			encodestr = byte2Hex(messageDigest.digest());
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return encodestr;
	}
 
	/**
	 * 将byte转为16进制
	 * 
	 * @param bytes
	 * @return
	 */
	private static String byte2Hex(byte[] bytes) {
		StringBuffer stringBuffer = new StringBuffer();
		String temp = null;
		for (int i = 0; i < bytes.length; i++) {
			temp = Integer.toHexString(bytes[i] & 0xFF);
			if (temp.length() == 1) {
				stringBuffer.append("0");
			}
			stringBuffer.append(temp);
		}
		return stringBuffer.toString();
	}
}    

收不到异步通知排查方案#

若商户未收到异步通知,请按以下顺序排查:
1.
异步通知地址是否已报备
所有地址(包括默认地址和多地址列表中的地址)必须提前向运营报备,可向运营咨询目前的通知地址配置。
2.
接口上传的地址是否正确
测试环境:上传原始地址。
生产环境:如需显式上传,必须上传映射后的内网地址,否则无法匹配多地址列表,最终推送至默认地址。
若不传地址,确保默认地址已配置且报备。
3.
地址的可访问性
异步通知地址必须外网可正常访问(生产环境映射地址由星驿付内部网络访问,商户侧无需关心其外网可达性,但商户的原始地址必须能接收来自星驿付服务器的回调)。
4.
地址权限校验
若商户的异步通知地址设有IP白名单、HTTP Basic认证等访问控制,需确保星驿付服务器IP在允许范围内,或临时关闭校验进行测试。
详细规则请参考上文 【使用说明】 部分。

请求参数

Header 参数

Body 参数application/json

示例

返回响应

🟢200成功
application/json
Body

🟢200成功
请求示例请求示例
Java
Shell
C#
PHP
响应示例响应示例
200 - 成功 - 示例 1
{
    "rspCod": "000000",
    "rspMsg": "success"
}
修改于 2026-04-01 01:26:45
上一页
交易对账文件-ISV
下一页
交易手续费通知
Built with