一、文档概述
1.1 文档目的
本文档用于规范我方支付系统与第三方超市系统的支付码对接流程,明确核心业务逻辑、数据库表设计、接口定义、加密签名规则及对账机制,确保对接过程合规、安全、高效,支撑线下超市扫码支付、退款、对账等全场景业务落地。
1.2 业务场景
我方系统为用户提供支付码,用户在第三方超市消费时,出示我方支付码,超市收银系统扫码后调用我方接口完成扣款,我方系统负责用户余额校验、扣款、交易记录留存及退款、对账等操作,不介入超市自身订单生成、商品管理等业务。
1.3 角色定义
我方系统:支付能力提供方,负责用户账户管理、支付码生成与核验、交易风控、扣款/退款、流水记录、对账清算,提供接口供超市系统调用。
第三方超市系统:收单侧商户系统,负责商品订单生成、扫码读取支付码、发起支付/退款请求、接收支付结果、完成对账,调用我方接口实现支付功能。
用户:我方系统注册用户,通过我方系统生成支付码,在超市完成消费支付。
二、数据库表设计(必选+辅助)
核心原则:我方系统不存储超市订单详情,仅留存支付相关流水记录,确保交易可追溯、可对账、可退款,所有金额字段均以“分”为单位存储,避免浮点精度误差。
2.1 必选表(核心表,缺一不可)
2.1.1 支付流水表(payment_trade)
用途:存储每一笔超市扫码支付的完整交易记录,作为支付、查询、对账、退款的核心依据。
| 字段名 | 字段类型 | 是否必填 | 字段说明 |
|---|---|---|---|
| id | bigint | 是 | 主键,我方系统唯一支付流水号(自增/雪花算法生成) |
| out_trade_no | varchar(64) | 是 | 第三方超市系统的订单号,唯一标识超市侧订单,用于关联对账、查询、退款 |
| auth_code | varchar(32) | 是 | 用户出示的支付码(脱敏存储,如仅存后4位,前几位用*代替),用于核验支付有效性 |
| user_id | varchar(64) | 是 | 我方系统用户ID,关联用户账户,明确扣款对象 |
| total_amount | int | 是 | 交易总金额(单位:分),避免浮点误差 |
| status | varchar(16) | 是 | 交易状态:WAIT(支付中)、PAID(支付成功)、FAIL(支付失败)、CANCEL(交易取消)、REFUND(全额退款) |
| pay_time | datetime | 否 | 支付成功时间,支付失败则为空 |
| refund_amount | int | 是 | 累计已退款金额(单位:分),初始值为0,退款后累计更新 |
| refund_status | varchar(16) | 是 | 退款状态:NO_REFUND(无退款)、REFUNDING(退款中)、REFUNDED(全额退款)、PART_REFUND(部分退款) |
| channel | varchar(32) | 是 | 渠道标识,如“SUPERMARKET_XX”(XX为超市编码),区分不同合作超市 |
| store_id | varchar(32) | 否 | 超市门店编号,可选,用于超市分门店对账 |
| terminal_id | varchar(32) | 否 | 超市收银机终端号,可选,用于定位具体收银设备交易 |
| subject | varchar(128) | 是 | 交易标题,默认“超市消费”,可由超市传入商品描述 |
| create_time | datetime | 是 | 交易创建时间(超市发起支付请求的时间) |
| update_time | datetime | 是 | 交易状态更新时间,每次状态变更自动更新 |
| remark | varchar(256) | 否 | 备注,存储支付失败原因、异常说明等 |
2.1.2 退款流水表(payment_refund)
用途:存储每一笔退款交易记录,关联支付流水,支撑退款查询、对账及财务凭证留存。
| 字段名 | 字段类型 | 是否必填 | 字段说明 |
|---|---|---|---|
| id | bigint | 是 | 主键,我方系统唯一退款流水号(自增/雪花算法生成) |
| refund_no | varchar(64) | 是 | 我方系统生成的退款单号,唯一标识一笔退款 |
| trade_id | bigint | 是 | 关联支付流水表的主键(id),绑定对应的支付交易 |
| out_trade_no | varchar(64) | 是 | 第三方超市的原订单号,与支付流水表一致,用于对账 |
| out_refund_no | varchar(64) | 是 | 第三方超市系统的退款单号,唯一标识超市侧退款请求 |
| refund_amount | int | 是 | 退款金额(单位:分),可支持部分退款(不超过原交易金额) |
| status | varchar(16) | 是 | 退款状态:PROCESSING(退款中)、SUCCESS(退款成功)、FAIL(退款失败) |
| refund_time | datetime | 否 | 退款成功时间,退款失败则为空 |
| reason | varchar(256) | 否 | 退款原因,由超市传入(如“用户退货”“多收金额”) |
| create_time | datetime | 是 | 退款请求创建时间 |
| update_time | datetime | 是 | 退款状态更新时间,每次状态变更自动更新 |
2.2 辅助表(按需可选,提升业务完整性)
2.2.1 用户账户表(user_account)
用途:存储我方系统用户的账户信息及余额,用于扣款、退款操作(若我方系统已有用户体系,可复用现有表)。
| 字段名 | 字段类型 | 是否必填 | 字段说明 |
|---|---|---|---|
| user_id | varchar(64) | 是 | 主键,我方系统用户唯一ID,与支付流水表user_id关联 |
| balance | int | 是 | 用户账户余额(单位:分),初始值为0,支付扣款、退款到账时更新 |
| status | varchar(16) | 是 | 账户状态:NORMAL(正常)、FROZEN(冻结)、DISABLED(禁用),冻结/禁用时无法支付 |
| create_time | datetime | 是 | 账户创建时间 |
| update_time | datetime | 是 | 账户信息更新时间(如余额、状态变更) |
2.2.2 商户信息表(merchant_info)
用途:存储合作超市的商户信息,用于接口鉴权、渠道区分、对账等。
| 字段名 | 字段类型 | 是否必填 | 字段说明 |
|---|---|---|---|
| merchant_id | varchar(32) | 是 | 主键,我方系统分配给超市的唯一商户号,用于接口鉴权 |
| merchant_name | varchar(64) | 是 | 超市名称,用于用户账单展示、对账备注 |
| app_secret | varchar(128) | 是 | 商户密钥,用于接口签名、鉴权,需加密存储 |
| channel | varchar(32) | 是 | 渠道标识,与支付流水表channel一致 |
| callback_url | varchar(256) | 是 | 超市提供的支付结果异步回调地址,我方系统推送交易终态 |
| status | varchar(16) | 是 | 商户状态:NORMAL(正常)、DISABLED(禁用),禁用时无法调用接口 |
2.2.3 对账文件记录表(reconciliation_file)
用途:存储每日对账文件的生成信息,用于对账文件管理、查询、追溯。
| 字段名 | 字段类型 | 是否必填 | 字段说明 |
|---|---|---|---|
| id | bigint | 是 | 主键,自增 |
| merchant_id | varchar(32) | 是 | 关联商户信息表,对应具体超市 |
| reconciliation_date | date | 是 | 对账日期(T+1,即前一日交易) |
| file_type | varchar(16) | 是 | 文件类型:DETAIL(明细文件)、SUMMARY(汇总文件) |
| file_url | varchar(256) | 是 | 对账文件下载地址(临时有效,如24小时) |
| file_md5 | varchar(64) | 是 | 文件MD5校验值,用于超市校验文件完整性 |
| create_time | datetime | 是 | 文件生成时间(每日凌晨生成前一日文件) |
三、核心业务流程
整体流程围绕“支付-查询-退款-对账”闭环展开,核心为线下被扫支付,不涉及我方系统订单生成,仅负责支付相关操作。
3.1 支付流程(核心流程)
用户在我方系统生成一次性支付码(内部流程,无需对外接口,支付码需设置有效期,如30秒);
用户在超市收银台出示支付码,超市收银系统通过扫码枪读取支付码码值(auth_code);
超市系统生成自身订单(out_trade_no),调用我方“付款码支付接口”,传入核心参数并通过appsecret完成签名;
我方系统接收请求后,先进行接口鉴权(验证商户号有效性)及签名验证(通过appsecret校验签名合法性);
我方系统核验支付码有效性(是否过期、是否挂失、是否绑定有效用户);
核验通过后,查询用户账户余额,判断余额是否充足;
余额充足:扣减用户账户余额,在支付流水表插入一条记录(status=PAID,pay_time=当前时间),返回支付成功结果;
余额不足/支付码无效:在支付流水表插入一条记录(status=FAIL,remark=失败原因),返回支付失败结果;
我方系统将支付终态(成功/失败)主动推送至超市提供的回调地址(异步回调),作为同步接口的兜底机制;
超市系统接收支付结果/回调通知,完成收银闭环,向用户展示支付结果。
3.2 支付结果查询流程(异常处理流程)
当超市调用“付款码支付接口”超时、返回“支付中”(WAIT),或收银机断网、设备故障时,超市系统调用我方“支付结果查询接口”,传入参数并通过appsecret签名;
我方系统接收请求,完成签名验证及商户鉴权后,通过“超市订单号(out_trade_no)”或“我方支付流水号(id)”查询支付流水表;
返回该笔交易的最终状态(PAID/FAIL)、交易金额、支付时间等信息;
超市系统根据查询结果,更新自身订单状态,避免单边账(用户扣款成功、超市未确认)。
3.3 退款流程(售后流程)
用户退货/超市多收金额,超市系统生成自身退款订单(out_refund_no);
超市系统调用我方“退款接口”,传入原支付订单号、退款金额、退款原因等参数并通过appsecret签名;
我方系统接收请求,进行商户鉴权、签名验证,查询原支付流水记录(确认交易已成功,且未全额退款);
验证通过后,在退款流水表插入一条记录(status=PROCESSING);
我方系统将退款金额原路退回用户账户,更新用户账户余额;
更新退款流水表状态为SUCCESS(退款成功),更新支付流水表的refund_amount(累计退款金额)和refund_status(退款状态);
返回退款结果给超市系统,同时可通过异步回调推送退款终态;
若退款失败(如用户账户异常),更新退款流水表状态为FAIL,返回失败原因,超市可重新发起退款。
3.4 对账流程(财务闭环流程)
每日凌晨,我方系统生成前一日(T-1)的对账文件(明细文件+汇总文件),明细文件包含所有支付、退款交易详情,汇总文件包含交易总额、退款总额、净收入等;
我方系统将对账文件存储至安全服务器,生成下载地址,同时在对账文件记录表插入一条记录;
超市系统通过“对账文件下载接口”,传入对账日期、文件类型并通过appsecret签名,获取对账文件下载地址;
超市系统下载文件,通过MD5校验值验证文件完整性,将文件内容与自身订单记录、收银记录进行对账;
若对账一致,双方确认当日交易无误;若对账不一致,通过支付流水表、退款流水表排查差异(如单边账、金额不符),进行差错处理。
四、接口设计(必选+辅助)
核心原则:所有接口均采用HTTPS协议传输,无需Token鉴权,仅通过「商户号+appsecret签名」实现接口安全校验;接口请求/响应均采用JSON格式,编码为UTF-8;所有金额参数均以“分”为单位,日期时间格式统一为“yyyy-MM-dd HH
ss”;接口超时时间统一设置为30秒,超市系统需做好超时重试机制(建议重试2次,间隔1秒)。
4.1 接口通用规则
4.1.1 通用请求参数(所有接口必传)
| 参数名 | 参数类型 | 是否必填 | 参数说明 |
|---|---|---|---|
| merchant_id | String | 是 | 我方系统分配给超市的唯一商户号(对应商户信息表merchant_id) |
| timestamp | Long | 是 | 当前时间戳(毫秒级),用于防重放,请求时间与我方系统时间差不超过5分钟 |
| nonce_str | String | 是 | 随机字符串(长度16-32位),由超市系统生成,用于防重放、防篡改 |
| sign | String | 是 | 签名值,通过appsecret按照指定规则生成(详见4.1.2签名规则) |
4.1.2 签名规则(所有接口统一)
签名目的:验证请求数据的完整性和合法性,防止数据被篡改、伪造请求,仅通过appsecret实现,无需额外Token。
参数排序:将请求中所有非空参数(含通用请求参数+接口专属参数)按照参数名ASCII码升序排列(字典序);
拼接字符串:将排序后的参数以“key=value”形式拼接,用“\&”连接,例如:merchant_id=123\&nonce_str=abc\×tamp=1690000000000\&out_trade_no=CS20260414001;
补充密钥:在拼接字符串末尾拼接“\&appsecret=XXX”(XXX为商户信息表中的app_secret,需加密存储,双方严格保密);
生成签名:将上述拼接好的字符串进行MD5加密(32位小写),得到的结果即为sign值;
验证规则:我方系统接收请求后,按相同规则重新计算签名,与请求中的sign值对比,不一致则拒绝处理请求。
4.1.3 通用响应参数(所有接口统一返回)
| 参数名 | 参数类型 | 是否必返 | 参数说明 |
|---|---|---|---|
| code | String | 是 | 响应码:200-请求成功;非200-请求失败(具体失败码见各接口说明) |
| message | String | 是 | 响应信息:成功返回“success”,失败返回具体失败原因(如“签名验证失败”“支付码已过期”) |
| data | Object | 否 | 响应数据:请求成功时返回接口专属数据,失败时为null |
| sign | String | 是 | 响应签名:我方系统用appsecret按4.1.2规则生成,超市系统可验证响应数据完整性 |
4.2 必选接口(核心接口,支撑支付、退款、对账核心流程)
4.2.1 付款码支付接口(核心支付接口)
接口用途:超市系统扫码获取支付码后,调用该接口完成用户扣款,是核心交易接口。
| 接口信息 | 请求方式:POST;接口地址:/api/v1/payment/scan-pay;请求格式:JSON;超时时间:30秒 | |||
|---|---|---|---|---|
| 专属请求参数 | 参数名 | 参数类型 | 是否必填 | 参数说明 |
| out_trade_no | String | 是 | 超市系统订单号(对应支付流水表out_trade_no),唯一,长度不超过64位 | |
| auth_code | String | 是 | 用户支付码码值(对应支付流水表auth_code),无需脱敏,原样传入 | |
| total_amount | Integer | 是 | 交易总金额(单位:分),大于0,对应支付流水表total_amount | |
| subject | String | 是 | 交易标题,默认“超市消费”,可传入商品描述,长度不超过128位(对应支付流水表subject) | |
| store_id | String | 否 | 超市门店编号(对应支付流水表store_id),可选,用于分门店对账 | |
| terminal_id | String | 否 | 收银机终端号(对应支付流水表terminal_id),可选,用于定位具体收银设备 |
响应数据(data)
| 参数名 | 参数类型 | 说明 |
|---|---|---|
| trade_id | Long | 我方系统支付流水号(对应支付流水表id) |
| out_trade_no | String | 超市系统订单号(与请求参数一致) |
| status | String | 交易状态:PAID(支付成功)、FAIL(支付失败) |
| pay_time | String | 支付成功时间(status=PAID时返回,否则为null) |
| balance | Integer | 用户支付后账户余额(单位:分,status=PAID时返回) |
常见失败响应码
400-参数缺失/格式错误;401-商户号无效/商户已禁用;403-签名验证失败;405-支付码已过期/无效/挂失;406-用户账户冻结/禁用;407-用户余额不足;500-我方系统异常
4.2.2 支付结果查询接口(异常处理接口)
接口用途:当支付接口超时、返回支付中,或设备故障时,超市系统调用该接口查询交易最终状态,避免单边账。
| 接口信息 | 请求方式:GET;接口地址:/api/v1/payment/query;请求格式:JSON;超时时间:30秒 | |||
|---|---|---|---|---|
| 专属请求参数(二选一) | 参数名 | 参数类型 | 是否必填 | 参数说明 |
| out_trade_no | String | 二选一 | 超市系统订单号(对应支付流水表out_trade_no) | |
| trade_id | Long | 二选一 | 我方系统支付流水号(对应支付流水表id) |
响应数据(data)
| 参数名 | 参数类型 | 说明 |
|---|---|---|
| trade_id | Long | 我方系统支付流水号 |
| out_trade_no | String | 超市系统订单号 |
| total_amount | Integer | 交易总金额(单位:分) |
| status | String | 交易最终状态:PAID(支付成功)、FAIL(支付失败) |
| pay_time | String | 支付成功时间(status=PAID时返回,否则为null) |
| refund_status | String | 退款状态(对应支付流水表refund_status) |
| remark | String | 备注(支付失败时返回失败原因) |
常见失败响应码
400-参数缺失/格式错误(未传二选一参数);401-商户号无效/禁用;403-签名验证失败;404-交易不存在;500-我方系统异常
4.2.3 退款接口(售后接口)
接口用途:超市系统发起退款请求(用户退货、多收金额等场景),调用该接口完成退款,原路退回用户账户。
| 接口信息 | 请求方式:POST;接口地址:/api/v1/payment/refund;请求格式:JSON;超时时间:30秒 | |||
|---|---|---|---|---|
| 专属请求参数 | 参数名 | 参数类型 | 是否必填 | 参数说明 |
| out_trade_no | String | 是 | 原支付对应的超市系统订单号(对应支付流水表out_trade_no) | |
| out_refund_no | String | 是 | 超市系统退款单号(对应退款流水表out_refund_no),唯一,长度不超过64位 | |
| refund_amount | Integer | 是 | 退款金额(单位:分),大于0,不超过原交易总金额(对应退款流水表refund_amount) | |
| reason | String | 否 | 退款原因(如“用户退货”“多收金额”),长度不超过256位(对应退款流水表reason) |
响应数据(data)
| 参数名 | 参数类型 | 说明 |
|---|---|---|
| refund_no | String | 我方系统退款单号(对应退款流水表refund_no) |
| out_refund_no | String | 超市系统退款单号(与请求参数一致) |
| out_trade_no | String | 原支付对应的超市系统订单号 |
| refund_amount | Integer | 退款金额(单位:分) |
| status | String | 退款状态:SUCCESS(退款成功)、FAIL(退款失败)、PROCESSING(退款中) |
| refund_time | String | 退款成功时间(status=SUCCESS时返回,否则为null) |
常见失败响应码
400-参数缺失/格式错误;401-商户号无效/禁用;403-签名验证失败;404-原交易不存在;405-原交易未支付/已全额退款;406-退款金额超过原交易金额;407-用户账户异常;500-我方系统异常
4.2.4 对账文件下载接口(财务闭环接口)
接口用途:超市系统每日调用该接口,下载前一日的对账文件(明细+汇总),完成对账操作。
| 接口信息 | 请求方式:GET;接口地址:/api/v1/reconciliation/download;请求格式:JSON;超时时间:30秒 | |||
|---|---|---|---|---|
| 专属请求参数 | 参数名 | 参数类型 | 是否必填 | 参数说明 |
| reconciliation_date | String | 是 | 对账日期,格式“yyyy-MM-dd”,仅支持下载前一日(T-1)的文件(对应对账文件记录表reconciliation_date) | |
| file_type | String | 是 | 文件类型:DETAIL(明细文件)、SUMMARY(汇总文件)(对应对账文件记录表file_type) |
响应数据(data)
| 参数名 | 参数类型 | 说明 |
|---|---|---|
| file_url | String | 对账文件下载地址(临时有效,24小时内有效,对应对账文件记录表file_url) |
| file_md5 | String | 文件MD5校验值(用于验证文件完整性,对应对账文件记录表file_md5) |
| file_size |
(注:文档部分内容可能由 AI 生成)
