# Quickstart: OMG AIO 创建支付订单验证

## Prerequisites

1. 使用 JDK 21：`C:\Users\qmj\.jdks\graalvm-jdk-21.0.7`。
2. 开发者已手动执行 `updatesql/sql.md` 中 `pos_order_omg_attempt` DDL。
3. 测试门店在可信 `pos_store_omg` 中录入并启用 OMG stage 凭证。
4. 准备该用户自己的单门店订单：`dd_id = parent_dd_id`、`pay_type='2'`、`pay_status=0`、`state IN (0,1,2)`、`amount>0`。
5. `omgpay.return-url` 是受控 HTTPS 地址，路径精确为 `/pay/omg/notify`。

## Automated verification

```powershell
$env:JAVA_HOME='C:\Users\qmj\.jdks\graalvm-jdk-21.0.7'
$env:Path="$env:JAVA_HOME\bin;$env:Path"
mvn -pl ruoyi-system -am -Dtest='OmgPaymentAttemptServiceTest,OmgIpnAuditServiceTest' -Dsurefire.failIfNoSpecifiedTests=false test
mvn -pl ruoyi-admin -am -Dtest='OmgCheckMacSignerTest,OmgPaymentFormFactoryTest,OmgPaymentCreateServiceTest,OmgPaymentControllerTest,OmgNotifyFormParserTest,OmgNotifyRawBodyFilterTest,OmgPaymentNotifyServiceTest,OmgPaymentNotifyControllerTest' -Dsurefire.failIfNoSpecifiedTests=false test
mvn -pl ruoyi-admin -am -DskipTests package
```

## Create request

```powershell
$headers = @{ token = '<USER_TOKEN>' }
$body = @{ orderId = '<SINGLE_STORE_DD_ID>'; paymentMethod = 'CREDIT' } | ConvertTo-Json
$response = Invoke-RestMethod -Method Post `
  -Uri 'https://<API_HOST>/pay/omg/create' `
  -Headers $headers -ContentType 'application/json' -Body $body
$response.data
```

Expected:

- `status=CREATED`。
- `gatewayUrl` 精确等于 stage AioCheckOut V5 地址。
- `CREDIT` 响应包含 `ChoosePayment=Credit`、`UnionPay=2`，且不包含 `IgnorePayment`。
- 将 `paymentMethod` 改为 `APPLE_PAY` 后，新响应包含 `ChoosePayment=ApplePay`，且不包含 `UnionPay` 或 `IgnorePayment`。
- 两种响应都不包含 `ChoosePayment=ALL`。
- `CheckMacValue` 是 64 位大写十六进制。
- 响应不含 HashKey 或 HashIV。

## Current-page form POST

在客户端当前页面创建 `<form method="post">`，action 设置为 `gatewayUrl`，逐一把 `formFields` 的键和值创建为 hidden input，然后调用 `form.submit()`。不要设置 iframe target，也不要 `window.open()`。

Expected: 分别提交 `CREDIT` 和 `APPLE_PAY` 表单时，浏览器直接进入对应 OMG stage 付款流程并显示正确金额；不得出现超商快付、AFTEE 或其他渠道选择项。Apple Pay 仍需门店已开通且设备环境支持。

## Duplicate request

对同一 `orderId` 再调用创建接口。

Expected:

- 返回 `PAYMENT_ATTEMPT_EXISTS`。
- 没有 `formFields`。
- 数据库没有第二条该 `dd_id` 的 `CREATED` 行。
- 不生成或返回新的 MerchantTradeNo。

## Concurrent request

用两个 PowerShell 作业同时提交同一订单：

```powershell
$uri = 'https://<API_HOST>/pay/omg/create'
$tokenValue = '<USER_TOKEN>'
$json = (@{ orderId = '<SINGLE_STORE_DD_ID>' } | ConvertTo-Json -Compress)
$jobs = 1..2 | ForEach-Object {
  Start-Job -ScriptBlock {
    param($u, $t, $b)
    Invoke-RestMethod -Method Post -Uri $u -Headers @{ token = $t } `
      -ContentType 'application/json' -Body $b
  } -ArgumentList $uri, $tokenValue, $json
}
$jobs | Wait-Job | Receive-Job
$jobs | Remove-Job
```

Expected: 一个请求成功，一个请求返回 `PAYMENT_ATTEMPT_EXISTS`。手动查询：

```sql
SELECT dd_id, COUNT(*) AS active_count
FROM pos_order_omg_attempt
WHERE active_dd_id = '<SINGLE_STORE_DD_ID>'
GROUP BY dd_id;
```

`active_count` 必须为 1。

## Negative cases

分别验证无效 token、他人订单、多门店子订单、state=3/4、pay_status 非 0、amount<=0、pay_type 非 2、无启用凭证。每项都必须：

- 返回国际化业务错误与稳定 status；
- 不写新尝试；
- 不泄露其他订单或门店凭证详情。

## Log review

确认日志可按 orderId 定位创建开始、校验、成功或拒绝，异常路径含服务端堆栈。全文搜索不得出现：

- 请求 token；
- HashKey / HashIV 的值；
- 完整 CheckMacValue；
- 完整 `formFields` 或凭证对象序列化内容。

## Callback expectation

## 付款结果回调重放

使用真实 OMG stage 回调日志中的完整 form-urlencoded 内容重放；不要自行删除空值字段或额外字段：

```powershell
$body = '<从 ipn_log.ipn_log 或应用日志复制的完整回调内容>'
Invoke-WebRequest -Method Post `
  -Uri 'https://foodieapi.waimai-paotui.com/pay/omg/notify' `
  -ContentType 'application/x-www-form-urlencoded' `
  -Body $body
```

预期：

- 签名、商户、交易号和金额匹配时返回纯文本 `1|OK`。
- `RtnCode=1`（包括 `SimulatePaid=1`）后，尝试为 `PAID`、订单 `pay_status=1`，订单 `state/delivery_status` 不变。
- 验签通过且 `RtnCode!=1` 后，尝试为 `FAILED`、订单仍未付款，下一次 create 可生成新尝试。
- 相同通知重复重放仍返回 `1|OK`，不重复推进任何订单业务状态。
- 修改金额、商户号、检查码或删除任何实际字段后返回 `0|ERROR`，支付和订单事实不变。
- 每次请求都在 `ipn_log` 新增 `type=omg` 的完整原文，包括验签失败请求。

本阶段不验收查询、补单、取号、退款或推送；不得把旧 OMG 回调或旧查询结果作为新流程证据。
