SIEM · migration readiness · read-only audit

Multi-Gateway Payment Migration Audit

1. Summary · 14 Oct 2026 · All 38 A findings and all 90 D register entries are resolved in source. Codex's direct source recheck found two narrower gaps under D72 and D32 (a queued push surviving a same-plan asset or interval change, and the standalone admin subscription page lacking the first renewal amount); both are fixed. Added since the audit: D84–D90 (agent removal, agent selection, price-push duplicate emails, trial conversion to ACTIVE, invoice attachments, currency and credit units). Nothing has been run; flows 37, 45 and 47 are proved only by the dev rehearsal, and flow 49 keeps one unused interface method (StripeProvider.verifySetupCompletion) pending an owner decision.

46 / 50 Complete · 92%0 Broken0 Missing1 Stale code present · 2%3 Unverified · 6%38 / 38 A findings resolved · 100%90 / 90 D register marked resolved · 100%0 register entries pending
35 dependencies31 Complete0 Broken0 Stale code present4 Unverified

Batch 1 audited commits · all three repositories report branch @payment-integration; their working trees were clean at inspection.

siem-server-new/src/server.ts:59 · server HEAD 1df9558830dec412106b634d38b89c47dca76b87
siem-client-new/action/subscriptionAction.ts:231 · client HEAD 328b87cc476443c24228f3e6f17679ac4aa66701
siem-admin-new/app/dashboard/plans/page.tsx:374 · admin HEAD a4603afb354bab221208c8cbc866f9281dd7ebfe

Commit/branch/clean-tree metadata came from read-only git log, branch --show-current and status --short, not the older index summary.

Historical batch scope · superseded by the fix refresh

Batch 2 source scope: same three HEAD commits and @payment-integration branches; this batch reads the current uncommitted working tree, which changed during inspection. Concurrent Batch 1 resolution edits in this page were retained. Earlier verdicts and A1–A7 are historical; appended notes point to later source evidence without certifying a fresh Batch 1 audit.

The counts above retain those earlier verdicts. A1–A7 carry existing resolution notes; A8–A14 are seven new findings from flows 14–25. Working-tree fixes observed here include inactive-plan/rate guards, onboarding currency and status polling. References in newly added rows use the source lines read for Batch 2; later edits may move them.

Historical batch scope · superseded by the fix refresh

Batch 3 scope and patched Batch 1: all three HEAD commits remain as listed above, on @payment-integration. This batch audits current server/client working-tree edits; admin is at its listed clean HEAD. The owner confirms Batch 1 is patched, and A1–A7’s existing resolved notes are retained. Earlier board verdicts remain the original batch history; the counts are counts of those preserved rows, not seven reopened Batch 1 findings.

Flows 26–37 add two Complete and ten Broken verdicts, eleven new findings and source-only cross-flow notes. Known D10/D19 patches agree with their current resolved register status; D23/D34/D35/D43/D45/D59 and D33 recovery remain pending. No runtime evidence was collected.

How to read this board: verdicts assess source handoffs against the supplied design, not observed production execution. Expand an audited row for its end-to-end trace and the checked status of its known register IDs. A broken verdict can describe a shared edge case even when the ordinary signup path exists.

No app, tests, type checks, linters, builds, installs, migrations, database connections or gateway calls were run. Gateway-account settings and actual schema/client deployment remain unverified, not code defects. Thingstoconfigurebeforeproduction.md:5. Only this audit HTML and one index link card are authorized output edits.

Historical batch scope · superseded by the fix refresh

Batch 4 scope: flows 38–50 and the whole-board billing map/rehearsal checklist. HEAD commits remain those listed above. Current server/client working-tree source changed during inspection; prior rows, A1–A25 and all patch notes are preserved, including concurrent A8–A14 resolution notes detected at save. Batch 4 adds 11 findings (A26–A36), including two critical paths, and checks all 70 cataloged deleted paths. Counts describe the preserved readiness board, not a fresh certification of patched Batch 1.

“Complete” means the inspected source chain is present; gateway execution, actual database inventory, Redis schedules, mail delivery and deployment routing remain unverified. Known register defects are referenced by ID, including partially resolved D32/D72/D76/D77.

Historical batch scope · superseded by the fix refresh

Batch 5 scope: the updated CodexMigrationAuditPrompt explicitly adds 35 untouched billing dependencies. This batch traces their billing contracts and appends evidence notes to affected earlier rows, keeping their verdicts and A1–A36 unchanged. A37/A38 are the only new findings; A30 gains an additional IAM cause. Sections 3 and 8 are now finalized from all five batches.

The three HEAD commits remain the ones listed above (read-only git rev-parse); inspected files include current working-tree patches. A1–A14’s resolution notes are preserved without a whole-core re-audit. Flow totals therefore retain earlier historical verdicts; dependency totals are shown separately.

Previous Batch 1/2-only refresh · historical totals

Current readiness after Batch 1/2 fixes: all 14 findings A1–A14 are resolved. Seven affected flows move to Complete; two move to Stale code present. Other affected flows remain Broken only for independently recorded later findings or register gaps. After stale-code cleanup, the board reports 19 Complete, 27 Broken, 1 Missing, 2 Stale code present and 1 Unverified.

This is a report-data refresh supported by the owner’s confirmation, existing fix records and targeted source reads—not a fresh execution of the billing flows. Runtime rehearsal and later A15–A38 work remain outstanding. Batch 1 fix records Batch 2 fix records

Previous resolution refresh · historical totals

Latest defect resolutions: A15–A29, A31–A33 and A35–A36 now agree with targeted source checks; their status attributes, affected flow reasons and dependency verdicts are reconciled. A30’s reported client fix is real, but its restricted-status IAM gap remains. The server also correctly protects chargeable saved methods; terminal CANCELLED/ENDED/EXPIRED may release them under the current lifecycle. siem-server-new/src/controllers/iam.controller.ts:615 siem-server-new/src/services/subscription.service.ts:2222.

A34 historical invoice lookup, A37 false-success switching and A38 encrypted replay remain open. Many Broken rows now reflect independent known D gaps rather than repaired A findings. Existing register totals remain 63/83 marked resolved and 20 pending; source-partial D32/D72/D76/D77 are still qualified in the board. HEAD commits remain the listed values; actual schema/deployment/runtime fixes have not been exercised.

Previous A-only verdict refresh · historical totals

Current verdicts: all 38 A findings are resolved in source. Flows blocked only by those findings now show Complete; Broken rows identify independent D gaps. Schema application and cutover are Unverified rather than claiming the newly built script is Missing. Country seed writes country/provider/currency together, including default IN; cutover apply supplies price/cycle/paid-period data. siem-server-new/scripts/seed-subscription-countries.ts:49 siem-server-new/scripts/stripe-cutover.ts:222.

The script’s active-customer mapping is ACTIVE until the paid-through date, then normal missing-method grace; this differs from the original PENDING_REAUTH target and requires target reconciliation plus rehearsal. All A findings resolved is a source-defect count, not a 100% readiness claim. Flow 47 evidence

Previous register snapshot · historical totals

Latest D register: 78 resolved and five pending out of 83 (94.0% / 6.0%). Pending entries are D39, D40, D41, D46 and D50. Targeted reads confirm the newly implemented invoice sequence, common plan-change quote, grace/reminder paths, portal/public-accept removals, cutover, lock reconciliation and wallet race fixes. siem-server-new/src/prisma_repos/invoice.repository.ts:8 siem-server-new/src/services/subscription.service.ts:1111 siem-server-new/src/payment/stripeChargeReconcile.ts:57.

Four flows remain Broken: 29 (D46), 50 (pending access/context issues), 38 (the documented D72 actual-notification timing qualifier) and 39 (the documented D32 pushed-price confirmation qualifier). The last two are marked resolved in the register but remain source-incomplete for those scenarios. Flow 17 is now stale informational code, not unpaid quote acceptance; cutover is owner-approved in source and unrun, not Missing. D72 qualifier D32 qualifier

Previous all-closed snapshot · historical qualifier descriptions

Latest register: 83/83 resolved, none pending. New closures D39/D40/D41/D46/D50 agree with their source implementations and explicit owner decisions. Access now follows the current subscription’s shared subscriptionAccess table, customers cannot delete subscription rows, and late renewal payment inside grace starts the new paid period at payment. siem-server-new/src/utils/subscriptionAccess.ts:24 siem-server-new/src/services/subscription.service.ts:948 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:546.

41 flows are Complete; the remaining two Broken rows are source qualifiers against already-closed D72/D32 (flows 38/39), not pending register entries. Three stale-code and four Unverified rows remain. An all-closed register is not proof of production readiness or a successful cutover.

Latest source recheck: price push now recalculates its first cycle from successful email time; customer detail now supplies server-calculated firstRenewalCharge. Those original qualifiers are fixed. Two narrower paths remain incomplete: a queued push can use an old amount after a same-plan asset/interval change (D72), and standalone admin subscription detail does not supply firstRenewalCharge to the shared activation dialog (D32). siem-server-new/src/services/pricePush.service.ts:149 siem-server-new/src/services/user.service.ts:673 siem-server-new/src/bullmq/queues/pricePushQueue.ts:18 siem-server-new/src/controllers/admin-subscription.controller.ts:194.

The board is 44 Complete, 2 Broken, 1 Stale code present and 3 Unverified; other saved resolutions and cleanup updates are retained. D/A register closure remains 100%; these are source qualifiers against existing IDs, not new A findings or reopened register rows. No charge or activation was executed.

2. Readiness board

50 flows · 50 audited. Known D findings are referenced here rather than repeated as new A findings. The board now incorporates the owner-confirmed Batch 1/2 fixes. Earlier traces on refreshed rows are collapsed as history; later independent blockers remain active.

FlowVerdictReason / traceRegister IDsKey files
1. Subscription statuses and transitionsCompleteThe shared map accepts first-charge failure/recovery and terminal grace transitions; signup settlement uses the map.
Trace and register status

The enum includes UNPAID, PAYMENT_FAILED, PENDING_SETUP, ACTIVE, TRIALING, PAST_DUE, PENDING_REAUTH, CANCELLED, ENDED, EXPIRED and the vestigial PAID value (siem-server-new/prisma/schema.prisma:902). updateStatus reads the current row, validates the destination, then delegates the write (siem-server-new/src/prisma_repos/subscription.repository.ts:107). UNPAID → PAYMENT_FAILED, PAYMENT_FAILED → PENDING_SETUP, CANCELLED → EXPIRED and PENDING_SETUP → ENDED are present (siem-server-new/src/utils/subscriptionStatusMachine.ts:27). First-charge settlement and failure take the subscription lock and validate before their direct transaction writes (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:475, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:579, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:747). D3/D4 agree with resolved; D15's claim-deadline transition is present. This verdict covers the shared gate and Batch 1 writers; later lifecycle paths remain unaudited.

D3 · D4 · D15siem-server-new/src/utils/subscriptionStatusMachine.ts:8 siem-server-new/src/prisma_repos/subscription.repository.ts:107
2. Gateway by company registration countryCompleteValidated country fixes the stored subscription binding; normal updates cannot replace it.
Trace and register status

The country selector uses country-state-city ISO codes (siem-client-new/components/reusable/CompanyCountrySelect.tsx:25); checkout collects a country alongside the organization and displays only that currency (siem-client-new/components/reusable/price-calculator/index.tsx:809, siem-client-new/components/reusable/price-calculator/index.tsx:454). Strict server schemas reject a provider/currency selector (siem-server-new/src/validators/subscriptionSchema.ts:11). getCompanyBilling validates against the supported set: IN → razorpay/inr, every other supported code → stripe/usd; getSubscriptionBilling rejects inconsistent stored fields (siem-server-new/src/utils/companyRegistrationCountry.ts:6). Creation validates the binding; updateById/updateWhere reject country, provider and currency edits; null-country initialization is conditional on PENDING_REAUTH (siem-server-new/src/prisma_repos/subscription.repository.ts:43, siem-server-new/src/prisma_repos/subscription.repository.ts:57, siem-server-new/src/prisma_repos/subscription.repository.ts:93). The processor checks provider and successful-payment currency (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:43). Recovery's full behavior belongs to flow 32.

—siem-server-new/src/utils/companyRegistrationCountry.ts:6 siem-client-new/components/reusable/CompanyCountrySelect.tsx:25
3. Providers, registry, charge lock and billing transactionsCompleteCore contracts and guards exist. The Razorpay recurring flag is not an order parameter: Razorpay's card-recurring Create Order API takes amount, currency, customer_id, method, token {max_amount, expire_at, frequency}, receipt and notes, and recurring is a Checkout option, which the widget sets (razorpay.com/docs/api/payments/recurring-payments/cards/create-authorization-transaction, read 13 Oct 2026). The plan document's “recurring:1 on the order” is the mismatch, not the code. A real bank-confirmed token is still to be seen in the dev rehearsal.
Trace and register status

The contract carries setup amounts, cycle metadata, charge idempotency and unknown outcomes (siem-server-new/src/payment/PaymentProvider.interface.ts:62, siem-server-new/src/payment/PaymentProvider.interface.ts:90). API and dedicated worker startup register both providers (siem-server-new/src/server.ts:59, siem-server-new/src/bullmqserver.ts:19). Redis refuses a charge when unavailable and uses a 24-hour NX lock; billing transactions use row locks, database time and bounded deadlock retries (siem-server-new/src/payment/chargeLock.ts:15, siem-server-new/src/payment/billingTransaction.ts:5, siem-server-new/src/payment/billingTransaction.ts:31). Stripe creates an off-session confirmed PaymentIntent with its request idempotency key (siem-server-new/src/payment/providers/StripeProvider.ts:131); Razorpay sends token, email, contact, recurring:true and order_id to create/recurring (siem-server-new/src/payment/providers/RazorpayProvider.ts:274). D14's durable unique attempt and original-order inspection are present (siem-server-new/src/payment/providers/RazorpayProvider.ts:199); this agrees with its source-resolved status, not a gateway certification. Registration creates an order with customer_id and token configuration but no recurring:1 (siem-server-new/src/payment/providers/RazorpayProvider.ts:77), although the main plan explicitly requires it (Migrationphasewiseplan.md:205). The widget does set recurring:1 (siem-client-new/components/reusable/price-calculator/index.tsx:658). Confirmation would require an authoritative accepted request contract or a captured signup order with a bank-confirmed reusable token; neither was obtained in this read-only run. Do not infer an API rejection from the omission alone.

D14siem-server-new/src/payment/PaymentProvider.interface.ts:62 siem-server-new/src/payment/providers/RazorpayProvider.ts:74 siem-server-new/src/payment/billingTransaction.ts:31
4. Plans, rates, Enterprise and admin plan managementCompleteInactive-plan/rate and quote-only admission findings A1/A16 are resolved; D73 rate guards and existing plan management remain implemented.

Latest resolution reconciliation: A16 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/services/subscription.service.ts:1049

Earlier audit trace and patch notes · historical
A1 and D73 are fixed; quote-only Enterprise admission still has the later plan-change bypass A16.

Fix refresh: owner confirms Batch 1/2 A findings fixed; existing resolution records and targeted current-source reads support this update. siem-server-new/src/services/subscription.service.ts:367siem-server-new/src/services/plan.service.ts:314Remaining A16

Original audit and later notes · historical, before this refresh
Deactivation is bypassed at checkout (A1); D73 rate validation remains incomplete.
Trace and register status

Admin create/edit maps the two base prices and interval discounts into tax-inclusive stored per-asset monthly rates; editing preserves stored rates until pricing inputs change (siem-admin-new/app/dashboard/plans/page.tsx:203, siem-admin-new/app/dashboard/plans/page.tsx:286, siem-admin-new/app/dashboard/plans/page.tsx:374). Server routes are super-admin guarded and plan create/edit write database rows without Stripe products or prices (siem-server-new/src/routes/plans.routes.ts:25, siem-server-new/src/services/plan.service.ts:185, siem-server-new/src/services/plan.service.ts:292). D1/D2 tax multiplication and D28 gateway-dependent plan writes agree with resolved in these paths. Activate/deactivate persists isActive; listing filters inactive plans (siem-server-new/src/services/plan.service.ts:533, siem-server-new/src/prisma_repos/plan.repository.ts:64), but direct selection/setup accepts their IDs (A1). Enterprise is seeded at 101–150 with isCustom:true and requires usable quote context for checkout (siem-server-new/scripts/seed-plans.ts:8, siem-server-new/src/services/discountSelection.service.ts:96). Plan stats group ACTIVE/TRIALING together and sum paid money separately in USD/INR; the modal consumes those fields (siem-server-new/src/services/plan.service.ts:938, siem-server-new/src/services/plan.service.ts:951, siem-admin-new/app/dashboard/plans/plan-details-modal.tsx:145). D73 is listed resolved, and the listed UI fixes exist, but the saved-rate guard is incomplete: the form allows 100% interval discounts with positive bases, producing rate 0; server create/update currency rates are only z.number(), and services check positive basePriceINR rather than all saved rates (siem-admin-new/app/dashboard/plans/page.tsx:123, siem-admin-new/app/dashboard/plans/page.tsx:307, siem-server-new/src/validators/planSchema.ts:16, siem-server-new/src/validators/planSchema.ts:50, siem-server-new/src/services/plan.service.ts:101, siem-server-new/src/services/plan.service.ts:257). A saved zero currency rate then makes getLockedPriceAmount reject checkout (siem-server-new/src/services/subscription.service.ts:62). No new A number duplicates this D73 validation gap.

Later evidence: Batch 2 / flows 16–17: current uncommitted source now rejects inactive setup/selection and validates all six paid-plan rates. This observed external change supersedes the specific A1/D73 source observations; the earlier verdict and historical findings are preserved. siem-server-new/src/services/subscription.service.ts:354 siem-server-new/src/services/discountSelection.service.ts:175 siem-server-new/src/services/plan.service.ts:18 siem-server-new/src/services/plan.service.ts:195 siem-server-new/src/services/plan.service.ts:313.

Later evidence: Batch 3 / flow 26: the plan-change endpoint bypasses quote-only Enterprise admission (A16), independently of the patched inactive-plan/rate defects. The earlier verdict and A1 resolution remain untouched. siem-server-new/src/services/subscription.service.ts:1017 siem-server-new/scripts/seed-plans.ts:8.

D1 · D2 · D28 · D73siem-admin-new/app/dashboard/plans/page.tsx:203 siem-server-new/src/services/plan.service.ts:533 siem-server-new/src/validators/planSchema.ts:7
5. Price preview and locked priceCompleteThe server resolves the full interval price, compares the displayed charge and locks it without extra tax.
Trace and register status

The public action sends plan, interval, country and assets to GET /price-preview; route validation and controller pass them to the preview service (siem-client-new/action/subscriptionAction.ts:178, siem-server-new/src/routes/subscription.routes.ts:114, siem-server-new/src/controllers/subscription.controller.ts:272). The rate helper chooses the USD/INR interval column and multiplies per-asset monthly rate × assets × 1/3/12, without a second tax adjustment (siem-server-new/src/services/subscription.service.ts:49). For example, a stored $10 yearly rate and 5 assets produce $600 for the full cycle; yearly duration is separately 360 days (siem-server-new/src/services/subscription.service.ts:71, siem-server-new/src/services/subscription.service.ts:171). Public preview returns full price, currency, running promotions and INR headroom suggestions (siem-server-new/src/services/discountSelection.service.ts:172). Signed-in checkout displays selection.chargeAmount, sends it only for comparison and re-resolves server selection; a changed amount returns PRICE_CHANGED before charging (siem-client-new/components/reusable/price-calculator/index.tsx:500, siem-server-new/src/services/subscription.service.ts:469, siem-server-new/src/controllers/subscription.controller.ts:51). The subscription stores its full locked price and selected discount independently (siem-server-new/src/services/subscription.service.ts:510). D1/D2 and D60 agree with resolved for these price handoffs. Plan eligibility is reported separately in flow 4.

D1 · D2 · D60siem-server-new/src/services/subscription.service.ts:49 siem-server-new/src/services/discountSelection.service.ts:172 siem-client-new/action/subscriptionAction.ts:178
6. Free-trial signup and organization validationCompleteA2/A3/A7 are resolved: locked eligibility, atomic local provisioning with compensation, and pre-claim organization validation are present.

Fix refresh: owner confirms Batch 1/2 A findings fixed; existing resolution records and targeted current-source reads support this update. siem-server-new/src/services/subscription.service.ts:392siem-server-new/src/services/subscription.service.ts:404siem-server-new/src/services/subscription.service.ts:448siem-server-new/src/validators/subscriptionSchema.ts:11

Original audit and later notes · historical, before this refresh
Normal claim exists, but concurrent claims and failed provisioning are unsafe; invalid organization names pass (A2/A3/A7).
Trace and register status

Onboarding renders PriceCalculator; the calculator queries /claimed_free, validates organization availability and submits the free plan through /create-checkout-session (siem-client-new/app/(web)/onboarding/OnboardingClient.tsx:1073, siem-client-new/components/reusable/price-calculator/index.tsx:330, siem-client-new/components/reusable/price-calculator/index.tsx:763, siem-client-new/components/reusable/price-calculator/index.tsx:558). Both checkout route names reach the same controller/service (siem-server-new/src/routes/subscription.routes.ts:61, siem-server-new/src/controllers/subscription.controller.ts:19). Seeded free plan ID is 10000000-0000-4000-8000-000000000001 (siem-server-new/prisma/seed.ts:166). Claim creates PENDING_SETUP, starts the trial clock, writes the user/default pointer and calls provisioning/IAM/emails/reminder scheduling without creating a PaymentCustomer or opening a gateway (siem-server-new/src/services/subscription.service.ts:366, siem-server-new/src/services/subscription.service.ts:412). Country-derived provider/currency are stored as future billing routing, not a gateway authorization (siem-server-new/src/services/subscription.service.ts:381). Admin trial activation preserves the deadline; trial-ending and daily finalization accept an unused PENDING_SETUP trial (siem-server-new/src/services/subscription.service.ts:1448, siem-server-new/src/bullmq/workers/lifecycleWorker.ts:26, siem-server-new/src/bullmq/workers/repeatableWorker.ts:712): this agrees with D15's resolved claim-clock behavior. A2/A3/A7 prevent a complete verdict.

D15siem-client-new/components/reusable/price-calculator/index.tsx:534 siem-server-new/src/services/subscription.service.ts:355 siem-server-new/src/services/subscription.service.ts:366
7. Stripe paid signup and first chargeCompleteStripe signup findings and encrypted refresh replay A38 are resolved; setup/token/cycle-0/settlement handoffs are present.

Verdict refresh: current resolution records and targeted source evidence supersede earlier reasons. siem-client-new/auth/lib/apiClientV2.ts:128siem-client-new/auth/lib/apiClientV2.ts:194

Earlier audit verdict and evidence · historical
Signup findings A7/A8/A35/A36 and the encrypted 401-refresh replay A38 are resolved.

Latest resolution reconciliation: A35,A36 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/middlewares/iam.middleware.ts:60siem-server-new/src/middlewares/iam.middleware.ts:64 siem-server-new/src/services/iam/iam.registry.ts:50siem-server-new/src/services/iam/iam.registry.ts:52

Earlier audit trace and patch notes · historical
A7/A8 and D49 are fixed; the later encrypted 401 replay failure A38 still affects Stripe setup.

Fix refresh: owner confirms Batch 1/2 A findings fixed; existing resolution records and targeted current-source reads support this update. siem-server-new/src/services/subscription.service.ts:546A7 resolvedD49 resolvedsiem-client-new/auth/lib/apiClientV2.ts:133

Original audit and later notes · historical, before this refresh
The core chain is present, but invalid org names can collect money without a workspace (A7), and cancellation still goes to a missing route.
Trace and register status

PriceCalculator → setupSubscriptionAction → POST /setup → createSubscription → createAndInitiatePayment fixes country, server price/discount and an UNPAID cycle-0 row, resolves PaymentCustomer and returns setupUrl (siem-client-new/components/reusable/price-calculator/index.tsx:593, siem-client-new/action/subscriptionAction.ts:231, siem-server-new/src/routes/subscription.routes.ts:107, siem-server-new/src/services/subscription.service.ts:500, siem-server-new/src/services/subscription.service.ts:565). Hosted setup receives both Checkout Session and SetupIntent metadata (siem-server-new/src/payment/providers/StripeProvider.ts:45). Signed webhook normalization rejects Indian cards or emits TOKEN_SAVED; processor stores the card and queues cycle 0; worker claims the single signup charge and confirms an off-session PaymentIntent (siem-server-new/src/server.ts:76, siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:66, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:105, siem-server-new/src/bullmq/workers/billingCycleWorker.ts:269, siem-server-new/src/payment/providers/StripeProvider.ts:131). payment_intent.succeeded → invoice/payment + PENDING_SETUP transaction → durable settlement follow-up → organization, owner IAM and onboarding emails (siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:120, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:475, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:658). No paid-period clock starts in first settlement; admin activation starts it (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:578, siem-server-new/src/services/subscription.service.ts:1475). D36 agrees with resolved redelivery behavior, D37's per-subscription polling exists; D49 remains pending: cancel_url uses /onboarding/pricing, for which no page exists (siem-server-new/src/controllers/subscription.controller.ts:34, siem-client-new/app/(web)/onboarding/page.tsx:1; directory listing found only the base, success and payment-failed routes). D19 remains pending: the onboarding admin email passes currency:"usd" (siem-server-new/src/services/subscription.service.ts:692). Shared A1/A7 also affect this signup.

Later evidence: Batch 2 / flow 16: A8 identifies a separate concurrent paid-checkout boundary failure across two subscription IDs; per-subscription charge locking does not cover it. siem-server-new/src/services/subscription.service.ts:480 siem-server-new/src/services/subscription.service.ts:517 siem-server-new/src/services/discountSelection.service.ts:144.

Later dependency evidence: Batch 5 / apiClientV2: A38 prevents a 401-refresh replay of encrypted setup JSON from reaching valid billing input. siem-client-new/auth/lib/apiClientV2.ts:133 siem-server-new/src/lib/payloadSecurity.ts:45. Section 10

D19 · D36 · D37 · D49siem-client-new/components/reusable/price-calculator/index.tsx:593 siem-server-new/src/payment/providers/StripeProvider.ts:45 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:269
8. Razorpay paid signup, mandate and verifyCompleteRazorpay caller/order binding and encrypted refresh replay are fixed; signup mandate/capture/verification handoffs are present.

Verdict refresh: current resolution records and targeted source evidence supersede earlier reasons. siem-client-new/auth/lib/apiClientV2.ts:128siem-client-new/auth/lib/apiClientV2.ts:194

Earlier audit verdict and evidence · historical
Caller/order binding, A35/A36 and the encrypted 401-refresh setup/verify replay A38 are resolved.

Latest resolution reconciliation: A35,A36 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/middlewares/iam.middleware.ts:60siem-server-new/src/middlewares/iam.middleware.ts:64 siem-server-new/src/services/iam/iam.registry.ts:50siem-server-new/src/services/iam/iam.registry.ts:52

Earlier audit trace and patch notes · historical
D18/D57 verification binding is fixed; the later encrypted 401 replay failure A38 still affects Razorpay setup/verify.

Fix refresh: owner confirms Batch 1/2 A findings fixed; existing resolution records and targeted current-source reads support this update. D18/D57 resolvedsiem-server-new/src/services/subscription.service.ts:669siem-client-new/auth/lib/apiClientV2.ts:245

Original audit and later notes · historical, before this refresh
Signed capture can settle and save the mandate, but browser verify still accepts another subscription/payment binding (D18/D57).
Trace and register status

The same /setup path returns an order for the discounted first-cycle amount and a full-price-plus-headroom mandate ceiling (siem-server-new/src/services/subscription.service.ts:487, siem-server-new/src/services/subscription.service.ts:573, siem-server-new/src/payment/providers/RazorpayProvider.ts:74). The popup uses order/customer IDs, recurring:1, the exact first amount, timeout:1800 and a verify callback (siem-client-new/components/reusable/price-calculator/index.tsx:654). Browser verification checks the order|payment HMAC and bank-confirmed token, then emits TOKEN_SAVED; Razorpay token saving does not enqueue another first charge (siem-server-new/src/payment/providers/RazorpayProvider.ts:145, siem-server-new/src/services/subscription.service.ts:620, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:132). Independently, signed payment.captured fetches server order notes, checks gateway/currency and mandate customer binding, persists a pending token, then attempts bank confirmation; token.confirmed finds that pending token if confirmation comes later (siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:31, siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:104, siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:85). Shared settlement/follow-up writes money, PENDING_SETUP, organization, IAM and emails (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:475, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:658). D13 and D36 agree with source-resolved behavior; D44 agrees with resolved: payment.authorized has no capture branch and only captured events settle (siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:98). D18 and D57 remain pending: verify receives no caller ID and never checks order notes against the posted subscription or payment.customer_id against its owner before saving the token (siem-server-new/src/routes/subscription.routes.ts:127, siem-server-new/src/controllers/subscription.controller.ts:316, siem-server-new/src/services/subscription.service.ts:648). D19's USD admin email and A1/A7 also apply. Registration-order API acceptance is Unverified in flow 3.

Later dependency evidence: Batch 5 / apiClientV2: the same encrypted-request replay problem affects Razorpay setup/verification when authentication needs refreshing (A38). siem-client-new/auth/lib/apiClientV2.ts:245. Section 10

D13 · D18 · D19 · D36 · D44 · D57siem-server-new/src/payment/providers/RazorpayProvider.ts:74 siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:104 siem-server-new/src/services/subscription.service.ts:620
9. Fully discounted zero-amount signupCompleteA7 is resolved; valid zero-cost signup settles internally without a gateway. Separate quote-acceptance gaps remain in flow 17.

