data-model.md 8.9 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 1 ── N pos_order_line_payment (append-only attempts)
                       │
                       ├── credential_id -> exact credential version
                       ├── 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 整数金额
currency CHAR(3) ASCII no 首版 TWD
payment_url_web VARCHAR(1000) yes Sandbox/浏览器跳转 URL
payment_url_app VARCHAR(1000) yes 生产 LINE App URL,可空
payment_provider VARCHAR(16) yes 原始值可空;展示归一为 TSP
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)
  • 所有状态更新条件至少包含 id + expected status + version,领取租约还需 lease_until IS NULL OR lease_until < NOW()

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

PAID、UNKNOWN、MANUAL_REVIEW 不释放 active_dd_id。旧尝试迟到证实 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)

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 raw JSON/body/error summary
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

不新增 pos_order 字段。使用现有:

  • 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 操作。