# PiPayment

PiPayment 是 `@pisell/private-materials` 的统一支付模块，由正式 SDK 和 EFTPOS UI
两部分组成：

- `sdk/`：`core`、`contracts`、`services`、`infrastructure` 和可注册的 Provider Adapter。
- `ui/eftpos/`：EFTPOS 对外接口、页面 schema、message resolver 与领域结果适配。

SDK 的能力矩阵、事件协议、生命周期、接入示例和 Tyro 中断边界见
[`sdk/README.md`](./sdk/README.md)。

## 公开入口

包入口导出：

- `PiPaymentSDK`、`PiPaymentSDKOptions`
- `PiPaymentEftpos` 及其 ref、props、open options 和 change event 类型
- `PaymentSession`、pay/refund/query/reader/action/result/error 等 SDK 契约
- `EftposProvider`、`PaymentMode`、`PaymentUsageScene`、`PaymentSchemaActionType`
- EFTPOS UI 使用的金额 quote 与页面 schema 类型

EFTPOS UI 的宿主事件统一为扁平的 `PiPaymentEftposChangeEvent`。业务调用方直接读取
`paymentNumber`、`transactionNumber` 及当前事件的金额或错误字段；完整 SDK
result/error、页面、Session 和 attempt 生命周期不会暴露。不再提供 `event.params`、
`event.snapshot` 或历史 Checkout 命名字段。

每次支付 attempt 创建后，UI 会向宿主发送一次 `processing`；超时打印请求使用
`printOnTimeout`，随后按主动关闭语义发送 `closed` 并释放当前 Session。
Provider 返回的普通小票由 UI 直接派发 `print_string` DeviceTask，不再通过
`printRequested` 交给业务调用方处理。

SCI Provider 的成功小票只读取统一 `payment.receipt`，不回退
`provider_response` 中的厂商旧字段。`receipt` 按 `type` 区分 `merchant`、
`customer`、`signature` 和 `unknown`；当前成功打印忽略 `signature`，其余按
`merchant`、`customer`、`unknown` 的顺序打印。空数组表示没有可用小票。

正式 SDK 是唯一生产实现。UI、业务代码、测试和文档不得依赖归档实现。

## EFTPOS UI 边界

`PiPaymentEftpos` 通过 imperative ref 保留现有 `open(options)` / `close()` 接入方式。
内部 `usePiPaymentEftposFlow` 只负责组合展示状态，具体职责分为：

- `flowUtils`：无 React 依赖的首屏、Reader、失败参数和 attempt 参数决策。
- `usePaymentSessionController`：Session 生命周期、支付命令、action lock、任务去重和销毁。
- `useSessionEventHandler`：SDK 事件到页面状态和宿主事件的唯一映射入口。
- `useNetworkFlow`：浏览器网络监听、显式网络状态优先级和断网页恢复。

跨 hook、SDK 回调和公开动作使用稳定函数引用；动态 options、state、context 和 logger
通过当前 render 或 ref 读取，避免重渲染触发重复 Session、Reader 查询或事件监听。

UI 保持以下业务行为：固定金额跳过金额页、单 Reader 自动选择、失败重试、签名和
取消 action、打印/打印超时、人工确认、成功延迟通知、断网恢复以及关闭/卸载销毁。

Adyen POS 的签名属于交易成功后的本地人工复核，不复用普通 `signature` action：

- `processing` 且终端要求签名时继续轮询，并禁用 Abort。
- SDK 收到成功终态后释放请求资源，通过 Provider 中立的 `postSuccessReview` effect
  暂缓 UI 向宿主发送 `success`。
- 接受签名后发送 `success`；拒绝签名需要二次确认，且确认后仍发送 `success`。拒绝
  不会调用后端 action，也不会自动退款。

Checkout 只把实体 `EFTPOS_ADYENPOS` 映射到 PiPayment 的 `adyenpos` Provider。
Adyen Alipay/WeChat Scan 不属于本接入，仍沿用原流程；共享灰度开关关闭时 Adyen POS
也继续回退旧 EFTPOS 流程。

## 文案边界

- SDK 只输出 `PaymentDisplayText`：稳定 message key 或标注来源的动态 literal。
- UI 通过唯一 resolver 依次应用宿主 resolver、宿主 messages、默认 Provider/UI 文案和 fallback。
- 组件和页面不得按 Provider 判断文案，技术诊断信息不得直接展示。
- 定制文案通过 `PiPaymentEftpos` 的 `messages` 或 `messageResolver` 注入，无需修改 SDK。

## 目录约束

- SDK 测试位于 `sdk/__tests__/`。
- EFTPOS UI 测试位于 `ui/eftpos/__tests__/`。
- SDK 不导入 UI；UI 只通过正式 SDK 的公开类型和 Session 事件交互。
- UI 不允许深层导入 SDK 的 schema、locales 或 Provider 实现。
- provider 原始 response 和 action payload 在 SDK 内保持不透明，页面只渲染中立 schema。
