Payments — Code Map
API — apps/api/src/payments/
Module + DI
payments.module.ts— wires all services, adapters, controllers.onModuleInitregisters each provider intoPaymentProviderRegistry.
Providers (providers/)
payment-provider.registry.ts—Map<PaymentProviderType, PaymentProviderAdapter>withregister/get/getOrThrow/list.cardcom.provider.ts— LowProfile/Create + GetLpResult + ChargeWithToken + v11RefundByTransactionId.tokenOnlypages runCreateTokenOnly, orSuspendedDeal+ J5 when the request carriescardValidation: 'issuer_hold';classifyDealreads Cardcom’s 700/701 J-validation success codes as settled on those two operations only, and the token mapper falls back fromTokenInfotoTranzactionInfo.FailedRedirectUrltakesrequest.failedUrlwhen present (falling back tocancelUrl), so a declined card no longer lands on the “you cancelled” page;CancelRedirectUrlis unchanged. No webhook signature.getRefundCapability() → 'manual'= refund API implemented but unverified against a live terminal;RefundCapabilityServicestill tries it for a bring-your-own Cardcom terminal.icredit.provider.ts— Rivhit hosted page.GroupPrivateTokenbody signature.meshulam.provider.ts— Light-API iframe + JSON IPN.webhookKey === credentials.apiKeysignature.morning.provider.ts— Single-URL Morning webhook (?org=…). Signature TBD (:941). Issues חשבונית/קבלה automatically.tranzila.provider.ts— Stub-grade adapter; signature returnstrue.
Services (services/)
payment.service.ts— orchestrator + the exportedwithSubParam(stampssub=<id>on every return leg, idempotently).createHostedPayment,createCourseHostedPayment,charge,createRecurring,refund(capability switch),completeManualRefundTask,verifyAndActivateReturn,sendPaymentReceipt.card-validation-policy.service.ts— answerscard-issuer-validationper org for every tokenise-only page (presale checkout viaPaymentService.createHostedPayment, card registration viaCardRegistrationController). Only a definitetrueasks for an issuer hold; off/unset/unevaluable keeps the provider default.refund-capability.service.ts— third gate inrefund’s automatic-vs-manual routing: bring-your-own Cardcom terminals whose adapter reportsmanualtry the provider API and fall back to the manual task when it refuses; refuses when the charge predates the active provider config.payment-transaction.service.ts— CRUD onpayment_transactions.upsertPending,completePendingBySubscriptionId,findByProviderTransactionId.payment-provider-config.service.ts— encrypted credential CRUD onpayment_provider_configs; redacted reads.payment-provider-clients.service.ts—payment_provider_clientsupserts (Morning client linkage).credential-encryption.service.ts— AES-256-GCM round trip. Key fromPAYMENT_CREDENTIALS_ENCRYPTION_KEY(32-byte hex).webhook-processing.service.ts— dispatchespayment.completed | payment.failed | subscription.renewed | subscription.cancelled | refund.completed | client.created. Cross-org guard on course entitlements (:215,:362).handlePaymentFailed’s first-purchase arm marks the transactionfailedand leaves the subscriptionpending(emittingpayment.activation_failedwithreason: 'first_charge_failed') — the charge failed, not the checkout.recurring-charge.service.ts— daily 02:00 UTC cron.FOR UPDATE SKIP LOCKEDsweep, charge-then-update, debt promotion at 3 fails.payment-method.service.ts—replacePaymentMethod(deactivate old, insert new),getActivePaymentMethod.debt.service.ts—clearDebt(subscriptionId)charges the accumulateddebtAmountInCentsand zeroes it.payment-analytics.service.ts— KPIs (collected this month, MRR snapshot, forecasted recurring, recurring-charges list), all on recurring plans only; outstanding and members in debt come fromsumOutstanding.revenue-metrics.sql.ts—RECURRING_PLAN,sumOutstanding/outstandingRowCondition,sumPacksSold: the money nouns shared with analytics and insights (definitions).payment-monitoring.service.ts— the stale/abandoned-checkout guard.detectStuckPending(15-min alert),sweepAbandonedCheckouts(24h release),voidPendingTransaction(owner, by txn id) andvoidPendingSubscription(member rollback + staff member-page dismiss, by subscription id — takesrequestedByUserId,reason,intent,source, and stamps the decision onto the abandoned transaction’smetadata.checkoutCancel). ExportsABANDONED_CHECKOUT_REASON, the marker that keeps a released row revivable.checkout-urls.ts(+.unit.spec.ts) —deriveFailedUrl(cancelUrl): flipsstatus=cancelled→status=failedso a REFUSED charge can land somewhere other than the “you cancelled” page. Returnsundefinedrather than guessing when there is nothing to flip, and adapters fall back tocancelUrl.payment-observability.service.ts— typedemit(eventName, payload)interface; the central spine for all payment telemetry.manual-charge.service.ts(FIT-254 §3.3/§3.4) —ManualChargeService.charge(desk charge) +.clearDebt(debt collection wrapper overDebtService). ExportsCHARGE_VERIFIED_PROVIDERS(['cardcom', 'meshulam', 'test']), the synchronous-token-charge provider allowlist also imported byplan-change.service.ts.manual-charge.service.unit.spec.ts— unit tests.terminal-provisioning.service.ts(FIT-286) — opens a Cardcom terminal under Taikan’s dealer agreement (CompanyOperations/NewCompany). Derives the KYC volume estimates from the org’s plans + member count, refuses a second application while one ispending_kyc, and stores the result viacreatePendingManagedConfig. Deliberately NOT aPaymentProviderAdapter— it uses dealer-level credentials, not the gym’s.terminal-provisioning.service.unit.spec.ts— derivation, duplicate guard, pending-not-active storage,CompanyNumber-not-CompanyInternalIDcapture,GetCompanyStatusGET-only behavior.managed-terminal-status.service.ts(FIT-286) —CompanyOperations/GetCompanyStatuschecks forpending_kycterminals. Two entry points on one code path: the hourly@ScheduledJob(…, '30 * * * *')sweep, gated per-org onmanaged-terminal-kyc-poll(and skipping any terminal with no storedcompanyNumber), and the owner’s ungated on-demand “check status”. Activates onIsDone, re-encrypting the credentials from the approving response; otherwise stores the outstanding requirements instatus_reason. The sweep swallows per-org failures so one bad terminal cannot stall it; the on-demand path deliberately propagates them instead.managed-terminal-status.service.unit.spec.ts— approval flip, settle-before-email ordering, on-demand provider-failure propagation, failed-query safety, and the sweep: flag on/off/unevaluable, per-org rollout, missing-company-number skip, sweep isolation.
Controllers (controllers/)
payment.controller.ts—GET/POST /organizations/:orgId/payments[…]. Member-facing list, refund, refund-task complete, verify-return, transaction detail, cash credit note (credit-note/preview,credit-note) andsettle-cash-refund.payment-webhook.controller.ts—@Publicpath-style and query-style webhook entry points.payment-provider-config.controller.ts—GET/POST/PATCH/DELETE /organizations/:orgId/payment-config, plusPOST …/provision(owner/admin, opens a managed terminal),POST …/managed-status/refresh(owner/admin, re-checks KYC now) andPOST …/managed-status(@PlatformAdmin, manual override / the only way to close a rejection).card-registration.controller.ts— admin-initiates-card-on-file for a member, plus member-side equivalents.manual-charge.controller.ts(FIT-254 §3.3/§3.4) —POST members/:membershipId/charge(desk charge),POST subscriptions/:subscriptionId/clear-debt.app-return.controller.ts—@PublicGET /payments/app-return. HTTPS→taikan://bridge for hosted-page returns from the native app (providers reject custom-scheme return URLs; Morning errors 1103).payment-analytics.controller.ts—GET /organizations/:orgId/payment-analytics/*.invoicing.controller.ts—/organizations/:orgId/invoicing/*proxy onto the provider’slistDocuments / getDocumentadapter methods.
DTOs (dto/)
refund.dto.ts—{ amountInCents?, reason?, idempotencyKey? }(the key is required for a manual transaction).credit-note.dto.ts(TKN-425) —CreditNoteDto { idempotencyKey (UUID), amountInCents?, reason (1–255), cashReturned };SettleCashRefundDto { idempotencyKey }.settle-unpaid-term.dto.ts— settle (method,paidAt?,paidOn?YYYY-MM-DD, reference, note, cheque,skipDocument?) and write-off (reason?) bodies.configure-provider.dto.ts—{ provider, credentials, config? }+ update variant.manual-charge.dto.ts(FIT-254 §3.3) —{ amountInCents: int ≥ 100, description: string }.provision-terminal.dto.ts(FIT-286) — nested{ business, owner, bankAccount, expectedActivity? }; the estimates are optional because the API derives them. PlusSettleTerminalStatusDto.
Shared — libs/shared/src/lib/
schemas/payment.schema.ts— Zod schemas forPaymentTransactionResponse, refund DTOs, transaction filters.interfaces/payment-provider.ts(canonicalPaymentProviderAdaptercontract,ChargeRequest,RefundRequest,WebhookEvent,RefundCapability,ClientInfo, etc.).constants/platform-tiers.ts—tierHasFeatureconsumed byRequiresFeature.types/cardcom-provisioning.types.ts(FIT-286) —NewCompanywire contracts mirrored from Cardcom (including itsAvarageTransactionAmountInCreditCardmisspelling),TerminalProvisioningInput/Draft/Result.
DB — libs/db/src/lib/schema/
payments.ts—plans,subscriptions(+ this feature’scancellation_reason_codeand nineretention_*columns),member_payment_methods,payment_provider_configs,payment_transactions,invoicing_configs,payment_provider_clients. Nocancellation_requeststable.enums.ts—paymentProvider,planType,planInterval,subscriptionStatus,transactionStatus,transactionType,taskType(includesmanual_refund,cancellation_review), plus this feature’scancellationReasonCode,retentionOutcome,retentionCaseKind.
Web — apps/web/src/
components/payments/— owner dashboard widgets:transactions-table.tsx(refund confirm dialog + optional partial amount +refundExecution-driven task note),kpi-cards.tsx,payment-tabs.tsx,refund-task-complete-dialog.tsx,recurring-charges-table.tsx,documents-table.tsx,payments-data-table.tsx,summary-detail-toggle.tsx,date-range-filter.tsx.components/subscriptions/staff-cancel-dialog.tsx— staff cancel dialog (no default initiator, per-notice-date preview, reason codes). Seesubscriptions-plans/code-map.md.components/overview/members/member-payments-tab.tsx— member-detail refund confirm dialog + partial amount +refundExecutiontask note (same pattern astransactions-table.tsx).components/member/plan-card.tsx— member-facing plan/subscription summary.app/[lang]/(protected)/dashboard/payments/— owner payment console.app/[lang]/(protected)/dashboard/settings/checkout-return/page.tsx— Cardcom return page; callsverify-checkoutfor platform-billing.app/[lang]/buy/courses/[id]/page.tsx— public course buy page (handles Clerk email-code flow). Seecourses/code-map.md.components/settings/provider-config-dialog.tsx(+.driver.tsx,.int.spec.tsx) — mode choice (Taikan-managed vs bring-your-own) → explanation → application → one-time provider login.components/settings/managed-terminal-form.tsx(FIT-286) — the ~15-field terminal application.components/address/israeli-address-picker.tsx(FIT-286) — reusable locality + street combobox over the national registry. Emitsסמל ישוב/סמל רחובcodes; the street list is city-scoped and resets when the city changes, because street codes are unique per city only.components/settings/payment-provider-config.tsx(+.driver.tsx,.int.spec.tsx) — settings card; renders thepending_kyc/rejectedstates, lists what Cardcom is still waiting on, swaps Configure for Check status while a review is outstanding.
Tests
apps/api/src/payments/services/refund-flow.int.spec.ts— end-to-end refund integration test.apps/api/src/payments/services/payment-transaction.service.unit.spec.tsapps/api/src/payments/services/credential-encryption.service.unit.spec.tsapps/api/src/payments/services/manual-charge.service.unit.spec.ts(FIT-254 §3.3/§3.4) — guard-stack ordering, pending-row-first, allowlist.apps/api/src/payments/providers/*.unit.spec.ts— per-provider adapter contract tests.apps/api/src/subscriptions/cancellation-flow.int.spec.ts— cancellation-request + refund handshake.
Web — card-on-file & manual charge (FIT-254 §3)
apps/web/src/components/overview/members/payment-methods-card.tsx(+.driver.tsx,.int.spec.tsx) — member-detail Memberships-tab card: active card, expiry warning, collapsed inactive history, “Register card” CTA.apps/web/src/components/members/manual-charge-dialog.tsx(+.driver.tsx,.int.spec.tsx) — staff desk-charge confirm dialog (amount, description, last-4, confirm-with-amount button).apps/web/src/components/members/collect-debt-dialog.tsx— staff debt-collection confirm dialog.apps/web/src/components/payments/debt-report-table.tsx— payments-dashboard debt report rows, links into collect-debt.
Billing resilience (TKN-353)
Behavior: behavior.md › Billing resilience. Operations: runbook.
API — apps/api/src/payments/services/
billing-resilience-policy.service.ts— thepayment-billing-resilienceflag ason | off | unevaluable;enabled()isononly.billing-attempt.service.ts— member recurring attempts:collect(claim → provider → outcome),recordOutcome,apply(atomic settlement),recover(five-minute external-id lookup on the original terminal),hasUnresolved,hasProtectedObligation,terminalState(open | busy | blocked), settlement outboxes (notifications,pendingLifecycleEvents), deduplicatedalert.billing-collection.service.ts— the same protocol for desk charges and debt collection (manualAttempt,collect,apply,recoverevery five minutes).billing-charge-guard.ts— lock order (lockBillingOrganization,lockBillingAccount),pendingMemberCollection, andwithLegacyBillingAdmissionfor legacy pre-debit writers.billing-settlement.ts— pure settlement math:billingSettlement(success/decline patch: debt opened / retried / cleared),missedPaymentDebtEntry,chargeRowDebtPolicy,debtOpenedEvent,scheduledBillingRow,cardUsableAt.billing-resolution-state.ts/billing-resolution.service.ts— owner reconciliation:inspect,reconcile,confirmNoDebit(owner only),authorizeRetry;TECHNICAL_RETRY_DELAYS_MS, external-id lineage for retries.billing-notification.service.ts— frozen-payload receipt/failure mail with stable idempotency keys (prepare,deliver).billing-due-query.ts— read-only due candidates for the catch-up (organizations,subscriptions), including the persisted-protocol filter for un-opted organizations.billing-catchup.service.ts—collectDue, every minute on thebilling-collectionqueue (20 orgs × 5 rows, 30 s start budget, Redis fairness cursors).billing-schedule.service.ts— opening-day and staff charge-date writers under the billing locks (protects,updateOrganization,moveChargeDate).presale-checkout.service.ts— protected card-save checkout fulfillment (handle): exact page, verified provider, terminal identity, expired-page reopen, abandoned revival.billing-readiness.service.ts/billing-readiness-actions.service.ts— presale readiness report and theverifyAccount/repairSchedulerepairs.- Touched legacy services:
recurring-charge.service.ts(routing,processResilientCharge,collectProtectedCharge,recoverBillingAttempts),webhook-processing.service.ts(protected webhook ownership, page-expired marker),payment.service.ts(billingCheckouton card-save pages),payment-method.service.ts(scheduled-card protection, terminal binding),manual-charge.service.ts/debt.service.ts(claims and idempotency),payment-transaction.service.ts(publicTransactionMetadatastrips the protocol snapshots from API responses).
API — controllers, jobs, DTOs
payments/controllers/billing-resolution.controller.ts—/organizations/:orgId/payments/billing/transactions/:transactionId(GET,reconcile,confirm-no-debit,authorize-retry);dto/billing-resolution.dto.ts.admin/controllers/admin-billing-readiness.controller.ts—/admin/organizations/:orgId/billing-readiness(GET,verify-account,repair-schedule),PlatformAdmin;payments/dto/billing-readiness.dto.ts.payments/dto/verify-return.dto.ts—POST /payments/verify-returnbody; the provider deal’s own subscription is authoritative.common/scheduled-jobs/billing-collection.processor.ts— the dedicatedbilling-collectionBullMQ worker.
Shared, web, admin
libs/shared/src/lib/types/billing-resolution.types.ts,billing-readiness.types.ts;constants/payment-error-codes.ts(billing_*codes);constants/feature-flags.ts(PAYMENT_BILLING_RESILIENCE).apps/web/src/components/payments/billing-review-dialog.tsx(+ driver, int spec) — Review billing, opened fromtransactions-table.tsxandoverview/members/member-payments-tab.tsx.apps/web/src/components/members/manual-charge-dialog.tsx,collect-debt-dialog.tsx— pending-reconciliation vs confirmed-decline outcomes, request-key retention.apps/admin/src/pages/organizations/billing-readiness-panel.tsx— readiness panel on the admin organization page.
Tests
- Real-PostgreSQL suites, run by
apps/api/vitest.billing-resilience.config.ts:billing-resilience.int.spec.ts(+.int.driver.ts),billing-resilience-{provider,settlement,execution,presale,readiness}.int.spec.ts,common/scheduled-jobs/billing-collection.processor.int.spec.ts. - Unit:
billing-resilience-policy.service,billing-settlement,billing-lifecycle,billing-review-api,billing-resolution(controller),cardcom-resilience(provider),admin-billing-readiness(controller).
Desk money, refunds and unpaid payments (TKN-425 / 426 / 427)
Behavior: Cash credit notes, Pay-later terms, Unpaid payment, Refund flows.
API
payments/services/cash-credit-note.service.ts—CashCreditNoteService(preview,issue,settleCashRefund,recordCredit+completeCreditfor callers holding their own transaction) and the purecomputeCreditable/priorCreditsInCents.payments/services/manual-document.service.ts—ManualDocumentService.issue: files a desk document (sale or credit) through the active provider or a named one, writes the outcome (incl.document_url) on the row, never throws.payments/credit-document-kind.ts—creditKindFor(original document → credit kind) andcreditableOriginal(the 409 verdicts).payments/services/manual-payment.service.ts—refund(now the credit core),cancelCashPlan/previewCashCancellation(credit amount +cashReturned),settleCashCancellation(reservations),settleUnpaidTerm(paidOn, client key),writeOffUnpaidTerm,record(settlement replay),prepayCashPlan/settlesCashLapse/quoteCashCoverage(desk restore of acash_lapseddebt), audit rows;distributeCredit,unpaidSettlementKey.payments/services/unpaid-term-close.ts—closeUnpaidTerm(closes a pay-later term and clears its debt: restore or stay cancelled),refreshMembershipPaymentStatus.payments/services/payment.service.ts— manual routing and taskorigininrefund;processAutomaticRefund(sale lock, cap, pending row before the provider);openManualRefundTask(lock, cap,metadata.manualRefund, portal-first line);getManualRefundTask(preview);completeManualRefundTask(amount + document number, refund row, flip at 100%, cash-notice refusal); refund-row credit documents inissueCreditDocument.payments/controllers/payment.controller.ts—GET refund-tasks/:taskId,POST refund-tasks/:taskId/complete;payments/dto/complete-refund-task.dto.ts.payments/cash-renewal-task.ts—cashRenewalDueDate(org-calendar day, 7 days before coverage ends);openCashRenewalTasknow takestimezone.payments/services/cash-plan-coverage.service.ts— task escalation compares due dates with each org’s own today.payments/providers/cardcom.provider.ts—refundoutcomerefused/unknown, credit twin mapping,ExternalId,Comments,DocumentDate(toCardcomDocumentDate), Email ≤ 50 rule,Name≤ 50,ISOCoinIDfromCURRENCY_TO_COIN_ID.morning.provider.ts— both credit kinds → 330.payments/billing-debt.ts—DebtPolicy,debtCancelsAt,debtPayBy,debtRetryInstants,nextDebtRetry,resolveBillingDebt,hasOpenDebtAfterCancellation,earliestBillingDebt.payments/services/debt-entry.ts—enterDebtPatch,retryFailedPatch,debtClearedPatch,debtClearedFields.organizations/debt-policy.ts—loadDebtPolicy(db, orgId).payments/subscription-events.ts—SUBSCRIPTION_DEBT_OPENED(reason,payBy,cancelsAt),BILLING_CARD_UNUSABLE/BillingCardUnusableEvent.payments/services/recurring-charge.service.ts—markCardUnusable, debt entry / retry / clear inhandleChargeFailureandhandleChargeSuccess, presale day-0 retention case; the same inbilling-settlement.ts,webhook-processing.service.ts(handlePaymentFailed,handleSubscriptionRenewed),cancellation-tail-settlement.service.ts; restore indebt.service.ts(clearDebt,getDebtReport),billing-collection.service.ts,external-links/external-payment-apply.service.ts.subscriptions/subscription-lifecycle.service.ts—SubscriptionLifecycleService.tick(hourly),runForOrg, purelifecycleTransition;subscriptions/subscriptions.service.ts—endSubscription,findBookableForMembership.bookings/booking-horizon.ts—bookingHorizon,isCreditSource,closedMoneyWall,startsBeyondHorizon;bookings/bookings.service.ts—loadLiveSubscriptions,assertBillingAllowsBooking.subscriptions/subscriptions.controller.ts(/myand staff listbillingDebt,paymentLastDay;POST my/:id/pay-debt),subscriptions/member-action.ts(pay_debt),memberships/member-status.ts(billingDebt,non_payment),memberships/memberships.service.ts(getDebtPolicy,nonPaymentCondition).notifications/billing-debt-notifications.service.ts—BillingDebtNotificationsService:debtOpened,debtLastDay,debtCancelled, card-unusable listener,runCardExpiryReminders;notifications/email-types.ts(CARD_EXPIRING,CARD_UNUSABLE,DEBT_OPENED,DEBT_LAST_DAY,DEBT_CANCELLED); templates innotifications/templates/payment-emails.ts, push copy inpush-notifications/notification-copy.ts.audit/audit-actions.ts—payment.cash_recorded,payment.credit_note_issued,payment.cash_plan_cancelled,payment.unpaid_term_settled,payment.unpaid_term_written_off.
Shared and DB
libs/shared/src/lib/types/payment-provider.types.ts—TaxDocumentKind('credit'replaced bytax_invoice_receipt_credit/receipt_credit),CREDIT_DOCUMENT_KINDS,isCreditDocumentKind;IssueDocumentRequest.{externalId, linkedDocumentNumber, documentDate},RefundDocumentRequest.externalId.libs/shared/src/lib/constants/payment-error-codes.ts—credit_note_*,refund_task_use_cash_settlement,manual_refund_idempotency_required,refund_amount_exceeds_cap,refund_task_amount_required,debt_partial_payment_refused,booking_blocked_debt,plan_required_to_book,plan_not_started.libs/shared/src/lib/plan-duration.ts—planDurationDays,addDaysToDateString,unpaidTermDates.libs/shared/src/lib/schemas/—payment.schema.ts(transactionmethod,paidAt,document*,refund_pending;manualRefundOriginValues,completeRefundTaskSchema,refundTaskPreviewSchema),plan.schema.ts(billingDebtSchema,debtReasonValues,NON_PAYMENT_CANCELLATION_REASON,pay_debt,paymentLastDay),membership.schema.ts(member statusbillingDebt,non_payment,paymentLastDay),organization.schema.ts(billingGraceDays,MAX_BILLING_GRACE_DAYS = 30),scheduling.schema.ts(eligibilitydebt_blocked,plan_not_started),cancellation.schema.ts(refund.cashReturned, previewcreditableInCents/capInCents),notification-prefs.ts(billing).libs/db/src/lib/schema/payments.ts,organizations.ts; migrationlibs/db/drizzle/0164_special_meteorite.sql(data-model).
Web
components/payments/credit-note-dialog.tsx(+ driver, int spec) —CreditNoteDialog,isCashCreditable,useCodedErrorCopy,invalidatePaymentViews.components/payments/confirm-cash-returned-dialog.tsx(+ driver, int spec) —ConfirmCashReturnedDialog,pendingCashRefundOriginalId.components/payments/refund-task-complete-dialog.tsx(+ driver, int spec) — preview-driven amount (default, cap hint), portal-first line, posts amount + document number;refund_task_use_cash_settlementcopy and member link.components/payments/transactions-table.tsx,components/overview/members/member-payments-tab.tsx— credit-note and confirm-cash actions, document number / failed pill.components/overview/members/enroll-member-dialog.tsx— dated unpaid notice, cash received now/later.components/overview/members/member-memberships-tab.tsx,components/members/settle-unpaid-term-dialog.tsx,components/members/write-off-unpaid-term-dialog.tsx(+ drivers, int specs) — unpaid-term badge actions.components/member/unpaid-term-banner.tsx(+ driver, int spec),components/overview/members/member-status-badge.tsx— last day as a plain date.components/subscriptions/staff-cancel-dialog.tsx— cash credit amount and Cash returned switch.app/[lang]/(protected)/dashboard/settings/(shell)/booking/page.tsx—billing-grace-daysandplan-required-to-booksettings.components/member/billing-debt-banner.tsx(+ driver, int spec),app/[lang]/(protected)/(member)/profile/payments/page.tsx— grace / cancelled-for-non-payment banner, pay-debt button, contact link.components/member/schedule/session-detail-drawer.tsx— booking refusal codes (booking_blocked_debtwith amount).
Tests
- API:
credit-note.int.spec.ts,cash-credit-note.service.unit.spec.ts,cash-plan-payment.int.spec.ts,refund-flow.int.spec.ts,credit-document-kind.unit.spec.ts,cash-renewal-task.unit.spec.ts,refund-task.int.spec.ts,billing-debt.unit.spec.ts,debt-entry.unit.spec.ts,subscription-lifecycle.service.{unit,int}.spec.ts,billing-debt-notifications.service.unit.spec.ts,booking-horizon.unit.spec.ts,bookings.service.unit.spec.ts(billing gate),renew-early.int.spec.ts(renewing and debt), provider specs (cardcom.provider,card-registration-roundtrip,morning-issue-document). - Shared:
plan-duration.unit.spec.ts,i18n/dictionaries.unit.spec.ts(locale parity, no “now”/“from today” in unpaid notices).
Address registry (FIT-286)
libs/db/src/lib/schema/israel-addresses.ts—israel_cities(1,310 rows),israel_streets(63,571 rows, PK(city_code, code)). Street code9000is the registry’s “the locality itself” entry, present once per city and the only option where a locality has no named streets.libs/db/data/israel-streets.csv.gz— committed UTF-8 snapshot of data.gov.il’s “רשימת רחובות בישראל” (source file is cp1255).scripts/seed-israel-streets.ts(pnpm db:seed:streets) — idempotent upsert; needs running once per environment, including production.apps/api/src/addresses/—AddressesModule,IsraelAddressService,GET /addresses/israel/cities|streets. Authenticated but not org-scoped: public government data, identical for every gym.
Env
PAYMENT_CREDENTIALS_ENCRYPTION_KEY— 64-char hex (32 bytes) AES-256-GCM key.API_BASE_URL— used to construct webhook URLs (payment.service.ts:128).FRONTEND_URL— used for cancellation-request review links.- Cardcom platform terminal:
PLATFORM_BILLING_TERMINAL_NUMBER,PLATFORM_BILLING_API_NAME,PLATFORM_BILLING_API_PASSWORD(seeplatform-billing/). CARDCOM_PROVISIONING_DEBUG(FIT-286) —truelogs theNewCompanybody verbatim for a Cardcom support ticket. Default (unset/false) redacts the dealer secret, ת.ז, bank account, mother’s maiden name and the generated dashboard password, keeping field names, structure and every other value. Local dev only.- Cardcom dealer provisioning (FIT-286):
CARDCOM_SUPPLIER_USERNAME,CARDCOM_SUPPLIER_PASSWORD,CARDCOM_SUPPLIER_SECRET,CARDCOM_MODULE_IDS— required together;resolveSupplierthrowsServiceUnavailablenaming what is missing rather than half-opening a terminal.CARDCOM_AGREEMENT_IDis optional (Cardcom does not require it): unset means theAgreementInfoblock is omitted from the request rather than sent as0.