| 1. Subscription statuses and transitions | Complete | The shared map accepts first-charge failure/recovery and terminal grace transitions; signup settlement uses the map.Trace and register statusThe 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 · D15 | siem-server-new/src/utils/subscriptionStatusMachine.ts:8 siem-server-new/src/prisma_repos/subscription.repository.ts:107 |
| 2. Gateway by company registration country | Complete | Validated country fixes the stored subscription binding; normal updates cannot replace it.Trace and register statusThe 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 transactions | Complete | Core 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 statusThe 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. | D14 | siem-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 management | Complete | Inactive-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 · historicalA1 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 refreshDeactivation is bypassed at checkout (A1); D73 rate validation remains incomplete. Trace and register statusAdmin 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 · D73 | siem-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 price | Complete | The server resolves the full interval price, compares the displayed charge and locks it without extra tax.Trace and register statusThe 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 · D60 | siem-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 validation | Complete | A2/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 refreshNormal claim exists, but concurrent claims and failed provisioning are unsafe; invalid organization names pass (A2/A3/A7). Trace and register statusOnboarding 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. | D15 | siem-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 charge | Complete | Stripe 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 · historicalSignup 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 · historicalA7/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 refreshThe 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 statusPriceCalculator → 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 · D49 | siem-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 verify | Complete | Razorpay 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 · historicalCaller/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 · historicalD18/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 refreshSigned capture can settle and save the mandate, but browser verify still accepts another subscription/payment binding (D18/D57). Trace and register statusThe 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 · D57 | siem-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 signup | Complete | A7 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 refreshInternal settlement correctly avoids gateways; the shared organization-validation gap still strands this signup (A7). Trace and register statusAfter 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 · D62 | siem-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 route | Complete | A5/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 refreshOwner-scoped polling exists, but it has no two-minute bound (A5); the session-invoice path is stale (D27). Trace and register statusEvery 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 · D37 | siem-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 over | Complete | A6 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 | D3 · D38 | siem-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-cleanup | Complete | A4 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 refreshNon-coupon cleanup protects Razorpay late payment; coupon/quote-linked abandoned subscriptions are never deleted (A4). Trace and register statusThe 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 finalization | Complete | A3/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 refreshPaid activation is conditional and schedules renewal; free provisioning can be stranded (A3/A7), and task:provision-org has no producer. Trace and register statusAdmin 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 · D32 | siem-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 redemption | Complete | Admin CRUD, eligibility, last-slot reservation and first-settlement redemption are wired; checkout concurrency is assessed in flow 16.Trace and register statusAdmin 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 · D61 | siem-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 application | Complete | Validated management and currency eligibility feed the server's default promotion selection.Trace and register statusAdmin 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 · D71 | siem-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 maintenance | Complete | A8 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 refreshHold/capacity/expiry handoffs exist, but concurrent checkout calls can each open a signup (A8). Trace and register statusCalculator 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 · D71 | siem-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 expiry | Complete | D48'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 · historicalGeneration/expiry are implemented; public acceptance still consumes an unpaid quote (D48), and old-token/USD-only helper assumptions remain. Trace and register statusAdmin 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 · D64 | siem-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 counting | Complete | Finite/lifetime allowances are consumed only at successful settlement, including zero charges; duplicate-cycle money consumes no extra allowance.Trace and register statusServer 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 · D62 | siem-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 gateways | Complete | A9/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 refreshNormal renewal is wired; missing price can become a free cycle (A9), and planned hourly billing reconciliation is absent (A14). Trace and register statusActivation/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 · D33 | siem-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_REAUTH | Complete | An applicable pushed full price above a known positive mandate ceiling changes status to PENDING_REAUTH and sends no debit.Trace and register statusAfter 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 · D72 | siem-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 deactivation | Complete | Renewal 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 · historicalA10/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 refreshFailure/retry policy exists; queue errors lose grace handoff (A10), expiry cleanup cannot retry safely (A11), and lock collisions end retries (A12). Trace and register statusAdapters 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 · D55 | siem-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 retry | Complete | D6 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 · historicalA35/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 · historicalA35/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 · historicalAmount and ordinary result text are correct; stale pre-lock reads and expired unknown-outcome locks still permit duplicate payments (D6/D54). Trace and register statusPortal 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 · D54 | siem-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 reconciliation | Complete | D54 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 · historicalSame-request replay/search exists; a still-pending intent stops reconciliation while its 24-hour lock can expire (D54). Trace and register statusStripe 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. | D54 | siem-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-up | Complete | A13 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 refreshApplied invoices retain a recoverable queue handoff, but returned email failures are ignored and follow-up is marked complete (A13). Trace and register statusSettlement 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 · D36 | siem-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 reminders | Complete | D42 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 · historicalTrial/reminder jobs exist; normal paid signup/renewal reminders remain unreliable (D42) and grace dates remain wrong (D55). Trace and register statusFree 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 · D55 | 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/lifecycleWorker.ts:75 |
| 26. Upgrade and higher Razorpay mandate | Complete | D23/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 · historicalA15–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 · historicalThe 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 statusModal → 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 · D72 | siem-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 end | Complete | Plan-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 · historicalInterval-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 · historicalA 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 statusThe 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 · D72 | siem-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 statusThe 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. | D32 | siem-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 resume | Complete | D46 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 · historicalCancellation 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 · historicalFinal 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 | D4 · D20 · D24 · D46 | siem-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 conversion | Complete | Trial 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 · historicalA20 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 · historicalPlan/flags change only after payment, as D30/D31 require, but conversion bypasses durable post-settlement follow-up (A20). Trace and register statusA 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 · D72 | siem-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/delete | Complete | Subscription 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 | D18 · D57 · D68 · D69 · D70 | siem-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_REAUTH | Complete | D33 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 · historicalMethod 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 · historicalA35/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 · historicalLocked/pushed price recovery and one-time country initialization are present; Stripe PENDING_REAUTH charging is still skipped (known D33). Trace and register statusRecovery 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 · D57 | siem-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/rejected | Complete | A23 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 | D13 | siem-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? | Complete | D43 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 | D43 | siem-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 gate | Complete | Raw-body canonical routes, signatures, event-ID dedupe and normalized dispatch exist; business-handler defects are recorded on their own flows.Trace and register statusBoth 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 · D75 | siem-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 invoices | Complete | D17 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 · historicalA15/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 · historicalA15/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 · historicalNormal 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 statusSuccessful 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 · D62 | siem-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 templates | Unverified | Currency, 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 | D19 · D29 · D42 · D55 · D64 | siem-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 subscription | Complete | D72'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; D72 | siem-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 override | Complete | The 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 recheckD32 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 | D32 | siem-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 expiry | Complete | A26 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 · historicalRelated 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 verification | Complete | Wallet 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 | D74; D75; D79; D80 | siem-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 expiry | Complete | D76 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 | D74; D75; D76; D77; D78 | siem-server-new/src/modules/wallet/services/wallet-payment-event.service.ts:54siem-server-new/src/payment/processor/PaymentEventProcessor.ts:47 |
| 43. Wallet top-up invoices | Complete | Settlement assigns a separate unique WLT number; independent customer/admin PDF routes and downloads exist.Trace and register statusInvoice 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 | D81 | siem-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 access | Complete | Wallet 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 | D82; D51 | siem-server-new/src/modules/wallet/services/wallet.service.ts:63siem-client-new/components/GlobalRouteController.tsx:17 |
| 45. Schema push and existing rows | Unverified | Legacy-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 | D33 | siem-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 backfill | Complete | Country 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 · historicalA32 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 | D33 | siem-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: cutover | Unverified | D33 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 · historicalCutover 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 · historicalscripts/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 statusD33 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. | D33 | siem-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 rows | Complete | Trial 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 · historicalA33 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 | D15; D30; D33 | siem-server-new/src/bullmq/workers/repeatableWorker.ts:586siem-server-new/src/services/subscription.service.ts:1030 |
| 49. Deleted files, stale routes, cron and obsolete schedules | Stale code present | Portal 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 · historicalConfirmed-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 | D21; D27; D43 | MigrationChangedFiles.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 IAM | Complete | D39/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 | D39; D40; D41; D50; D51; D52; D53 | siem-server-new/src/middlewares/iam.middleware.ts:59siem-server-new/src/services/iam/iam.registry.ts:28siem-client-new/components/GlobalRouteController.tsx:102 |