Fix refresh: owner confirms Batch 1/2 A findings fixed; existing resolution records and targeted current-source reads support this update. A7 resolvedsiem-server-new/src/validators/subscriptionSchema.ts:11Separate D48 quote flow

Original audit and later notes · historical, before this refresh
Internal settlement correctly avoids gateways; the shared organization-validation gap still strands this signup (A7).
Trace and register status

After server price selection and coupon attachment, chargeAmount ≤ 0 emits a deterministic internal PAYMENT_SUCCEEDED with amountPaid:0 and the actual discount snapshot; resolveTargetCustomer and gateway setup are below the return and never run (siem-server-new/src/services/subscription.service.ts:500, siem-server-new/src/services/subscription.service.ts:552). The shared transaction redeems the booking, creates invoice/payment, consumes one discounted cycle, accepts a linked quote only at settlement, writes PENDING_SETUP and the user's current subscription (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:494, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:524, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:578). Follow-up provisions the org, IAM and onboarding emails; the client routes to the same success page without a paid-gateway signal (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:666, siem-client-new/components/reusable/price-calculator/index.tsx:623). D11's zero-settlement handoff, D48 acceptance timing and D62 stored discount details agree with resolved for this path; the complete discount subsystem and PDF remain for later batches. Country routing survives for future nonzero cycles, with no saved payment method requested (siem-server-new/src/services/subscription.service.ts:546). A7 applies before that follow-up can complete.

Later evidence: Batch 2 / flow 17: settlement accepts a quote after payment, but the still-public /quote/accept route also accepts it before payment (D48 remains incomplete). siem-server-new/src/routes/plans.routes.ts:22 siem-server-new/src/services/plan.service.ts:879.

D11 · D48 · D62siem-server-new/src/services/subscription.service.ts:552 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:494 siem-client-new/components/reusable/price-calculator/index.tsx:623
10. Success page, status polling and legacy checkout routeCompleteA5/D27 are fixed; bounded owner polling works and the unused legacy session-invoice route was removed after caller recheck.

Fix refresh: owner confirms Batch 1/2 A findings fixed; existing resolution records and targeted current-source reads support this update. siem-client-new/app/(web)/onboarding/success/page.tsx:21D27 resolvedRemaining stale route evidence

Original audit and later notes · historical, before this refresh
Owner-scoped polling exists, but it has no two-minute bound (A5); the session-invoice path is stale (D27).
Trace and register status

Every paid/free/zero signup passes subscriptionId; Stripe also adds session_id (siem-server-new/src/services/subscription.service.ts:582, siem-server-new/src/payment/providers/StripeProvider.ts:50, siem-client-new/components/reusable/price-calculator/index.tsx:563, siem-client-new/components/reusable/price-calculator/index.tsx:679). GET /status/:id is authenticated and returns only the owner's row plus checkoutClosed/setupReady; setupReady waits for first-settlement follow-up (siem-server-new/src/routes/subscription.routes.ts:155, siem-server-new/src/services/subscription.service.ts:1764). The page polls every 2 seconds, waits for both settled status and follow-up, refreshes auth/IAM and handles failed/closed/not-found outcomes (siem-client-new/app/(web)/onboarding/success/page.tsx:44). D37's successful/failed status branches agree with resolved, but A5 identifies an uncovered indefinitely pending case. D27 agrees with pending: after success, /session-details still expands a Stripe session invoice; mode:setup has no checkout invoice, so the download link never receives an ID (siem-client-new/app/(web)/onboarding/success/page.tsx:79, siem-server-new/src/services/subscription.service.ts:1627, siem-server-new/src/payment/providers/StripeProvider.ts:47). /create-checkout-session is LIVE as the free-trial caller's alias of /setup, not a dead gateway-subscription implementation (siem-client-new/components/reusable/price-calculator/index.tsx:558, siem-server-new/src/routes/subscription.routes.ts:61). ACTIVE does not auto-redirect: it enters the same done view with a manual navigation button (siem-client-new/app/(web)/onboarding/success/page.tsx:19, siem-client-new/app/(web)/onboarding/success/page.tsx:151); see documentation mismatch notes.

Later evidence: Batch 2 / flow 24: current uncommitted status polling returns invoice.externalId and the success screen has a delayed-confirmation state, superseding those A5/D27 observations. The earlier verdict is preserved; later flow 24 still finds ignored delivery failures (A13). siem-server-new/src/services/subscription.service.ts:1798 siem-client-new/app/(web)/onboarding/success/page.tsx:48.

Cleanup: old session-details route and Stripe invoice helpers removed; original trace is historical.

D27 · D37siem-client-new/app/(web)/onboarding/success/page.tsx:44 siem-server-new/src/services/subscription.service.ts:1764 siem-server-new/src/routes/subscription.routes.ts:61
11. Declined first charge and starting overCompleteA6 is resolved: first-charge failure email uses the attempted amount, proper currency and a start-over link.

Fix refresh: owner confirms Batch 1/2 A findings fixed; existing resolution records and targeted current-source reads support this update. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:809A6 resolved

Original audit and later notes · historical, before this refresh
Failure state and popup/new-checkout behavior are correct; the failure email reports the wrong money and an empty recovery link (A6).
Trace and register status

Normalized first-charge failure uses the subscription/coupon locks, rejects stale/closed attempts and writes PAYMENT_FAILED. Stripe also closes checkout and releases its booking; Razorpay leaves the popup's booking alive (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:747). The first-charge branch sends one email only on the first transition and returns before retry/grace scheduling (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:782). The popup leaves retry at its documented default and changes nothing on dismissal except loading state (siem-client-new/components/reusable/price-calculator/index.tsx:654; expected retry behavior is DiscountSelectionplan.md:53). Stripe's new card requires starting over; new signup closes unfinished checkouts and refuses a pending prior payment; a captured prior Razorpay payment is settled before refusing a new signup (siem-server-new/src/services/subscription.service.ts:460, siem-server-new/src/services/couponBooking.service.ts:130). Manual retry accepts only PAST_DUE, not PAYMENT_FAILED (siem-server-new/src/services/subscription.service.ts:1668). D3 and D38 agree with resolved for these state/checkout behaviors. A6 is an additional customer-message defect, not a re-report of D3.

Later evidence: Batch 2 / flow 21: current uncommitted failure handling now passes the actual attempted amount and a billing CTA; the sender formats that amount in the supplied currency. This supersedes the A6 snapshot, without re-auditing the full first-charge screen. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:819 siem-server-new/src/email/email.service.ts:1631.

D3 · D38siem-server-new/src/payment/processor/PaymentEventProcessor.ts:745 siem-server-new/src/services/couponBooking.service.ts:130 siem-client-new/components/reusable/price-calculator/index.tsx:654
12. Abandoned signup and task:abandoned-cleanupCompleteA4 is resolved: closed coupon/quote signups are removable once no BOOKED redemption remains; legacy provider-null rows are tracked in flow 48.

Fix refresh: owner confirms Batch 1/2 A findings fixed; existing resolution records and targeted current-source reads support this update. siem-server-new/src/bullmq/workers/repeatableWorker.ts:584siem-server-new/src/bullmq/workers/repeatableWorker.ts:619Separate legacy A33

Original audit and later notes · historical, before this refresh
Non-coupon cleanup protects Razorpay late payment; coupon/quote-linked abandoned subscriptions are never deleted (A4).
Trace and register status

The queue runs abandoned-cleanup daily at 00:15 and selects UNPAID/PAYMENT_FAILED older than 25 hours, provider set, no in-flight debit and couponId:null (siem-server-new/src/bullmq/queues/repeatableQueue.ts:51, siem-server-new/src/bullmq/workers/repeatableWorker.ts:570). Thus the age threshold is 25 hours, but actual deletion can be almost one extra daily interval later; that follows the existing schedule rather than an exact 25-hour timer. Non-coupon Razorpay rows query order/payment history: captured money is settled, pending or failed-within-three-days authorization is retained, and only definitively closed rows are deleted under a subscription lock (siem-server-new/src/bullmq/workers/repeatableWorker.ts:598, siem-server-new/src/payment/providers/RazorpayProvider.ts:108). Coupon/quote bookings use minute maintenance and gateway-aware expiry to close checkout/release the slot (siem-server-new/src/bullmq/queues/repeatableQueue.ts:20, siem-server-new/src/services/couponBooking.service.ts:182), but never clear subscription.couponId or delete the row (siem-server-new/src/services/couponBooking.service.ts:214): A4. Stripe setup closure and in-flight protection are implemented in the booking and provider paths (siem-server-new/src/services/couponBooking.service.ts:104, siem-server-new/src/payment/providers/StripeProvider.ts:78). Legacy null-provider rows are outside this cleanup query; their cutover handling belongs to flows 45–48.

Later evidence: Batch 2 / flow 16: external working-tree cleanup edits now allow a closed cycle-0 row when its coupon booking is no longer BOOKED, superseding the specific A4 skip observation. The earlier historical verdict is preserved. siem-server-new/src/bullmq/workers/repeatableWorker.ts:584. siem-server-new/src/bullmq/workers/repeatableWorker.ts:619.

—siem-server-new/src/bullmq/workers/repeatableWorker.ts:568 siem-server-new/src/services/couponBooking.service.ts:182 siem-server-new/src/payment/providers/RazorpayProvider.ts:108
13. Admin activation, provisioning and finalizationCompleteA3/A7/A13 are resolved and the current provisioning/follow-up is wired. Owner decision B (13 Oct 2026): the producerless provisioning-jobs queue is retired. Its worker is no longer loaded by bullmqserver.ts and its Bull Board entry is removed. The unreferenced files provisioningQueue.ts and provisioningWorker.ts are left for the owner to delete, and any leftover bull:provisioning-jobs:* keys in Redis are cleared by hand.

Fix refresh: owner confirms Batch 1/2 A findings fixed; existing resolution records and targeted current-source reads support this update. A3 resolvedA13 resolvedsiem-server-new/src/payment/processor/PaymentEventProcessor.ts:675siem-server-new/src/bullmq/queues/provisioningQueue.ts:22

Original audit and later notes · historical, before this refresh
Paid activation is conditional and schedules renewal; free provisioning can be stranded (A3/A7), and task:provision-org has no producer.
Trace and register status

Admin UI offers only PENDING_SETUP → TRIALING for a free plan or ACTIVE for a paid plan, explains the clock and sends PUT /update-subscription/:id (siem-admin-new/app/dashboard/customers/[id]/components/EditSubscriptionStatus.tsx:48, siem-server-new/src/routes/subscription.routes.ts:68). Server validates source/destination and conditionally updates only the still-eligible row. Paid activation starts the period now, increments the cycle once, schedules renewal at currentPeriodEnd and restores activation fields on a caught scheduling failure (siem-server-new/src/services/subscription.service.ts:1425, siem-server-new/src/services/subscription.service.ts:1475, siem-server-new/src/services/subscription.service.ts:1503, siem-server-new/src/services/subscription.service.ts:1532). D32 agrees with source-resolved conditional activation and ordinary scheduling-failure rollback. Trial activation preserves the original claim deadline; midnight finalization and trial-ending handle unused trials (siem-server-new/src/services/subscription.service.ts:1448, siem-server-new/src/bullmq/workers/repeatableWorker.ts:678, siem-server-new/src/bullmq/workers/lifecycleWorker.ts:26): D15 agrees with resolved for those steps. Paid/zero provisioning runs in durable settlement follow-up, recovered by minute maintenance (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:658, siem-server-new/src/bullmq/workers/repeatableWorker.ts:48). Free signup instead calls provisioning inline (A3). task:provision-org's queue helper and worker exist, but searching scheduleProvisioning found only its declaration; no signup path enqueues it (siem-server-new/src/bullmq/queues/provisioningQueue.ts:22, siem-server-new/src/bullmq/workers/provisioningWorker.ts:6). A7 blocks provisioning even on the durable paths. Process-crash atomicity between DB commit and queue add is not established by the caught-error rollback; no fault injection was allowed.

Later evidence: Batch 2 / flow 24: durable money follow-up still ignores returned email failures and can mark the required delivery step complete (A13). Earlier activation/provisioning observations and concurrent resolution notes are retained. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:679 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:693 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:703.

Later evidence: Batch 3 / flow 30: paid trial conversion uses a separate post-money branch without the ordinary durable follow-up (A20). This does not reopen patched free-trial provisioning A3; it concerns conversion after its charge. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:376 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:439 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:197.

Later dependency evidence: Batch 5 / provisioningQueue and provisioningWorker: confirmed no runtime enqueue caller. Current free claim uses transaction-aware remote/local helpers and paid settlement follow-up calls addNewOrg directly; remote re-entry after a partial failure is Unverified, not a newly proven defect. siem-server-new/src/bullmq/queues/provisioningQueue.ts:22 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:687. Section 10

Cleanup: session invoice/getStripe/manual plan-script/CustomPricing dead code and unused provisioning producer removed. Provisioning queue/worker retained pending Redis job inventory; live customer portal/quote and other billing defects are not deleted. Cleanup record

D15 · D32siem-server-new/src/services/subscription.service.ts:1425 siem-admin-new/app/dashboard/customers/[id]/components/EditSubscriptionStatus.tsx:48 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:658
14. Coupons: create, toggle, delete, reservation and redemptionCompleteAdmin CRUD, eligibility, last-slot reservation and first-settlement redemption are wired; checkout concurrency is assessed in flow 16.
Trace and register status

Admin list/create/toggle/delete → Super Admin routes → validated bodies/IDs → DiscountController → DiscountService → Coupon rows (siem-admin-new/app/dashboard/coupons/page.tsx:145, siem-server-new/src/routes/admin.routes.ts:89, siem-server-new/src/controllers/discount.controller.ts:15). Server limits percentages to 100, requires positive integer finite cycles/caps, USD/INR for fixed amounts, and existing paid plan IDs; QUOTE rows are hidden by default (siem-server-new/src/validators/discountSchema.ts:5, siem-server-new/src/services/discount.service.ts:6, siem-admin-new/app/dashboard/coupons/page.tsx:130). Customer codes go through authenticated discount-selection. couponOffer checks dates, plan, currency, active/held status, prior redemption and remaining capacity; select locks coupon rows before the user, and reserveForSelection increments bookingCount (siem-server-new/src/services/discountSelection.service.ts:69, siem-server-new/src/services/discountSelection.service.ts:222, siem-server-new/src/services/couponBooking.service.ts:38, siem-server-new/src/services/couponBooking.service.ts:243). With maxRedemptions=1 and both counters 0, A reserves the slot under the coupon lock and B is rejected as currently booked. First settlement atomically changes BOOKED → REDEEMED and moves one count from booking to redemption; released late-paid checkouts are intentionally honored, even beyond a subsequently filled cap (siem-server-new/src/services/couponBooking.service.ts:76, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:501). Deletion locks the coupon and refuses any redemption/booking/subscription history (siem-server-new/src/services/discount.service.ts:63). The UI checks only redemptionCount before offering Delete, so a booked coupon's deletion attempt gets the server's explanatory refusal (siem-admin-new/app/dashboard/coupons/page.tsx:505). D11 reservation/redemption, D12 currency isolation and D61 paid-plan validation agree with source-resolved status here; A8 is a distinct same-customer checkout race.

D11 · D12 · D61siem-admin-new/app/dashboard/coupons/page.tsx:122 siem-server-new/src/services/discount.service.ts:19 siem-server-new/src/services/couponBooking.service.ts:38
15. Promotions: management and applicationCompleteValidated management and currency eligibility feed the server's default promotion selection.
Trace and register status

Admin list/create/toggle → Super Admin routes → DiscountController → DiscountService → Promotion rows (siem-admin-new/app/dashboard/promotions/page.tsx:102, siem-admin-new/app/dashboard/promotions/page.tsx:120, siem-server-new/src/routes/admin.routes.ts:94, siem-server-new/src/controllers/discount.controller.ts:44, siem-server-new/src/services/discount.service.ts:83). Server checks percentage/fixed currency, positive finite cycles, paid plan IDs and endDate > startDate (siem-server-new/src/validators/discountSchema.ts:25, siem-server-new/src/services/discount.service.ts:95). Preview/selection filter active/in-date offers by plan and country-derived currency, calculate each discount on the full cycle price and sort largest amount first (siem-server-new/src/services/discountSelection.service.ts:276, siem-server-new/src/services/discountSelection.service.ts:287). Without a quote/held offer, KEEP defaults to the first promotion. Checkout stores its ID, concrete amount and cycle allowance; settlement consumes that allowance (siem-server-new/src/services/discountSelection.service.ts:123, siem-server-new/src/services/subscription.service.ts:493, siem-server-new/src/services/subscription.service.ts:543, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:581). A held switched-off promotion remains valid until its end date; renewals use the locked allowance rather than campaign expiry (siem-server-new/src/services/discountSelection.service.ts:211, siem-server-new/src/services/subscription.service.ts:112). D12/D61 agree with resolved; type/percentage labels implement D71 (siem-client-new/components/reusable/price-calculator/index.tsx:505). Main-plan “best deal wins” versus selected-offer wording remains a documentation conflict, not stacking.

D12 · D61 · D71siem-admin-new/app/dashboard/promotions/page.tsx:102 siem-server-new/src/services/discount.service.ts:83 siem-server-new/src/services/discountSelection.service.ts:287
16. Discount selection, hold and booking maintenanceCompleteA8 is resolved: the user-locked signup transaction refuses a second open cycle-0 checkout; selection/hold maintenance remains wired.

Fix refresh: owner confirms Batch 1/2 A findings fixed; existing resolution records and targeted current-source reads support this update. siem-server-new/src/services/subscription.service.ts:546A8 resolved

Original audit and later notes · historical, before this refresh
Hold/capacity/expiry handoffs exist, but concurrent checkout calls can each open a signup (A8).
Trace and register status

Calculator actions → discountSelectionAction → authenticated strict /discount-selection → controller → select (siem-client-new/components/reusable/price-calculator/index.tsx:400, siem-client-new/action/subscriptionAction.ts:203, siem-server-new/src/routes/subscription.routes.ts:120, siem-server-new/src/controllers/subscription.controller.ts:148). Selection uses database time, sorted coupon locks then user lock, expiry=min(now+35 minutes, firstSelectedAt+24 hours), and one reused row per customer (siem-server-new/src/services/discountSelection.service.ts:79, siem-server-new/src/services/discountSelection.service.ts:144). KEEP honors an inactive held offer, revalidates its dates/plan/currency and releases/falls back atomically when unavailable (siem-server-new/src/services/discountSelection.service.ts:198, siem-server-new/src/services/discountSelection.service.ts:135). Checkout re-resolves KEEP and uses the browser amount only for PRICE_CHANGED, then locks/attaches the booking and marks selection USED (siem-server-new/src/services/subscription.service.ts:486, siem-server-new/src/services/subscription.service.ts:517). Minute maintenance expires idle selections, releases unattached slots and queues attached booking checks. Captured Razorpay money is settled; pending money is rechecked; failed payments stay through the three-day late window; closed checkout releases its slot (siem-server-new/src/bullmq/queues/repeatableQueue.ts:20, siem-server-new/src/bullmq/workers/repeatableWorker.ts:48, siem-server-new/src/services/couponBooking.service.ts:150, siem-server-new/src/services/couponBooking.service.ts:182). D11/D47 handoffs agree with resolved in those branches, but A8 shows the overall checkout boundary is incomplete: closeUnfinishedSignups runs before user locking, and the second request can reactivate a USED selection. D65/D71's all-offer list and labels exist, differing from the older hide-other-offers document (siem-client-new/components/reusable/price-calculator/index.tsx:508). Current uncommitted changes reject inactive plans in selection/preview/setup (siem-server-new/src/services/discountSelection.service.ts:175, siem-server-new/src/services/discountSelection.service.ts:353, siem-server-new/src/services/subscription.service.ts:354); the earlier flow 4 verdict is preserved with a note.

D11 · D47 · D65 · D71siem-server-new/src/services/discountSelection.service.ts:69 siem-server-new/src/services/couponBooking.service.ts:150 siem-server-new/src/services/subscription.service.ts:480
17. Custom quotes, quote coupon and expiryCompleteD48's public unpaid-accept route is removed, and valid quote checkout and settlement work. The quote-validate response no longer returns the legacy USD-only priceMonthly/Quarterly/Yearly and originalPrices (13 Oct 2026); neither caller (onboarding, buy-new-subscription) read them. Quotes are priced by the server's preview and checkout in the quote's own currency.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/src/services/plan.service.ts:836D48 resolved

Earlier register verdict/evidence · historical
Generation/expiry are implemented; public acceptance still consumes an unpaid quote (D48), and old-token/USD-only helper assumptions remain.
Trace and register status

Admin reply-with-quote sends typed message, plan, integer duration and UTC expiry/null through the Super Admin contact route/controller to sendQuote (siem-admin-new/app/dashboard/contact-us/page.tsx:510, siem-server-new/src/routes/contact-us.routes.ts:25, siem-server-new/src/controllers/contact-us.controller.ts:102, siem-server-new/src/validators/contactUsSchema.ts:61). Inquiry country fixes currency; free and now all inactive plans are refused. A discount creates a single-use QUOTE coupon with plan/currency/cycles and matching expiry; an undiscounted custom quote has no coupon (siem-server-new/src/services/plan.service.ts:571, siem-server-new/src/services/plan.service.ts:648). Email prices use full currency-aware intervals per asset, fixed discounts are stated separately, signup counts in the duration, and expiry shows UTC or “No expiry”. Returned email failure deletes the new coupon and refuses SENT; the signed JWT has no intrinsic expiry (siem-server-new/src/services/plan.service.ts:669, siem-server-new/src/services/plan.service.ts:718, siem-server-new/src/email/email.service.ts:2124, siem-server-new/src/utils/auth.ts:88). D16/D58/D61/D63/D64 agree with source-resolved behavior in those branches. Customer onboarding/portal uses /quote/validate for account routing, then plan/selection checks matching email, plan, country, current token/SENT (unless an honored hold), and expiry; Enterprise requires usable context (siem-client-new/app/(web)/onboarding/OnboardingClient.tsx:700, siem-client-new/app/portal/(me)/buy-new-subscription/page.tsx:47, siem-server-new/src/services/discountSelection.service.ts:311). First payment accepts a linked quote inside settlement (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:591). However public /quote/accept is still live and acceptQuote immediately writes ACCEPTED/clears tokens without payment (siem-server-new/src/routes/plans.routes.ts:22, siem-server-new/src/controllers/plan.controller.ts:115, siem-server-new/src/services/plan.service.ts:851, siem-server-new/src/services/plan.service.ts:877). No current client caller was found, but a link bearer can invoke the route and block unpaid checkout. Thus D48's settlement-only resolved claim is incomplete. Public validate/accept also omit matching the current stored token/SENT status, so a replaced/revoked signed token remains usable there when current contact expiry permits (siem-server-new/src/services/plan.service.ts:749, siem-server-new/src/services/plan.service.ts:918). D60's calculator authority fix exists; legacy USD-only rate fields remain in these public helpers, not the live charge source (siem-server-new/src/services/plan.service.ts:835, siem-server-new/src/services/plan.service.ts:907).

D16 · D48 · D58 · D60 · D61 · D63 · D64siem-admin-new/app/dashboard/contact-us/page.tsx:510 siem-server-new/src/services/plan.service.ts:571 siem-server-new/src/routes/plans.routes.ts:21
18. Discount cycles and settlement countingCompleteFinite/lifetime allowances are consumed only at successful settlement, including zero charges; duplicate-cycle money consumes no extra allowance.
Trace and register status

Server caps a discount at the full price and rounds to cents/paise; signup stores its per-cycle amount and finite count/null lifetime (siem-server-new/src/services/subscription.service.ts:155, siem-server-new/src/services/subscription.service.ts:543). resolveCyclePricing applies the discount while used < total or forever for null; an applicable notified pushed price uses its pending discount (siem-server-new/src/services/subscription.service.ts:112). Billing/retry/manual charges send the exact applied amount in server metadata, parsed by both webhook adapters (siem-server-new/src/bullmq/workers/billingCycleWorker.ts:290, siem-server-new/src/bullmq/workers/retryPaymentWorker.ts:93, siem-server-new/src/services/subscription.service.ts:1762, siem-server-new/src/payment/discountMetadata.ts:1). Settlement locks coupon/subscription, repeats invoice dedupe in the transaction and distinguishes already-settled cycles before state application (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:482). Only a nonduplicate successful payment with positive discount increments used; declines do not. Internal zero charges share settlement (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:581, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:615, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:754, siem-server-new/src/bullmq/workers/billingCycleWorker.ts:187). A 3-cycle offer covers signup 0, renewal 1 and renewal 2; renewal 3 has used=3 and no discount. Duplicate paid money is recorded, without another period or allowance increment and without a refund (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:491, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:583). D11's counters/dedupe and D62's stored invoice discount snapshot agree with resolved here; PDF rendering remains flow 36. Expiring or disabling the original offer does not erase already-locked cycles (siem-server-new/src/services/subscription.service.ts:112).

D11 · D12 · D62siem-server-new/src/services/subscription.service.ts:112 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:482 siem-server-new/src/payment/discountMetadata.ts:1
19. Renewal charges on both gatewaysCompleteA9/A14 are resolved: missing locked price enters recovery before zero settlement, and hourly missed-renewal reconciliation is registered.

Fix refresh: owner confirms Batch 1/2 A findings fixed; existing resolution records and targeted current-source reads support this update. siem-server-new/src/bullmq/workers/billingCycleWorker.ts:188siem-server-new/src/bullmq/queues/billingCycleQueue.ts:54siem-server-new/src/bullmq/queues/repeatableQueue.ts:108

Original audit and later notes · historical, before this refresh
Normal renewal is wired; missing price can become a free cycle (A9), and planned hourly billing reconciliation is absent (A14).
Trace and register status

Activation/follow-up schedules task:billing-cycle at currentPeriodEnd. Queue attempts=1 with fixed cycle job ID, removing a finished job before re-adding (siem-server-new/src/bullmq/queues/billingCycleQueue.ts:7, siem-server-new/src/bullmq/queues/billingCycleQueue.ts:26, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:686). Worker accepts ACTIVE/PAST_DUE for cycle >0, rejects stale cycles, delays early ACTIVE jobs until period end, and delegates cancellation/downgrade before charging (siem-server-new/src/bullmq/workers/billingCycleWorker.ts:99, siem-server-new/src/bullmq/workers/billingCycleWorker.ts:117). It resolves locked/pushed price minus the running discount, internally settles legitimate zero cycles, checks method/ceiling and takes the charge lock (siem-server-new/src/bullmq/workers/billingCycleWorker.ts:175, siem-server-new/src/bullmq/workers/billingCycleWorker.ts:204, siem-server-new/src/bullmq/workers/billingCycleWorker.ts:264). Stripe confirms an off-session PaymentIntent with the request key; Razorpay uses its durable cycle attempt/order then token,email,contact,recurring:true debit (siem-server-new/src/payment/providers/StripeProvider.ts:122, siem-server-new/src/payment/providers/RazorpayProvider.ts:196, siem-server-new/src/payment/providers/RazorpayProvider.ts:278). Adapters → shared money/status/period transaction → follow-up → next cycle/email (siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:120, siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:109, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:482, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:665). D1 tax-inclusive, D14 durable Razorpay guard, D24 period-end scheduling and D26 finished-job replacement agree with resolved. D25 remains pending: missing token on a nonzero cycle causes PAST_DUE/grace without an immediate failure email (siem-server-new/src/bullmq/workers/billingCycleWorker.ts:204). Before that guard, null locked price becomes 0 and can advance a free period (A9). Missing/invalid country-provider binding instead fails earlier, matching part of D33; full cutover remains flow 47 (siem-server-new/src/bullmq/workers/billingCycleWorker.ts:42). No billing-cycle-reconciliation registration/handler was found (A14).

Later evidence: Batch 3 / flow 26: an interval upgrade that creates an earlier period end does not move the existing later renewal job (A17). This is additional evidence beyond A9/A14; D24’s guard only delays early jobs. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:327 siem-server-new/src/services/subscription.service.ts:1245 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:117.

Latest lifecycle/access decision: D50 now starts a late-paid renewal period at payment, while on-time renewal starts at the old period end. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:546

D1 · D5 · D14 · D24 · D25 · D26 · D33siem-server-new/src/bullmq/workers/billingCycleWorker.ts:99 siem-server-new/src/payment/providers/StripeProvider.ts:122 siem-server-new/src/payment/providers/RazorpayProvider.ts:196
20. Razorpay mandate ceiling and PENDING_REAUTHCompleteAn applicable pushed full price above a known positive mandate ceiling changes status to PENDING_REAUTH and sends no debit.
Trace and register status

After period/cycle checks, worker compares the applicable notified full pending price—not the discounted charge—to the stored positive mandate ceiling. A breach writes PENDING_REAUTH/PRICE_EXCEEDS_MANDATE, schedules grace/final deactivation, emails reauthorization and returns before charge lock/debit (siem-server-new/src/services/subscription.service.ts:112, siem-server-new/src/bullmq/workers/billingCycleWorker.ts:230, siem-server-new/src/bullmq/workers/billingCycleWorker.ts:259). Full ₹6,000, ₹1,000 discount and ₹5,500 ceiling still require reauthorization: the ₹5,000 net payment cannot hide the full-price breach. A not-yet-applicable push uses locked pricing; an applicable successful charge promotes pending price/discount (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:606). D24/D72 agree with resolved for this gate. Push creation/notification stays flow 38 and full recovery stays flow 32. Client offers reauthorization and hides same-card retry on a mandate problem (siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:342). Generic grace warning date defects are D55 in flow 25, not an above-ceiling debit. Runtime mandate data/configuration were not tested.

