data-model.md 12 KB

LINE Pay Data Model

Relationship Overview

pos_store 1 ── N pos_store_line_pay (immutable credential versions)
                       │
                       └── current_store_id UNIQUE: one current version/store

pos_order (`order_source=USER/MERCHANT`)
          1 ── N pos_order_line_payment (append-only attempts)
                       │
                       ├── credential_id -> exact credential version
                       ├── payment_mode -> ONLINE/OFFLINE gateway dispatch
                       ├── active_dd_id UNIQUE: one blocking attempt/order
                       └── 1 ── 0..1 pos_order_line_refund

payment_gateway_log ── optional payment_id/refund_id/credential_id references

不声明物理外键,保持与现有业务表部署习惯一致;Service 在事务内保证逻辑引用存在。

1. pos_store_line_pay

不可变门店凭证版本。Secret 按用户决定以明文保存。普通列表仅返回 hasCredential,平台详情接口可返回当前版本 Secret。

Column Type Null Notes
id BIGINT no auto increment
store_id BIGINT no pos_store.id
credential_version INT no store 内从 1 递增
channel_id VARCHAR(50) ASCII no LINE Channel ID;业务层按字符串处理
channel_secret VARCHAR(255) no 明文
environment VARCHAR(16) no SANDBOX/PRODUCTION
credential_status VARCHAR(32) no VERIFIED/VERIFY_FAILED;仅 VERIFIED 可成为 current
is_enabled TINYINT no current 版本的新支付开关
current_store_id BIGINT yes current 时等于 store_id,历史版本 NULL
verify_return_code VARCHAR(8) yes 最后探测结果码
verify_return_message VARCHAR(255) yes 摘要
verified_time DATETIME yes 验证通过时间
create_time/update_time DATETIME no 审计时间
create_by/update_by VARCHAR(64) yes 管理员

Constraints and indexes:

  • UNIQUE(store_id, credential_version)
  • UNIQUE(current_store_id);MySQL 允许多个 NULL。
  • INDEX(store_id, create_time)
  • 当前版本切换使用事务:锁定当前行/版本,插入新行,再把旧 current_store_id 置 NULL,新行置 storeId。
  • 相同 Channel ID、Secret 和 environment 重复保存时直接返回现有当前版本,不创建重复行。

2. pos_order_line_payment

每次真正发起的 LINE Request 对应一行,历史永不覆盖。行内更新仅用于同一尝试的状态推进和补充网关标识。

Column Type Null Notes
id BIGINT no paymentId
dd_id VARCHAR(64) no pos_order.dd_id
line_order_id VARCHAR(100) ASCII BINARY no Request 前生成,永久唯一
transaction_id VARCHAR(32) ASCII BINARY yes Request 成功后获得,禁止 Long
credential_id BIGINT no 发起时选中的不可变凭证版本
store_id BIGINT no 门店快照
amount INT no TWD 整数金额
captured_amount INT yes LINE 实际扣款金额;正常支付等于 amount,金额异常时作为全额退款金额与恢复证据
currency CHAR(3) ASCII no 首版 TWD
payment_mode VARCHAR(16) ASCII no ONLINE/OFFLINE;默认 ONLINE,新尝试显式写入
payment_url_web VARCHAR(1000) yes Sandbox/浏览器跳转 URL
payment_url_app VARCHAR(1000) yes 生产 LINE App URL,可空
payment_provider VARCHAR(16) yes 网关原值;进入 PAID 时必须非空,已知 TSP/EPI 但不封闭枚举
status VARCHAR(40) no 下方状态机
active_dd_id VARCHAR(64) yes 阻断新尝试时等于 ddId,明确终止后 NULL
version BIGINT no CAS,从 0 递增
next_reconcile_at DATETIME yes 下次扫描
reconcile_deadline DATETIME yes 当前恢复阶段截止
reconcile_count INT no 默认 0
lease_owner VARCHAR(64) yes 行租约节点/批次 ID
lease_until DATETIME yes 租约过期时间
status_changed_at DATETIME no 状态变化时间
auth_completed_time DATETIME yes Check 0110 时间
pay_time DATETIME yes Capture 时间
create_time/update_time DATETIME no 审计时间

