# EDU-020 — Activate tenant-scoped native Pay administration - **Status:** done — bounded Pay App/Channel runtime activation implemented and verified - **Type:** platform reuse / tenant security / database takeover - **Phase:** 4 / tenant operations - **Blockers:** EDU-018, native Pay module, PostgreSQL Flyway ## Problem V4300 deliberately reused the native Pay App/Channel controllers, permissions, and Vben page, but the repository reactor and `yudao-server` still excluded `yudao-module-pay`, and the active PostgreSQL baseline had no `pay_app` or `pay_channel` tables. The menu was therefore only navigational evidence, not an operational payment-configuration backend. Stock/global Pay tables are also unsafe to adopt silently in a multi-tenant education deployment. A channel supplied with an arbitrary `appId` must not bind to an application outside the current tenant. ## Delivered contract - `yudao-module-pay` is included in the reactor and server runtime. - V4320 creates Pay-owned `pay_app` and `pay_channel` tables with tenant columns, logical-delete audit fields, active-row uniqueness, status checks, and tenant-first indexes. - Existing Pay tables without `tenant_id` fail migration with an explicit mapping error; V4320 never assigns legacy credentials to tenant `0` or another guessed tenant. - `PayAppDO` explicitly extends `TenantBaseDO`, so framework MyBatis tenant interception is a declared contract rather than an implicit table convention. - Channel create/update verifies that the referenced application is visible to the current tenant before persisting the channel. - `/pay/app/list` now uses the actual `pay:app:query` permission already granted by V4300 instead of the obsolete `pay:merchant:query` permission. - The existing `pay/app/index` Vben page remains authoritative; no Education payment form or credential table is duplicated. ## Verification - 17 `PayChannelServiceTest` checks pass, including missing/cross-tenant-parent rejection seams. - Two Pay tenant/permission contract checks pass. - All 45 current PostgreSQL Flyway tests pass through V4380, including same `app_key` across tenants, duplicate rejection within a tenant, channel uniqueness, V4320 history, and fail-closed adoption of a global `pay_app` table. - `mvn -pl yudao-server -am -DskipTests compile` includes and compiles the native Pay module. ## Explicitly open - EDU-021 now provides an explicit, audited single-account import for `tenant_collect` WeChat/Alipay manifests. Platform/service-provider modes, unsupported providers, and production bulk export/runbook work remain open. - EDU-022 now activates tenant-scoped native Pay order, refund, and notification persistence/UI; EDU-023 adds bounded terminal legacy transaction import; EDU-024 activates native Transfer/Wallet persistence and UI without inventing opening balances. - Payment credentials remain Pay-owned. Generic tenant secret encryption/rotation and PNVS remain separate slices. - EDU-025 delivers native Mall Product activation. Explicit legacy product import, Promotion/Trade, coupon redemption, purchase fulfillment, refund-to-entitlement revocation, and reconciliation remain separate commercialization slices.