D24 · D72siem-server-new/src/bullmq/workers/billingCycleWorker.ts:225 siem-server-new/src/services/subscription.service.ts:112 siem-server-new/src/bullmq/queues/lifecycleQueue.ts:35
21. Renewal failure, grace, retries and deactivationCompleteRenewal grace, warnings, expiry date and deactivation use the shared lifecycle path; D20/D55 are now resolved.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/src/services/subscription.service.ts:2367siem-server-new/src/bullmq/workers/repeatableWorker.ts:725

Earlier register verdict/evidence · historical
A10/A11/A12 are resolved; separate grace-warning/date defects D20/D55 remain.

Fix refresh: owner confirms Batch 1/2 A findings fixed; existing resolution records and targeted current-source reads support this update. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:849siem-server-new/src/bullmq/workers/lifecycleWorker.ts:147siem-server-new/src/bullmq/workers/retryPaymentWorker.ts:77Remaining D20/D55

Original audit and later notes · historical, before this refresh
Failure/retry policy exists; queue errors lose grace handoff (A10), expiry cleanup cannot retry safely (A11), and lock collisions end retries (A12).
Trace and register status

Adapters carry cycle, attempted amount, decline/error reason, retryAttempt and mandate classification; synchronous provider rejection also calls the normalized failure path (siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:153, siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:158, siem-server-new/src/bullmq/workers/billingCycleWorker.ts:302). Locked failure settlement rejects stale/closed cycles and writes PAST_DUE; only its first failure sends email and establishes grace. Temporary reasons schedule renewal→attempt 1, 1→2, 2→3, each 3 days later; manual failure starts no chain and mandate failure gets reauth reminders instead (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:754, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:815, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:856, siem-server-new/src/payment/retryableFailure.ts:36). Retry jobs keep IDs, attempt once, require PAST_DUE/current cycle/method/price and take the charge lock (siem-server-new/src/bullmq/queues/retryPaymentQueue.ts:4, siem-server-new/src/bullmq/workers/retryPaymentWorker.ts:26). Success settles through the shared processor and follow-up cancels old grace/retries; duplicate paid money is recorded without another cycle/refund (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:491, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:690). Grace/finalization are shared with CANCELLED/ENDED/PENDING_REAUTH; paid grace uses interval-configured tiers (siem-server-new/src/bullmq/queues/lifecycleQueue.ts:49, siem-server-new/src/bullmq/workers/lifecycleWorker.ts:75, siem-server-new/src/services/subscription.service.ts:2244). D4/D36 source fixes exist. D5's bounded policy exists, but A12 drops remaining automatic retries on a manual-payment collision. A10 loses first grace/retry scheduling on queue exception, and A11 strands cleanup after EXPIRED. D55 warning dates and D20 midnight paid-warning omission remain pending. If monthly grace is configured to 7 days, the day-9 third retry is after expiry and skipped; no actual production grace value was assumed. Current failure email now passes amountAttempted and a subscription CTA, unlike the Batch 1 snapshot (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:819).

Latest lifecycle/access decision: D50’s owner decision keeps SIEM off while unpaid; payment inside grace starts a fresh period. EXPIRED cannot recover. siem-server-new/src/utils/subscriptionAccess.ts:31siem-server-new/src/payment/processor/PaymentEventProcessor.ts:546

D4 · D5 · D20 · D25 · D36 · D55siem-server-new/src/payment/processor/PaymentEventProcessor.ts:754 siem-server-new/src/bullmq/workers/retryPaymentWorker.ts:22 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:135
22. Manual payment retryCompleteD6 stale pre-lock read is rechecked under the charge lock; D54 pending outcomes keep reconciliation and lock extension.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/src/services/subscription.service.ts:1865siem-server-new/src/payment/stripeChargeReconcile.ts:41

Earlier register verdict/evidence · historical
A35/A36 authorization and A38 replay are resolved; separately recorded D6/D54 stale-read/unknown-outcome gaps remain.

Verdict refresh: current resolution records and targeted source evidence supersede earlier reasons. Remaining D54 evidencesiem-client-new/auth/lib/apiClientV2.ts:128siem-client-new/auth/lib/apiClientV2.ts:194

Earlier audit verdict and evidence · historical
A35/A36 authorization and request replay A38 are fixed; known D6/D54 payment-outcome gaps remain.

Latest resolution reconciliation: A35,A36 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/middlewares/iam.middleware.ts:60siem-server-new/src/middlewares/iam.middleware.ts:64 siem-server-new/src/services/iam/iam.registry.ts:50siem-server-new/src/services/iam/iam.registry.ts:52

Earlier audit trace and patch notes · historical
Amount and ordinary result text are correct; stale pre-lock reads and expired unknown-outcome locks still permit duplicate payments (D6/D54).
Trace and register status

Portal requires siem:billing:retry, PAST_DUE, method and no mandate reauthorization problem. Retry action → IAM-guarded /retry-payment/:id → controller → service (siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:332, siem-client-new/action/subscriptionAction.ts:63, siem-server-new/src/routes/subscription.routes.ts:136, siem-server-new/src/controllers/subscription.controller.ts:128). Service refuses first-payment retry, charges the same pushed/locked amount minus remaining discount, settles zero internally and takes the charge lock for a nonzero charge (siem-server-new/src/services/subscription.service.ts:1694, siem-server-new/src/services/subscription.service.ts:1736). Manual metadata prevents an automatic chain from its failures; result text distinguishes ordinary processing/success and the client displays the server message (siem-server-new/src/services/subscription.service.ts:1747, siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:360). D6's amount/message parts agree with resolved, but its duplicate-safety claim is incomplete: subscription/cycle were read before locking and never re-read after it. If a scheduled retry settles cycle N while this request is awaiting other reads, the stale request can acquire the released lock and send another cycle-N debit with a timestamp key (siem-server-new/src/services/subscription.service.ts:1695, siem-server-new/src/services/subscription.service.ts:1714, siem-server-new/src/services/subscription.service.ts:1736, siem-server-new/src/services/subscription.service.ts:1757). Two successful $500 Stripe debits then mean $1,000 recorded for one period; duplicate settlement avoids a second period but refunds are forbidden (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:491). Unknown Stripe results keep the lock and enqueue reconcile; D54's >24-hour pending gap is confirmed in flow 23. Both register entries are listed resolved but these safety cases remain; they are not re-reported as new A findings.

Later evidence: Flow 50 found that scoped billing IAM can reject this otherwise-wired retry before its controller, and unscoped tenant checks can be bypassed (A35/A36). Later trace

Later dependency evidence: Batch 5 / request layer: encrypted manual retry after a 401/refresh can fail body validation (A38), in addition to earlier IAM/service observations. siem-client-new/auth/lib/apiClientV2.ts:203. Section 10

Latest lifecycle/access decision: Late manual renewal payment now receives the fresh paid period under D50. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:546

D6 · D54siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:357 siem-server-new/src/routes/subscription.routes.ts:136 siem-server-new/src/services/subscription.service.ts:1694
23. Unknown Stripe charge outcome reconciliationCompleteD54 is resolved: reconciliation extends an existing lock and rechecks a still-processing intent after the idempotency window.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/src/payment/stripeChargeReconcile.ts:41siem-server-new/src/payment/stripeChargeReconcile.ts:57

Earlier register verdict/evidence · historical
Same-request replay/search exists; a still-pending intent stops reconciliation while its 24-hour lock can expire (D54).
Trace and register status

Stripe connection/API/5xx/idempotency errors become pending/outcomeUnknown. Billing/retry/manual paths keep the lock and enqueue original params/key; worker handles task:stripe-charge-reconcile (siem-server-new/src/payment/providers/StripeProvider.ts:160, siem-server-new/src/bullmq/workers/billingCycleWorker.ts:301, siem-server-new/src/bullmq/workers/retryPaymentWorker.ts:99, siem-server-new/src/services/subscription.service.ts:1774, siem-server-new/src/bullmq/workers/repeatableWorker.ts:56). Backoff is 2/4/8/16/32 minutes then hourly for outcomeUnknown. Within its 24-hour window reconcile resends the same request; afterward it searches by chargeKey and sends success/failure to the processor with cycle/discount snapshots (siem-server-new/src/payment/stripeChargeReconcile.ts:24, siem-server-new/src/payment/stripeChargeReconcile.ts:33, siem-server-new/src/payment/stripeChargeReconcile.ts:57, siem-server-new/src/payment/providers/StripeProvider.ts:181). These D54 mechanisms exist, but a still-pending found intent returns without another check/lock extension; ordinary pending recordOutcome also returns (siem-server-new/src/payment/stripeChargeReconcile.ts:50, siem-server-new/src/payment/stripeChargeReconcile.ts:58). Redis's lock expires in 24 hours, without refresh here (siem-server-new/src/payment/chargeLock.ts:12, siem-server-new/src/payment/chargeLock.ts:19). A PAST_DUE manual charge still processing afterward can be followed by another timestamp-key retry before its settlement arrives (siem-server-new/src/services/subscription.service.ts:1757). D54 is only partially fixed. A failed enqueue also has no durable unresolved-request scanner: task producer is scheduleStripeChargeReconcile, while minute recovery scans applied invoices (siem-server-new/src/payment/stripeChargeReconcile.ts:24, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:651). This is retained under known D54 rather than a duplicate A number. Pending/late scenarios are source deductions, not gateway experiments.

Later evidence: Batch 3 / flow 26: the upgrade service does not schedule Stripe unknown-outcome reconciliation; it returns pending for that result. The earlier reconcile verdict remains unchanged, with D54’s scope gap noted on flow 26. siem-server-new/src/services/subscription.service.ts:1203 siem-server-new/src/services/subscription.service.ts:1316.

D54siem-server-new/src/payment/stripeChargeReconcile.ts:24 siem-server-new/src/payment/providers/StripeProvider.ts:181 siem-server-new/src/payment/chargeLock.ts:12
24. Settlement follow-upCompleteA13 is resolved: failed email delivery throws, successful email keys persist, and unfinished follow-up retries only missing messages.

Fix refresh: owner confirms Batch 1/2 A findings fixed; existing resolution records and targeted current-source reads support this update. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:675siem-server-new/src/payment/processor/PaymentEventProcessor.ts:679siem-server-new/src/payment/processor/PaymentEventProcessor.ts:681

Original audit and later notes · historical, before this refresh
Applied invoices retain a recoverable queue handoff, but returned email failures are ignored and follow-up is marked complete (A13).
Trace and register status

Settlement commits an applied invoice with no follow-up timestamp, then enqueues a stable invoice-ID task; redelivery re-enqueues unfinished follow-up rather than writing money again (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:197, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:644, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:646). Minute maintenance scans/claims up to 100 due invoices and re-enqueues; thrown task errors get BullMQ retry and later recovery (siem-server-new/src/bullmq/workers/repeatableWorker.ts:48, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:651, siem-server-new/src/bullmq/queues/repeatableQueue.ts:6). Cycle 0 creates missing organization membership, owner IAM and onboarding emails. A current just-settled renewal schedules the next cycle and removes old lifecycle/retry jobs, then sends success email. Cache invalidation and completion timestamp are last (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:665). A13: sender {success:false} results are ignored, so SMTP failure gets neither retry nor an unfinished follow-up. D19 is listed pending, but the current uncommitted source now passes subscription currency into the admin email; this observed fix is separate from A13 (siem-server-new/src/services/subscription.service.ts:725); D36 agrees with resolved for thrown processor errors. Current uncommitted status polling now returns invoice.externalId as invoiceId (siem-server-new/src/services/subscription.service.ts:1798); flow 10 receives a note, not a rewritten verdict. Full PDFs/messages stay flows 36–37.

Later evidence: Batch 3 / flow 37: current register D19 is now resolved and source still forwards subscription currency. A13’s ignored delivery-result issue remains separate and is not reopened as a new finding. siem-server-new/src/services/subscription.service.ts:725 siem-server-new/src/email/email.service.ts:1218 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:703.

Later evidence: Flow 45 observed new follow-up email fields while source was changing. This is not a re-audit or confirmation that A13 is fixed. Later trace

D19 · D36siem-server-new/src/payment/processor/PaymentEventProcessor.ts:646 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:665 siem-server-new/src/email/email.service.ts:1607
25. Billing, expiry, mandate and trial remindersCompleteD42 reminders are rendered from current billing state and refreshed at lifecycle changes; D55 warnings use the actual grace expiry.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/src/services/subscription.service.ts:1746siem-server-new/src/services/subscription.service.ts:2367

Earlier register verdict/evidence · historical
Trial/reminder jobs exist; normal paid signup/renewal reminders remain unreliable (D42) and grace dates remain wrong (D55).
Trace and register status

Free claim calls updateScheduledReminders, which schedules 7/3/1-day emails on original trialEnd plus a trial-ending lifecycle task. Deterministic readymade jobs clear old reminders and propagate sender failure to BullMQ retry (siem-server-new/src/services/subscription.service.ts:455, siem-server-new/src/services/subscription.service.ts:211, siem-server-new/src/bullmq/services/emailQueue.service.ts:48, siem-server-new/src/bullmq/workers/emailWorker.ts:18). Trial ending accepts PENDING_SETUP/TRIALING, preserves the claim deadline and schedules remaining grace to trialEnd+7 days; daily finalization backs it up: D15 agrees with source-resolved clock behavior (siem-server-new/src/bullmq/workers/lifecycleWorker.ts:26, siem-server-new/src/bullmq/workers/repeatableWorker.ts:685). Paid reminders require a period end and use “Plan Expires … Renew Plan Now”. Daily 03:00 reminder-sync selects only emailReminders=DbNull (siem-server-new/src/services/subscription.service.ts:267, siem-server-new/src/bullmq/queues/repeatableQueue.ts:57, siem-server-new/src/bullmq/workers/repeatableWorker.ts:638). Pending-setup paid signup has no period end, so that sync writes []; activation/renewal follow-up do not rebuild reminders, and [] is excluded forever. D42 remains pending (siem-server-new/src/services/subscription.service.ts:294, siem-server-new/src/services/subscription.service.ts:1459, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:665). Reactive mandate failure schedules immediate/mid-grace reminders; the worker accepts PAST_DUE/PENDING_REAUTH and links recovery (siem-server-new/src/payment/processor/PaymentEventProcessor.ts:845, siem-server-new/src/bullmq/queues/lifecycleQueue.ts:75, siem-server-new/src/bullmq/workers/lifecycleWorker.ts:102). Proactive price-ceiling gating sends its own email, not that helper; no extra reminder design is invented (siem-server-new/src/bullmq/workers/billingCycleWorker.ts:230). Generic grace warnings read expiryDate, which renewal/failure/ceiling do not move to grace end: D55 remains pending (siem-server-new/src/bullmq/workers/lifecycleWorker.ts:87, siem-server-new/src/payment/processor/PaymentEventProcessor.ts:610). Paid midnight finalization omits warnings, agreeing with pending D20 (siem-server-new/src/bullmq/workers/repeatableWorker.ts:749). Final cleanup reliability is A11 in flow 21.

Later evidence: Batch 3 / flow 37: payment-success/proration emails contain dollar-prefixed INR amounts (A25). This is now traced rather than left as an off-path observation; D42/D55 remain as previously audited. siem-server-new/src/email/new-email-templates/templates/new/payment-success.html:27 siem-server-new/src/email/new-email-templates/templates/new/proration-adjustment.html:26.

D15 · D20 · D42 · D55siem-server-new/src/services/subscription.service.ts:211 siem-server-new/src/bullmq/services/emailQueue.service.ts:48 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:75
26. Upgrade and higher Razorpay mandateCompleteD23/D34/D35/D45/D59 fixes unify quote, permission and charge, apply paid reactivation/no-method changes at settlement, and preserve current A fixes. An upgrade or reactivation to fewer assets than are onboarded asks which agents to keep (14 Oct 2026, D86). The choice is stored in Subscription.pendingChangeRemovedAgentIds and applied when the payment settles, so an unpaid upgrade never alters a scheduled downgrade's selection; needs db push.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/src/services/subscription.service.ts:1111siem-server-new/src/services/subscription.service.ts:1308Updated plan-change resolutions

Earlier register verdict/evidence · historical
A15–A19 are fixed; known D23/D34/D35/D45/D54/D59 and price-push D72 edge cases still block the whole upgrade flow.

Latest resolution reconciliation: A15,A16,A17,A18,A19 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/services/subscription.service.ts:1275siem-server-new/src/payment/processor/PaymentEventProcessor.ts:270 siem-server-new/src/services/subscription.service.ts:1049 siem-server-new/src/bullmq/queues/billingCycleQueue.ts:50siem-server-new/src/services/subscription.service.ts:1340siem-server-new/src/payment/processor/PaymentEventProcessor.ts:349 siem-server-new/src/services/subscription.service.ts:1120 siem-server-new/src/services/subscription.service.ts:157siem-server-new/src/services/subscription.service.ts:1292siem-server-new/src/payment/processor/PaymentEventProcessor.ts:342

Earlier audit trace and patch notes · historical
The one-popup mandate path exists, but known pricing/status defects remain; new gaps affect money recording, quote-only Enterprise and renewal scheduling.
Trace and register status

Modal → action → IAM-classified /upgrade → controller → server pricing → gateway → normalized plan-change metadata → settlement. Country/currency come from the subscription, amounts from the server and stored rates; tax is not added again. siem-client-new/app/portal/(me)/subscription/components/UpdateSubscriptionModal.tsx:87 siem-client-new/action/subscriptionAction.ts:143 siem-server-new/src/routes/subscription.routes.ts:93 siem-server-new/src/controllers/subscription.controller.ts:198 siem-server-new/src/services/subscription.service.ts:1015 siem-server-new/src/payment/planChangeMetadata.ts:9. D66’s currency-aware rates and D67’s close-confirmation-before-popup fix are present. For a higher Razorpay ceiling, one order takes max(proration, ₹1), authorizes the new full price plus headroom, and carries the plan/assets/interval to captured settlement. No PENDING_REAUTH write occurs on that branch: D7/D9/D56 agree with resolved; D29’s declined plan change leaves status/period/grace unchanged. siem-server-new/src/services/subscription.service.ts:1152 siem-client-new/app/portal/(me)/subscription/components/UpdateSubscriptionModal.tsx:368 siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:124 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:270 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:728. D34/D45 remain pending: currentValue uses live plan rates, not the locked price; same-interval proration is (new−current)×remaining fraction, while an interval change charges the full requested cycle. The browser estimates unused credit from amountPaid and subtracts it. D23 still uses legacy USD rates for the IAM choice; D35 still allows CANCELLED plan changes; D59 still writes the target before a no-token setup is paid. siem-server-new/src/services/subscription.service.ts:1029 siem-server-new/src/services/subscription.service.ts:1082 siem-server-new/src/services/subscription.service.ts:1146 siem-client-new/app/portal/(me)/subscription/components/UpdateSubscriptionModal.tsx:181 siem-client-new/app/portal/(me)/subscription/components/UpdateSubscriptionModal.tsx:239 siem-server-new/src/routes/subscription.routes.ts:47 siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:113. New A15: the synchronous succeeded path writes plan, invoice and payment separately; A16: the server admits custom Enterprise without a quote; A17: a shorter resulting period retains the later old renewal job. D54’s unknown-outcome machinery is not called by this upgrade branch: an uncertain Stripe result merely returns pending, unlike the audited renewal/retry paths. D72’s pushed fields do clear on applied upgrades. siem-server-new/src/services/subscription.service.ts:1234 siem-server-new/src/services/subscription.service.ts:1017 siem-server-new/src/services/subscription.service.ts:1316 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:327. Ordinary pending success is applied by the webhook; observed gateway timing is unverified.

Later evidence: Flow 50 adds the cross-company upgrade authorization failure (A35) and scoped resolver failure (A36). Flow 38 also found that a queued old price push can reappear after a plan change (D72). Later trace

Related path: A queued push can carry an old asset/interval price across a same-plan upgrade even though settlement clears pushed fields; tracked under D72 in flow 38. siem-server-new/src/bullmq/queues/pricePushQueue.ts:18siem-server-new/src/payment/processor/PaymentEventProcessor.ts:333

D1 · D7 · D9 · D14 · D23 · D29 · D34 · D35 · D45 · D54 · D56 · D59 · D66 · D67 · D72siem-client-new/app/portal/(me)/subscription/components/UpdateSubscriptionModal.tsx:307 siem-server-new/src/services/subscription.service.ts:1002 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:270
27. Downgrade at period endCompletePlan-change permission/pricing and pending downgrade use the shared server quote; D23/D34 and A18/A19 are resolved. Agent handling fixed 14 Oct 2026 (D84, D85, D86): the customer must keep exactly the new asset count of their own agents whenever more are onboarded (only then is the list shown, and Confirm waits for it to load); the others are marked “To be removed” and, at the period end, leave the portal and an admin is emailed to remove them from the SIEM server by hand. Each new downgrade replaces the previous marks, and an abandoned upgrade cannot change them.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/src/services/subscription.service.ts:1111siem-server-new/src/services/subscription.service.ts:1239

Earlier register verdict/evidence · historical
Interval-only staging and upgrade/downgrade conflicts A18/A19 are fixed; known currency/locked-price permission and comparison gaps D23/D34 remain.

Latest resolution reconciliation: A18,A19 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/services/subscription.service.ts:1120 siem-server-new/src/services/subscription.service.ts:157siem-server-new/src/services/subscription.service.ts:1292siem-server-new/src/payment/processor/PaymentEventProcessor.ts:342

Earlier audit trace and patch notes · historical
A lower target is staged and applied at renewal, but interval-only changes are discarded (A18) and a paid upgrade retains the old downgrade (A19).
Trace and register status

The same modal/action/upgrade endpoint chooses the downgrade branch when requestedValue ≤ current live plan value, stages plan/assets/interval, and emails its period-end date. Selected assets are retained by marking the others pendingRemoval; no payment is sent while scheduling. siem-client-new/app/portal/(me)/subscription/components/UpdateSubscriptionModal.tsx:317 siem-server-new/src/services/subscription.service.ts:1085 siem-server-new/src/services/subscription.service.ts:1091 siem-server-new/src/services/subscription.service.ts:1107 siem-server-new/src/services/subscription.service.ts:1116. At currentPeriodEnd, worker first respects the early-job guard and cancellation, then resolves the new plan’s currency/interval/assets price, applies the target, clears old pushed fields and downgrade fields, removes marked agents, reloads and charges through the ordinary pipeline. D24/D72 agree with resolved for those guards/clears. siem-server-new/src/bullmq/workers/billingCycleWorker.ts:117 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:131 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:149 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:154 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:482. A18: identical plan/assets short-circuits without testing interval; yearly→monthly is called no change. A19: an intervening paid upgrade leaves the staged lower target and pendingRemoval flags, so renewal can undo the paid upgrade and delete the formerly rejected assets. Clearing the staged target by “no change” also leaves removal flags for a future downgrade. siem-server-new/src/services/subscription.service.ts:1086 siem-server-new/src/services/subscription.service.ts:1095 siem-server-new/src/services/subscription.service.ts:1235 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:315 siem-server-new/src/services/subscription.service.ts:752. D23/D34’s legacy permission/live-price comparisons remain pending. Removal failures are logged and ignored in the shared helper; whether any retained row exists in a database is unverified. siem-server-new/src/services/subscription.service.ts:798. No separate downgrade route or charge executor is missing.

Related path: The same fixed queued-price snapshot is not bound to the interval/assets on later execution; current queue guard checks only planId. See the D72 scenario in flow 38. siem-server-new/src/services/pricePush.service.ts:114

D23 · D24 · D34 · D72siem-server-new/src/services/subscription.service.ts:1085 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:131 siem-server-new/src/services/subscription.service.ts:752
28. Interval/asset endpoint: live or stale?Complete/update-subscription/:id is a live admin activation endpoint; actual asset/interval changes use /upgrade/:id, with their defects in flows 26–27.
Trace and register status

The name is historical, but this is not a dead interval/asset mutator: current route isAdmin + ID/body validation → updateSubscriptionStatus controller, which reads only status → service activation gate. Body validation permits ACTIVE/TRIALING; the current admin status dialog calls this exact URL. siem-server-new/src/routes/subscription.routes.ts:69 siem-server-new/src/validators/subscriptionSchema.ts:36 siem-server-new/src/controllers/subscription.controller.ts:79 siem-server-new/src/services/subscription.service.ts:1466 siem-admin-new/app/dashboard/customers/[id]/components/EditSubscriptionStatus.tsx:57. No current client caller changing assets/interval through that URL was found. Those fields are sent via the live upgrade action and strict upgrade schema. D32 agrees with resolved in this alias’s narrow source/status guard; full admin-operation auditing remains flow 39. siem-client-new/action/subscriptionAction.ts:143 siem-server-new/src/validators/subscriptionSchema.ts:46. Do not remove this URL on the assumption it is unused.

D32siem-server-new/src/routes/subscription.routes.ts:69 siem-server-new/src/controllers/subscription.controller.ts:76 siem-admin-new/app/dashboard/customers/[id]/components/EditSubscriptionStatus.tsx:57
29. Cancel and resumeCompleteD46 is resolved by the owner’s no-customer-deletion decision: unpaid signup cancellation is refused; paid cancellation remains at period end.

Latest register reconciliation: owner-approved decisions and source reads supersede the older pending assessment. siem-server-new/src/services/subscription.service.ts:948D46 owner decision A

Earlier assessment · historical
Cancellation grace is fixed (D20); the independent first-charge Delete UI remains unreachable (pending D46).

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. D20 resolved; D46 pending

Earlier register verdict/evidence · historical
Final cleanup A11 is fixed; known missing grace warnings D20 and unreachable Delete control D46 remain.

Latest resolution reconciliation: A11 fix records reconciled; the current verdict covers remaining independent blockers. A11 resolution

Earlier audit trace and patch notes · historical
Normal cancellation/revocation is DB-only; final cleanup still has A11 and the fallback omits warnings (D20). The Delete screen branch is stale (D46).
Trace and register status

Cancel/Revoke controls → actions → separate IAM permissions → controllers pass caller → service checks owner. Paid cancellation sets cancelAtPeriodEnd and removes renewal reminders, leaving status/access unchanged until the boundary; it never cancels a gateway subscription. Resume before full cancellation clears the flag, sends a resumed message and rebuilds reminders; fully CANCELLED is refused. siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:517 siem-client-new/action/subscriptionAction.ts:30 siem-server-new/src/routes/subscription.routes.ts:77 siem-server-new/src/routes/subscription.routes.ts:85 siem-server-new/src/controllers/subscription.controller.ts:185 siem-server-new/src/services/subscription.service.ts:838 siem-server-new/src/services/subscription.service.ts:883. At period end, billing worker runs beginCancellationGracePeriod: CANCELLED + grace expiryDate, warnings, final-deactivation and status email. D24’s early-charge guard and D4’s CANCELLED→EXPIRED transition exist. siem-server-new/src/bullmq/workers/billingCycleWorker.ts:117 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:125 siem-server-new/src/services/subscription.service.ts:2255 siem-server-new/src/utils/subscriptionStatusMachine.ts:39. End-to-end deactivation remains broken by already-reported A11 (flow 21); the midnight fallback schedules final deactivation without paid warnings, agreeing with pending D20. D46 remains pending: the UNPAID/PAYMENT_FAILED Delete branch cannot render because its parent mounts the button only on ACTIVE/TRIALING. The backend deletion branch still exists and refuses coupon/in-flight checkout deletion. siem-server-new/src/bullmq/workers/lifecycleWorker.ts:135 siem-server-new/src/bullmq/workers/repeatableWorker.ts:749 siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:113 siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:522 siem-server-new/src/services/subscription.service.ts:846. Trial cancellation is explicitly refused by this implementation; trial ending is handled by lifecycle jobs, not this paid cancel route. siem-server-new/src/services/subscription.service.ts:843.

D4 · D20 · D24 · D46siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:517 siem-server-new/src/services/subscription.service.ts:838 siem-server-new/src/services/subscription.service.ts:883
30. Free trial to paid conversionCompleteTrial conversion has durable settlement follow-up (A20), and initiating authenticated replay A38 is fixed. Changed 14 Oct 2026 (owner decisions, D88): a paid conversion makes a TRIALING or ENDED trial ACTIVE at once instead of PENDING_SETUP, with the first paid period starting at the payment (the trial is cycle 0, the payment cycle 1, the next renewal cycle 2). A trial still in PENDING_SETUP is not offered the upgrade, in the portal or the API. The follow-up schedules the renewal and sends the payment-success email with the invoice; admins get the Payment Received email; the Stripe charge lock is released; a failed conversion changes nothing. When the new asset count is below the onboarded agents, the customer chooses which to keep (D86) and the others are removed when the payment settles.

Verdict refresh: current resolution records and targeted source evidence supersede earlier reasons. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:698siem-client-new/auth/lib/apiClientV2.ts:128siem-client-new/auth/lib/apiClientV2.ts:194

Earlier audit verdict and evidence · historical
A20 conversion settlement follow-up is durable, and the initiating plan-change API's encrypted 401 replay A38 is resolved.

Latest resolution reconciliation: A20 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:413siem-server-new/src/payment/processor/PaymentEventProcessor.ts:698

Earlier audit trace and patch notes · historical
Plan/flags change only after payment, as D30/D31 require, but conversion bypasses durable post-settlement follow-up (A20).
Trace and register status