Constraints and indexes:

  • UNIQUE(line_order_id)
  • UNIQUE(transaction_id);允许多个 NULL。
  • UNIQUE(active_dd_id);允许历史多 NULL。
  • INDEX(dd_id, create_time, id)
  • INDEX(status, next_reconcile_at, id)
  • INDEX(credential_id, create_time)
  • INDEX(payment_mode, status, next_reconcile_at, id)
  • 所有状态更新条件至少包含 id + expected status + version,领取租约还需 lease_until IS NULL OR lease_until < NOW()
  • 既有支付行通过 DDL 默认值保持 ONLINE;新建 Online/Offline 尝试必须显式写模式,禁止根据 URL、交易号或状态猜测。

Payment states:

REQUESTING
  ├─ WAITING_AUTH
  ├─ REQUEST_UNKNOWN
  └─ FAILED (only explicit no-side-effect failure; releases active key)

WAITING_AUTH / REQUEST_UNKNOWN
  ├─ READY_CONFIRM
  ├─ PAID
  ├─ CANCELLED_OR_EXPIRED (releases active key)
  ├─ FAILED (releases active key)
  └─ MANUAL_REVIEW

READY_CONFIRM
  ├─ CONFIRMING
  └─ AUTH_DONE_ORDER_CANCELLED

CONFIRMING
  ├─ PAID
  └─ CONFIRM_UNKNOWN

CONFIRM_UNKNOWN
  ├─ PAID
  └─ MANUAL_REVIEW

OFFLINE REQUESTING
  ├─ WAITING_AUTH (1145/1169;AUTH_READY)
  ├─ PAID (0000 且资金事实匹配)
  ├─ REQUEST_UNKNOWN (超时/响应丢失/重复待核实)
  ├─ AMOUNT_MISMATCH (真实付款但金额不一致)
  └─ FAILED (明确无副作用失败;释放 active key)

OFFLINE WAITING_AUTH / REQUEST_UNKNOWN
  ├─ PAID (Check COMPLETE 且事实匹配)
  ├─ CANCELLED_OR_EXPIRED (Check CANCEL;释放 active key)
  ├─ FAILED (Check FAIL;释放 active key)
  ├─ AMOUNT_MISMATCH
  └─ MANUAL_REVIEW (截止仍未知或必要事实缺失)

PAID、UNKNOWN、AMOUNT_MISMATCHMANUAL_REVIEW 不释放 active_dd_id。Offline 金额不一致退款成功后原订单仍关闭支付,商家必须创建新订单。旧尝试迟到证实 PAID 时照实记录,并阻断/退款,不覆盖当前尝试。

3. pos_order_line_refund

首版只支持每笔已付款交易一次全额退款。

Column Type Null Notes
id BIGINT no refund id
payment_id BIGINT no payment,唯一
dd_id VARCHAR(64) no 查询快照
credential_id BIGINT no 默认 payment 的凭证版本
transaction_id VARCHAR(32) ASCII BINARY no 原支付交易号
refund_transaction_id VARCHAR(32) ASCII BINARY yes LINE refund transaction
amount INT no 本地全额审计;调用时不传 refundAmount
status VARCHAR(32) no CREATED/PROCESSING/UNKNOWN/RETRY_WAIT/REFUNDED/FAILED/MANUAL_REVIEW
source VARCHAR(32) no USER_CANCEL/STORE_CANCEL/ADMIN/TASK
version BIGINT no CAS
next_reconcile_at/reconcile_deadline DATETIME yes 恢复调度
reconcile_count INT no 默认 0
lease_owner/lease_until VARCHAR(64)/DATETIME yes 行租约
refund_time DATETIME yes 确证全退时间
create_time/update_time DATETIME no 审计时间

Constraints and indexes:

  • UNIQUE(payment_id)
  • UNIQUE(refund_transaction_id);允许多个 NULL。
  • INDEX(status, next_reconcile_at, id)
  • INDEX(dd_id, create_time, id)
  • 退款端点不在退款行重复保存模式;通过不可变 payment_id 读取支付行 payment_mode,Online 使用 transaction_id,Offline 使用 line_order_id

Refund transitions:

CREATED / RETRY_WAIT --CAS claim--> PROCESSING
PROCESSING ├─ REFUNDED
           ├─ UNKNOWN
           ├─ RETRY_WAIT (only documented explicit retryable result)
           └─ FAILED (explicit terminal failure)
