# LINE Pay Implementation Quickstart ## 1. Prerequisites - JDK 21: `C:\Users\qmj\.jdks\graalvm-jdk-21.0.7` - MySQL schema changes from `updatesql/sql.md` applied manually by the developer - Redis/Redisson available - LINE Pay Sandbox Channel ID/Secret - Public HTTPS API base URL reachable by LINE redirect - Platform frontend: `E:\QtwCode\foodie\foodie-admin-vue` Do not execute schema migration from Codex. Do not use the deprecated `TestTask`, `ZaloPayController`, `PayController`, or `OrderAppealController` flows. ## 2. Runtime configuration `ruoyi-admin/src/main/resources/application.yml` contains non-secret defaults: ```yaml line-pay: environment: sandbox base-url: https://sandbox-api-pay.line.me confirm-url: https://api.example.com/pay/line/confirm cancel-url: https://api.example.com/pay/line/cancel reconcile: fixed-delay-ms: 60000 initial-delay-ms: 60000 batch-size: 20 row-lease-seconds: 120 round-budget-seconds: 45 auth-deadline-minutes: 30 unknown-deadline-hours: 24 ``` Channel credentials are saved per store through the platform API; they are not placed in application.yml. ## 3. Local verification Use JDK 21 only in the current PowerShell process: ```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='*Line*Test' -Dsurefire.failIfNoSpecifiedTests=false test mvn -pl ruoyi-admin -am -Dtest='*LinePay*Test,OrderLifecycleServiceTest,OmgPayControllerTest' -Dsurefire.failIfNoSpecifiedTests=false test mvn -pl ruoyi-admin -am -DskipTests package ``` Frontend: ```powershell Set-Location E:\QtwCode\foodie\foodie-admin-vue npm run lint npm run build:prod ``` Repository checks: ```powershell Set-Location E:\QtwCode\foodie\foodie_server git diff --check git status --short ``` ## 4. Sandbox acceptance flow 1. Platform opens Store Payment → LINE Pay. 2. Enter store Channel ID/Secret; verify the detail shows a new current version and enabled state. 3. Create one single-store order with `payType=3`. 4. Call `/pay/line/create`; assert payment row is `WAITING_AUTH` and response uses `paymentUrl.web`. 5. Repeat create before completion; assert the same `paymentId/lineOrderId` is returned with `reusedAttempt=true`. 6. Complete Sandbox web authorization and allow confirm redirect. 7. Confirm page returns quickly and App query eventually reports `PAID`. 8. Query DB read-only and verify payment remains `PAID`, order `pay_status=1`, and gateway logs contain REQUEST/CHECK/CONFIRM/RETRIEVE events. 9. Cancel a paid order; verify exactly one refund row is created, eventually `REFUNDED`, while payment remains `PAID` and order becomes `pay_status=2`. 10. Create a second attempt only after the first is explicitly cancelled/expired/failed; verify both rows remain and only the second has `active_dd_id`. ## 5. Required concurrency tests - Two simultaneous create calls for the same LINE order return one attempt. - LINE create and OMG create for the same order cannot both claim the payment channel. - confirm redirect, scheduled task and admin reconcile cannot Confirm twice. - user cancellation before late Capture still records PAID and creates one refund intent. - store cancellation and late Capture produce the same result and enforce store ownership. - credential rotation during a waiting/paid transaction does not change its `credential_id`; old transaction remains queryable/refundable. - UNKNOWN and MANUAL_REVIEW never release `active_dd_id` or directly repeat side effects. ## 6. Production acceptance items Sandbox cannot prove every App handoff behavior. Before production enablement, verify on real iOS and Android devices: - LINE payment browser returns to the intermediate page and the fixed Scheme handoff works. - `com.twanmsdyh.app://payment/result` is registered and handled. - auto-open works where allowed; the manual button works in LINE WebView and external browsers. - App always calls `/query` and never trusts the deep-link as success. - current production credentials complete an end-to-end TWD payment and full refund.