A free-plan subscription enters startTrialConversion from /upgrade. It accepts only convertible trial statuses and a paid plan, preserves the current trial until payment, prices the chosen country/currency interval, and embeds trial_upgrade metadata. Razorpay popup charges the full paid price plus a separate authorized headroom; Stripe setup saves a method and queues the chosen paid-price job. siem-server-new/src/services/subscription.service.ts:1024 siem-server-new/src/services/subscription.service.ts:945 siem-server-new/src/services/subscription.service.ts:954 siem-server-new/src/services/subscription.service.ts:970 siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:112 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:47. Success creates invoice/payment and moves to PENDING_SETUP with chosen plan/assets/interval, clears trial flags/current period/expiry and old pushed fields. Admin later begins the paid period. Declines/abandonment leave the trial unchanged; D29/D30/D31 and D72’s clear are implemented for an ordinary source path. No discount is applied to conversion invoices. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:363 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:414 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:429 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:728 siem-server-new/src/services/subscription.service.ts:1473. A20: after that transaction the branch cancels trial jobs, rebuilds reminders and sends onboarding messages inline, then returns. Its invoice is not marked settlementApplied and no durable follow-up is queued, so a stopped process or thrown follow-up cannot resume through redelivery or the maintenance scan. This is separate from A13’s ignored returned mail result. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:376 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:439 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:447 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:197 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:654 siem-server-new/prisma/schema.prisma:439. The free claim creates no gateway customer/payment, but stores a derived provider/currency pair that conversion and repository creation currently require; the literal owner instruction “free trials … have no gateway” versus these stored fields is recorded as a plan/code clarification, not an assertion of a free-trial charge. siem-server-new/src/services/subscription.service.ts:399 siem-server-new/src/prisma_repos/subscription.repository.ts:45. Existing legacy trial rows are reserved for flow 48.

D15 · D29 · D30 · D31 · D54 · D72siem-server-new/src/services/subscription.service.ts:938 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:47 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:363
31. Payment methods: list/add/default/deleteCompleteSubscription defaults, method-use protection, scoped authorization and encrypted refresh replay are resolved.

Verdict refresh: current resolution records and targeted source evidence supersede earlier reasons. siem-server-new/src/services/subscription.service.ts:2054siem-server-new/src/services/subscription.service.ts:2222siem-client-new/auth/lib/apiClientV2.ts:128siem-client-new/auth/lib/apiClientV2.ts:194

Earlier audit verdict and evidence · historical
Subscription-specific defaults and method-use protection A21/A22 and the method mutations' encrypted 401 replay A38 are fixed. Owner decisions 13 Oct 2026: a card used only by CANCELLED/ENDED/EXPIRED subscriptions can be deleted, and their token is cleared so renewal sets up a new card; a card added to a subscription with no card becomes its default.

Latest resolution reconciliation: A21,A22,A36 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/services/subscription.service.ts:2054siem-server-new/src/services/subscription.service.ts:2167 siem-server-new/src/services/subscription.service.ts:2222 siem-server-new/src/services/iam/iam.registry.ts:50siem-server-new/src/services/iam/iam.registry.ts:52

Earlier audit trace and patch notes · historical
₹1 registration is isolated and replacement cleanup exists; Stripe default labels are global (A21), and paid pending-setup defaults can be detached (A22).
Trace and register status

Portal mounts PaymentMethods for the current subscription in every displayed status. The controls use read/create/update/delete IAM actions on subscription-ID routes, validated IDs and method bodies. Controllers delegate to country-bound context, owner’s PaymentCustomer and the selected gateway. siem-client-new/app/portal/(me)/subscription/page.tsx:127 siem-client-new/app/portal/(me)/subscription/components/PaymentMethods.tsx:48 siem-server-new/src/routes/subscription.routes.ts:198 siem-server-new/src/routes/subscription.routes.ts:207 siem-server-new/src/routes/subscription.routes.ts:215 siem-server-new/src/routes/subscription.routes.ts:224 siem-server-new/src/routes/subscription.routes.ts:233 siem-server-new/src/controllers/subscription.controller.ts:452 siem-server-new/src/controllers/subscription.controller.ts:473 siem-server-new/src/services/subscription.service.ts:2029. Stripe add-card setup carries subscription ID + method_registration; its webhook checks Indian-issued cards then stops without default change or charge (D69 resolved). ACTIVE Razorpay add opens a ₹1 recurring registration order at highest locked/pushed price + headroom; browser verification and captured webhook exclude default binding, and its invoice/payment branch settles no period/status/cycle (D68 resolved). siem-server-new/src/services/subscription.service.ts:2047 siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:76 siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:112 siem-server-new/src/services/subscription.service.ts:2073 siem-server-new/src/services/subscription.service.ts:679 siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:124 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:226. Set-default validates ownership; Razorpay requires the chosen mandate’s own limit to cover the higher price and stores that limit/token. Delete checks ownership, refuses a token referenced by an ACTIVE/PAST_DUE row and refuses the last method. D18/D57 verify ownership/order/customer patches are present. siem-server-new/src/services/subscription.service.ts:2119 siem-server-new/src/services/subscription.service.ts:2133 siem-server-new/src/services/subscription.service.ts:2208 siem-server-new/src/services/subscription.service.ts:2229 siem-server-new/src/services/subscription.service.ts:646 siem-server-new/src/services/subscription.service.ts:672. D70 replacement cleanup compares card identity/UPI ID, refuses removal when any subscription still references the token, then detaches only the same instrument. It does not claim to remove pre-existing or intentionally added duplicates. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:145 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:157. A21: Stripe display still reads customer.invoice_settings.default_payment_method instead of the subscription token; A22: the delete guard excludes paid PENDING_SETUP, allowing its saved default to be removed before first renewal. Actual payment-method/bank states and applied schema remain unverified. siem-server-new/src/payment/providers/StripeProvider.ts:253 siem-server-new/src/services/subscription.service.ts:2041 siem-server-new/src/services/subscription.service.ts:2212.

Later evidence: Flow 50 adds unsupported scoped payment-method metadata resolution (A36); the earlier method-service verdict is retained. Later trace

Later dependency evidence: Batch 5 / request layer: encrypted method mutations after 401/refresh can fail validation (A38). siem-client-new/auth/lib/apiClientV2.ts:133. Section 10

D18 · D57 · D68 · D69 · D70siem-client-new/app/portal/(me)/subscription/components/PaymentMethods.tsx:56 siem-server-new/src/services/subscription.service.ts:2035 siem-server-new/src/services/subscription.service.ts:2113 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:157
32. Recover PAST_DUE / PENDING_REAUTHCompleteD33 owner-approved cutover maps migrated customers to ACTIVE/PAST_DUE, avoiding the obsolete Stripe PENDING_REAUTH migration assumption; recovery remains fixed.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/scripts/stripe-cutover.ts:23siem-server-new/scripts/stripe-cutover.ts:222D33 resolved in source; unrun

Earlier register verdict/evidence · historical
Method authorization/replay is fixed; direct legacy Stripe PENDING_REAUTH recovery still needs D33 target reconciliation.

Verdict refresh: current resolution records and targeted source evidence supersede earlier reasons. Current cutover statesiem-client-new/auth/lib/apiClientV2.ts:128siem-client-new/auth/lib/apiClientV2.ts:194

Earlier audit verdict and evidence · historical
A35/A36 billing authorization and request replay A38 are fixed. The cutover script no longer routes migrated Stripe customers through PENDING_REAUTH (flow 47); PENDING_REAUTH is still missing from the derived payment_issue status (Batch 5 note).

Latest resolution reconciliation: A35,A36 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/middlewares/iam.middleware.ts:60siem-server-new/src/middlewares/iam.middleware.ts:64 siem-server-new/src/services/iam/iam.registry.ts:50siem-server-new/src/services/iam/iam.registry.ts:52

Earlier audit trace and patch notes · historical
Locked/pushed price recovery and one-time country initialization are present; Stripe PENDING_REAUTH charging is still skipped (known D33).
Trace and register status

Recovery screen shows setup for PENDING_REAUTH, missing-token PAST_DUE or mandate breach, even with an existing token. No gateway selector exists. Missing country is collected once only for PENDING_REAUTH. Action → billing:retry route → controller → setupPaymentMethodForExistingSubscription. siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:339 siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:353 siem-server-new/src/routes/subscription.routes.ts:145 siem-server-new/src/controllers/subscription.controller.ts:352 siem-server-new/src/services/subscription.service.ts:1813. Service rejects other statuses and saved-country changes. Null-country initialization conditionally writes country/derived binding/price and clears tokens/ceiling; a pending price or discount in another currency blocks that initialization. Normal recovery keeps locked price, charges resolveCycleChargeAmount and snapshots its discount. Example locked ₹1,000, live plan ₹1,500, running ₹200 discount: recovery ₹800, not ₹1,500. Mandate authorization covers highest full locked/pushed price plus existing headroom. D10’s repricing is fixed in code despite the country document’s stale pending wording; D8 agrees with resolved. siem-server-new/src/services/subscription.service.ts:1838 siem-server-new/src/prisma_repos/subscription.repository.ts:55 siem-server-new/src/services/subscription.service.ts:1861 siem-server-new/src/services/subscription.service.ts:1880 siem-server-new/src/services/subscription.service.ts:1891. Razorpay captured settlement and token confirmation use the real current cycle; Stripe TOKEN_SAVED saves method then schedules that cycle. D13/D18/D57’s verified customer/order binding and D26’s finished-job replacement exist. Ordinary PAST_DUE recovery can settle ACTIVE without admin reactivation. siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:124 siem-server-new/src/services/subscription.service.ts:672 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:112 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:610 siem-server-new/src/bullmq/queues/billingCycleQueue.ts:26. D33 remains pending: Stripe PENDING_REAUTH cycle >0 is excluded by billingCycleWorker, so successful method setup schedules a job that returns without a charge/status recovery. Cycle-0 cutover ambiguity and preservation of previously paid Stripe periods remain flow 47, not invented here. siem-server-new/src/bullmq/workers/billingCycleWorker.ts:99. Razorpay zero-net recovery explicitly refuses a mandate payment, matching the stated gateway constraint. siem-server-new/src/services/subscription.service.ts:1863.

Later evidence: Flow 47 confirms the revised existing-customer cutover is still missing under D33; flow 50 adds scoped recovery authorization failure (A36). Later trace

Later dependency evidence: Batch 5 / user.util/auth: PENDING_REAUTH is not included in derived payment_issue or hasPaymentIssue; existing D39/D41 access issues remain. Recovery also inherits encrypted replay failure A38. siem-server-new/src/utils/user.util.ts:16 siem-server-new/src/utils/user.util.ts:98 siem-client-new/auth/lib/apiClientV2.ts:245. Section 10

Latest lifecycle/access decision: The latest D39/D41 rule includes PENDING_REAUTH as a payment issue and allows subscription/profile/team/wallet access during that status. siem-server-new/src/utils/user.util.ts:22siem-server-new/src/utils/subscriptionAccess.ts:33

D8 · D10 · D13 · D18 · D26 · D33 · D57siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:374 siem-server-new/src/services/subscription.service.ts:1813 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:99
33. Razorpay token cancelled/rejectedCompleteA23 is fixed: revocation clears every matching subscription conditionally, preserving replacement tokens.

Latest resolution reconciliation: A23 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:179

Earlier audit trace and patch notes · historical
Events normalize to TOKEN_REVOKED, but only one shared-token subscription is updated and an interleaved replacement can be cleared (A23).
Trace and register status

Signed token.cancelled/rejected → event ID dedupe → TOKEN_REVOKED with token ID and CANCELLED_BY_CUSTOMER/REJECTED. process locates a subscription and verifies its country/provider binding; handler clears externalTokenId, stores reason and cancels that cycle’s retry jobs, leaving status until billing/recovery. siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:183 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:50 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:178. The next nonzero renewal without a token enters PAST_DUE/grace, and recovery UI allows a new setup. D13 concerns confirmation/saving, not this revocation bug. siem-server-new/src/bullmq/workers/billingCycleWorker.ts:204 siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:353. A23: findByExternalTokenId is findFirst; shared customer mandates can be defaults of multiple subscriptions, so only the first loses its token/retries. The update by subscription ID also does not re-check the revoked token, so a replacement saved after lookup can be erased. Signed duplicate delivery is marked processed and will not repair the other rows. siem-server-new/src/prisma_repos/subscription.repository.ts:24 siem-server-new/src/services/subscription.service.ts:2142 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:186 siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:27.

D13siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:183 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:178 siem-server-new/src/prisma_repos/subscription.repository.ts:20
34. Stripe customer portal: live or stale?CompleteD43 is resolved by removing the Stripe portal route/button/provider contract; methods are managed on the subscription page.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. D43 owner-approved removal and source locations

Earlier register verdict/evidence · historical
The live “Update & Pay via Stripe” control opens a customer portal that does not bind a new card or charge this subscription (known D43).
Trace and register status

The payment-error box still offers Open Stripe Portal under billing:portal permission. Action → live /customer-portal route → controller → Stripe-only service resolves PaymentCustomer → createBillingPortalSession(customer, returnUrl). This is a current caller, not dead code. siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:504 siem-client-new/action/subscriptionAction.ts:119 siem-server-new/src/routes/subscription.routes.ts:163 siem-server-new/src/controllers/subscription.controller.ts:157 siem-server-new/src/services/subscription.service.ts:1914 siem-server-new/src/payment/providers/StripeProvider.ts:285. The portal session carries no internal subscription setup metadata; Stripe normalization ignores setup intents without that subscription ID, and the service neither invokes a billing charge nor adopts a customer-level card change. D43 agrees with pending: clicking the screen’s “Update & Pay” is not this system’s recovery flow. siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:67 siem-server-new/src/services/subscription.service.ts:1927. Whether the Stripe account enables its portal is configuration, not a missing code fix. Thingstoconfigurebeforeproduction.md:22.

D43siem-client-new/app/portal/(me)/subscription/components/SubscriptionAccordion.tsx:445 siem-server-new/src/services/subscription.service.ts:1914 siem-server-new/src/payment/providers/StripeProvider.ts:285
35. Webhook intake, normalization and binding gateCompleteRaw-body canonical routes, signatures, event-ID dedupe and normalized dispatch exist; business-handler defects are recorded on their own flows.
Trace and register status

Both canonical routes are registered before express.json and pass the raw Buffer plus signature; success is 200, exceptions are 400. Stripe uses SDK constructEvent; Razorpay HMACs raw bytes and requires the header event ID. Gateway signature/webhook account settings were not executed or certified. siem-server-new/src/server.ts:76 siem-server-new/src/server.ts:91 siem-server-new/src/server.ts:114 siem-server-new/src/payment/providers/StripeProvider.ts:277 siem-server-new/src/payment/providers/RazorpayProvider.ts:405. Adapters skip only processed (eventId,provider) rows; failed processor deliveries can run again, and processing uses upsert to tolerate competing logs. D36 agrees with resolved for that adapter dedupe behavior; it does not repair swallowed post-commit effects (A10/A13/A20) or A15’s existing-invoice short circuit. siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:27 siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:45 siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:23 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:197. Stripe setup intent → TOKEN_SAVED, success/failed PaymentIntent → normalized outcome, setup-mode session completion → no-op. method_registration checks card origin then does not bind/charge. Razorpay payment metadata comes from the server order, captures bind pending bank-confirmed tokens, token.confirmed finishes their confirmation, failures normalize reason/retry/mandate fields, token cancelled/rejected → TOKEN_REVOKED. payment.authorized no longer calls capture, agreeing with resolved D44 and Automatic capture configuration. siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:64 siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:126 siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:156 siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:184 siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:44 siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:107 siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:199. process dispatches walletPurchaseId before subscription binding; subscription events must match the stored country-derived gateway, and successful money its currency. D74/D75 routing fixes are present at this boundary; wallet purchase correctness remains flows 41–44. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:46 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:58. The older unsuffixed /subscription/webhook path has no live registration in current source; canonical endpoints are the configured targets. siem-server-new/src/server.ts:77 siem-server-new/src/server.ts:92. Stripe normalization happens before the adapter’s failure-log try; those errors still propagate but are not logged there. siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:35.

Later dependency evidence: Batch 5 / payloadSecurity: current Stripe/Razorpay raw-body handlers run before the generic decryptor, preserving signed input. Wallet events use the same endpoints; no extra wallet webhook exemption is needed. siem-server-new/src/server.ts:77 siem-server-new/src/server.ts:92 siem-server-new/src/server.ts:253. Section 10

D13 · D18 · D36 · D44 · D57 · D69 · D74 · D75siem-server-new/src/server.ts:76 siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:22 siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:15 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:46
36. Money records and subscription invoicesCompleteD17 invoice numbers now use an atomic per-day sequence inside the invoice transaction; A15/A24/A34 money and lookup fixes remain.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/src/prisma_repos/invoice.repository.ts:8siem-server-new/src/prisma_repos/invoice.repository.ts:12

Earlier register verdict/evidence · historical
A15/A24 upgrade records and A34 legacy identifier export/apply are fixed; known invoice-number race D17 remains.

Verdict refresh: current resolution records and targeted source evidence supersede earlier reasons. siem-server-new/scripts/stripe-cutover.ts:119siem-server-new/scripts/stripe-cutover.ts:285siem-server-new/scripts/stripe-cutover.ts:303D17 pending

Earlier audit verdict and evidence · historical
A15/A24 money atomicity, upgrade invoice labels and historical lookup A34 (cutover script) are fixed; known numbering race D17 remains.

Latest resolution reconciliation: A15,A24 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/services/subscription.service.ts:1275siem-server-new/src/payment/processor/PaymentEventProcessor.ts:270 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:278siem-server-new/src/payment/processor/PaymentEventProcessor.ts:308

Earlier audit trace and patch notes · historical
Normal USD/INR records/PDFs/filenames are wired; D17 numbering race remains, A15 can lose an upgrade payment record, and A24 labels its invoice with the old plan.
Trace and register status

Successful ordinary settlement records invoice/payment atomically under the billing lock, keeps paid amounts in their actual currency and captures USD/INR revenue legs from one rate snapshot. Duplicate/late money is recorded without another period or refund. Upgrade/trial/method-registration branches also write their own money, but A15 covers the synchronous upgrade’s split writes. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:482 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:531 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:556 siem-server-new/src/utils/fxRate.ts:66 siem-server-new/src/services/subscription.service.ts:1254. Discount metadata supplies gross subtotal, actual reduction, source/label and finite/lifetime cycle; line items use minor units and include renewal interval/period. PDF uses PaymentCustomer and invoice currency, prints Subtotal/discount when present, Total/Amount Paid and “inclusive of all taxes*” without a split. D1/D62 agree with resolved for ordinary invoices. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:503 siem-server-new/src/prisma_repos/invoice.repository.ts:18 siem-server-new/src/services/invoice-pdf.service.ts:47 siem-server-new/src/services/invoice-pdf.service.ts:256 siem-server-new/src/services/invoice-pdf.service.ts:326. Portal/admin payment tables use externalId to call the internal authenticated PDF route. Controller looks up externalId, accepts invoice owner or admin, generates PDF and one UTC filename: SOLDIERX_Invoice_<cmdId>_<date>. Clients consume Content-Disposition; CORS exposes it. Date helpers display “Feb 11, 2026”. siem-client-new/app/portal/(me)/subscription/components/PaymentsSection.tsx:63 siem-admin-new/app/dashboard/customers/[id]/components/PaymentsSection.tsx:58 siem-server-new/src/routes/subscription.routes.ts:178 siem-server-new/src/controllers/subscription.controller.ts:380 siem-server-new/src/controllers/subscription.controller.ts:395 siem-client-new/app/portal/(me)/subscription/utils/invoice.ts:28 siem-admin-new/app/dashboard/customers/[id]/utils/invoice.ts:34 siem-server-new/src/server.ts:194 siem-client-new/lib/utils/date.ts:9 siem-admin-new/lib/date.ts:1. D17 agrees with pending: CMD sequence is day count+1, not serialized across different subscriptions; cmdId is indexed but not unique. A24: webhook upgrade description names the pre-change plan, although the applied plan is the target. A ₹0 payment is displayed as a dash in both payment tables; no money is lost, but zero-paid display is not certified as a formatted ₹0.00. siem-server-new/src/prisma_repos/invoice.repository.ts:7 siem-server-new/prisma/schema.prisma:449 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:295 siem-client-new/app/portal/(me)/subscription/components/PaymentsSection.tsx:68 siem-admin-new/app/dashboard/customers/[id]/components/PaymentsSection.tsx:64. PDF rendering and authorization on a live deployment remain unverified.

Later evidence: Flow 45 adds missing historical invoice identifier preservation before schema removal (A34); the earlier new-invoice verdict is retained. Later trace

D1 · D17 · D62siem-server-new/src/payment/processor/PaymentEventProcessor.ts:531 siem-server-new/src/services/invoice-pdf.service.ts:44 siem-server-new/src/controllers/subscription.controller.ts:373
37. Billing messages and email templatesUnverifiedCurrency, reminder and grace-email defects are resolved. Owner decision A (13 Oct 2026): the invoice PDF is attached to the renewal payment-success email and to the signup confirmation (paid or $0 signup and trial conversion; a free claim has none). It uses the same filename as the portal download, and the email link now opens /portal/subscription instead of the API route. Actual mail and PDF delivery remain to be seen in the dev rehearsal.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/src/services/subscription.service.ts:2367Invoice delivery verification boundary

Earlier register verdict/evidence · historical
A13/A25 delivery-result handling and currency text are fixed; known reminder/grace-date gaps D42/D55 and deployed invoice-link delivery remain unresolved/unverified.

Latest resolution reconciliation: A25 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/email/email.service.ts:1559siem-server-new/src/email/email.service.ts:1994siem-server-new/src/email/new-email-templates/templates/new/payment-success.html:27

Earlier audit trace and patch notes · historical
Billing calls/templates exist; INR renewal/upgrade emails still say dollars (A25), while known delivery/reminder defects A13/D42/D55 remain.
Trace and register status

The server assembles modular layout/header/footer, injects values and evaluates conditional blocks before sending. A first successful signup sends admin pending-setup, user pending-setup and SIEM onboarding contact through durable follow-up; patched Batch 1 failure emails now format the real attempted amount/currency and provide a CTA. D19 now agrees with resolved currency forwarding; its admin template omits the amount entirely. siem-server-new/src/email/email.service.ts:233 siem-server-new/src/email/email.service.ts:404 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:679 siem-server-new/src/services/subscription.service.ts:725 siem-server-new/src/email/email.service.ts:1631. Renewal success uses payment-success; failure/grace/mandate messages use failure/warning/ended templates. Upgrade success uses proration-adjustment, downgrade scheduling its own template; trial conversion uses onboarding messages after applying the paid plan. Default-method email is called by the Stripe default-change path. Quote messages were traced in flow 17; price-push delivery remains flow 38. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:693 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:819 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:92 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:155 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:338 siem-server-new/src/services/subscription.service.ts:1116 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:447 siem-server-new/src/services/subscription.service.ts:2179. A25: amount formatting is decimal-only and currency is not inserted into payment-success/proration templates; literal dollar signs relabel an INR payment. Known A13 still drops returned sender failures; A20 adds missing conversion handoff recovery. D42/D55 remain as audited in flow 25. D29’s plan-change decline message says the existing plan/trial is unchanged and starts no grace/retry. siem-server-new/src/email/email.service.ts:1577 siem-server-new/src/email/new-email-templates/templates/new/payment-success.html:27 siem-server-new/src/email/email.service.ts:1995 siem-server-new/src/email/new-email-templates/templates/new/proration-adjustment.html:26 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:728. The success call provides an invoice URL but no attachment. Main §3.10 says success email + invoice PDF; whether link-only delivery meets that requirement is unverified, rather than assuming an attachment policy. Its FRONTEND_URL API link requires deployment routing and browser authentication; current Next config contains no API rewrite and no local app/api route was found. A served link/delivered message would confirm that deployment behavior; do not count missing deployment configuration as a code defect. Migrationphasewiseplan.md:349 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:696 siem-server-new/src/email/email.service.ts:1592 siem-client-new/next.config.mjs:23. No SMTP delivery, PDF attachment or gateway webhook was executed. Each send’s source path is mapped below.

StepMessage/templateCaller
Free claim / paid first settlementadmin-subscription-pending; subscription-pending-user; siem-onboarding-contactsiem-server-new/src/services/subscription.service.ts:455 siem-server-new/src/services/subscription.service.ts:717 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:679 siem-server-new/src/email/email.service.ts:1170 siem-server-new/src/email/email.service.ts:1244 siem-server-new/src/email/email.service.ts:1364
Admin activation / resumed renewalsubscription-status-changesiem-server-new/src/services/subscription.service.ts:1642 siem-server-new/src/services/subscription.service.ts:910 siem-server-new/src/email/email.service.ts:1856
Renewal success / first or renewal declinepayment-success / payment-failedsiem-server-new/src/payment/processor/PaymentEventProcessor.ts:693 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:796 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:819 siem-server-new/src/email/email.service.ts:1572 siem-server-new/src/email/email.service.ts:1633
Upgrade / scheduled downgrade / conversionproration-adjustment / subscription-downgrade-scheduled / onboardingsiem-server-new/src/payment/processor/PaymentEventProcessor.ts:338 siem-server-new/src/services/subscription.service.ts:1297 siem-server-new/src/services/subscription.service.ts:1116 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:447 siem-server-new/src/email/email.service.ts:1921 siem-server-new/src/email/email.service.ts:1995
Trial end / grace / mandate recovery / expirytrial-ended; grace-deactivation-warning; payment-failed; grace-deactivation-ended; admin-subscription-expiredsiem-server-new/src/bullmq/workers/lifecycleWorker.ts:65 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:92 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:117 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:155 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:169
Stripe default change / Razorpay ₹1 additiondefault-payment-updated / invoice only; registration does not invoke subscription settlement emailssiem-server-new/src/services/subscription.service.ts:2179 siem-server-new/src/email/email.service.ts:2604 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:226 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:267

D19 · D29 · D42 · D55 · D64siem-server-new/src/email/email.service.ts:1541 siem-server-new/src/email/email.service.ts:1962 siem-server-new/src/email/new-email-templates/templates/new/payment-success.html:27
38. Price push: plan-wide and per subscriptionCompleteD72's email-time notice fix is present. The same-plan asset/interval gap is fixed (14 Oct 2026): each queued push carries the plan, asset count and interval its price was computed for, and applyPush skips the subscription under its lock when any of them changed, so a paid upgrade's own price is never replaced by an older pushed one. Pushes are plan-wide only and are queued with fixed job IDs (D87).

Direct source recheck: The prior slow-email notice boundary now recomputes the cycle using notifiedAt. Remaining case: five assets × $20 queues $100; a same-plan paid upgrade to ten assets locks $200 and clears pushed fields; the queued job still passes the planId check and stages $100. The job has no asset/interval binding. D72 is partly resolved for queued plan changes; no new A number duplicates it. siem-server-new/src/bullmq/queues/pricePushQueue.ts:18siem-server-new/src/services/pricePush.service.ts:75siem-server-new/src/services/pricePush.service.ts:114siem-server-new/src/payment/processor/PaymentEventProcessor.ts:333

Previous qualifier · superseded by this recheck
D22 plan-wide-only pushes and D72 queued-plan binding are fixed. The 24-hour notice edge is closed (13 Oct 2026): when the price email is recorded as sent, the first pushed cycle is re-checked against the actual send time under the subscription lock. A slow or retried email moves the price one cycle later, so the customer is never charged earlier than 24 hours after the email. pricePush.service.ts applyPush.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/src/services/pricePush.service.ts:114siem-server-new/src/services/pricePush.service.ts:124siem-server-new/src/services/pricePush.service.ts:142

Earlier register verdict/evidence · historical
The queue, discount recomputation and mandate gate exist; D22 remains pending and D72’s resolved timing/plan-change protection is incomplete.
Trace and register status

Plan edits optionally fan out on price-push-jobs; individual pushes stage price/discount, send email and stamp notification. Percentage discounts recompute; fixed amounts stay fixed. siem-server-new/src/services/plan.service.ts:323 siem-server-new/src/bullmq/workers/pricePushWorker.ts:11 siem-server-new/src/services/pricePush.service.ts:193. D22 agrees with pending: individual push is isAdmin, not SuperAdmin. D72 agrees for ordinary >24-hour selection and clearing fields, but not every interleaving: a queued subscription-only old-price job can execute after an upgrade clears fields and reintroduce that price. The payload has no plan/version binding. Also, firstPushedCycle uses staging time; an email staged 24 hours and 5 seconds before renewal that finishes 10 seconds later still qualifies for that renewal. Notification stamping checks price/cycle, not elapsed notice. D72 is only partially resolved. siem-server-new/src/bullmq/queues/pricePushQueue.ts:24 siem-server-new/src/services/pricePush.service.ts:143 siem-server-new/src/services/pricePush.service.ts:158 siem-server-new/src/services/pricePush.service.ts:171.

D22; D72siem-server-new/src/services/pricePush.service.ts:130siem-server-new/src/bullmq/queues/pricePushQueue.ts:6siem-server-new/src/routes/subscription.routes.ts:243
39. Admin status overrideCompleteThe activation confirmation uses the server's firstRenewalCharge on both admin pages (14 Oct 2026): the customer page and the standalone subscription page (GET /admin/subscriptions/:id) get it from one function, SubscriptionService.firstRenewalCharge, so the dialog no longer shows “Unavailable”.

Direct source recheck: The original locked-price calculation is replaced on the customer page. The standalone detail endpoint returns the raw subscription plus orgName/wallet, not the server-calculated firstRenewalCharge. Its page passes that response unchanged to the shared confirmation component, whose missing-value fallback is “Unavailable.” This is the remaining D32 handoff; status activation itself is still guarded. siem-server-new/src/services/user.service.ts:673siem-server-new/src/controllers/admin-subscription.controller.ts:194siem-admin-new/app/dashboard/subscriptions/[...id]/page.tsx:91siem-admin-new/app/dashboard/customers/[id]/components/EditSubscriptionStatus.tsx:75

