Files
ruoyi-vue-pro/docs/education/migration/issues/EDU-022-native-pay-transactions.md

46 lines
5.4 KiB
Markdown

# EDU-022 — Activate tenant-scoped native Pay transactions
- **Status:** done — bounded order/refund/notification takeover and native administration UI verified
- **Type:** platform reuse / financial isolation / database takeover
- **Phase:** 4 / commercialization foundation
- **Blockers:** EDU-020, EDU-021, native Pay runtime, PostgreSQL Flyway
## Problem
EDU-020 made native Pay application/channel configuration operational, but the active PostgreSQL baseline still had no order, order-extension, refund, notification-task, or notification-log tables. The existing Pay controllers and Vben pages therefore could not administer real transaction state.
The stock transaction data objects were also inconsistent: notification tasks were tenant-aware, while orders, order extensions, refunds, and notification logs inherited only `BaseDO`. Silently creating global financial tables or assigning existing rows to a guessed tenant would make callbacks, exports, and background retries cross tenant boundaries.
## Delivered contract
- V4340 creates Pay-owned `pay_order`, `pay_order_extension`, `pay_refund`, `pay_notify_task`, and `pay_notify_log` tables for PostgreSQL. Education does not create a parallel order, refund, or webhook ledger.
- `PayOrderDO`, `PayOrderExtensionDO`, `PayRefundDO`, `PayNotifyTaskDO`, and `PayNotifyLogDO` all inherit `TenantBaseDO`, so normal MyBatis tenant interception scopes native admin queries and mutations.
- Composite tenant foreign keys bind orders to Pay applications/channels, extensions to orders/channels, refunds to their application/channel/order, and logs to their notification task. Cross-tenant references fail in PostgreSQL even if application code is bypassed.
- Active merchant order/refund identifiers are unique inside a tenant application but may be reused by another tenant. Native Pay numbers and extension numbers are tenant-scoped.
- One active notification task is allowed for each `(tenant_id,type,data_id)`. This closes duplicate terminal-callback races; a deliberately soft-deleted task may be recreated.
- Existing transaction tables without `tenant_id` fail V4340. No global financial row is assigned to tenant `0` or inferred from an application ID.
- The native order/refund callback entry points continue to resolve the channel first and execute the business update inside `TenantUtils.execute(channel.tenantId, ...)`. The existing notification retry job continues to use `@TenantJob`.
- Existing Pay controllers remain authoritative: `/pay/order` and `/pay/refund` provide tenant-filtered query/export contracts, while `/pay/notify` provides tenant-filtered task/detail reads and the provider callback entry points.
- V4340 exposes the existing `pay/order/index`, `pay/refund/index`, and `pay/notify/index` Vben pages below Education with the original `pay:order:*`, `pay:refund:*`, and `pay:notify:query` permissions. No custom Education transaction page was added.
## Database safety
The circular order/extension relationship is created in two steps. The final `fk_pay_order_extension` installation is guarded through `pg_constraint`: an equivalent named composite tenant foreign key is accepted, a conflicting named constraint fails closed, and a missing constraint is installed. V4340 also verifies the complete required column shape after table creation.
The migration intentionally creates empty native transaction ledgers. It does **not** import legacy `orders`, `payments`, `payment_events`, or `commerce_refund_requests`; importing those records requires an explicit, reconciled mapping with amount/status/identifier/callback ownership rules.
## Verification
- All 45 PostgreSQL Flyway integration tests pass through V4380. V4340 coverage proves same merchant order ID across tenants, composite-FK cross-tenant rejection, active notification-task uniqueness and soft-delete recreation, native menu/permission shape, successful migration history, and fail-closed adoption of a global `pay_order` table.
- The combined native Pay transaction regression passes 86 tests: 46 order, 28 refund, 11 notification, and one tenant-inheritance contract.
- The previously disabled `PayNotifyServiceTest` is active; its asynchronous scheduling assertions and retry-count fixtures now match the production contract.
- `mvn -pl yudao-server -am -DskipTests compile` and the Vben `@vben/web-antd` typecheck are the closing reactor/UI gates for this slice.
## Explicitly open
- EDU-023 now supplies a bounded, terminal-only reconciled import. Production export tooling, reviewed Member-ID mapping, dry-run/runbook evidence, and operator sign-off remain required; direct table copying is still forbidden.
- EDU-024 now activates tenant-aware native Pay Transfer and Wallet persistence plus the existing administration pages. Historical opening balances remain deliberately unpopulated pending a reviewed source artifact.
- EDU-025 delivers native Mall Product activation and EDU-026 delivers native Promotion Coupon activation. Explicit legacy product/code-coupon import, Trade/other Promotion activation, and automatic purchase-to-entitlement fulfillment are not delivered.
- Refund completion does not yet revoke or shorten Education entitlements; commerce reconciliation must define partial-refund and replay semantics first.
- Coupons, commissions, referrals, dunning, settlement/reconciliation, generic Pay credential encryption/rotation, tenant PNVS, production deployment, and browser/API integration evidence remain separate work.