所有 JSON 业务响应沿用项目 AjaxResult 外壳。交易号字段始终是 JSON string。业务错误文本从 MessageUtils.message(...) 获取。
POST /pay/line/createAnnotations: @Anonymous, @Auth, @RequestHeader String token, @RequestBody LinePayOrderRequest.
Request:
{
"ddId": "202608120001"
}
Success data:
{
"ddId": "202608120001",
"paymentId": 42,
"lineOrderId": "LP20260812...",
"transactionId": "2026081200000000001",
"paymentUrl": "https://sandbox-web-pay.line.me/...",
"status": "WAITING_AUTH",
"reusedAttempt": false
}
Rules:
payType="3",单门店、未支付、合法非终态、门店当前 LINE 版本已验证并启用。reusedAttempt=true,不再 Request。CANCELLED_OR_EXPIRED/FAILED 才新增行。POST /pay/line/queryAnnotations 与 create 相同。
Request:
{
"ddId": "202608120001"
}
Success data:
{
"ddId": "202608120001",
"paymentId": 42,
"orderPayStatus": 1,
"paymentStatus": "PAID",
"refundStatus": null,
"transactionId": "2026081200000000001",
"updatedAt": "2026-08-12T12:34:56+08:00"
}
该接口只读本地状态,不在 App 请求线程中调用 LINE。
GET /pay/line/confirm?orderId={lineOrderId}&transactionId={transactionId}orderId 是 LINE Request 的 line_order_id,不是 ddId。@RequestParam;按 lineOrderId 定位并校验 transactionId。text/html;charset=UTF-8 和:
Cache-Control: no-store, no-cache, must-revalidatePragma: no-cacheReferrer-Policy: no-referrerContent-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'; frame-ancestors 'none'com.twanmsdyh.app://payment/result?orderId=<urlEncodedDdId>,并显示手动按钮和“正在确认支付,请回 App 查询”的安全提示。GET /pay/line/cancelOptional explicit query parameters: orderId, transactionId.
Base path: /system/storeLinePay
GET /listPermission: chanting:storeLinePay:list.
Query uses explicit optional @RequestParam: posName, isStall, credentialStatus, isEnabled.
Rows include store identity/current version/status/channelId/hasCredential/verifiedTime, never include channelSecret.
GET /{storeId}Permission: chanting:storeLinePay:query.
Returns current credential detail. Per approved product decision this privileged response may contain plaintext channelSecret.
PUT /saveCredentialsPermission: chanting:storeLinePay:saveCredentials.
{
"storeId": 123,
"channelId": "2000000000",
"channelSecret": "plain-secret"
}
Business validation is in Controller/Service without @Valid. Candidate is verified before an immutable version becomes current and enabled. Failed/unknown probe leaves existing current version untouched.
PUT /toggleEnablePermission: chanting:storeLinePay:toggleEnable.
{
"storeId": 123,
"enabled": false
}
Only changes whether the current verified version may serve new payments; old transactions remain queryable/refundable through their credential_id.
GET /system/order/{id}/status-contextExtend existing response with:
{
"linePaymentId": 42,
"linePaymentStatus": "PAID",
"lineRefundStatus": null,
"lineTransactionId": "2026081200000000001"
}
Only populate LINE fields when a matching pos_order_line_payment exists. Never infer LINE solely from payType="3".
POST /system/order/{id}/line-payment/reconcilePermission: system:order:linePaymentReconcile. Uses the selected paymentId from stable server-side rules, takes a row lease, then performs Retrieve/Check recovery.
POST /system/order/{id}/line-refundPermission: system:order:lineRefund. Creates/uses the unique full-refund row for the selected PAID payment. UNKNOWN only retrieves; it never directly repeats Refund.
POST /v4/payments/request, 10 second timeout.
{
"amount": 100,
"currency": "TWD",
"orderId": "LP20260812...",
"packages": [{
"id": "202608120001",
"amount": 100,
"products": [{"name": "Order 202608120001", "quantity": 1, "price": 100}]
}],
"redirectUrls": {
"confirmUrl": "https://api.example/pay/line/confirm",
"cancelUrl": "https://api.example/pay/line/cancel"
},
"options": {
"payment": {"capture": true}
}
}
The total amount must equal package amount and product price * quantity sum.
GET /v4/payments/requests/{transactionId}/check, 20 second timeout.
POST /v4/payments/{transactionId}/confirm, 40 second read timeout or greater.
{"amount": 100, "currency": "TWD"}
Confirm is sent only after local CAS claim and amount/currency/order/store/credential/active checks.
GET /v4/payments?orderId={encodedLineOrderId} or ?transactionId={encodedTransactionId}, 20 second timeout. The exact ordered query string is included in the HMAC input.
POST /v4/payments/{transactionId}/refund, 20 second timeout, body {}. refundAmount is intentionally omitted for full refund; there is no currency request field.
X-LINE-ChannelId: <channel id>
X-LINE-Authorization-Nonce: <unique nonce>
X-LINE-Authorization: Base64(HMAC-SHA256(channelSecret, channelSecret + URI + bodyOrQuery + nonce))
Content-Type: application/json
The mobile application must register and handle:
com.twanmsdyh.app://payment/result?orderId=<ddId>
On open, the App reads its normal login token and calls /pay/line/query. It must not trust Scheme parameters as payment success. Auto-open and fallback button require production-device acceptance testing; Sandbox web payment cannot prove LINE App handoff behavior.