Previous qualifier · superseded by this recheck
D32 activation gate is fixed. The confirmation amount is fixed (13 Oct 2026): the admin customer details now carry firstRenewalCharge, the server's own amount for cycle 1 (a notified pushed price, a scheduled downgrade and the running discount included), and the dialog shows it instead of calculating locked price minus discount. user.service.ts getCustomerDetails · EditSubscriptionStatus.tsx.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-admin-new/app/dashboard/customers/[id]/components/EditSubscriptionStatus.tsx:75D32 marked resolved; this source qualifier remains

Earlier register verdict/evidence · historical
The activation-only transition is enforced; D32’s confirmation amount misses a notified pushed price.
Trace and register status

The admin uses PUT /update-subscription/:id, not the owner-only GET /status/:id. Only PENDING_SETUP to paid ACTIVE or free TRIALING is allowed; prior activation trace and rollback remain as flow 13. siem-server-new/src/routes/subscription.routes.ts:69 siem-server-new/src/routes/subscription.routes.ts:85. D32’s transition restriction agrees with resolved status; its next-renewal explanation is incomplete. The dialog calculates locked price minus discount and omits pending price. With locked price 100 and an already-notified pushed price 200 on PENDING_SETUP, push starts at cycle 1 while confirmation still displays 100 before discount. siem-admin-new/app/dashboard/customers/[id]/components/EditSubscriptionStatus.tsx:73 siem-server-new/src/services/pricePush.service.ts:171.

D32siem-admin-new/app/dashboard/customers/[id]/components/EditSubscriptionStatus.tsx:57siem-server-new/src/routes/subscription.routes.ts:69
40. Admin customer/subscription views, assignment, team and membership expiryCompleteA26 expiry is enqueued before access grant; A27 is resolved by the owner’s read-only admin team decision. Views and assignment remain wired.

Latest resolution reconciliation: A26,A27 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/controllers/admin-membership.controller.ts:118siem-server-new/src/controllers/admin-membership.controller.ts:128 siem-admin-new/app/dashboard/customers/[id]/components/TeamSection.tsx:37

Earlier audit trace and patch notes · historical
Views and assignment are wired; team changes are rejected and a failed expiry enqueue leaves the temporary owner grant active (A26/A27).
Trace and register status

Customer detail reads the customer/subscription repository; assigned and global subscription views and SuperAdmin assignment use their admin routes. Assignment writes assignedAdminId and sends old/new administrator messages. siem-server-new/src/routes/user.routes.ts:158 siem-server-new/src/prisma_repos/user.repository.ts:274 siem-server-new/src/routes/admin.routes.ts:73 siem-server-new/src/controllers/admin-subscription.controller.ts:68. Team reads use the admin endpoint, but writes use user-only IAM routes (A27). SuperAdmin temporary membership creates active membership and owner policy, switches the user's current subscription and schedules revocation; an enqueue failure leaves these writes committed (A26). The expiry worker does revoke membership/policy and invalidate caches when it actually runs. siem-server-new/src/controllers/admin-membership.controller.ts:130 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:210.

Later dependency evidence: Batch 5 / admin dependencies: assignment remains SuperAdmin-enforced, and customer-status editing changes User.status, not subscription status. Old onboarding labels require owner-policy clarification; A26 grant/expiry handoff is confirmed in its untouched dependency. siem-server-new/src/controllers/admin-membership.controller.ts:148 siem-server-new/src/controllers/user.controller.ts:876. Section 10

Related path: The standalone subscription detail activation dialog is missing firstRenewalCharge from its endpoint; this narrower confirmation issue is tracked under D32 in flow 39. siem-server-new/src/controllers/admin-subscription.controller.ts:194

—siem-server-new/src/controllers/admin-membership.controller.ts:123siem-admin-new/app/dashboard/customers/[id]/components/TeamSection.tsx:116
41. Wallet top-up checkout, gateway choice and verificationCompleteWallet gateway checkout and verification are wired; encrypted authenticated refresh replay A38 is fixed.

Verdict refresh: current resolution records and targeted source evidence supersede earlier reasons. siem-client-new/auth/lib/apiClientV2.ts:128siem-client-new/auth/lib/apiClientV2.ts:194

Earlier audit verdict and evidence · historical
Ordinary gateway checkout/verification remains wired; the request layer's encrypted 401-refresh replay A38 is resolved.

Latest resolution reconciliation: A28 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:59siem-server-new/src/modules/wallet/controllers/wallet.controller.ts:180

Earlier audit trace and patch notes · historical
Either gateway is selected per purchase; server-priced checkout and stored-order/captured-payment verification are wired.
Trace and register status

The wallet screen sends amount/gateway; schema validates input and service derives charge currency/rates, server discounts and the immutable purchase. ACTIVE/TRIALING guard runs even before zero-payment completion. Stripe creates one-time mode:payment Checkout; Razorpay creates a one-time order. Neither takes subscription gateway from the country. siem-server-new/src/modules/wallet/validators/walletSchema.ts:3 siem-server-new/src/modules/wallet/services/wallet-topup.service.ts:75 siem-server-new/src/payment/providers/StripeProvider.ts:200 siem-server-new/src/payment/providers/RazorpayProvider.ts:332. Razorpay verification authorizes purchaser/member, compares the stored order, verifies HMAC and fetches captured payment; authorized-only returns pending. D74/D75/D79/D80 agree with their resolved status for these ordinary paths. siem-server-new/src/modules/wallet/controllers/wallet.controller.ts:180 siem-server-new/src/payment/providers/RazorpayProvider.ts:359. Checkout execution and real gateway responses remain outside this source audit.

Later dependency evidence: Batch 5 / request layer: encrypted wallet purchase/verify mutations after 401/refresh can fail validation (A38). The ordinary checkout source verdict is retained. siem-client-new/auth/lib/apiClientV2.ts:245. Section 10

D74; D75; D79; D80siem-client-new/app/portal/(me)/wallet/page.tsx:399siem-server-new/src/modules/wallet/services/wallet-topup.service.ts:33siem-server-new/src/modules/wallet/controllers/wallet.controller.ts:150
42. Wallet webhooks, settlement, coupon slots and expiryCompleteD76 failure writes are conditional; D77 coupon accounting uses the transitioned status and transactional checkout failure cleanup. Earlier A fixes remain.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:60siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:133siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:268

Earlier register verdict/evidence · historical
A28/A29/A31 late cancelled payment and confirmation handoffs/links are fixed; D76/D77 failure/expiry concurrency cases remain only partially resolved.

Latest resolution reconciliation: A28,A29,A31 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:59siem-server-new/src/modules/wallet/controllers/wallet.controller.ts:180 siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:224siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:237siem-server-new/src/bullmq/workers/repeatableWorker.ts:1116siem-server-new/prisma/schema.prisma:1313 siem-server-new/src/email/email.service.ts:2672siem-server-new/src/email/email.service.ts:2729

Earlier audit trace and patch notes · historical
Purchase routing is fixed; failure/expiry races remain under D76/D77, and cancellation/email handoffs fail (A28/A29/A31).
Trace and register status

walletPurchaseId routes before the subscription gate, and the purchase provider/currency gate controls settlement. Razorpay skips subscription precheck for wallet_topup; Stripe Checkout and payment events normalize into this branch. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:85 siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:109. Transactional completion applies credits/ledger, coupon accounting and a separate invoice; same-purchase success has conditional status transition. siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:88 siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:146. D76 is partially resolved: ordinary failed/expired retry works, but failure reads pending outside the write; success can commit completed before unconditional failure update writes failed, allowing a later success to credit again. siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:235. D77 is partially resolved: success can read pending, expiry releases its slot, then success accepts expired but uses the stale pending snapshot to finalize/release again; checkout creation failure also releases before marking failed, which expiry later releases again. siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:127 siem-server-new/src/modules/wallet/services/wallet-topup.service.ts:232 siem-server-new/src/modules/wallet/services/wallet-topup-expiry.service.ts:15. task:wallet-coupon-expiry is registered every five minutes. siem-server-new/src/bullmq/queues/repeatableQueue.ts:102. D78’s currency fix agrees; new defects are cancelled paid sessions ignored and non-durable/wrong-link confirmations, not the known currency defect.

D74; D75; D76; D77; D78siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:54siem-server-new/src/payment/processor/PaymentEventProcessor.ts:47
43. Wallet top-up invoicesCompleteSettlement assigns a separate unique WLT number; independent customer/admin PDF routes and downloads exist.
Trace and register status

Invoice number and paidAt are assigned inside top-up settlement, not subscription invoices. WLT{YYMMDD}{seq3} has a unique database constraint; a colliding count-based allocation rolls back its transaction rather than committing duplicate numbers. Runtime redelivery under a collision is not demonstrated. siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:146 siem-server-new/prisma/schema.prisma:1310. The PDF reads purchase currency, discount/net paid, gateway reference and credits, with inclusive-of-all-taxes wording; customer READ and admin routes expose it with SOLDIERX_Invoice_<number>_<date>.pdf. siem-server-new/src/modules/wallet/services/wallet-invoice-pdf.service.ts:51 siem-server-new/src/modules/wallet/services/wallet-invoice-pdf.service.ts:76 siem-server-new/src/modules/wallet/controllers/wallet.controller.ts:246. Both download helpers use the returned filename. siem-client-new/app/portal/(me)/wallet/utils/invoice.ts:8 siem-admin-new/app/dashboard/subscriptions/[...id]/utils/walletInvoice.ts:8. D81 agrees for new settled purchases; old purchases without number remain outside this new allocation path.

Later dependency evidence: Batch 5 / payloadSecurity: top-up invoice response bypasses encryption through its PDF Content-Type even when its URL ends in /invoice rather than containing /invoice/. siem-server-new/src/lib/payloadSecurity.ts:90 siem-server-new/src/modules/wallet/controllers/wallet.controller.ts:246. Section 10

D81siem-server-new/src/modules/wallet/services/wallet-invoice-pdf.service.ts:21siem-server-new/src/modules/wallet/controllers/wallet.controller.ts:229
44. Wallet eligibility and view accessCompleteWallet view follows the owner’s latest D41 current-status access rule; top-ups remain ACTIVE/TRIALING only. Earlier all-status view wording is superseded.

Latest register reconciliation: owner-approved decisions and source reads supersede the older pending assessment. siem-server-new/src/utils/subscriptionAccess.ts:24siem-server-new/src/controllers/iam.controller.ts:603

Earlier assessment · historical
Wallet remains readable in restricted statuses: both client guard and IAM preserve READ; only ACTIVE/TRIALING may top up.

Verdict refresh: current resolution records and targeted source evidence supersede earlier reasons. siem-server-new/src/controllers/iam.controller.ts:604siem-server-new/src/controllers/iam.controller.ts:616siem-client-new/components/GlobalRouteController.tsx:23

Earlier audit verdict and evidence · historical
A30 is resolved: the client route guard allows the wallet and restricted-status IAM responses keep wallet READ; top-ups stay ACTIVE/TRIALING only.

Latest resolution reconciliation: A30 is resolved; iam.controller.ts keeps the wallet module for restricted statuses. siem-client-new/components/GlobalRouteController.tsx:23siem-server-new/src/controllers/iam.controller.ts:603siem-server-new/src/controllers/iam.controller.ts:615

Earlier audit trace and patch notes · historical
ACTIVE/TRIALING-only top-ups and can_top_up are correct; restricted subscriptions are redirected away from the view (A30).
Trace and register status

D82 agrees with resolved status: server guard rejects other statuses, summary reports can_top_up, top-up button disables, and navigation requires wallet READ. siem-server-new/src/modules/wallet/services/wallet-topup.service.ts:33 siem-client-new/app/portal/(me)/wallet/page.tsx:589. Summary has no status refusal, but the global limited-status allowlist omits wallet. With no active sibling subscription, payment_issue/ended/pending_setup customers cannot reach the wallet view. This contradicts the explicit all-status viewing requirement, a new gap beyond D82 (A30). siem-client-new/components/GlobalRouteController.tsx:102 Migrationphasewiseplan.md:706.

Later dependency evidence: Batch 5 / iam.controller: restricted-status effective-permission allowlists also omit wallet, so even fixing the client redirect leaves wallet READ absent. This is an additional independent cause of existing A30, not a duplicate new finding. siem-server-new/src/controllers/iam.controller.ts:603 siem-server-new/src/controllers/iam.controller.ts:615. Section 10

D82; D51siem-server-new/src/modules/wallet/services/wallet.service.ts:63siem-client-new/components/GlobalRouteController.tsx:17
45. Schema push and existing rowsUnverifiedLegacy-ID export/apply and owner-approved cutover are implemented; actual export-before-schema-push/data restoration remain unverified.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/scripts/stripe-cutover.ts:119siem-server-new/scripts/stripe-cutover.ts:285

Earlier register verdict/evidence · historical
Legacy-ID preservation is built (A34); actual export-before-schema-push and data application in each environment remain unverified.

Verdict refresh: current resolution records and targeted source evidence supersede earlier reasons. siem-server-new/scripts/stripe-cutover.ts:119siem-server-new/scripts/stripe-cutover.ts:285siem-server-new/scripts/stripe-cutover.ts:303

Earlier audit verdict and evidence · historical
Old billing bindings are migrated by scripts/stripe-cutover.ts and the country seed, and A34's Stripe IDs are saved before the push and copied back after it. The script was written after this audit and has not been run against a database.
Trace and register status

Read-only schema git diff against the catalog baseline shows intentional removal of legacy Stripe fields and StripeEventLog; new provider/token/country/locked price fields are nullable and cycle number defaults to 0. Existing statuses and period dates survive, but defaults neither create payment customers/mandates nor cancel Stripe subscriptions. MigrationChangedFiles.md:13 siem-server-new/prisma/schema.prisma:194 siem-server-new/prisma/schema.prisma:213. Invoice.externalId is nullable, not a required-column schema-push blocker; old Stripe invoice/payment lookup identifiers have no copy-before-drop script (A34). siem-server-new/prisma/schema.prisma:431 siem-server-new/scripts/payment-currency-backfill.ts:49. D33 remains pending. The 16 dev and 1 production customers are owner-supplied inventory, not queried counts. Actual existing rows, schema application and constraints require a separate environment rehearsal; none was executed.

D33siem-server-new/prisma/schema.prisma:190siem-server-new/prisma/schema.prisma:431siem-server-new/src/controllers/subscription.controller.ts:380
46. Seeds and payment currency backfillCompleteCountry seed defaults missing country to IN and writes gateway/currency together; cutover apply prices migrated subscriptions, and targeted seeds remain.

Verdict refresh: current resolution records and targeted source evidence supersede earlier reasons. siem-server-new/scripts/seed-subscription-countries.ts:6siem-server-new/scripts/seed-subscription-countries.ts:49siem-server-new/scripts/stripe-cutover.ts:209

Earlier audit verdict and evidence · historical
A32 reset seed and its entrypoints are removed. The country seed defaults a missing owner country to IN and writes the matching gateway, currency and Razorpay mandate headroom with it; the cutover script prices migrated subscriptions from the seeded plan rates (owner decision B1).

Latest resolution reconciliation: A32 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/prisma.config.ts:16siem-server-new/package.json:32

Earlier audit trace and patch notes · historical
Named seeds are limited but country seed misses target bindings/default IN; the configured general reset seed is destructive (A32).
Trace and register status

seed-plans upserts only custom Enterprise 101–150. Currency seed converts old USD rates into six interval/currency rates and resets free prices to zero; rerunning overwrites these fields, so its documented conversion is not preservation of later edited regional prices. siem-server-new/scripts/seed-plans.ts:41 siem-server-new/scripts/seed-plan-currencies.ts:53 siem-server-new/scripts/seed-plan-currencies.ts:74. Country seed selects null country, validates User.country, skips absent/invalid values and writes country only; it neither defaults missing country to IN nor sets provider/currency/locked price/status/cycles, contrary to the owner's D33 cutover target. siem-server-new/scripts/seed-subscription-countries.ts:16 siem-server-new/scripts/seed-subscription-countries.ts:36. Currency backfill fills missing USD/INR payment legs using a rate, not legacy external IDs. siem-server-new/scripts/payment-currency-backfill.ts:25 siem-server-new/scripts/payment-currency-backfill.ts:49. The separately configured general seed deletes subscriptions/users/payments (A32); the named migration seeds do not call it. siem-server-new/package.json:19 siem-server-new/prisma.config.ts:18.

D33siem-server-new/scripts/seed-plans.ts:5siem-server-new/scripts/seed-subscription-countries.ts:36siem-server-new/prisma/seed.ts:11
47. Existing paying customers: cutoverUnverifiedD33 is resolved in source with explicit owner-approved ACTIVE/PAST_DUE mapping; export/API cancellation/apply exists but has not been rehearsed or run.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/scripts/stripe-cutover.ts:23siem-server-new/scripts/stripe-cutover.ts:183D33 owner decisions A1/B1/C1/3:A

Earlier register verdict/evidence · historical
Cutover export/API cancellation/apply now exists; it is unrun and maps active customers to ACTIVE rather than the prompt’s PENDING_REAUTH target.

Verdict refresh: current resolution records and targeted source evidence supersede earlier reasons. siem-server-new/scripts/stripe-cutover.ts:23siem-server-new/scripts/stripe-cutover.ts:183siem-server-new/scripts/stripe-cutover.ts:222siem-server-new/scripts/stripe-cutover.ts:249

Earlier audit verdict and evidence · historical
scripts/stripe-cutover.ts now covers D33. export runs before the push: it saves each Stripe subscription's status and paid-through period, then cancels it through the API with no proration and no final invoice. apply runs after the seeds. A Stripe-active subscription becomes ACTIVE until its paid-through date at cycle 1, with its renewal job scheduled. A past_due/unpaid one becomes PAST_DUE with the normal grace period. Each is priced from the plan rate in its own currency, its customer is emailed to add a card, and the first card added becomes the default. Written after this audit; not run.

Note: the earlier trace below describes the design before the script existed; migrated customers no longer pass through PENDING_REAUTH.

Trace and register status

D33 agrees with pending status under the revised owner target. Runtime/script search finds no reader of stripeSubscriptionId paired with a Stripe API cancellation before schema removal. Country-only seed does not set the matching gateway, currency, price, PENDING_REAUTH or a meaningful cycle. siem-server-new/prisma/schema.prisma:194 siem-server-new/scripts/seed-subscription-countries.ts:36. Legacy default cycle 0 makes a Razorpay recovery success hit duplicate-first handling in PENDING_REAUTH: money is recorded without intended recovery activation. For cycle >0, billing worker accepts ACTIVE/PAST_DUE, excluding PENDING_REAUTH, so saved Stripe recovery method does not produce the intended charge. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:491 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:99. No cutover producer preserves the already-paid Stripe boundary while scheduling the next server period, or arms normal grace/final-deactivation when customers never return. Existing recovery screens are present (flow 32), but do not replace this missing migration. Expected cutover is the owner's explicit target; obsolete “0 real paid customers” wording is superseded. Migrationphasewiseplan.md:60.

D33siem-server-new/scripts/seed-subscription-countries.ts:36siem-server-new/src/bullmq/workers/billingCycleWorker.ts:99siem-server-new/src/payment/processor/PaymentEventProcessor.ts:491
48. Existing free trials and abandoned rowsCompleteTrial deadlines remain preserved; country seed now initializes the billing pair, and provider-null abandoned rows are included in cleanup.

siem-server-new/scripts/seed-subscription-countries.ts:49 siem-server-new/src/bullmq/workers/repeatableWorker.ts:573. Actual migration execution is tracked as Unverified in flows 45/47.

Earlier legacy-row assessment · historical
A33 provider-null abandoned cleanup is fixed; legacy free-trial billing-binding inventory and same-row conversion remain unverified.

Latest resolution reconciliation: A33 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/bullmq/workers/repeatableWorker.ts:573

Earlier audit trace and patch notes · historical
Trial deadlines remain readable; old provider-null abandoned rows are excluded from cleanup (A33), and legacy conversion needs binding inventory.
Trace and register status

Existing trialStart/trialEnd/status columns are retained. Trial-ending jobs and final grace use the preserved trial deadline; D15's fixed deadline treatment agrees with resolved status. Fresh paid /setup exists independently; this is not a reopening of the patched free-claim flow. siem-server-new/prisma/schema.prisma:190 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:26. Same-row conversion first calls getSubscriptionBilling, so legacy trials with null provider/currency cannot use it without suitable initialization; country-only seed does not establish the pair. Actual legacy-trial field inventory is Unverified and must distinguish fresh checkout from same-row conversion. siem-server-new/src/services/subscription.service.ts:1030 siem-server-new/scripts/seed-subscription-countries.ts:36. Legacy UNPAID/PAYMENT_FAILED rows receive null provider and both abandoned worker and unfinished-signup retirement require non-null provider, so old abandoned rows are never reaped (A33). siem-server-new/src/bullmq/workers/repeatableWorker.ts:586 siem-server-new/src/services/couponBooking.service.ts:133.

D15; D30; D33siem-server-new/src/bullmq/workers/repeatableWorker.ts:586siem-server-new/src/services/subscription.service.ts:1030
49. Deleted files, stale routes, cron and obsolete schedulesStale code presentPortal and public quote acceptance are removed and cleanup fixes are retained. The provisioning queue is retired, the legacy quote price fields are removed and the vestigial PAID status is dropped (no rows use it in either dump; needs db push), all on 13 Oct 2026. Remaining: StripeProvider.verifySetupCompletion, an unused implementation the provider interface requires (the Stripe webhook holds the live Indian-card guard); and two now-unreferenced files for the owner to delete (provisioningQueue.ts, provisioningWorker.ts).

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-server-new/src/services/plan.service.ts:836siem-server-new/src/bullmq/workers/provisioningWorker.ts:6D43/D48 removed

Earlier register verdict/evidence · historical
Confirmed-unused payment helpers/session route/scripts were removed; the live old Stripe portal and quote assumptions remain, and provisioning retirement needs queue inventory.

Latest resolution reconciliation: A32 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/prisma.config.ts:16siem-server-new/package.json:32

Earlier audit trace and patch notes · historical
All 70 cataloged deletions are absent without exact runtime imports; session invoice helpers and the live old portal remain stale.
Trace and register status

Checked all 70 deleted paths (47 server, 7 client, 16 admin), without opening old contents. No exact deleted-module import/require/dynamic import remains in searched runtime paths; basename hits are current middleware names or provenance comments. Replacement responsibilities are detailed below. MigrationChangedFiles.md:16 siem-server-new/src/prisma_repos/invoice.repository.ts:4. /create-checkout-session is live via the calculator alias; /update-subscription is live admin activation. /session-details and getInvoiceUrl retain old Stripe invoice projections with no real client caller found; /customer-portal is still called and relies on removed Stripe-owned recurring billing (pending D43). siem-client-new/components/reusable/price-calculator/index.tsx:558 siem-admin-new/app/dashboard/customers/[id]/components/EditSubscriptionStatus.tsx:57 siem-server-new/src/services/subscription.service.ts:1676 siem-server-new/src/services/subscription.service.ts:1950 siem-client-new/action/subscriptionAction.ts:124. Startup removes the stripe-event-cleanup repeatable entry, but actual Redis removal is Unverified. The surviving severityEmail cron is alerts, outside scope. siem-server-new/src/bullmq/queues/repeatableQueue.ts:37. New webhook retention maintenance is D21/out of scope, not reconstructed as billing logic.

Later dependency evidence: Batch 5: provisioning producer remains dead; manual test-plan-update still mutates only legacy priceMonthly, and CustomPricing has no current caller and supplies an example email. These are inspected stale dependencies, not new live billing defects. siem-server-new/src/test-plan-update.ts:16 siem-client-new/components/modules/custom-pricing/index.tsx:9. Section 10

Cleanup: session invoice/getStripe/manual plan-script/CustomPricing dead code and unused provisioning producer removed. Provisioning queue/worker retained pending Redis job inventory; live customer portal/quote and other billing defects are not deleted. Cleanup record

D21; D27; D43MigrationChangedFiles.md:16siem-server-new/src/bullmq/queues/repeatableQueue.ts:37siem-server-new/src/services/subscription.service.ts:1676
50. Login landing, subscription status and billing IAMCompleteD39/D40/D41/D50 are closed: all owned rows are loaded, extra-purchase switching is intentional, one current-subscription access rule drives server/client/IAM, and late-paid periods restart at payment.

Latest register reconciliation: owner-approved decisions and source reads supersede the older pending assessment. siem-server-new/src/services/user.service.ts:1289siem-server-new/src/utils/subscriptionAccess.ts:24siem-server-new/src/middlewares/auth.ts:307siem-server-new/src/payment/processor/PaymentEventProcessor.ts:546

Earlier assessment · historical
D52 sidebar and D53 truthful gate documentation are fixed; D39/D40/D41 status/context gaps and D50 grace access remain pending.

Updated register reconciliation: current D status and targeted source evidence supersede older reasons. siem-client-new/components/workspace/SidebarMenuNew.tsx:250Pending D39/D40/D41/D50

Earlier register verdict/evidence · historical
A30/A35/A36/A37/A38 are resolved; known derived-status/sidebar/server-gate gaps D39/D41/D52/D53 remain.

Verdict refresh: current resolution records and targeted source evidence supersede earlier reasons. siem-server-new/src/controllers/iam.controller.ts:604siem-server-new/src/controllers/iam.controller.ts:616siem-client-new/components/workspace/header/switchModal.tsx:46siem-client-new/components/workspace/header/switchModal.tsx:52siem-client-new/auth/lib/apiClientV2.ts:128siem-client-new/auth/lib/apiClientV2.ts:194

Earlier audit verdict and evidence · historical
A35/A36 tenant and scoped-resolver gaps and A30/A37/A38 are fixed; D39/D41/D52/D53 remain.

Latest resolution reconciliation: A35,A36,A30 fix records reconciled; the current verdict covers remaining independent blockers. siem-server-new/src/middlewares/iam.middleware.ts:60siem-server-new/src/middlewares/iam.middleware.ts:64 siem-server-new/src/services/iam/iam.registry.ts:50siem-server-new/src/services/iam/iam.registry.ts:52 siem-client-new/components/GlobalRouteController.tsx:23siem-server-new/src/controllers/iam.controller.ts:603siem-server-new/src/controllers/iam.controller.ts:615

Earlier audit trace and patch notes · historical
Known status/landing gaps persist; unscoped IAM bypasses tenant checks and scoped billing metadata is missing (A35/A36).
Trace and register status

Login routes pending onboarding, pending setup and payment problems separately; active sibling handling agrees with D51's resolved status. Owned context query omits PENDING_REAUTH/UNPAID/CANCELLED, payment_issue derivation covers PAYMENT_FAILED/PAST_DUE only, and raw-status API guard disagrees with derived statuses (D39/D40/D41 pending). D50 service access pause, D52 restricted sidebar and D53 client-only portal guard remain consistent with their pending entries. siem-client-new/app/(web)/signin/SignInClient.tsx:288 siem-server-new/src/services/user.service.ts:1294 siem-server-new/src/utils/user.util.ts:4 siem-server-new/src/middlewares/auth.ts:293. Wallet view adds A30. Owner policies grant siem:* on *; evaluator returns empty scope lists and IAM tenant/resource checks run only for nonempty scopes. Upgrade service loads requested subscription without comparing caller and charges its saved method (A35). Conversely explicit scoped billing/subscription/paymentmethods actions call metadata registry with unsupported module and receive 404 (A36). siem-server-new/src/services/subscription.service.ts:1966 siem-server-new/src/services/iam/iam.evaluator.ts:186 siem-server-new/src/middlewares/iam.middleware.ts:122 siem-server-new/src/services/subscription.service.ts:1028 siem-server-new/src/services/subscription.service.ts:1222.

Later dependency evidence: Batch 5 confirms pending D41/D52/D53 and existing A35/A36. It also finds false-success workspace switching (A37), nested encryption on authenticated mutation replay (A38), and an IAM filter removing wallet READ for restricted statuses (extends A30). siem-client-new/components/workspace/header/switchModal.tsx:50 siem-client-new/auth/lib/apiClientV2.ts:133 siem-server-new/src/controllers/iam.controller.ts:615. Section 10

D39; D40; D41; D50; D51; D52; D53siem-server-new/src/middlewares/iam.middleware.ts:59siem-server-new/src/services/iam/iam.registry.ts:28siem-client-new/components/GlobalRouteController.tsx:102

3. How billing works now

The server owns plans, tax-inclusive rates, one selected discount, locked price, periods and status. Subscription country fixes Razorpay/INR for IN and Stripe/USD otherwise; providers save methods and execute charges. siem-server-new/src/utils/companyRegistrationCountry.ts:1 siem-server-new/src/payment/PaymentProvider.interface.ts:1 Flows 1–5.

Free claim locks eligibility and commits local trial/membership/IAM together, compensating remote creation when local setup fails. Paid signup saves the method and settles the first charge into PENDING_SETUP before admin activation. Organization validation, duplicate checkout prevention, bounded polling and ordinary post-settlement email recovery are fixed. siem-server-new/src/services/subscription.service.ts:392 siem-server-new/src/services/subscription.service.ts:546 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:685 Flows 6–18.

BullMQ performs renewals, price/discount/mandate checks, retry/grace and final deactivation. Missing locked price, grace handoff, cleanup retry, busy-lock retry and hourly missed-renewal recovery have fixes. Stripe unknown-outcome reconciliation and reminder/grace-date gaps now have source fixes (D54/D42/D55). siem-server-new/src/bullmq/queues/billingCycleQueue.ts:54 Flows 19–25 and their remaining D findings.

Upgrade now writes plan/invoice/payment atomically, blocks quote-only Enterprise admission, replaces an interval-changed renewal job and clears scheduled downgrade state. Trial conversion has settlement follow-up; methods show the subscription’s actual token and protect chargeable use. The common server quote now drives pricing, status/reactivation, permission and client estimate (D23/D34/D35/D45/D59 resolved); encrypted authenticated replay A38 is now fixed. siem-server-new/src/services/subscription.service.ts:1275 siem-server-new/src/services/subscription.service.ts:1049 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:413 Flows 26–37.