UNKNOWN    ├─ REFUNDED (Retrieve refundList proves it)
           └─ MANUAL_REVIEW (deadline)

4. payment_gateway_log

仅记录 LINE Pay;不迁移 OMG。日志采用追加写,不用于决定当前支付状态。

Column Type Null Notes
id BIGINT no auto increment
correlation_id VARCHAR(64) ASCII no 同次 request/response 关联
payment_id/refund_id/credential_id BIGINT yes 结构化关联
store_id BIGINT yes 凭证验证也可关联门店
dd_id VARCHAR(64) yes 业务订单
gateway_order_id VARCHAR(100) ASCII BINARY yes line_order_id
transaction_id VARCHAR(32) ASCII BINARY yes 网关交易号
action VARCHAR(32) no CREDENTIAL_VERIFY/REQUEST/CHECK/CONFIRM/RETRIEVE/REFUND/REDIRECT
direction VARCHAR(8) no REQUEST/RESPONSE/EVENT
source VARCHAR(32) no APP/CALLBACK/TASK/ADMIN/CANCEL
http_status INT yes HTTP status
return_code VARCHAR(8) yes LINE code
return_message VARCHAR(255) yes LINE message
success TINYINT no transport/business action summary
duration_ms BIGINT yes elapsed
payload LONGTEXT yes 脱敏 JSON/body/error summary;Offline REQUEST 不得包含原始 oneTimeKey
create_time DATETIME no append time

Indexes:

  • (payment_id, create_time)
  • (refund_id, create_time)
  • (gateway_order_id, create_time)
  • (transaction_id, create_time)
  • (correlation_id)
  • (store_id, create_time)

5. Order integration

新增字段:

Column Type Null Notes
order_source VARCHAR(16) ASCII no USER/MERCHANT;默认 USER,仅商家创建入口显式写 MERCHANT

兼容与写入规则:

  • 现有行和用户端新订单使用数据库默认 USER,不得根据 userId、创建时间、payType 或入口日志推断/回填。
  • /system/orderShOprate/createOrder 创建的每个子订单显式写 MERCHANT;父订单不作为 Offline 支付归属事实。
  • Offline 扫码必须同时满足 order_source=MERCHANT、当前 token 商家归属、单门店、未支付、未取消、未完成、金额大于零和 pay_type="3"。普通/夜市商家除校验 sh_id=userId 外,还必须校验订单 md_id 对应 pos_store.user_id=userId,禁止组合本人 shId 与其他商家的 mdId 使用他店凭证。

继续使用现有字段:

  • pay_type="3": 新 LINE 订单选择;历史同值不自动视为 LINE,必须存在匹配 Line payment。
  • pay_status=0/1/2: 未付/已付/已确认全额退款。
  • pay_url: 当前活跃尝试的 web URL,可作为兼容展示;支付选择以 Line payment 为准。
  • state=4: 已取消;仍允许记录迟到 PAID,随后创建退款 intent。

6. Query selection invariants

  1. App 以 ddId 查询时先读取订单 pay_status
  2. 未付款优先唯一 active_dd_id=ddId;没有则返回最新终态尝试。
  3. 已付款选唯一尚未证实全退的 PAID;已退款选带 REFUNDED 的付款/退款组合。
  4. 出现多条未全退 PAID 不自动任选,返回 MANUAL_REVIEW
  5. 回跳只按唯一 line_order_id 定位,transactionId 存在时必须匹配。
  6. 定时/平台动作只按领取到的 paymentId/refundId 操作。
  7. 商家 Offline 状态查询先验证 order_source=MERCHANT 和当前商家归属,再按唯一活跃尝试或最近终态选择 payment_mode=OFFLINE 的记录;不得返回其他商家或 Online 尝试的敏感上下文。

7. Migration and compatibility

  • updatesql/sql.md 追加 pos_order.order_source VARCHAR(16) NOT NULL DEFAULT 'USER'
  • updatesql/sql.md 追加 pos_order_line_payment.payment_mode VARCHAR(16) NOT NULL DEFAULT 'ONLINE'、可空 captured_amount 及模式扫描索引。
  • Entity、MyBatis paymentColumns、所有显式 select、insert、update 和 Service 构造参数同步新增字段;不直接执行 DDL。
  • 默认值只提供历史兼容,业务代码创建订单/支付尝试时仍必须显式写入来源和模式,防止调用入口变化后静默误分类。