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 在事务内保证逻辑引用存在。
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。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()。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_MISMATCH、MANUAL_REVIEW 不释放 active_dd_id。Offline 金额不一致退款成功后原订单仍关闭支付,商家必须创建新订单。旧尝试迟到证实 PAID 时照实记录,并阻断/退款,不覆盖当前尝试。
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)
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)新增字段:
| Column | Type | Null | Notes |
|---|---|---|---|
order_source |
VARCHAR(16) ASCII | no | USER/MERCHANT;默认 USER,仅商家创建入口显式写 MERCHANT |
兼容与写入规则:
USER,不得根据 userId、创建时间、payType 或入口日志推断/回填。/system/orderShOprate/createOrder 创建的每个子订单显式写 MERCHANT;父订单不作为 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。ddId 查询时先读取订单 pay_status。active_dd_id=ddId;没有则返回最新终态尝试。PAID;已退款选带 REFUNDED 的付款/退款组合。PAID 不自动任选,返回 MANUAL_REVIEW。line_order_id 定位,transactionId 存在时必须匹配。paymentId/refundId 操作。order_source=MERCHANT 和当前商家归属,再按唯一活跃尝试或最近终态选择 payment_mode=OFFLINE 的记录;不得返回其他商家或 Online 尝试的敏感上下文。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 及模式扫描索引。paymentColumns、所有显式 select、insert、update 和 Service 构造参数同步新增字段;不直接执行 DDL。