Admin activation remains narrow; assignment and customer views exist. Temporary membership expiry is queued before access grant, and the owner selected read-only admin team views. Price pushes are owner-approved plan-wide only (D22); queued-plan and successful-email notice timing protection are fixed. The fixed job amount is still unbound to later same-plan asset/interval changes (D72). Billing IAM now resolves named resources and checks tenant boundaries even for unscoped grants. siem-server-new/src/controllers/admin-membership.controller.ts:118 siem-server-new/src/middlewares/iam.middleware.ts:60 Flows 38–40 and 50.

Wallet purchases choose either gateway and settle against WalletPurchase. Captured cancelled purchases now receive credits; confirmation enqueue has a durable marker/recovery sweep and correct portal links. Top-up stays ACTIVE/TRIALING only. D76/D77 failure and coupon-slot races have source fixes; Wallet view now follows D41’s latest current-status access table; setup/grace statuses may view it, while unpaid first signup/EXPIRED/none are profile/subscription-only. Top-up stays ACTIVE/TRIALING. siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:59 siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:237 siem-server-new/src/controllers/iam.controller.ts:615 Flows 41–44.

The destructive reset seed and confirmed-unused payment helpers were removed. Cutover export/cancellation/apply and historical identifier preservation are now built; D33’s ACTIVE/PAST_DUE mapping is explicitly owner-approved; actual execution remains Unverified. Legacy abandoned rows enter cleanup and the country seed initializes the billing pair; actual migration application and provisioning queue inventory remain unverified. siem-server-new/prisma.config.ts:16 siem-server-new/src/bullmq/workers/repeatableWorker.ts:573 Flows 45–49. A37/A38 request-layer fixes and the latest shared access rule and its owner-approved behavior are detailed in section 10.

Latest access and paid-period decisions: ACTIVE/TRIALING permits SIEM/team/profile/subscription/wallet; PENDING_SETUP/PAST_DUE/PENDING_REAUTH/CANCELLED/ENDED permits team/profile/subscription/wallet; UNPAID/PAYMENT_FAILED/EXPIRED/none permits profile/subscription only. IAM still controls actions within allowed areas. A paid extra purchase intentionally becomes the current workspace; a failed one leaves the current workspace. A late renewal inside grace starts its new period when paid; an on-time renewal starts at the old period end. siem-server-new/src/utils/subscriptionAccess.ts:24 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:546 D39/D40/D41/D50 decisions.

4. Defects found

All A1–A38 now have resolved records and source implementations. A30’s server IAM fix, A34’s identifier export/apply, A37’s switch-result handling and A38’s encrypted replay guard supersede earlier partial/open assessments. Resolved source findings do not certify deployment or close independent D defects. Original evidence and prior assessments are preserved as history.

Fixes applied 13 Oct 2026, after Batch 1 (source only, not re-audited and not executed): A1–A7 are resolved (A3 by the owner's all-or-nothing free-claim decision). Register defects found on the board are also resolved in source: D18 (verify-payment accepts only the subscription owner or an active member of its workspace), D57 (the verified order must name this subscription and be paid by the owner's Razorpay customer), D19 (the admin pending-setup email receives the subscription's currency; the template does not print it), D27 (the success page takes the first-charge invoice from /status/:id; it no longer calls /session-details), D49 (Stripe's cancel link returns to onboarding step 2 or the portal buy page) and the D73 remainder (a paid plan is saved only when all six USD/INR rates are above 0). The flow 42 note below is also fixed. Board verdicts for flows 1–13 are left as audited.

A1highresolved

An inactive plan can still be purchased

Resolved 13 Oct 2026: a deactivated plan is refused with “This plan is no longer available.” at signup and free claim (createSubscription), discount selection, price preview, and as the target of an upgrade or downgrade; a quote can be sent only for an active plan, standard or custom. A subscription already on the plan keeps renewing and may change its own assets or interval. Owner decision 13 Oct 2026: a quote already sent stays valid until it expires even if its plan is deactivated afterwards; quote landing, discount selection, pricing and createSubscription accept a deactivated plan only with an open (SENT, unexpired, same email, same plan) quote, and price preview no longer checks the plan's active flag. siem-server-new/src/services/subscription.service.ts (PLAN_NOT_AVAILABLE, createSubscription, upgradeSubscription) · discountSelection.service.ts preview, loadPricing · plan.service.ts sendQuote

Original failure scenario · resolved

An admin deactivates a plan; the status route writes isActive:false and the public list hides it. A customer who already has the plan ID, or retained a loaded calculator, can request /discount-selection and /setup anyway. Neither pricing load nor checkout tests plan.isActive: it only checks existence, free-plan kind and asset bounds. A new paid subscription and gateway checkout are still created. siem-server-new/src/routes/plans.routes.ts:29 siem-server-new/src/services/plan.service.ts:533 siem-server-new/src/services/discountSelection.service.ts:347 siem-server-new/src/services/subscription.service.ts:346 siem-server-new/src/services/subscription.service.ts:510

Expected: Deactivation must prevent new sales at the server boundary, as the admin confirmation promises “New customers and new quotes can no longer choose this plan.” siem-admin-new/app/dashboard/plans/page.tsx:1361. This is a checkout enforcement gap beyond D73’s formerly missing status toggle. Affects flows 4, 6–9.

A2highresolved

Two simultaneous requests can claim two free trials

Resolved 13 Oct 2026: the free claim runs in a billing transaction that locks the user row and repeats the eligibility check inside it; the second of two simultaneous claims waits, then sees the first trial and is refused. siem-server-new/src/services/subscription.service.ts createSubscription (free branch), checkFreePlanClaimed(userId, db)

Original failure scenario · resolved

A new customer submits two free claims before either commits, using different valid org names. Both checkFreePlanClaimed calls can read zero owned subscriptions/memberships before entering the free-claim transaction. Each creates its own PENDING_SETUP trial; each later overwrites currentSubscriptionId. The free transaction neither locks the user nor repeats the eligibility check, and Subscription has no uniqueness constraint for a user’s trial claim. siem-server-new/src/services/subscription.service.ts:355 siem-server-new/src/services/subscription.service.ts:367 siem-server-new/src/services/subscription.service.ts:405 siem-server-new/src/services/subscription.service.ts:1988 siem-server-new/prisma/schema.prisma:279

Expected: Only one eligible free-trial claim should be accepted for a customer, including concurrent requests; the existing “free plan is only available to new users” rejection must remain true at creation. siem-server-new/src/services/subscription.service.ts:357. Affects flow 6. This concurrency outcome is inferred directly from the read-before-write sequence; no concurrent requests were executed.

A3highresolved

Free-trial provisioning can fail after the claim is committed

Resolved 13 Oct 2026 (owner decision: all or nothing): the remote organization is created first (createRemoteOrg); if that fails nothing is saved. The trial, user activation, current subscription, owner membership (recordOrgOwnership) and IAM bootstrap (owner policy and L1/L2 roles, now error-propagating) are written in one billing transaction. If it rolls back, the remote organization is deleted (removeRemoteOrg) and the customer sees “Service is unavailable right now. Please try again in some time or contact us.” (HTTP 503), or the free-plan eligibility message for a duplicate claim. Timezone sync, emails, reminders and the trial-ending job run after the commit; their failures are logged and task:reminder-sync / task:sub-finalization cover reminders and trial end. If the compensating delete fails, an unused remote organization remains and is logged. siem-server-new/src/services/subscription.service.ts createSubscription (free branch) · lib/middlewareApi.ts · lib/role-policies-bootstrap.ts · utils/errors/ServiceUnavailableError.ts · controllers/subscription.controller.ts

Original failure scenario · resolved

A customer claims a free trial while organization provisioning is unavailable. The transaction already commits PENDING_SETUP, User.active and currentSubscriptionId. The next inline provisionOrg call throws, so IAM bootstrap, onboarding emails, reminder scheduling and trial-ending enqueue are not reached; the controller returns 500. Retrying the claim is refused because the committed trial now makes hasClaimedFree true. Unlike paid/zero settlement, this free claim creates no invoice carrying a recoverable settlement follow-up. siem-server-new/src/services/subscription.service.ts:367 siem-server-new/src/services/subscription.service.ts:412 siem-server-new/src/lib/middlewareApi.ts:129 siem-server-new/src/controllers/subscription.controller.ts:60 siem-server-new/src/services/subscription.service.ts:357 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:644

Expected: The committed trial needs a durable handoff that can finish organization/IAM setup after a temporary failure, without another claim or a restarted deadline. task:provision-org exists, but searching scheduleProvisioning in src found only its declaration, so that retry worker is not the handoff for this flow. siem-server-new/src/bullmq/queues/provisioningQueue.ts:22 siem-server-new/src/bullmq/workers/provisioningWorker.ts:6. Daily trial finalization may still end the row; it does not repair provisioning. siem-server-new/src/bullmq/workers/repeatableWorker.ts:678. Affects flows 6 and 13.

A4mediumresolved

Coupon and quote signups are permanently excluded from abandoned deletion

Resolved 13 Oct 2026: cleanup also selects coupon/quote signups whose checkout was closed, and deletes one only when no BOOKED coupon redemption still points at it. The Razorpay late-authorization protection is unchanged. siem-server-new/src/bullmq/workers/repeatableWorker.ts task:abandoned-cleanup

Original failure scenario · resolved

A customer abandons a coupon/quote-linked paid signup. Expiry closes the checkout and releases the booking, but keeps subscription.couponId. The 25-hour cleanup query requires couponId:null and rechecks that same condition before deletion. Therefore the released signup still fails the cleanup predicate after 25 hours, 3 days or any later scan; the coupon maintenance path closes/releases but does not delete it. siem-server-new/src/services/couponBooking.service.ts:182 siem-server-new/src/services/couponBooking.service.ts:214 siem-server-new/src/bullmq/workers/repeatableWorker.ts:582 siem-server-new/src/bullmq/workers/repeatableWorker.ts:616

Expected: Once no payment can still complete and the coupon slot is safely released, abandoned signup rows should be eligible for the ordinary cleanup. Preserve the intentional Razorpay pending/three-day late-authorization protection. Migrationphasewiseplan.md:351 DiscountSelectionplan.md:103. Affects flow 12. This is retained subscription data, not a claim that coupon slots remain booked.

A5mediumresolved

The confirmation page can poll forever

Resolved 13 Oct 2026: polling stops after two minutes and the page shows “Still confirming…” with Check again (restarts a two-minute poll) and Contact on WhatsApp; no charge is retried. siem-client-new/app/(web)/onboarding/success/page.tsx (CONFIRMATION_LIMIT_MS, delayed state)

Original failure scenario · resolved

A customer returns from Stripe setup while the first charge or follow-up never finishes—for example, an Indian-issued card is rejected, which returns no token event and leaves the row UNPAID. The page stays confirming and repeats /status/:id every 2 seconds. It has neither an elapsed-time limit nor a delayed-state exit; non-404 errors are also only logged. siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:82 siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:101 siem-client-new/app/(web)/onboarding/success/page.tsx:44 siem-client-new/app/(web)/onboarding/success/page.tsx:65 siem-client-new/app/(web)/onboarding/success/page.tsx:75

Expected: The main plan bounds confirmation polling at two minutes. After that, a customer needs a finite processing/delay outcome rather than an indefinite spinner; this does not authorize retrying a charge. Migrationphasewiseplan.md:446. Affects flow 10. D37’s resolved failed/closed-state fixes are present; this remaining pending-state case is additional.

A6mediumresolved

A failed first-charge email shows the full price as dollars and links nowhere

Resolved 13 Oct 2026: the email formats the amount in its own currency ($ or ₹) and the template no longer adds “$”. First-charge and renewal failures send the attempted amount. Links: a declined signup goes to start over (signupStartAgainUrl: onboarding step 2, or the portal buy page), renewal and plan-change failures go to the subscription page, and the Stripe Indian-card rejection links by the same rule. The link reads “continue here”. siem-server-new/src/email/email.service.ts sendPaymentFailedEmail · payment-failed.html · PaymentEventProcessor.ts · StripeWebhookAdapter.ts

Original failure scenario · resolved

An Indian customer attempts a ₹1,000 cycle with a ₹200 discount and the ₹800 first charge is declined. The processor sends lockedPriceAmount (₹1,000), not event.amountAttempted (₹800), and passes invoiceUrl:"". The email service formats a number without currency, and the template prefixes a literal "$" and uses that empty URL for its billing-portal link. The customer receives “Amount Attempted: $1,000.00” and a recovery link with no destination. The same blank link is sent for a declined Stripe first charge, despite the required start-again behavior. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:789 siem-server-new/src/email/email.service.ts:1629 siem-server-new/src/email/new-email-templates/templates/new/payment-failed.html:27 siem-server-new/src/email/new-email-templates/templates/new/payment-failed.html:48

Expected: The first-failure email should describe the actual attempted amount in its actual currency and direct this customer to starting over, consistently with the checkout closure decision. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:757 siem-client-new/app/(web)/onboarding/success/page.tsx:92. Affects flow 11. This is separate from known D19, which concerns the admin onboarding email.

A7highresolved

Organization availability accepts names that provisioning always rejects

Resolved 13 Oct 2026: /validate-org-name, /setup and /create-checkout-session refuse a name whose cleaned workspace form (cleanToCamelCase) is not 2–50 letters or numbers, before any payment or free claim; the checkout form shows the same rule. siem-server-new/src/validators/subscriptionSchema.ts (orgNameSchema) · siem-client-new/components/reusable/price-calculator/index.tsx handleOrgNameSubmit

Original failure scenario · resolved

A customer enters organization name "a". The browser checks only nonempty text; /validate-org-name checks whether the cleaned name already exists in SiemAuth, so "a" is advertised as available if unused. Setup also accepts this name. After Stripe/Razorpay takes the first charge, settlement writes payment/invoice and PENDING_SETUP; follow-up cleans "a" to "a", then addOrg rejects it because the tenant title must have 2–50 characters. Follow-up cannot succeed with the stored name, and setupReady stays false. A free claim hits the same rejection immediately after its transaction. siem-client-new/components/reusable/price-calculator/index.tsx:745 siem-server-new/src/validators/subscriptionSchema.ts:16 siem-server-new/src/services/subscription.service.ts:845 siem-server-new/src/prisma_repos/SiemAuthRepository.ts:226 siem-server-new/src/helper/helper.ts:206 siem-server-new/src/middleApi/modules/org/org.controllers.ts:20 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:666

Expected: The name accepted before checkout must satisfy the actual normalized tenant-title rules before money or a free claim is committed. This deterministic validation mismatch is distinct from A3’s temporary provisioning outage. siem-server-new/src/lib/middlewareApi.ts:121 siem-server-new/src/lib/middlewareApi.ts:131. Affects flows 6–9 and 13.

A8highresolved

Two concurrent checkout requests can each open and charge a signup

Resolved 13 Oct 2026: after earlier unfinished checkouts are closed, the signup transaction (under the user lock) refuses to create a signup while any unfinished cycle-0 checkout with an open checkout is still present — that one was created by a concurrent request. The second tab gets “Another checkout is already open for your account. Finish it or close it, then try again.” siem-server-new/src/services/subscription.service.ts createAndInitiatePayment · couponBooking.service.ts (UNFINISHED_SIGNUP, CHECKOUT_ALREADY_OPEN)

Original failure scenario · resolved

An existing customer submits the same $100 signup in two tabs while no unfinished signup exists. Both calls can finish closeUnfinishedSignups before either creates a row. Request A refreshes selection S, creates signup A under the user lock, and marks S USED. Request B then runs select: a USED selection is not held, but its upsert reuses S and sets ACTIVE again; B's checkout can create signup B. Both IDs reach gateway setup, and Stripe charge locks/idempotency are per subscription, so completing both can collect $200. The existing customer can already have a PaymentCustomer, so customer creation is not needed for this interleaving. siem-server-new/src/services/subscription.service.ts:480 siem-server-new/src/services/subscription.service.ts:486 siem-server-new/src/services/subscription.service.ts:517 siem-server-new/src/services/subscription.service.ts:558 siem-server-new/src/services/discountSelection.service.ts:87 siem-server-new/src/services/discountSelection.service.ts:144 siem-server-new/src/services/couponBooking.service.ts:130 siem-server-new/src/payment/chargeLock.ts:13 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:285.

Expected: Starting over must close/refuse an unfinished prior checkout even across concurrent requests; reusing a customer selection ID must not bypass that boundary. This is a different race from the correctly locked last coupon slot in flow 14 and from Batch 1's free-claim A2. No concurrent requests or gateway payments were executed. DiscountSelectionplan.md:60 DiscountSelectionplan.md:114. Affects flow 16 and the first-charge handoffs noted on flows 7–9.

A9highresolved

Missing locked price is treated as a fully discounted renewal

Resolved 13 Oct 2026: the renewal worker checks lockedPriceAmount === null before the zero-amount branch and moves such a subscription to PAST_DUE with the normal grace period (payment-setup recovery), the same path as a missing provider or token. siem-server-new/src/bullmq/workers/billingCycleWorker.ts (moveToMissingFieldsRecovery)

Original failure scenario · resolved

When an ACTIVE current-cycle row has a valid country/provider/currency pair but lockedPriceAmount:null, no pending price and no discount, resolveCyclePricing resolves null to 0. Worker emits an internal PAYMENT_SUCCEEDED for zero before reaching its missing-price guard. Shared settlement records zero-paid money and advances the period/cycle instead of surfacing payment setup. This is a source scenario, not a claim that a counted database row currently has these values. The schema allows null. siem-server-new/prisma/schema.prisma:199 siem-server-new/src/services/subscription.service.ts:112 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:175 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:187 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:204 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:610.

Expected: A real full-price discount may settle at zero, but an unknown/missing price must follow the explicitly designed missing-fields recovery/grace handling. No added tax or gateway authorization is required for a legitimate zero cycle. Migrationphasewiseplan.md:313. Affects flow 19; this is distinct from D25's missing-token message and D33's invalid legacy gateway/country binding.

A10highresolved

A queue error can permanently lose renewal grace and retries

Resolved 13 Oct 2026: grace and retry scheduling errors are no longer caught, so the webhook is recorded failed and redelivered (or the worker job retries). A redelivery finds the subscription PAST_DUE and schedules the grace deadline and reminders whenever no grace-final-<sub> job is queued. siem-server-new/src/payment/processor/PaymentEventProcessor.ts handlePaymentFailed · bullmq/queues/lifecycleQueue.ts hasFinalDeactivationJob

Original failure scenario · resolved

A renewal fails; the transaction commits PAST_DUE. If scheduling grace reminders throws before final deactivation/retry scheduling, the processor catches/logs the exception and resolves successfully. The webhook adapter then records the event processed. Redelivery is skipped, and another failure sees PAST_DUE so it does not run first-failure grace scheduling again. Daily finalization scans cancelled/trial rows, not a normal unpaid renewal, and follow-up recovery scans successful invoices. Thus this accepted failure can have no deactivation deadline or automatic retry job. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:754 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:815 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:839 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:871 siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:38 siem-server-new/src/payment/adapters/StripeWebhookAdapter.ts:44 siem-server-new/src/bullmq/workers/repeatableWorker.ts:692 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:651.

Expected: The accepted failure needs a recoverable grace/retry handoff; a scheduling exception must not turn the webhook into permanently finished work with no deadline. D36's processed-only dedupe cannot repair an error swallowed before the adapter sees it. Migrationphasewiseplan.md:351 Phase3DeviationsFixplan.md:375. Affects flow 21. No queue outage was injected.

A11highresolved

EXPIRED is committed before cleanup that cannot reliably retry

Resolved 13 Oct 2026: final deactivation removes the assets first and writes EXPIRED only afterwards; the cleanup throws when any asset could not be removed, so the job's retry (status still in the grace set) removes the rest. The “Data Permanently Removed” email is sent only after every asset is gone. siem-server-new/src/bullmq/workers/lifecycleWorker.ts task:final-deactivation, performAgentCleanup

Original failure scenario · resolved

At grace end, final-deactivation first writes EXPIRED. If the following asset query/cleanup throws, BullMQ retries—but the task accepts only ENDED/CANCELLED/PAST_DUE/PENDING_REAUTH, so the now-EXPIRED row is skipped and assets/default cleanup/emails do not resume. Separately, each asset delete/log failure is caught and ignored, after which the job can send the “Data Permanently Removed” email despite retained assets. The retry policy is present but cannot complete those remaining effects. siem-server-new/src/bullmq/workers/lifecycleWorker.ts:135 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:145 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:150 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:271 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:312 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:155 siem-server-new/src/bullmq/queues/lifecycleQueue.ts:7.

Expected: Final deactivation must complete the specified cleanup and notifications after a recoverable failure; terminal status must not make its unfinished effects unreachable or report all data removed when asset deletion failed. D4's map transition is fixed, but that does not fix this subsequent cleanup failure. Phase3DeviationsFixplan.md:33. Affects flow 21. This shared-helper defect is newly registered here; its introduction by this migration was not established.

A12mediumresolved

An in-flight manual payment can end an automatic retry chain early

Resolved 13 Oct 2026: a scheduled retry that finds another charge in flight is moved back by 15 minutes instead of completing; one charge at a time is kept, and if the other charge settles the cycle the PAST_DUE check skips the attempt. siem-server-new/src/bullmq/workers/retryPaymentWorker.ts (LOCK_BUSY_RECHECK_MS)

Original failure scenario · resolved

While automatic attempt 2 is due, the customer starts a manual retry whose bank result is still pending. Retry worker cannot obtain the subscription lock, breaks and completes attempt 2 without requeueing it. That scheduled attempt issued no charge and therefore produces no failure webhook to enqueue attempt 3. If the manual payment later fails, retryAttempt:"manual" deliberately schedules nothing, so the existing automatic chain ends even though its grace window and remaining allowance may still permit retries. The source comment acknowledges this shortcut. siem-server-new/src/bullmq/workers/retryPaymentWorker.ts:70 siem-server-new/src/bullmq/queues/retryPaymentQueue.ts:24 siem-server-new/src/services/subscription.service.ts:1761 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:856.

Expected: An already-running automatic chain should not lose every remaining attempt merely because a manual attempt was in flight. Preserve one charge at a time and the decision that manual failure does not start a new chain; retry opportunities still end at successful settlement or grace expiry. Migrationphasewiseplan.md:351. Affects flow 21. It prevents a duplicate debit but reduces the implemented retry service; this acknowledged source limit is not an owner-listed deliberate decision.

A13mediumresolved

Settlement follow-up ignores returned email failures

Resolved 13 Oct 2026 (owner decision A: per-email markers): the follow-up sends each email once: a delivered email's key is recorded on Invoice.followUpEmailsSent immediately, a returned failure throws, the follow-up stays unfinished, and its retry sends only the missing emails (payment success; admin pending, user pending, onboarding contact). The free claim and trial conversion keep best-effort sending with failures logged. Requires prisma generate and db push. siem-server-new/src/payment/processor/PaymentEventProcessor.ts completeSettlementFollowUp · subscription.service.ts sendOnboardingEmails (EmailDelivery) · prisma/schema.prisma Invoice

Original failure scenario · resolved

After a successful renewal, sendPaymentSuccessEmail catches an SMTP error and returns {success:false}; follow-up ignores that return and stamps settlementFollowUpAt. Minute recovery excludes that invoice forever, so no payment-success email is retried. Cycle-0 onboarding has the same problem: all three sender helpers return failure objects, sendOnboardingEmails ignores them, and retryOnFailure:true handles only thrown errors. The follow-up can finish and setupReady can become true with onboarding emails undelivered. Money/provisioning are not rolled back. siem-server-new/src/email/email.service.ts:1607 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:693 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:703 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:654 siem-server-new/src/email/email.service.ts:1215 siem-server-new/src/email/email.service.ts:1274 siem-server-new/src/email/email.service.ts:1391 siem-server-new/src/services/subscription.service.ts:698 siem-server-new/src/services/subscription.service.ts:748 siem-server-new/src/services/subscription.service.ts:1798.

Expected: The durable follow-up must leave a recoverable delivery failure unfinished rather than marking its required message step successful. This is independent of D19's admin email currency and of the external Batch 1 A6 failure-email fixes. Migrationphasewiseplan.md:342 Migrationphasewiseplan.md:345. Affects flow 24, with a cross-flow note on the earlier activation/provisioning trace.

A14mediumresolved

The planned hourly billing-cycle reconciliation is missing

Resolved 13 Oct 2026: task:billing-cycle-reconciliation is registered at 0 * * * *. Each run finds paid ACTIVE subscriptions due within the hour. A subscription whose period ended at least 15 minutes ago, with no waiting/delayed/active renewal job, no charge lock and no pending Razorpay debit, gets its renewal job back to run now; one ending within the next hour with no job at all gets it scheduled for the period end. The worker's own status, cycle and period checks still decide whether to charge; every restore is logged. siem-server-new/src/bullmq/queues/billingCycleQueue.ts reconcileMissedRenewals · repeatableQueue.ts · repeatableWorker.ts · payment/chargeLock.ts isChargeInFlight

Original failure scenario · resolved

A paid ACTIVE subscription misses its renewal enqueue—for example, the process stops after activation commits but before its queue add. Main §3.14 requires task:billing-cycle-reconciliation hourly, but no registration, producer or handler with that name exists in current runtime src. initRepeatableJobs ends after its listed jobs; repeatableWorker's default only logs an unknown task. Its existing sub-finalization scans cancellation/trial rows and does not repair a normal ACTIVE auto-renewal. There is no source implementation of the planned hourly safety step to pick up that missed paid cycle. siem-server-new/src/bullmq/queues/repeatableQueue.ts:18 siem-server-new/src/bullmq/queues/repeatableQueue.ts:107 siem-server-new/src/bullmq/workers/repeatableWorker.ts:692 siem-server-new/src/bullmq/workers/repeatableWorker.ts:1119 siem-server-new/src/services/subscription.service.ts:1538.

Expected: The hourly task required by the authoritative plan must be registered and handled; its exact repair algorithm is not invented by this audit. A caught activation enqueue error has rollback, but a stopped process between commit/enqueue is a different case. This is not the intentionally deleted Stripe-owned subscription reconciliation cron. Migrationphasewiseplan.md:368. Affects flow 19. Search absence plus the complete registration/dispatch paths are the evidence; Redis's actual repeatable schedules were not inspected.

A15highresolved

A synchronous upgrade can leave an invoice without its payment record

Resolved 13 Oct 2026: the synchronous upgrade writes plan, invoice and payment in one billing transaction under the subscription lock; the webhook writer does the same, and whichever runs second finds the invoice and stops. subscription.service.ts upgradeSubscription · PaymentEventProcessor.ts upgrade branch

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/services/subscription.service.ts:1275siem-server-new/src/payment/processor/PaymentEventProcessor.ts:270

Original failure evidence · resolved

A Stripe upgrade charge succeeds. The service applies the plan, commits the invoice, then separately creates Payment. If the process stops or FX/payment creation fails after the invoice, the customer was charged and the invoice exists but the payment-table entry does not. Redelivery finds that invoice and returns; it is not marked settlementApplied and cannot enter durable follow-up. If the webhook wins first, the service can instead fail at the unique invoice insert after its separate plan write.

Expected: One confirmed upgrade must leave consistent plan, invoice and payment records, and a retry must finish any incomplete handoff. The webhook’s atomic upgrade transaction exists; the synchronous writer bypasses it.

siem-server-new/src/services/subscription.service.ts:1234 siem-server-new/src/services/subscription.service.ts:1254 siem-server-new/src/services/subscription.service.ts:1276 siem-server-new/src/services/subscription.service.ts:1280 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:197 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:270 siem-server-new/prisma/schema.prisma:439 siem-server-new/prisma/schema.prisma:447 Affects flows 26 and 36. Source-derived scenario; no requests, payments or failures were injected.

A16highresolved

The upgrade endpoint sells quote-only Enterprise without a quote

Resolved 13 Oct 2026: the server refuses moving onto an isCustom plan by plan change (“available only through a quote”); a subscription already on it may change its own assets or interval. The client already hid custom plans (UpdateSubscriptionModal.tsx:133) and shows a customer's own custom plan only as their current plan. subscription.service.ts upgradeSubscription

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/services/subscription.service.ts:1049

Original failure evidence · resolved

The plan-change modal correctly hides isCustom plans. A customer can nevertheless post /upgrade with Enterprise’s ID and 101–150 assets. The server checks existence, activity and asset range, then proceeds to pricing and setup/charging without an isCustom/quote requirement. The strict body contains no quote proof. Successful settlement applies that custom plan. The same admission is used before the free-trial conversion branch.

Expected: Enterprise must be sold only through an eligible quote, including when a plan-change request comes directly to the server. Hiding it in the browser does not implement that decision.

siem-server-new/scripts/seed-plans.ts:8 siem-client-new/app/portal/(me)/subscription/components/UpdateSubscriptionModal.tsx:133 siem-server-new/src/services/subscription.service.ts:1017 siem-server-new/src/services/subscription.service.ts:1024 siem-server-new/src/services/subscription.service.ts:1207 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:318 siem-server-new/src/validators/subscriptionSchema.ts:46 Affects flows 26 and 30. Source-derived scenario; no requests, payments or failures were injected.

A17highresolved

A shorter upgraded period retains a later renewal job

Resolved 13 Oct 2026: when an upgrade changes the interval (and so the period end), both writers replace the cycle's renewal job with one at the new period end (rescheduleBillingCycleJob removes the delayed job first, since adds deduplicate by ID). billingCycleQueue.ts · subscription.service.ts · PaymentEventProcessor.ts

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/bullmq/queues/billingCycleQueue.ts:50siem-server-new/src/services/subscription.service.ts:1340siem-server-new/src/payment/processor/PaymentEventProcessor.ts:349

Original failure evidence · resolved

Take an annual subscription whose renewal job is due in 180 days. A higher-value monthly asset upgrade is charged in full and resets currentPeriodEnd to 30 days from now. Both the synchronous and webhook upgrade branches write that earlier end but schedule no replacement billing job before returning. The old job still waits 180 days; the worker’s “early job” guard can delay an early job but cannot make a later job fire sooner. Thus the next monthly debit can be 150 days late. A14’s missing hourly sweep provides no source recovery.

Expected: Once a plan change replaces the paid-period boundary, the next renewal must be queued for that new boundary. D24’s early-job guard does not cover this opposite direction.

siem-server-new/src/services/subscription.service.ts:1146 siem-server-new/src/services/subscription.service.ts:1245 siem-server-new/src/services/subscription.service.ts:1313 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:327 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:355 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:117 siem-server-new/src/bullmq/queues/billingCycleQueue.ts:26 Migrationphasewiseplan.md:347 Affects flow 26, with later evidence for flow 19. Source-derived scenario; no requests, payments or failures were injected.

A18mediumresolved

An interval-only downgrade is silently discarded

Resolved 13 Oct 2026: “no change” now requires the same plan, assets and interval; an interval-only change is scheduled for the next renewal like any downgrade, which already applies downgradeInterval. subscription.service.ts upgradeSubscription

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/services/subscription.service.ts:1120

Original failure evidence · resolved

The customer keeps the same plan and assets but changes yearly to monthly. For a configured $100 monthly / $1,200 yearly cycle, requestedValue ≤ currentValue takes the downgrade branch; its “already current plan” check compares only plan ID and asset count. It clears pending downgrade fields and returns No change instead of staging month. The client can show success while the next cycle stays yearly.

Expected: A changed interval must be part of deciding whether the requested subscription is unchanged; a genuine lower-cycle change must survive to its scheduled boundary.

siem-server-new/src/services/subscription.service.ts:1082 siem-server-new/src/services/subscription.service.ts:1085 siem-server-new/src/services/subscription.service.ts:1086 siem-server-new/src/services/subscription.service.ts:1107 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:134 siem-client-new/app/portal/(me)/subscription/components/UpdateSubscriptionModal.tsx:315 Affects flow 27. Source-derived scenario; no requests, payments or failures were injected.

A19highresolved

A paid upgrade leaves an earlier downgrade scheduled

Resolved 13 Oct 2026: cancelScheduledDowngrade clears the downgrade target and unmarks agents flagged for removal; it runs inside both upgrade writers and when the customer chooses their current plan again. subscription.service.ts · PaymentEventProcessor.ts

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/services/subscription.service.ts:157siem-server-new/src/services/subscription.service.ts:1292siem-server-new/src/payment/processor/PaymentEventProcessor.ts:342

Original failure evidence · resolved

A customer stages 10→5 assets for renewal, then buys an immediate upgrade to 20. Neither applied-upgrade writer clears downgradePlanId/downgradeAssetsCount/downgradeInterval. At renewal the worker still applies the old five-asset target, despite the newer paid upgrade, and runs removal flags created for that earlier request. A related stale state remains when “No change” cancels the target but leaves pendingRemoval flags for a later downgrade.

Expected: The effective paid upgrade must remain the renewal target; an earlier superseded downgrade and its removal intent must not undo it or delete assets later.

siem-server-new/src/services/subscription.service.ts:1095 siem-server-new/src/services/subscription.service.ts:1107 siem-server-new/src/services/subscription.service.ts:1235 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:315 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:131 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:154 siem-server-new/src/services/subscription.service.ts:1087 siem-server-new/src/services/subscription.service.ts:752 Affects flows 26–27. Source-derived scenario; no requests, payments or failures were injected.

A20mediumresolved

Trial conversion has no recoverable post-settlement handoff

Resolved 13 Oct 2026: the conversion invoice is written with settlementApplied: true and its cycle number, and queued to the settlement follow-up, which (cycle 0) cancels the trial's lifecycle jobs, rebuilds reminders and sends the onboarding emails once each (A13 markers), retried until complete. PaymentEventProcessor.ts trial conversion, completeSettlementFollowUp

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:413siem-server-new/src/payment/processor/PaymentEventProcessor.ts:698

Original failure evidence · resolved

A trial-to-paid charge succeeds and its transaction records money plus the paid PENDING_SETUP plan. If the process stops immediately afterward, trial-job cancellation, reminder refresh and onboarding messages never run. On webhook redelivery the existing-invoice check returns. The conversion invoice kept settlementApplied’s false default and was never queued for follow-up; the maintenance recovery scan selects only applied invoices. A thrown post-commit effect has the same no-resume outcome. This is separate from A13: that finding covers returned mail failures inside the normal durable follow-up.

Expected: A successful conversion must retain recoverable unfinished work for its required post-transaction steps, rather than making the committed invoice suppress completion on retry.

siem-server-new/src/payment/processor/PaymentEventProcessor.ts:376 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:439 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:444 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:447 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:455 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:197 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:654 siem-server-new/prisma/schema.prisma:439 Migrationphasewiseplan.md:343 Affects flow 30. Source-derived scenario; no requests, payments or failures were injected.

A21mediumresolved

Stripe’s default badge can describe the wrong card

Resolved 13 Oct 2026: for both gateways the listed default is the method this subscription charges (externalTokenId); the “default changed” email names this subscription's previous method. subscription.service.ts getPaymentMethods, setDefaultPaymentMethod

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/services/subscription.service.ts:2054siem-server-new/src/services/subscription.service.ts:2167

Original failure evidence · resolved

Subscriptions A and B belong to one Stripe customer and charge saved cards X and Y respectively. Setting Y as default for B also sets the Stripe customer default to Y. Listing A returns the provider’s customer-default badge unchanged, so A shows Y as default although off-session charging explicitly uses A.externalTokenId=X. Even a first setup can show no default badge because TOKEN_SAVED updates only the subscription. Razorpay’s listing already maps the badge to the selected subscription; Stripe’s does not.

Expected: The displayed default must identify the method that this selected subscription will charge. A customer-level Stripe invoice setting is not the server-owned subscription’s default.

siem-server-new/src/services/subscription.service.ts:2035 siem-server-new/src/services/subscription.service.ts:2041 siem-server-new/src/services/subscription.service.ts:2149 siem-server-new/src/services/subscription.service.ts:2166 siem-server-new/src/payment/providers/StripeProvider.ts:253 siem-server-new/src/payment/providers/StripeProvider.ts:261 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:131 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:277 Affects flow 31. Source-derived scenario; no requests, payments or failures were injected.

A22highresolved

A paid pending-setup subscription’s saved default can be deleted

Resolved 13 Oct 2026: deletion is refused while any of the owner's subscriptions other than CANCELLED/ENDED/EXPIRED uses the method as its token or pending token (PENDING_SETUP, an ACTIVE subscription set to cancel at period end, and PENDING_REAUTH included). Owner decision: a card used only by CANCELLED/ENDED/EXPIRED subscriptions can be deleted; their token is cleared, so a renewal through the plan-change path sets up a new card. subscription.service.ts deletePaymentMethod

Resolution reconciliation: Source recheck confirms protection for PENDING_SETUP and other chargeable statuses. Terminal CANCELLED/ENDED/EXPIRED are excluded by current code; an ACTIVE subscription with cancellation scheduled stays protected. siem-server-new/src/services/subscription.service.ts:2222

Original failure evidence · resolved

A paid signup is PENDING_SETUP with saved method X; the same customer has spare method Y. PaymentMethods renders and permits deletion. deletePaymentMethod blocks X only when an ACTIVE or PAST_DUE subscription uses it, so this paid pending row does not block detachment. With two methods, the last-method guard also passes. The row still points to X; admin activation accepts a provider without requiring a usable saved token, and the next nonzero renewal sends the detached token and fails.

Expected: A method retained for a paid subscription’s future charge must not become an unusable dangling default merely because activation has not happened yet. The existing prohibition needs to protect that promised future charge as well as current active billing.

siem-client-new/app/portal/(me)/subscription/page.tsx:127 siem-client-new/app/portal/(me)/subscription/components/PaymentMethods.tsx:85 siem-server-new/src/services/subscription.service.ts:2208 siem-server-new/src/services/subscription.service.ts:2212 siem-server-new/src/services/subscription.service.ts:2229 siem-server-new/src/services/subscription.service.ts:2235 siem-server-new/src/services/subscription.service.ts:1497 siem-server-new/src/bullmq/workers/billingCycleWorker.ts:277 Affects flow 31. Source-derived scenario; no requests, payments or failures were injected.

A23highresolved

Token revocation misses shared subscriptions and can erase a new token

Resolved 13 Oct 2026: every subscription holding the revoked token is cleared, each with a conditional update (where externalTokenId = revoked), so a replacement saved meanwhile is kept; retries are cancelled only where the token was cleared. PaymentEventProcessor.ts handleTokenRevoked

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:179

Original failure evidence · resolved

Two subscriptions use one Razorpay mandate, which set-default permits because methods belong to the owner’s gateway customer. token.cancelled/rejected looks up findFirst and clears/cancels retries for only that row; the other still appears to have a usable default and sends doomed debits. There is also an interleaving: the handler reads revoked token X, recovery saves replacement Y, then the handler’s unconditional update by subscription ID clears Y. The gateway event can then be logged processed although the valid replacement was lost.

Expected: A revocation must affect every still-current reference to the revoked token and must preserve a replacement saved before its write. It must not remove a different valid mandate.

siem-server-new/src/prisma_repos/subscription.repository.ts:20 siem-server-new/src/services/subscription.service.ts:2119 siem-server-new/src/services/subscription.service.ts:2142 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:178 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:186 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:193 siem-server-new/src/payment/adapters/RazorpayWebhookAdapter.ts:183 Affects flow 33. Source-derived scenario; no requests, payments or failures were injected.

A24mediumresolved

A webhook upgrade invoice names the previous plan

Resolved 13 Oct 2026: the webhook upgrade invoice uses the plan from upgradeTo. PaymentEventProcessor.ts upgrade branch

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/payment/processor/PaymentEventProcessor.ts:278siem-server-new/src/payment/processor/PaymentEventProcessor.ts:308

Original failure evidence · resolved

A Basic subscriber pays to change to Professional. The webhook loads subscription.planId before applying the target, then builds the paid invoice description from that old plan.name. It subsequently writes Professional from upgradeTo. The PDF renders the stored description verbatim, so the successful Professional upgrade is invoiced as “Basic — plan upgrade adjustment”. The synchronous branch uses newPlan.name, making the label depend on which writer wins.

Expected: An invoice for a plan upgrade must identify the plan actually purchased, consistently across gateway/webhook and synchronous settlement.

siem-server-new/src/payment/processor/PaymentEventProcessor.ts:218 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:295 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:318 siem-server-new/src/services/invoice-pdf.service.ts:392 siem-server-new/src/services/subscription.service.ts:1269 Affects flows 26 and 36. Source-derived scenario; no requests, payments or failures were injected.

A25mediumresolved

INR renewal and upgrade emails still label amounts as dollars

Resolved 13 Oct 2026: payment-success and proration emails format amounts in the payment's currency; the templates' literal “$” is removed, also from admin-payment-received (which already prints the currency code). email.service.ts · payment-success.html · proration-adjustment.html · admin-payment-received.html

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/email/email.service.ts:1559siem-server-new/src/email/email.service.ts:1994siem-server-new/src/email/new-email-templates/templates/new/payment-success.html:27

Original failure evidence · resolved

A Razorpay renewal takes ₹1,000. sendPaymentSuccessEmail formats the number as 1,000.00 without a symbol, and payment-success adds a literal $, so the customer reads “Amount Paid: $1,000.00”. A ₹500 upgrade likewise prints dollar-prefixed old/new/adjustment amounts: sendProrationAdjustmentEmail receives currency but never uses it in template values. The actual database and PDF currency can be correct while the emailed receipt is wrong. Patched first-failure formatting (A6) and resolved admin-currency D19 are different messages.

Expected: Every emailed billing amount must retain the currency of the real charge. No conversion or new tax calculation is intended.

siem-server-new/src/payment/processor/PaymentEventProcessor.ts:693 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:338 siem-server-new/src/email/email.service.ts:1557 siem-server-new/src/email/email.service.ts:1577 siem-server-new/src/email/new-email-templates/templates/new/payment-success.html:27 siem-server-new/src/email/email.service.ts:1983 siem-server-new/src/email/email.service.ts:1995 siem-server-new/src/email/new-email-templates/templates/new/proration-adjustment.html:26 Affects flow 37. Source-derived scenario; no requests, payments or failures were injected.

A26highresolved

Temporary admin access survives a failed expiry enqueue

Resolved 13 Oct 2026: the revocation job is queued before any access is granted; a queue failure grants nothing, and a failed grant is still revoked on time. admin-membership.controller.ts joinSubscription

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/controllers/admin-membership.controller.ts:118siem-server-new/src/controllers/admin-membership.controller.ts:128

Original failure evidence · resolved

Flow 40. A SuperAdmin grants an administrator temporary customer access for, for example, one hour. The controller commits active SiemAuth, an owner policy and currentSubscriptionId before enqueueing the one-hour expiry. If BullMQ enqueue throws, the API returns 500 but those grants remain; no persisted expiry deadline is checked on access. The worker revokes them only if the expiry job runs. The result is access extending beyond the selected duration. siem-server-new/src/controllers/admin-membership.controller.ts:123 siem-server-new/src/controllers/admin-membership.controller.ts:135 siem-server-new/src/controllers/admin-membership.controller.ts:148 siem-server-new/src/controllers/admin-membership.controller.ts:163 siem-server-new/src/bullmq/queues/lifecycleQueue.ts:139 siem-server-new/src/bullmq/workers/lifecycleWorker.ts:210.

A27mediumresolved

Admin team buttons call user-only routes

Resolved 13 Oct 2026 (owner decision: read-only): the admin customer team section only lists members; the add, edit and remove controls are removed. siem-admin-new/app/dashboard/customers/[id]/components/TeamSection.tsx

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-admin-new/app/dashboard/customers/[id]/components/TeamSection.tsx:37

Original failure evidence · resolved

Flow 40. An authenticated admin adds, edits or removes a customer's team member. TeamSection posts to /iam/team-members/manage or deletes /iam/team-members/:memberId. These routes require IAM middleware, which rejects authData.userType other than user with 403; admin authentication supplies admin. The customer team read works, but these admin mutation controls cannot reach their service. siem-admin-new/app/dashboard/customers/[id]/components/TeamSection.tsx:116 siem-admin-new/app/dashboard/customers/[id]/components/TeamSection.tsx:150 siem-admin-new/app/dashboard/customers/[id]/components/TeamSection.tsx:166 siem-server-new/src/routes/iam.routes.ts:472 siem-server-new/src/middlewares/iam.middleware.ts:36 siem-server-new/src/middlewares/auth.ts:419.

A28highresolved

Cancelled top-up can still take money and receives no settlement

Resolved 13 Oct 2026: a payment that arrives for a cancelled purchase completes it and adds the credits (money received is always credited; refunds are not allowed); its coupon counts one redemption, as its slot was released at cancellation. Verify-payment accepts a cancelled purchase too. A Razorpay order cannot be closed, so the checkout is not closed at cancellation. wallet-payment-event.service.ts · wallet.controller.ts

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:59siem-server-new/src/modules/wallet/controllers/wallet.controller.ts:180

Original failure evidence · resolved

Flow 42. An authorized customer calls the live purchase-cancel endpoint after Checkout/order creation. The service marks WalletPurchase cancelled and releases its coupon reservation without closing the gateway checkout/order. If the still-payable session later captures, success updates only pending, expired or failed purchases; cancelled yields zero updated rows and returns as already completed. The captured charge adds no credits, creates no invoice and is not recorded on that purchase. No ordinary screen caller was found, but the route is reachable. With refunds deliberately forbidden, late captured money still needs a recorded outcome. siem-server-new/src/modules/wallet/controllers/wallet.controller.ts:416 siem-server-new/src/modules/wallet/services/wallet-topup.service.ts:247 siem-server-new/src/modules/wallet/services/wallet-topup.service.ts:262 siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:57 siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:65.

A29mediumresolved

Wallet confirmation has no durable post-settlement handoff

Resolved 13 Oct 2026: WalletPurchase.confirmationQueuedAt is set once the confirmation email is queued; task:wallet-coupon-expiry (every 5 minutes) re-queues completed purchases without it after 2 minutes; the email job ID is fixed per purchase. Requires prisma generate/db push. wallet-payment-event.service.ts · repeatableWorker.ts · schema.prisma

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:224siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:237siem-server-new/src/bullmq/workers/repeatableWorker.ts:1116siem-server-new/prisma/schema.prisma:1313

Original failure evidence · resolved

Flow 42. A top-up succeeds: the transaction commits credits and invoice, then the handler queues the purchase email. If queueing fails, emailService catches and returns success:false, which this caller ignores. A crash after commit has the same handoff gap. Later webhook delivery sees completed and returns before email scheduling; no purchase email-pending marker/recovery task exists on this path. The customer has paid credits but can permanently miss the confirmation. siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:146 siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:195 siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:34 siem-server-new/src/email/email.service.ts:2709.

A30mediumresolved

Wallet viewing is blocked for restricted subscriptions

Resolved 13 Oct 2026: /portal/wallet is on the limited-status allow-list, and the restricted-status IAM context (PENDING_SETUP and every status other than ACTIVE/TRIALING) now keeps the wallet module, so siem:wallet:read reaches the client. Top-ups stay ACTIVE/TRIALING only: the wallet service refuses them through TOPUP_STATUSES, and the page shows the form only when can_top_up is true. GlobalRouteController.tsx · iam.controller.ts

Latest source confirmation: the current resolution is supported by these implementation lines. Earlier contrary observations below are historical; deployment/execution remains unverified. siem-server-new/src/controllers/iam.controller.ts:604siem-server-new/src/controllers/iam.controller.ts:616

Previous assessments and original evidence · historical

Resolution reconciliation: Source recheck agrees; execution and deployment remain unverified. siem-client-new/components/GlobalRouteController.tsx:23siem-server-new/src/controllers/iam.controller.ts:604siem-server-new/src/controllers/iam.controller.ts:616siem-server-new/src/modules/wallet/services/wallet-topup.service.ts:33

Original failure evidence · resolved

Flow 44. A customer whose only subscription is ENDED or payment_issue opens the wallet to view previously purchased credits. The server summary supports viewing and merely sets can_top_up false, but GlobalRouteController's limited-status allowlist excludes /portal/wallet and redirects to subscription. The same restriction applies to pending_setup without an active sibling. Only ACTIVE/TRIALING top-ups is correct; viewing must work in every status. siem-server-new/src/modules/wallet/services/wallet.service.ts:63 siem-client-new/components/GlobalRouteController.tsx:17 siem-client-new/components/GlobalRouteController.tsx:102 Migrationphasewiseplan.md:706.

A31mediumresolved

Top-up email links to a missing wallet route

Resolved 13 Oct 2026: the purchase and low-balance emails link to /portal/wallet?subscriptionId=… (the low-balance link adds action=topup, which now opens the top-up form when top-ups are allowed). email.service.ts · client wallet/page.tsx

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/email/email.service.ts:2672siem-server-new/src/email/email.service.ts:2729

Original failure evidence · resolved

Flow 42. A customer clicks the wallet button in a completed top-up confirmation. Settlement does not pass wallet_url, so emailService defaults to FRONTEND_URL/dashboard/wallet. The implemented page is /portal/wallet; current client app routes contain no dashboard/wallet page. Under this application's route tree the button leads to a missing page. Deployment-specific rewrites were not inspected. This is separate from resolved D78's currency formatting. siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:195 siem-server-new/src/email/email.service.ts:2676 siem-client-new/app/portal/(me)/wallet/page.tsx:1.

A32criticalresolved

Configured general seed resets real customer data

Resolved 13 Oct 2026 (owner decision): prisma/seed.ts, the seed entry in prisma.config.ts and the npm run seed script are deleted; the targeted seeds remain.

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/prisma.config.ts:16siem-server-new/package.json:32

Original failure evidence · resolved

Flow 46. An operator uses the configured general seed during a cutover rehearsal. package.json's seed invokes Prisma db seed; Prisma config selects prisma/seed.ts. That script disables foreign-key checks and deletes users, subscriptions, payments, plans and other tables before recreating seed data. It is an old reset entrypoint, not a customer-preserving migration, and now conflicts with retaining the owner's 16 dev and 1 production paying customers. Running it loses local customer billing data and may leave dependent records disconnected. The named seed:plans/currency/country scripts are separate and do not trigger this reset; schema push itself was not shown to invoke it. The cutover must explicitly avoid this reset and preserve the retained rows. siem-server-new/package.json:19 siem-server-new/prisma.config.ts:18 siem-server-new/prisma/seed.ts:11 siem-server-new/prisma/seed.ts:15 siem-server-new/prisma/seed.ts:31.

A33mediumresolved

Old provider-null abandoned signups escape cleanup

Resolved 13 Oct 2026: abandoned-cleanup no longer requires a provider; a signup with no gateway has no checkout that could still be paid. repeatableWorker.ts task:abandoned-cleanup

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/bullmq/workers/repeatableWorker.ts:573

Original failure evidence · resolved

Flow 48. A legacy UNPAID or PAYMENT_FAILED signup has already been abandoned for more than 25 hours. The new nullable provider field starts null, and the country seed changes only country. Both abandoned-cleanup and unfinished-signup retirement require provider not null, so this old row is never selected. It does not block a fresh checkout through this filter, but its planned retirement responsibility is missing for legacy rows. Actual numbers of affected rows require the cutover inventory. siem-server-new/prisma/schema.prisma:194 siem-server-new/scripts/seed-subscription-countries.ts:36 siem-server-new/src/bullmq/workers/repeatableWorker.ts:586 siem-server-new/src/services/couponBooking.service.ts:133.

A34highresolved

Historical invoice lookup is not preserved before dropping Stripe IDs

Resolved 13 Oct 2026 (owner decision A): the cutover script's export step reads every invoice's stripeInvoiceId and every payment's Stripe invoice/payment IDs on the old schema, before prisma db push, and saves them in the export file. Its apply step copies them into externalId with provider='stripe'; a payment takes its invoice's Stripe ID, which is what the portal looks the invoice up by. A payment whose ID is already used by another payment is reported, not written. The script is built and type-checks; it has not been run. scripts/stripe-cutover.ts

Latest source confirmation: the current resolution is supported by these implementation lines. Earlier contrary observations below are historical; deployment/execution remains unverified. siem-server-new/scripts/stripe-cutover.ts:119siem-server-new/scripts/stripe-cutover.ts:285siem-server-new/scripts/stripe-cutover.ts:303

Previous assessments and original evidence · historical

Resolution reconciliation: Source written after the audit; execution against a database remains unverified. siem-server-new/scripts/stripe-cutover.ts

Original failure evidence · resolved

Flow 45. A retained paying customer downloads a historical invoice after schema push. The schema diff removes Stripe invoice/payment identifier columns and adds nullable externalId/provider; the currency backfill copies only amount legs, and no pre-drop identifier-copy script was found. PDF download looks up Invoice.externalId only. Old rows with null externalId cannot be found by their former Stripe invoice reference or the UI's payment-ID fallback, so the download returns not found despite retained money rows. This scenario applies if historical invoices exist; their actual inventory was not queried. Expected cutover preserves lookup before dropping the original IDs. siem-server-new/prisma/schema.prisma:391 siem-server-new/prisma/schema.prisma:431 siem-server-new/scripts/payment-currency-backfill.ts:49 siem-server-new/src/controllers/subscription.controller.ts:380 siem-server-new/src/prisma_repos/invoice.repository.ts:55 siem-client-new/app/portal/(me)/subscription/components/PaymentsSection.tsx:63.

A35criticalresolved

Unscoped owner billing permission bypasses the tenant boundary

Resolved 13 Oct 2026 (owner decision: all modules): requireIam resolves and checks the named resource against the caller's workspace whenever the route names one, also for unscoped grants such as an owner's siem:*. Together with A36's resolver this covers billing, payment methods and subscription routes. iam.middleware.ts

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/middlewares/iam.middleware.ts:60siem-server-new/src/middlewares/iam.middleware.ts:64

Original failure evidence · resolved

Flow 50. An owner of company A sends an upgrade request using company B's subscription ID. The owner's default policy allows siem:* on *; its evaluator result has empty scope lists. IAM executes resource metadata and the default-subscription boundary only when a scope list is nonempty, so this owner skips that check. Upgrade service accepts caller userId but never compares it to the loaded subscription; its charge uses B's customer and saved token. A permitted upgrade can therefore charge and change another company's subscription. This is a source-reachable path, not a claim of a performed attack; cancel/resume's independent checks are not included in this claim. siem-server-new/src/services/subscription.service.ts:1966 siem-server-new/src/services/iam/iam.evaluator.ts:186 siem-server-new/src/middlewares/iam.middleware.ts:59 siem-server-new/src/middlewares/iam.middleware.ts:128 siem-server-new/src/routes/subscription.routes.ts:93 siem-server-new/src/services/subscription.service.ts:1023 siem-server-new/src/services/subscription.service.ts:1220.

A36highresolved

Scoped billing permissions reach no resource resolver

Resolved 13 Oct 2026: the IAM registry resolves subscription, billing and paymentmethods resources to the subscription (getSubscriptionMeta). iam.registry.ts · iam.ownership.ts

Resolution reconciliation: Targeted source recheck agrees with this resolution record; execution and deployment remain unverified. siem-server-new/src/services/iam/iam.registry.ts:50siem-server-new/src/services/iam/iam.registry.ts:52

Original failure evidence · resolved

Flow 50. A team member is explicitly allowed siem:billing:retry for the current subscription ID. Evaluation grants its nonempty scope, so IAM calls fetchResourceMeta for billing and the requested ID. The registry has no billing, subscription or paymentmethods case and returns null; middleware responds 404 before the controller. Thus correctly scoped retry/recovery, upgrade and method permissions cannot complete their intended API flows, even though unscoped owners skip this lookup. siem-server-new/src/routes/subscription.routes.ts:139 siem-server-new/src/middlewares/iam.middleware.ts:64 siem-server-new/src/middlewares/iam.middleware.ts:122 siem-server-new/src/services/iam/iam.registry.ts:28 siem-server-new/src/services/iam/iam.registry.ts:50 siem-server-new/src/services/iam/iam.evaluator.ts:199.

A37mediumresolved

Failed workspace switch is reported as successful

Resolved 13 Oct 2026: the switch mutation treats a fulfilled success:false response as an error, so onError shows the failure. The local default subscription changes only in onSuccess, after the server has switched it. When the server is offline the customer sees two error toasts: apiPost's “Server is offline” and the switch failure. switchModal.tsx

Latest source confirmation: the current resolution is supported by these implementation lines. Earlier contrary observations below are historical; deployment/execution remains unverified. siem-client-new/components/workspace/header/switchModal.tsx:46siem-client-new/components/workspace/header/switchModal.tsx:52

Previous assessments and original evidence · historical

Resolution reconciliation: Source recheck agrees; execution and deployment remain unverified. siem-client-new/components/workspace/header/switchModal.tsx:44

Original failure evidence · resolved

Flows 50. A customer switches from subscription A to B while the request cannot reach the server. switchModal changes the local default before posting. apiPost catches Network Error/ERR_NETWORK (or cancellation) and resolves with success:false; the mutation's onSuccess ignores that response, shows “Set default subscription”, routes to dashboard and reloads. Server currentSubscriptionId remains A in this scenario, so the customer is told that billing/wallet context switched when it did not. HTTP error responses that throw take the proper onError path; this finding concerns fulfilled error objects. Expected outcome is failure feedback and retained/restored current context. siem-client-new/components/workspace/header/switchModal.tsx:44 siem-client-new/components/workspace/header/switchModal.tsx:50 siem-client-new/components/workspace/header/switchModal.tsx:60 siem-client-new/auth/lib/apiWrapper.ts:36 siem-client-new/auth/lib/apiWrapper.ts:42 siem-server-new/src/controllers/user.controller.ts:829.

A38mediumresolved

Refreshed billing mutations are encrypted twice

Resolved 13 Oct 2026: the encryption interceptor skips a request with config._retry set; both refresh paths set it before replaying, and the replayed body is already encrypted once. Severity is medium, not high: it needs encryption on (development/production), an access token older than its 1-day lifetime in an open tab, and a body-carrying mutation as the first request after expiry; a second attempt succeeds. It affected every encrypted mutation in the app, not only billing. apiClientV2.ts

Latest source confirmation: the current resolution is supported by these implementation lines. Earlier contrary observations below are historical; deployment/execution remains unverified. siem-client-new/auth/lib/apiClientV2.ts:128siem-client-new/auth/lib/apiClientV2.ts:194

Previous assessments and original evidence · historical

Resolution reconciliation: Source recheck agrees (axios 1.15.0 replaces config.data with the serialized body before the 401, which the replay re-encrypted); browser execution with encryption on remains unverified. siem-client-new/auth/lib/apiClientV2.ts:128siem-client-new/auth/lib/apiClientV2.ts:194

Original failure evidence · resolved

Flows 7,8,22,31,32,41. In development/production with encryption enabled, a customer's setup, retry, method or wallet mutation receives 401 before its controller, then token refresh succeeds. The request interceptor has already replaced its data with an encrypted payload. Both refresh paths replay originalRequest through apiClient; the encryption interceptor has no already-encrypted/replay guard and encrypts that existing envelope or serialized envelope again. Server RequestDecryptor parses exactly one layer, leaving a string/envelope rather than the fields required by the billing route schema. The refreshed payment action fails validation instead of proceeding with a valid token. This is a source deduction, not a gateway experiment; local encryption-off requests avoid it. Expected authenticated replay must preserve the original semantic request body. siem-client-new/auth/lib/apiClientV2.ts:48 siem-client-new/auth/lib/apiClientV2.ts:133 siem-client-new/auth/lib/apiClientV2.ts:203 siem-client-new/auth/lib/apiClientV2.ts:245 siem-server-new/src/lib/payloadSecurity.ts:38 siem-server-new/src/routes/subscription.routes.ts:64.

Payment stale-code recheck and cleanup · 13 Oct 2026: repository-wide caller searches confirmed the removed helpers were unused and payment-related. Deleted the legacy plan-update script and unreferenced CustomPricing wrapper; removed calculator getStripe, Stripe invoice/session lookups, the session-details route/controller/validator/allowlist entry, and the unused provisioning enqueue service. Current status polling and invoice PDF paths remain. The provisioning queue, Bull Board registration and worker are retained because outstanding Redis jobs were not inventoried. Stripe verifySetupCompletion remains required by the provider interface; live old-assumption billing routes need fixes rather than automatic deletion. siem-server-new/src/payment/PaymentProvider.interface.ts:135 siem-server-new/src/bullmq/queues/provisioningQueue.ts:4 siem-server-new/src/bullmqserver.ts:126.

All earlier stale observations are historical unless reconciled below. No database/Redis/gateway, tests, typecheck or build was run. Removed public session-details now returns the normal unmatched-route response; no current repository caller was found. This cleanup does not certify external consumers absent.

5. Stale and dead code

Confirmed-unused payment code was removed in the recorded cleanup. The reset seed A32 and subscription-specific Stripe default behavior A21 are also fixed. Live old-model routes, required provider scaffolding and a retained provisioning consumer are separate from removable dead code; source-only caller searches do not prove Redis queues or external clients absent.

6. Deleted-file check

The catalog contains 70 deleted entries; no old contents were opened. Batch 1 searched the distinct deleted module/component names in current runtime source and inspected the replacement responsibilities on its paths. Generic page names, deleted tests/scratch files and off-path responsibilities are not certified by that basename search; flow 49 will complete the cross-batch responsibility check. MigrationChangedFiles.md:16.

7. Plan vs code mismatches

Higher-authority main-plan decisions override lower phase instructions. Explicit owner decisions override obsolete phase wording. Conflicts between supplied documents are called out rather than treated as new code defects without an agreed rule.

8. Must be built or fixed before the dev rehearsal

No D register entries or A findings remain pending. Source qualifiers and actual environment readiness still require work or verification.

  1. Finish the two remaining same-path handoffs. Flows 38/39; closed D72/D32. A push queued for five assets at $20 must not stage $100 after a same-plan paid upgrade to ten assets has locked $200. The standalone admin subscription-detail endpoint must supply the renewal amount expected by its shared activation dialog; the customer-detail endpoint already does. The original notice-timing and locked-price dialog defects are fixed. siem-server-new/src/bullmq/queues/pricePushQueue.ts:18 siem-server-new/src/services/pricePush.service.ts:114 siem-server-new/src/controllers/admin-subscription.controller.ts:194 siem-admin-new/app/dashboard/customers/[id]/components/EditSubscriptionStatus.tsx:75.
  2. Rehearse cutover and schema/data preservation. Flows 45/47; D33/A34 implemented, unrun. Verify before-drop export/API cancellation, identifiers restored to externalId/provider, country/gateway/price, paid-through dates, ACTIVE/PAST_DUE mapping, missing-method grace and PDFs. Deploy the new invoice sequence and follow-up/confirmation fields. siem-server-new/scripts/stripe-cutover.ts:183 siem-server-new/scripts/stripe-cutover.ts:285 siem-server-new/src/prisma_repos/invoice.repository.ts:12.
  3. Exercise the latest access and lifecycle decisions. Flows 19/21/22/29/44/50; D39/D40/D41/D46/D50. Verify each status/role across login, portal, team, profile, subscription and wallet; verify late-paid renewal starts at payment and on-time renewal at old period end. Fresh sessions carry subscriptionAccess; older cached contexts may require refresh. siem-server-new/src/utils/subscriptionAccess.ts:24 siem-server-new/src/middlewares/auth.ts:307 siem-server-new/src/payment/processor/PaymentEventProcessor.ts:546.
  4. Run the end-to-end regression and delivery checks. All repaired flows: actual gateway events, duplicates, retry/reconcile locks, slot races, concurrent invoice numbering, plan changes/reactivation, reminders, SMTP/PDF/invoice-link authentication and remote provisioning. Inventory provisioning-jobs before removing its retained consumer; residual informational helpers are cleanup work. No such execution occurred during this register reconciliation. Unverified delivery boundary siem-server-new/src/bullmq/workers/provisioningWorker.ts:6 Thingstoconfigurebeforeproduction.md:1.

9. Noted, not traced

Historical off-path observations remain below. The latest source recheck covers the supplied A15–A36 resolutions; it does not re-audit all legacy D gaps, remote systems or runtime behavior.

10. Untouched dependencies

Batch 5 · all 35 explicitly listed files, inspected only for billing interaction. “Untouched” names the prompt’s dependency scope; some files now contain earlier patch edits. Verdicts are current-source judgments, separate from the refreshed 50-flow counts. No runtime, database, gateway, Redis, remote provisioning or mail checks were run.

Latest dependency verdicts: 31 Complete · 0 Broken · 0 Stale code present · 4 Unverified. Removed dependencies retain their historical evidence below; Complete means no live dependency remains for those two removed files. New findings A37/A38; existing A26/A27/A30/A35/A36 and register entries are extended or confirmed, not renumbered.

siem-server-new/src/middlewares/auth.ts:307siem-server-new/src/utils/subscriptionAccess.ts:24

Earlier dependency assessment · historical
id="dependency-7" data-dependency="7" data-dependency-verdict="Complete">
siem-server-new/src/utils/user.util.ts:22siem-server-new/src/utils/user.util.ts:129

Earlier dependency assessment · historical
id="dependency-9" data-dependency="9" data-dependency-verdict="Complete">
File / affected flowsWhat billing relies onVerdictResult and evidence
siem-server-new/src/lib/middlewareApi.ts

Flows 6,13,24

Create organization and owner membership after settlement/free claim; validate local org existence.UnverifiedFree claim uses remote/local transaction-aware helpers; paid follow-up uses addNewOrg, whose remote-create then local-write ordering needs restart/failure proof. Remote create conflict semantics were not established. A3/A7 patch notes remain historical. siem-server-new/src/lib/middlewareApi.ts:137 siem-server-new/src/lib/middlewareApi.ts:145 siem-server-new/src/lib/middlewareApi.ts:118 siem-server-new/src/services/subscription.service.ts:448siem-server-new/src/payment/processor/PaymentEventProcessor.ts:687siem-server-new/src/middleApi/modules/org/org.service.ts:16
siem-server-new/src/lib/payloadSecurity.ts

Flows 7,8,35,41,43

Keep raw signed gateway bodies intact; decrypt client billing JSON and leave invoice PDFs binary.CompleteBoth raw gateway handlers precede this middleware. Plain bodies without payload pass; invoice URLs and PDF content types bypass response encryption. Wallet setup/verify JSON uses ordinary decrypt/encrypt. No extra wallet exemption is required for these paths. siem-server-new/src/lib/payloadSecurity.ts:13 siem-server-new/src/lib/payloadSecurity.ts:23 siem-server-new/src/lib/payloadSecurity.ts:63 siem-server-new/src/lib/payloadSecurity.ts:90 siem-server-new/src/server.ts:77siem-server-new/src/server.ts:92siem-server-new/src/server.ts:253siem-client-new/auth/lib/apiClientV2.ts:133
siem-server-new/src/lib/role-policies-bootstrap.ts

Flows 6,13,50

Bootstrap subscription L1/L2 IAM policies during owner setup.CompleteTransaction argument is honored and errors propagate; policies explicitly deny subscription/billing/paymentmethods for L1/L2. Lack of billing access for those default roles is intentional role policy, not a migration failure. siem-server-new/src/lib/role-policies-bootstrap.ts:6 siem-server-new/src/lib/role-policies-bootstrap.ts:17 siem-server-new/src/lib/role-policies-bootstrap.ts:68
siem-server-new/src/services/iam/index.ts

Flows 24,50

Export cache invalidation/evaluation used by billing lifecycle callers.CompleteBarrel exports the aggregator/evaluator/registry; no old Stripe billing implementation or independent status logic remains here. siem-server-new/src/services/iam/index.ts:5
siem-server-new/src/services/iam/iam.aggregator.ts

Flows 24,40,50

Compile workspace policy and cache permissions; clear after status/assignment changes.CompleteBase identity is filtered to resolved subscription and cache fields separate subscription/assumed role. Invalidation deletes both policy and effective caches. It does not enforce the route tenant boundary: A35/A36 are the middleware/registry defects. Actual Redis outage/recovery freshness is unverified. siem-server-new/src/services/iam/iam.aggregator.ts:20 siem-server-new/src/services/iam/iam.aggregator.ts:127 siem-server-new/src/services/iam/iam.aggregator.ts:431 siem-server-new/src/middlewares/iam.middleware.ts:59
siem-server-new/src/prisma_repos/SiemAuthRepository.ts

Flows 13,40,41

Store owner/member linkage used by provisioning and payment/wallet checks.CompleteCreation accepts the caller transaction; generic lookup does not automatically filter isActive, so callers must do so. Current wallet authorization uses its own explicit isActive filter; repository performs no gateway/status inference. siem-server-new/src/prisma_repos/SiemAuthRepository.ts:16 siem-server-new/src/prisma_repos/SiemAuthRepository.ts:135 siem-server-new/src/prisma_repos/SiemAuthRepository.ts:155 siem-server-new/src/modules/wallet/controllers/wallet.controller.ts:37
siem-server-new/src/middlewares/auth.ts

Flows 32,40,50

Separate customer/admin identity and allow recovery APIs under billing-related status.CompleteisUser/isAdmin are identity checks, but checkSubscriptionStatus uses raw names while user.util emits derived payment_issue/no_subscription (known pending D41). IAM /me is prefix-exempt, so its own filtering—not D41—controls wallet permissions there. siem-server-new/src/middlewares/auth.ts:293 siem-server-new/src/middlewares/auth.ts:305 siem-server-new/src/middlewares/auth.ts:319 siem-server-new/src/middlewares/auth.ts:395 D41 · pending, source agreessiem-server-new/src/routes/iam.routes.ts:95
siem-server-new/src/middlewares/iam.middleware.ts

Flows 22,26,31,32,40,50

Authorize billing requests and bind requested resources to current tenant.CompleteA35/A36 are resolved: named-resource tenant checking also runs for unscoped grants, with current billing resolvers. A27 admin mutations were removed by owner decision.

siem-server-new/src/middlewares/iam.middleware.ts:60siem-server-new/src/middlewares/iam.middleware.ts:64siem-server-new/src/services/iam/iam.registry.ts:50siem-server-new/src/services/iam/iam.registry.ts:52siem-admin-new/app/dashboard/customers/[id]/components/TeamSection.tsx:37

Original dependency inspection · historical
siem-server-new/src/utils/user.util.ts

Flows 32,47,50

Derive global subscriptionStatus and hasPaymentIssue for login/client recovery.CompletePENDING_REAUTH is absent from failure grouping, falls through to no_subscription and does not set hasPaymentIssue. This is the known D39/D41 access family; status mapping does not produce every raw name expected by auth middleware. siem-server-new/src/utils/user.util.ts:16 siem-server-new/src/utils/user.util.ts:33 siem-server-new/src/utils/user.util.ts:98 D39/D41 · pending, related access gaps persist
siem-server-new/src/controllers/user.controller.ts

Flows 40,50

Switch current subscription and serve adjacent customer/asset billing screens.CompleteDefault selection delegates with authenticated caller ID; customer status update invalidates user cache and does not change subscription status, gateway or price. Asset naming verifies ownership. Billing effects of selecting old user-state labels are noted separately. siem-server-new/src/controllers/user.controller.ts:829 siem-server-new/src/controllers/user.controller.ts:876 siem-server-new/src/controllers/user.controller.ts:1635
siem-server-new/src/controllers/admin-membership.controller.ts

Flows 40

Grant temporary customer workspace access and enqueue expiry.CompleteA26 is resolved: expiry is enqueued before membership/owner policy grant.

siem-server-new/src/controllers/admin-membership.controller.ts:118siem-server-new/src/controllers/admin-membership.controller.ts:128

Original dependency inspection · historical
Grant commits before expiry enqueue without a durable deadline/rollback: existing A26 remains. Worker revocation is a separate required handoff. siem-server-new/src/controllers/admin-membership.controller.ts:123 siem-server-new/src/controllers/admin-membership.controller.ts:148 siem-server-new/src/controllers/admin-membership.controller.ts:163 A26
siem-server-new/src/controllers/iam.controller.ts

Flows 44,50

Expose effective billing/method/wallet permissions to restricted-status screens.CompleteRestricted-status IAM now retains wallet alongside billing/payment-method modules; A30 is resolved.

siem-server-new/src/controllers/iam.controller.ts:604siem-server-new/src/controllers/iam.controller.ts:616

Earlier dependency evidence · historical
Resolved 13 Oct 2026 (A30): both restricted-status allowlists now keep the wallet module. Original evidence: restricted-status allowlists retained billing/paymentmethods but removed wallet entirely, independently preventing intended all-status wallet READ. This extends existing A30; no duplicate finding is added. siem-server-new/src/controllers/iam.controller.ts:599 siem-server-new/src/controllers/iam.controller.ts:603 siem-server-new/src/controllers/iam.controller.ts:615 A30 additional causesiem-client-new/store/hooks/useIam.ts:80

Latest D41 filters SIEM/team/wallet modules from the current subscriptionAccess rule rather than one broad restricted-status allowlist. siem-server-new/src/controllers/iam.controller.ts:603

siem-server-new/src/bullmq/queues/provisioningQueue.ts

Flows 13,49

Expected producer for task:provision-org.UnverifiedUnused enqueue service removed; queue retained until outstanding Redis jobs are inventoried.
Original inspection and cleanup evidence
scheduleProvisioning has no current runtime caller beyond its definition; Bull Board registers the queue. Current free claim and paid follow-up provision directly, so this is dead enqueue infrastructure, not proof that all provisioning is missing. siem-server-new/src/bullmq/queues/provisioningQueue.ts:22 siem-server-new/src/bullmq/queues/provisioningQueue.ts:30 siem-server-new/src/bullmq/dashboard/bullBoard.ts:55siem-server-new/src/payment/processor/PaymentEventProcessor.ts:687

Cleanup supersedes the earlier dependency observation: Unused provisioningQueueService/scheduleProvisioning removed. Queue object and Bull Board registration retained for existing jobs; current signup uses direct provisioning. Whether the queue itself can be retired requires Redis job inventory.

siem-server-new/src/bullmq/workers/provisioningWorker.ts

Flows 13,49

Consume the old provisioning-jobs queue.UnverifiedWorker retained for possible queued work; full retirement cannot be confirmed by source alone.
Original inspection and cleanup evidence
Worker is still loaded but no runtime producer was found. Comments promise role/user creation and internal duplicate skips that current addNewOrg does not implement. Existing queued jobs cannot be inventoried by source. siem-server-new/src/bullmq/workers/provisioningWorker.ts:6 siem-server-new/src/bullmq/workers/provisioningWorker.ts:15 siem-server-new/src/bullmq/workers/provisioningWorker.ts:25 siem-server-new/src/bullmqserver.ts:126siem-server-new/src/lib/middlewareApi.ts:118

Cleanup supersedes the earlier dependency observation: Retained to avoid abandoning outstanding provisioning-jobs; old misleading role/user/idempotency comments removed. No current producer exists, but queued jobs could still need this worker. Inventory the queue before retiring the consumer.

siem-server-new/src/bullmq/services/emailQueue.service.ts

Flows 25,37

Schedule and replace standard trial/billing reminders.CompleteUses deterministic subscription IDs, removes previous 7/3/1 reminders, skips elapsed delays and propagates enqueue failures. Known D42 concerns whether lifecycle callers refresh paid reminders, not this helper. siem-server-new/src/bullmq/services/emailQueue.service.ts:16 siem-server-new/src/bullmq/services/emailQueue.service.ts:34 siem-server-new/src/bullmq/services/emailQueue.service.ts:48 siem-server-new/src/bullmq/workers/emailWorker.ts:18D42 · pending caller gap
siem-server-new/src/utils/redisClient.ts

Flows 3,24,50

Invalidate auth and both IAM caches; supply Redis to charge locks.CompleteDeletes auth:user:<id>* and both IAM keys; charge locks use separate keys through the same client. Errors are logged and suppressed, so instantaneous invalidation during an outage is not guaranteed; no Redis connectivity claim is made. siem-server-new/src/utils/redisClient.ts:5 siem-server-new/src/utils/redisClient.ts:26 siem-server-new/src/utils/redisClient.ts:29 siem-server-new/src/payment/chargeLock.ts:19
siem-server-new/src/config/features.ts

Flows 4,35,41

Choose downgrade asset-removal query optimization and payload encryption environment.CompleteFlags choose query/connection behavior; encryption is off for local and on for development/production. There is no hidden Stripe-owned billing mode switch. siem-server-new/src/config/features.ts:23 siem-server-new/src/config/features.ts:29 siem-server-new/src/config/features.ts:42 siem-server-new/src/services/subscription.service.ts:778siem-server-new/src/server.ts:252
siem-server-new/src/test-plan-update.ts

Flows 4,46,49

Legacy manual plan-service smoke script.CompleteRemoved: unused manual billing-plan update script; no live dependency remains.
Original inspection and cleanup evidence
It picks a live paid plan and mutates only legacy priceMonthly +5, not the new regional interval rates; no package/runtime caller was found. Explicitly inspected as requested but never run. siem-server-new/src/test-plan-update.ts:5 siem-server-new/src/test-plan-update.ts:12 siem-server-new/src/test-plan-update.ts:16 siem-server-new/src/services/plan.service.ts:314

Cleanup supersedes the earlier dependency observation: Removed after caller recheck. Legacy manual script modified only old priceMonthly and had no current package/runtime caller; no live billing dependency remains.

siem-client-new/app/portal/(me)/buy-new-subscription/page.tsx

Flows 7,8,17

Open an authenticated fresh paid/quote checkout.CompleteHandles quote login/onboarding, blocks assumed-role purchase, saves designation then mounts current calculator. Country/discount/amount authority remains in calculator/server, not this wrapper. siem-client-new/app/portal/(me)/buy-new-subscription/page.tsx:37 siem-client-new/app/portal/(me)/buy-new-subscription/page.tsx:47 siem-client-new/app/portal/(me)/buy-new-subscription/page.tsx:102 siem-client-new/app/portal/(me)/buy-new-subscription/page.tsx:189
siem-client-new/components/modules/custom-pricing/index.tsx

Flows 17,49

Legacy CustomPricing calculator wrapper.CompleteRemoved: unreferenced quote-pricing wrapper; live quote entry is retained.
Original inspection and cleanup evidence
No current import/caller was found; passes a hard-coded example email. The live quote checkout uses buy-new-subscription and the current calculator, so no live quote defect is asserted from this dead wrapper. siem-client-new/components/modules/custom-pricing/index.tsx:7 siem-client-new/components/modules/custom-pricing/index.tsx:9

Cleanup supersedes the earlier dependency observation: Removed after caller recheck. Unreferenced calculator wrapper with a hard-coded example customer email; current quote entry uses buy-new-subscription.

siem-client-new/components/workspace/SidebarMenuNew.tsx

Flows 50

Render navigation using derived billing access status.CompleteD52 is resolved: restricted status shows Access Restricted and a subscription link; agent fetch for PENDING_SETUP is removed.

Updated D52/D53 resolution records

Earlier dependency interpretation · historical
Known pending D52 remains: payment_issue leaves an empty menu, while pending_setup/ended get restricted notice. Uses current subscription enum but omits a coherent recovery menu. siem-client-new/components/workspace/SidebarMenuNew.tsx:152 siem-client-new/components/workspace/SidebarMenuNew.tsx:251 siem-client-new/components/workspace/SidebarMenuNew.tsx:261 D52 · pending, source agrees
siem-client-new/components/workspace/header/switchModal.tsx

Flows 44,50

Choose the current subscription used for billing and wallet screens.CompleteFailed responses throw; local default changes only after server success. A37 is resolved.

siem-client-new/components/workspace/header/switchModal.tsx:46siem-client-new/components/workspace/header/switchModal.tsx:52

Earlier dependency evidence · historical
Resolved 13 Oct 2026 (A37): success:false is an error and the local default changes only after the server switches it. Original evidence: loads accessible subscriptions and posts an ID, but prewrites local default and treats returned success:false as success; new A37. PENDING_REAUTH falls back to raw label; expired switching is disabled. siem-client-new/components/workspace/header/switchModal.tsx:39 siem-client-new/components/workspace/header/switchModal.tsx:50 siem-client-new/components/workspace/header/switchModal.tsx:60 siem-client-new/components/workspace/header/switchModal.tsx:130 A37siem-client-new/auth/lib/apiWrapper.ts:36
siem-client-new/app/portal/(me)/subscription/components/SetupPendingAlert.tsx

Flows 13

Explain manual onboarding before admin activation.CompleteText directs PENDING_SETUP customers to contact admin, matching manual provisioning/activation. Contact configuration is owner setup, not a missing-code defect. siem-client-new/app/portal/(me)/subscription/components/SetupPendingAlert.tsx:6 siem-client-new/app/portal/(me)/subscription/components/SetupPendingAlert.tsx:19
siem-client-new/app/portal/(me)/subscription/components/CustomerHeader.tsx

Flows 7,13,40

Customer identity, signup date and new-subscription entry.CompleteBadges display User.status separately from subscription status; new purchase is hidden for assumed roles. Date rendering delegates to the date hook and is checked below. siem-client-new/app/portal/(me)/subscription/components/CustomerHeader.tsx:10 siem-client-new/app/portal/(me)/subscription/components/CustomerHeader.tsx:21 siem-client-new/app/portal/(me)/subscription/components/CustomerHeader.tsx:84 siem-client-new/app/portal/(me)/subscription/components/CustomerHeader.tsx:95 siem-client-new/hooks/useDateFormatter.ts:19siem-client-new/lib/utils/date.ts:17
siem-client-new/app/portal/(me)/subscription/components/AssetsSection.tsx

Flows 13,28,40

Show the current subscription’s asset capacity and usage beside billing.CompleteUses assetsReqCount for capacity and current subscription query keys. Server customer route resolves the authenticated default subscription; the page’s existing single-current-subscription contract is D40. Asset naming/agent features beyond this billing relationship are outside scope. siem-client-new/app/portal/(me)/subscription/components/AssetsSection.tsx:46 siem-client-new/app/portal/(me)/subscription/components/AssetsSection.tsx:65 siem-client-new/app/portal/(me)/subscription/components/AssetsSection.tsx:113 siem-server-new/src/controllers/user.controller.ts:1319D40 · pending single-current-subscription limitation
siem-client-new/action/onboardingActions.ts

Flows 2,6,7

Pass initial User.country and registration details to server.CompleteRegistration spreads form country/timezone/designation; this is user profile country, not trusted payment amount or a replacement for per-subscription company country. siem-client-new/action/onboardingActions.ts:92 siem-client-new/action/onboardingActions.ts:101 siem-client-new/action/onboardingActions.ts:106 siem-client-new/app/(web)/onboarding/OnboardingClient.tsx:883
siem-client-new/store/slices/onboardingSlice.ts

Flows 2,6

Keep onboarding step/email verification state across registration.CompleteSlice stores step/email/verification, not the form country; current form keeps country separately and sends it at registration. Absence of country in this slice is not loss of payment country. siem-client-new/store/slices/onboardingSlice.ts:9 siem-client-new/store/slices/onboardingSlice.ts:18 siem-client-new/store/slices/onboardingSlice.ts:31 siem-client-new/app/(web)/onboarding/OnboardingClient.tsx:874
siem-client-new/app/(web)/onboarding/schemas/onboarding.schema.ts

Flows 2,6

Validate registration form country and required identity fields.CompleteRequires country as a nonempty form field; current country picker supplies ISO2. Subscription country uses its own supported-country/server validation, so this schema is not gateway selection authority. siem-client-new/app/(web)/onboarding/schemas/onboarding.schema.ts:3 siem-client-new/app/(web)/onboarding/schemas/onboarding.schema.ts:16 siem-client-new/app/(web)/onboarding/OnboardingClient.tsx:493
siem-client-new/store/hooks/useAuth.ts

Flows 44,50

Expose server-derived status/default subscription to billing screens.CompleteThin Redux selectors and setters; performs no old Stripe/status derivation itself. Local default setter is used optimistically by switchModal, whose failure behavior is a separate finding. siem-client-new/store/hooks/useAuth.ts:14 siem-client-new/store/hooks/useAuth.ts:42 siem-client-new/store/hooks/useAuth.ts:49
siem-client-new/store/hooks/useIam.ts

Flows 22,31,44,50

Fetch and evaluate displayed billing permissions.CompleteReads effective map; only iam: gets owner shortcut. Billing/wallet permissions depend on server response and canDoScoped evaluates scopes. Failed refresh leaves prior map in memory; related runtime refresh behavior is not independently certified. siem-client-new/store/hooks/useIam.ts:35 siem-client-new/store/hooks/useIam.ts:68 siem-client-new/store/hooks/useIam.ts:80 siem-client-new/store/hooks/useIam.ts:138
siem-client-new/auth/lib/apiWrapper.ts

Flows 50

Return billing API data/errors to callers.CompleteThe billing switch caller now handles fulfilled success:false correctly. Its wrapper return contract no longer causes the recorded A37 failure.

siem-client-new/components/workspace/header/switchModal.tsx:46siem-client-new/components/workspace/header/switchModal.tsx:52

Earlier dependency evidence · historical
Resolved 13 Oct 2026 (A37): the one caller that ignored success:false (switchModal) now checks it; apiWrapper itself is unchanged. Original evidence: network/cancellation failures resolve success:false instead of throwing; switchModal assumes every fulfilled mutation succeeded. Existing callers that inspect success are correct. A new billing caller failure is recorded separately. siem-client-new/auth/lib/apiWrapper.ts:31 siem-client-new/auth/lib/apiWrapper.ts:36 siem-client-new/auth/lib/apiWrapper.ts:42 A37siem-client-new/components/workspace/header/switchModal.tsx:50
siem-client-new/auth/lib/apiClientV2.ts

Flows 7,8,22,31,32,41

Authenticate/encrypt setup/recovery/mutation requests and refresh expired access.CompleteEncrypted authenticated replay is guarded by _retry and remains encrypted once. A38 is resolved.

siem-client-new/auth/lib/apiClientV2.ts:128siem-client-new/auth/lib/apiClientV2.ts:194

Earlier dependency evidence · historical
Resolved 13 Oct 2026 (A38): the encryption interceptor skips a replayed request (_retry). Original evidence: on a 401, replays already-encrypted originalRequest through the encryption interceptor again, so a refreshed billing request reaches server with nested/wrong-shaped body. New A38; raw gateway callbacks do not use this client. siem-client-new/auth/lib/apiClientV2.ts:123 siem-client-new/auth/lib/apiClientV2.ts:133 siem-client-new/auth/lib/apiClientV2.ts:187 siem-client-new/auth/lib/apiClientV2.ts:245 A38siem-server-new/src/lib/payloadSecurity.ts:45
siem-client-new/proxy.ts

Flows 50

Server-side portal request gate.CompleteD53 is resolved by documenting the intentional client/API gate; this proxy supplies headers and does not claim to enforce subscription state.

Updated D52/D53 resolution records

Earlier dependency interpretation · historical
Always returns NextResponse.next and sets headers; it treats every subscription status identically and delegates access to the client/API. This agrees with pending D53; no missing server PortalGuard is supplied here. siem-client-new/proxy.ts:6 siem-client-new/proxy.ts:12 siem-client-new/proxy.ts:21 D53 · pending, source agrees
siem-admin-new/app/dashboard/customers/[id]/components/AssignAdmin.tsx

Flows 40

Assign/unassign a subscription administrator next to billing.CompleteSuperAdmin-only UI posts subscription ID/admin ID, handles returned success and refreshes customer. Current admin route enforces SuperAdmin; no legacy Stripe fields involved. siem-admin-new/app/dashboard/customers/[id]/components/AssignAdmin.tsx:22 siem-admin-new/app/dashboard/customers/[id]/components/AssignAdmin.tsx:39 siem-admin-new/app/dashboard/customers/[id]/components/AssignAdmin.tsx:41 siem-admin-new/app/dashboard/customers/[id]/components/AssignAdmin.tsx:89 siem-server-new/src/routes/admin.routes.ts:75
siem-admin-new/app/dashboard/customers/[id]/components/EditCustomerStatus.tsx

Flows 39,40,50

Change User.status independently of subscription lifecycle.UnverifiedStill offers paymentPending/inputServersPending/active/blocked and posts customer status. It does not alter server-owned subscription billing; whether allowing an already-paid user to be forced back through onboarding is desired is not specified. Owner policy confirmation is needed before calling this a defect. siem-admin-new/app/dashboard/customers/[id]/components/EditCustomerStatus.tsx:24 siem-admin-new/app/dashboard/customers/[id]/components/EditCustomerStatus.tsx:39 siem-admin-new/app/dashboard/customers/[id]/components/EditCustomerStatus.tsx:63 siem-server-new/src/controllers/user.controller.ts:876siem-server-new/src/services/user.service.ts:1768