@wopr-network/platform-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/biome.json +61 -0
- package/dist/admin/admin-audit-log-repository.d.ts +33 -0
- package/dist/admin/admin-audit-log-repository.js +102 -0
- package/dist/admin/audit-log.d.ts +49 -0
- package/dist/admin/audit-log.js +63 -0
- package/dist/admin/index.d.ts +6 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/role-store.d.ts +37 -0
- package/dist/admin/role-store.js +106 -0
- package/dist/auth/api-key-repository.d.ts +11 -0
- package/dist/auth/api-key-repository.js +33 -0
- package/dist/auth/api-key-repository.test.d.ts +1 -0
- package/dist/auth/api-key-repository.test.js +46 -0
- package/dist/auth/auth.test.d.ts +1 -0
- package/dist/auth/auth.test.js +140 -0
- package/dist/auth/better-auth.d.ts +42 -0
- package/dist/auth/better-auth.js +196 -0
- package/dist/auth/index.d.ts +186 -0
- package/dist/auth/index.js +422 -0
- package/dist/auth/login-history-repository.d.ts +14 -0
- package/dist/auth/login-history-repository.js +15 -0
- package/dist/auth/login-history-repository.test.d.ts +1 -0
- package/dist/auth/login-history-repository.test.js +47 -0
- package/dist/auth/middleware.d.ts +55 -0
- package/dist/auth/middleware.js +101 -0
- package/dist/auth/middleware.test.d.ts +1 -0
- package/dist/auth/middleware.test.js +213 -0
- package/dist/auth/scoped-tokens.test.d.ts +1 -0
- package/dist/auth/scoped-tokens.test.js +306 -0
- package/dist/auth/tenant-access.test.d.ts +1 -0
- package/dist/auth/tenant-access.test.js +62 -0
- package/dist/auth/user-creator.d.ts +9 -0
- package/dist/auth/user-creator.js +47 -0
- package/dist/auth/user-creator.test.d.ts +1 -0
- package/dist/auth/user-creator.test.js +78 -0
- package/dist/auth/user-role-repository.d.ts +31 -0
- package/dist/auth/user-role-repository.js +53 -0
- package/dist/auth/user-role-repository.test.d.ts +1 -0
- package/dist/auth/user-role-repository.test.js +122 -0
- package/dist/billing/drizzle-webhook-seen-repository.d.ts +10 -0
- package/dist/billing/drizzle-webhook-seen-repository.js +28 -0
- package/dist/billing/index.d.ts +7 -0
- package/dist/billing/index.js +7 -0
- package/dist/billing/payment-processor.d.ts +127 -0
- package/dist/billing/payment-processor.js +8 -0
- package/dist/billing/payment-processor.test.d.ts +1 -0
- package/dist/billing/payment-processor.test.js +71 -0
- package/dist/billing/payram/cents-credits-boundary.test.d.ts +1 -0
- package/dist/billing/payram/cents-credits-boundary.test.js +75 -0
- package/dist/billing/payram/charge-store.d.ts +41 -0
- package/dist/billing/payram/charge-store.js +72 -0
- package/dist/billing/payram/charge-store.test.d.ts +1 -0
- package/dist/billing/payram/charge-store.test.js +64 -0
- package/dist/billing/payram/checkout.d.ts +15 -0
- package/dist/billing/payram/checkout.js +24 -0
- package/dist/billing/payram/checkout.test.d.ts +1 -0
- package/dist/billing/payram/checkout.test.js +74 -0
- package/dist/billing/payram/client.d.ts +7 -0
- package/dist/billing/payram/client.js +15 -0
- package/dist/billing/payram/client.test.d.ts +1 -0
- package/dist/billing/payram/client.test.js +52 -0
- package/dist/billing/payram/index.d.ts +8 -0
- package/dist/billing/payram/index.js +4 -0
- package/dist/billing/payram/types.d.ts +40 -0
- package/dist/billing/payram/types.js +1 -0
- package/dist/billing/payram/webhook.d.ts +19 -0
- package/dist/billing/payram/webhook.js +67 -0
- package/dist/billing/payram/webhook.test.d.ts +7 -0
- package/dist/billing/payram/webhook.test.js +248 -0
- package/dist/billing/stripe/cents-credits-boundary.test.d.ts +1 -0
- package/dist/billing/stripe/cents-credits-boundary.test.js +62 -0
- package/dist/billing/stripe/checkout.d.ts +20 -0
- package/dist/billing/stripe/checkout.js +63 -0
- package/dist/billing/stripe/checkout.test.d.ts +1 -0
- package/dist/billing/stripe/checkout.test.js +148 -0
- package/dist/billing/stripe/client.d.ts +14 -0
- package/dist/billing/stripe/client.js +33 -0
- package/dist/billing/stripe/client.test.d.ts +1 -0
- package/dist/billing/stripe/client.test.js +58 -0
- package/dist/billing/stripe/credit-prices.d.ts +63 -0
- package/dist/billing/stripe/credit-prices.js +81 -0
- package/dist/billing/stripe/credit-prices.test.d.ts +1 -0
- package/dist/billing/stripe/credit-prices.test.js +87 -0
- package/dist/billing/stripe/index.d.ts +14 -0
- package/dist/billing/stripe/index.js +8 -0
- package/dist/billing/stripe/payment-methods-detach-all.test.d.ts +1 -0
- package/dist/billing/stripe/payment-methods-detach-all.test.js +40 -0
- package/dist/billing/stripe/payment-methods.d.ts +25 -0
- package/dist/billing/stripe/payment-methods.js +53 -0
- package/dist/billing/stripe/payment-methods.test.d.ts +1 -0
- package/dist/billing/stripe/payment-methods.test.js +122 -0
- package/dist/billing/stripe/portal.d.ts +10 -0
- package/dist/billing/stripe/portal.js +16 -0
- package/dist/billing/stripe/portal.test.d.ts +1 -0
- package/dist/billing/stripe/portal.test.js +48 -0
- package/dist/billing/stripe/setup-intent.d.ts +16 -0
- package/dist/billing/stripe/setup-intent.js +22 -0
- package/dist/billing/stripe/setup-intent.test.d.ts +1 -0
- package/dist/billing/stripe/setup-intent.test.js +58 -0
- package/dist/billing/stripe/stripe-payment-processor.d.ts +49 -0
- package/dist/billing/stripe/stripe-payment-processor.js +166 -0
- package/dist/billing/stripe/stripe-payment-processor.test.d.ts +1 -0
- package/dist/billing/stripe/stripe-payment-processor.test.js +413 -0
- package/dist/billing/stripe/tenant-store.d.ts +56 -0
- package/dist/billing/stripe/tenant-store.js +119 -0
- package/dist/billing/stripe/tenant-store.test.d.ts +1 -0
- package/dist/billing/stripe/tenant-store.test.js +97 -0
- package/dist/billing/stripe/types.d.ts +49 -0
- package/dist/billing/stripe/types.js +1 -0
- package/dist/billing/webhook-seen-repository.d.ts +14 -0
- package/dist/billing/webhook-seen-repository.js +13 -0
- package/dist/config/billing-env.test.d.ts +1 -0
- package/dist/config/billing-env.test.js +48 -0
- package/dist/config/index.d.ts +46 -0
- package/dist/config/index.js +38 -0
- package/dist/config/logger.d.ts +2 -0
- package/dist/config/logger.js +11 -0
- package/dist/config/provider-endpoints.d.ts +6 -0
- package/dist/config/provider-endpoints.js +12 -0
- package/dist/credits/auto-topup-charge.d.ts +27 -0
- package/dist/credits/auto-topup-charge.js +139 -0
- package/dist/credits/auto-topup-charge.test.d.ts +1 -0
- package/dist/credits/auto-topup-charge.test.js +242 -0
- package/dist/credits/auto-topup-event-log-repository.d.ts +16 -0
- package/dist/credits/auto-topup-event-log-repository.js +18 -0
- package/dist/credits/auto-topup-event-log-repository.test.d.ts +1 -0
- package/dist/credits/auto-topup-event-log-repository.test.js +83 -0
- package/dist/credits/auto-topup-schedule.d.ts +27 -0
- package/dist/credits/auto-topup-schedule.js +66 -0
- package/dist/credits/auto-topup-schedule.test.d.ts +1 -0
- package/dist/credits/auto-topup-schedule.test.js +145 -0
- package/dist/credits/auto-topup-settings-repository.d.ts +54 -0
- package/dist/credits/auto-topup-settings-repository.js +184 -0
- package/dist/credits/auto-topup-settings-repository.test.d.ts +1 -0
- package/dist/credits/auto-topup-settings-repository.test.js +104 -0
- package/dist/credits/auto-topup-usage.d.ts +22 -0
- package/dist/credits/auto-topup-usage.js +56 -0
- package/dist/credits/auto-topup-usage.test.d.ts +1 -0
- package/dist/credits/auto-topup-usage.test.js +181 -0
- package/dist/credits/credit-expiry-cron.d.ts +19 -0
- package/dist/credits/credit-expiry-cron.js +50 -0
- package/dist/credits/credit-expiry-cron.test.d.ts +1 -0
- package/dist/credits/credit-expiry-cron.test.js +67 -0
- package/dist/credits/credit-ledger-extra.test.d.ts +1 -0
- package/dist/credits/credit-ledger-extra.test.js +40 -0
- package/dist/credits/credit-ledger.bench.d.ts +1 -0
- package/dist/credits/credit-ledger.bench.js +33 -0
- package/dist/credits/credit-ledger.d.ts +130 -0
- package/dist/credits/credit-ledger.js +293 -0
- package/dist/credits/credit-ledger.test.d.ts +4 -0
- package/dist/credits/credit-ledger.test.js +203 -0
- package/dist/credits/credit-transaction-repository.d.ts +17 -0
- package/dist/credits/credit-transaction-repository.js +35 -0
- package/dist/credits/credit-transaction-repository.test.d.ts +1 -0
- package/dist/credits/credit-transaction-repository.test.js +232 -0
- package/dist/credits/credit.d.ts +75 -0
- package/dist/credits/credit.js +139 -0
- package/dist/credits/credit.test.d.ts +1 -0
- package/dist/credits/credit.test.js +196 -0
- package/dist/credits/dividend-cron.d.ts +29 -0
- package/dist/credits/dividend-cron.js +88 -0
- package/dist/credits/dividend-cron.test.d.ts +1 -0
- package/dist/credits/dividend-cron.test.js +128 -0
- package/dist/credits/dividend-repository.d.ts +29 -0
- package/dist/credits/dividend-repository.js +126 -0
- package/dist/credits/dividend-repository.test.d.ts +1 -0
- package/dist/credits/dividend-repository.test.js +176 -0
- package/dist/credits/index.d.ts +9 -0
- package/dist/credits/index.js +5 -0
- package/dist/credits/repository-types.d.ts +29 -0
- package/dist/credits/repository-types.js +1 -0
- package/dist/credits/signup-grant.d.ts +12 -0
- package/dist/credits/signup-grant.js +35 -0
- package/dist/credits/signup-grant.test.d.ts +1 -0
- package/dist/credits/signup-grant.test.js +51 -0
- package/dist/credits/tenant-customer-repository.d.ts +30 -0
- package/dist/credits/tenant-customer-repository.js +5 -0
- package/dist/db/auth-user-repository.d.ts +46 -0
- package/dist/db/auth-user-repository.js +90 -0
- package/dist/db/credit-column.d.ts +27 -0
- package/dist/db/credit-column.js +13 -0
- package/dist/db/index.d.ts +14 -0
- package/dist/db/index.js +8 -0
- package/dist/db/schema/account-deletion-requests.d.ts +203 -0
- package/dist/db/schema/account-deletion-requests.js +36 -0
- package/dist/db/schema/account-export-requests.d.ts +148 -0
- package/dist/db/schema/account-export-requests.js +19 -0
- package/dist/db/schema/admin-audit.d.ts +194 -0
- package/dist/db/schema/admin-audit.js +21 -0
- package/dist/db/schema/admin-users.d.ts +177 -0
- package/dist/db/schema/admin-users.js +23 -0
- package/dist/db/schema/affiliate-fraud.d.ts +160 -0
- package/dist/db/schema/affiliate-fraud.js +18 -0
- package/dist/db/schema/affiliate.d.ts +277 -0
- package/dist/db/schema/affiliate.js +32 -0
- package/dist/db/schema/coupon-codes.d.ts +143 -0
- package/dist/db/schema/coupon-codes.js +17 -0
- package/dist/db/schema/credit-auto-topup-settings.d.ts +232 -0
- package/dist/db/schema/credit-auto-topup-settings.js +27 -0
- package/dist/db/schema/credit-auto-topup.d.ts +130 -0
- package/dist/db/schema/credit-auto-topup.js +21 -0
- package/dist/db/schema/credits.d.ts +283 -0
- package/dist/db/schema/credits.js +38 -0
- package/dist/db/schema/dividend-distributions.d.ts +130 -0
- package/dist/db/schema/dividend-distributions.js +19 -0
- package/dist/db/schema/email-notifications.d.ts +99 -0
- package/dist/db/schema/email-notifications.js +21 -0
- package/dist/db/schema/index.d.ts +33 -0
- package/dist/db/schema/index.js +33 -0
- package/dist/db/schema/meter-events.d.ts +599 -0
- package/dist/db/schema/meter-events.js +55 -0
- package/dist/db/schema/notification-preferences.d.ts +165 -0
- package/dist/db/schema/notification-preferences.js +18 -0
- package/dist/db/schema/notification-queue.d.ts +236 -0
- package/dist/db/schema/notification-queue.js +40 -0
- package/dist/db/schema/org-memberships.d.ts +63 -0
- package/dist/db/schema/org-memberships.js +15 -0
- package/dist/db/schema/organization-members.d.ts +235 -0
- package/dist/db/schema/organization-members.js +27 -0
- package/dist/db/schema/payram.d.ts +164 -0
- package/dist/db/schema/payram.js +21 -0
- package/dist/db/schema/platform-api-keys.d.ts +143 -0
- package/dist/db/schema/platform-api-keys.js +20 -0
- package/dist/db/schema/promotion-redemptions.d.ts +143 -0
- package/dist/db/schema/promotion-redemptions.js +18 -0
- package/dist/db/schema/promotions.d.ts +445 -0
- package/dist/db/schema/promotions.js +48 -0
- package/dist/db/schema/provider-credentials.d.ts +201 -0
- package/dist/db/schema/provider-credentials.js +36 -0
- package/dist/db/schema/rate-limit-entries.d.ts +75 -0
- package/dist/db/schema/rate-limit-entries.js +7 -0
- package/dist/db/schema/secret-audit-log.d.ts +109 -0
- package/dist/db/schema/secret-audit-log.js +15 -0
- package/dist/db/schema/session-usage.d.ts +194 -0
- package/dist/db/schema/session-usage.js +19 -0
- package/dist/db/schema/spending-limits.d.ts +92 -0
- package/dist/db/schema/spending-limits.js +8 -0
- package/dist/db/schema/tenant-addons.d.ts +58 -0
- package/dist/db/schema/tenant-addons.js +9 -0
- package/dist/db/schema/tenant-api-keys.d.ts +131 -0
- package/dist/db/schema/tenant-api-keys.js +21 -0
- package/dist/db/schema/tenant-capability-settings.d.ts +79 -0
- package/dist/db/schema/tenant-capability-settings.js +12 -0
- package/dist/db/schema/tenant-customers.d.ts +303 -0
- package/dist/db/schema/tenant-customers.js +25 -0
- package/dist/db/schema/tenants.d.ts +126 -0
- package/dist/db/schema/tenants.js +18 -0
- package/dist/db/schema/user-roles.d.ts +98 -0
- package/dist/db/schema/user-roles.js +18 -0
- package/dist/db/schema/webhook-seen-events.d.ts +58 -0
- package/dist/db/schema/webhook-seen-events.js +9 -0
- package/dist/email/billing-emails.d.ts +51 -0
- package/dist/email/billing-emails.js +163 -0
- package/dist/email/billing-emails.test.d.ts +1 -0
- package/dist/email/billing-emails.test.js +162 -0
- package/dist/email/client.d.ts +51 -0
- package/dist/email/client.js +102 -0
- package/dist/email/client.test.d.ts +1 -0
- package/dist/email/client.test.js +120 -0
- package/dist/email/drizzle-billing-email-repository.d.ts +21 -0
- package/dist/email/drizzle-billing-email-repository.js +36 -0
- package/dist/email/drizzle-billing-email-repository.test.d.ts +1 -0
- package/dist/email/drizzle-billing-email-repository.test.js +42 -0
- package/dist/email/index.d.ts +33 -0
- package/dist/email/index.js +22 -0
- package/dist/email/notification-preferences-store.d.ts +12 -0
- package/dist/email/notification-preferences-store.js +82 -0
- package/dist/email/notification-preferences-store.test.d.ts +1 -0
- package/dist/email/notification-preferences-store.test.js +86 -0
- package/dist/email/notification-queue-store.d.ts +25 -0
- package/dist/email/notification-queue-store.js +97 -0
- package/dist/email/notification-queue-store.test.d.ts +1 -0
- package/dist/email/notification-queue-store.test.js +177 -0
- package/dist/email/notification-repository-types.d.ts +70 -0
- package/dist/email/notification-repository-types.js +6 -0
- package/dist/email/notification-service.d.ts +41 -0
- package/dist/email/notification-service.js +196 -0
- package/dist/email/notification-service.test.d.ts +1 -0
- package/dist/email/notification-service.test.js +160 -0
- package/dist/email/notification-templates.d.ts +18 -0
- package/dist/email/notification-templates.js +574 -0
- package/dist/email/notification-templates.test.d.ts +1 -0
- package/dist/email/notification-templates.test.js +238 -0
- package/dist/email/notification-worker.d.ts +24 -0
- package/dist/email/notification-worker.js +109 -0
- package/dist/email/notification-worker.test.d.ts +1 -0
- package/dist/email/notification-worker.test.js +153 -0
- package/dist/email/require-verified.d.ts +25 -0
- package/dist/email/require-verified.js +52 -0
- package/dist/email/require-verified.test.d.ts +1 -0
- package/dist/email/require-verified.test.js +62 -0
- package/dist/email/resend-adapter.d.ts +47 -0
- package/dist/email/resend-adapter.js +137 -0
- package/dist/email/resend-adapter.test.d.ts +1 -0
- package/dist/email/resend-adapter.test.js +190 -0
- package/dist/email/templates.d.ts +22 -0
- package/dist/email/templates.js +359 -0
- package/dist/email/templates.test.d.ts +1 -0
- package/dist/email/templates.test.js +170 -0
- package/dist/email/verification.d.ts +42 -0
- package/dist/email/verification.js +83 -0
- package/dist/email/verification.test.d.ts +1 -0
- package/dist/email/verification.test.js +141 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +23 -0
- package/dist/metering/aggregator.d.ts +54 -0
- package/dist/metering/aggregator.js +123 -0
- package/dist/metering/aggregator.test.d.ts +1 -0
- package/dist/metering/aggregator.test.js +179 -0
- package/dist/metering/dlq.d.ts +31 -0
- package/dist/metering/dlq.js +82 -0
- package/dist/metering/dlq.test.d.ts +1 -0
- package/dist/metering/dlq.test.js +117 -0
- package/dist/metering/drizzle-usage-summary-repository.d.ts +67 -0
- package/dist/metering/drizzle-usage-summary-repository.js +98 -0
- package/dist/metering/emitter.d.ts +66 -0
- package/dist/metering/emitter.js +185 -0
- package/dist/metering/emitter.test.d.ts +1 -0
- package/dist/metering/emitter.test.js +171 -0
- package/dist/metering/index.d.ts +11 -0
- package/dist/metering/index.js +5 -0
- package/dist/metering/load-test.bench.d.ts +1 -0
- package/dist/metering/load-test.bench.js +103 -0
- package/dist/metering/meter-event-repository.d.ts +33 -0
- package/dist/metering/meter-event-repository.js +58 -0
- package/dist/metering/meter-repositories.test.d.ts +1 -0
- package/dist/metering/meter-repositories.test.js +419 -0
- package/dist/metering/metering.test.d.ts +1 -0
- package/dist/metering/metering.test.js +1046 -0
- package/dist/metering/reconciliation-cron.d.ts +37 -0
- package/dist/metering/reconciliation-cron.js +85 -0
- package/dist/metering/reconciliation-cron.test.d.ts +1 -0
- package/dist/metering/reconciliation-cron.test.js +162 -0
- package/dist/metering/reconciliation-repository.d.ts +27 -0
- package/dist/metering/reconciliation-repository.js +43 -0
- package/dist/metering/reconciliation-repository.test.d.ts +1 -0
- package/dist/metering/reconciliation-repository.test.js +160 -0
- package/dist/metering/types.d.ts +88 -0
- package/dist/metering/types.js +1 -0
- package/dist/metering/wal.d.ts +49 -0
- package/dist/metering/wal.js +124 -0
- package/dist/metering/wal.test.d.ts +1 -0
- package/dist/metering/wal.test.js +175 -0
- package/dist/middleware/csrf.d.ts +24 -0
- package/dist/middleware/csrf.js +80 -0
- package/dist/middleware/csrf.test.d.ts +1 -0
- package/dist/middleware/csrf.test.js +152 -0
- package/dist/middleware/drizzle-rate-limit-repository.d.ts +9 -0
- package/dist/middleware/drizzle-rate-limit-repository.js +52 -0
- package/dist/middleware/drizzle-rate-limit-repository.test.d.ts +1 -0
- package/dist/middleware/drizzle-rate-limit-repository.test.js +74 -0
- package/dist/middleware/get-client-ip.d.ts +22 -0
- package/dist/middleware/get-client-ip.js +51 -0
- package/dist/middleware/get-client-ip.test.d.ts +1 -0
- package/dist/middleware/get-client-ip.test.js +40 -0
- package/dist/middleware/index.d.ts +5 -0
- package/dist/middleware/index.js +4 -0
- package/dist/middleware/rate-limit-repository.d.ts +19 -0
- package/dist/middleware/rate-limit-repository.js +1 -0
- package/dist/middleware/rate-limit.d.ts +57 -0
- package/dist/middleware/rate-limit.js +109 -0
- package/dist/middleware/rate-limit.test.d.ts +1 -0
- package/dist/middleware/rate-limit.test.js +247 -0
- package/dist/security/credential-vault/audit-repository.d.ts +27 -0
- package/dist/security/credential-vault/audit-repository.js +42 -0
- package/dist/security/credential-vault/audit-repository.test.d.ts +1 -0
- package/dist/security/credential-vault/audit-repository.test.js +78 -0
- package/dist/security/credential-vault/credential-repository.d.ts +94 -0
- package/dist/security/credential-vault/credential-repository.js +145 -0
- package/dist/security/credential-vault/credential-repository.test.d.ts +1 -0
- package/dist/security/credential-vault/credential-repository.test.js +206 -0
- package/dist/security/credential-vault/index.d.ts +12 -0
- package/dist/security/credential-vault/index.js +6 -0
- package/dist/security/credential-vault/key-rotation.d.ts +18 -0
- package/dist/security/credential-vault/key-rotation.js +52 -0
- package/dist/security/credential-vault/key-rotation.test.d.ts +1 -0
- package/dist/security/credential-vault/key-rotation.test.js +95 -0
- package/dist/security/credential-vault/migrate-plaintext.d.ts +15 -0
- package/dist/security/credential-vault/migrate-plaintext.js +80 -0
- package/dist/security/credential-vault/migrate-plaintext.test.d.ts +1 -0
- package/dist/security/credential-vault/migrate-plaintext.test.js +111 -0
- package/dist/security/credential-vault/migration-check.d.ts +15 -0
- package/dist/security/credential-vault/migration-check.js +71 -0
- package/dist/security/credential-vault/migration-check.test.d.ts +1 -0
- package/dist/security/credential-vault/migration-check.test.js +457 -0
- package/dist/security/credential-vault/store.d.ts +106 -0
- package/dist/security/credential-vault/store.js +181 -0
- package/dist/security/credential-vault/store.test.d.ts +1 -0
- package/dist/security/credential-vault/store.test.js +482 -0
- package/dist/security/encryption.d.ts +22 -0
- package/dist/security/encryption.js +53 -0
- package/dist/security/encryption.test.d.ts +1 -0
- package/dist/security/encryption.test.js +95 -0
- package/dist/security/host-validation.d.ts +11 -0
- package/dist/security/host-validation.js +108 -0
- package/dist/security/host-validation.test.d.ts +1 -0
- package/dist/security/host-validation.test.js +106 -0
- package/dist/security/index.d.ts +11 -0
- package/dist/security/index.js +11 -0
- package/dist/security/key-audit.d.ts +16 -0
- package/dist/security/key-audit.js +35 -0
- package/dist/security/key-audit.test.d.ts +1 -0
- package/dist/security/key-audit.test.js +50 -0
- package/dist/security/key-injection.d.ts +28 -0
- package/dist/security/key-injection.js +57 -0
- package/dist/security/key-injection.test.d.ts +1 -0
- package/dist/security/key-injection.test.js +97 -0
- package/dist/security/key-validation.d.ts +16 -0
- package/dist/security/key-validation.js +78 -0
- package/dist/security/key-validation.test.d.ts +1 -0
- package/dist/security/key-validation.test.js +87 -0
- package/dist/security/redirect-allowlist.d.ts +6 -0
- package/dist/security/redirect-allowlist.js +36 -0
- package/dist/security/redirect-allowlist.test.d.ts +1 -0
- package/dist/security/redirect-allowlist.test.js +55 -0
- package/dist/security/tenant-keys/capability-settings-store.d.ts +22 -0
- package/dist/security/tenant-keys/capability-settings-store.js +33 -0
- package/dist/security/tenant-keys/capability-settings-store.test.d.ts +1 -0
- package/dist/security/tenant-keys/capability-settings-store.test.js +77 -0
- package/dist/security/tenant-keys/index.d.ts +10 -0
- package/dist/security/tenant-keys/index.js +5 -0
- package/dist/security/tenant-keys/key-resolution-repository.d.ts +15 -0
- package/dist/security/tenant-keys/key-resolution-repository.js +18 -0
- package/dist/security/tenant-keys/key-resolution-repository.test.d.ts +1 -0
- package/dist/security/tenant-keys/key-resolution-repository.test.js +72 -0
- package/dist/security/tenant-keys/key-resolution.d.ts +39 -0
- package/dist/security/tenant-keys/key-resolution.js +59 -0
- package/dist/security/tenant-keys/key-resolution.test.d.ts +1 -0
- package/dist/security/tenant-keys/key-resolution.test.js +97 -0
- package/dist/security/tenant-keys/org-key-resolution.d.ts +30 -0
- package/dist/security/tenant-keys/org-key-resolution.js +50 -0
- package/dist/security/tenant-keys/org-key-resolution.test.d.ts +1 -0
- package/dist/security/tenant-keys/org-key-resolution.test.js +103 -0
- package/dist/security/tenant-keys/tenant-key-repository.d.ts +36 -0
- package/dist/security/tenant-keys/tenant-key-repository.js +96 -0
- package/dist/security/tenant-keys/tenant-key-repository.test.d.ts +1 -0
- package/dist/security/tenant-keys/tenant-key-repository.test.js +114 -0
- package/dist/security/types.d.ts +35 -0
- package/dist/security/types.js +15 -0
- package/dist/tenancy/drizzle-org-repository.d.ts +40 -0
- package/dist/tenancy/drizzle-org-repository.js +126 -0
- package/dist/tenancy/index.d.ts +6 -0
- package/dist/tenancy/index.js +3 -0
- package/dist/tenancy/org-member-repository.d.ts +57 -0
- package/dist/tenancy/org-member-repository.js +99 -0
- package/dist/tenancy/org-repository.test.d.ts +1 -0
- package/dist/tenancy/org-repository.test.js +143 -0
- package/dist/tenancy/org-service.d.ts +70 -0
- package/dist/tenancy/org-service.js +223 -0
- package/dist/tenancy/org-service.test.d.ts +1 -0
- package/dist/tenancy/org-service.test.js +550 -0
- package/dist/test/db.d.ts +33 -0
- package/dist/test/db.js +65 -0
- package/dist/trpc/index.d.ts +1 -0
- package/dist/trpc/index.js +1 -0
- package/dist/trpc/init.d.ts +49 -0
- package/dist/trpc/init.js +108 -0
- package/dist/trpc/init.test.d.ts +1 -0
- package/dist/trpc/init.test.js +154 -0
- package/drizzle/migrations/0000_slippery_mandrill.sql +559 -0
- package/drizzle/migrations/meta/0000_snapshot.json +4374 -0
- package/drizzle/migrations/meta/_journal.json +13 -0
- package/drizzle.config.ts +41 -0
- package/package.json +64 -0
- package/src/admin/admin-audit-log-repository.ts +135 -0
- package/src/admin/audit-log.ts +111 -0
- package/src/admin/index.ts +6 -0
- package/src/admin/role-store.ts +134 -0
- package/src/auth/api-key-repository.test.ts +63 -0
- package/src/auth/api-key-repository.ts +46 -0
- package/src/auth/auth.test.ts +166 -0
- package/src/auth/better-auth.ts +216 -0
- package/src/auth/index.ts +520 -0
- package/src/auth/login-history-repository.test.ts +54 -0
- package/src/auth/login-history-repository.ts +28 -0
- package/src/auth/middleware.test.ts +264 -0
- package/src/auth/middleware.ts +117 -0
- package/src/auth/scoped-tokens.test.ts +362 -0
- package/src/auth/tenant-access.test.ts +69 -0
- package/src/auth/user-creator.test.ts +98 -0
- package/src/auth/user-creator.ts +54 -0
- package/src/auth/user-role-repository.test.ts +149 -0
- package/src/auth/user-role-repository.ts +67 -0
- package/src/billing/drizzle-webhook-seen-repository.ts +34 -0
- package/src/billing/index.ts +22 -0
- package/src/billing/payment-processor.test.ts +93 -0
- package/src/billing/payment-processor.ts +150 -0
- package/src/billing/payram/cents-credits-boundary.test.ts +84 -0
- package/src/billing/payram/charge-store.test.ts +84 -0
- package/src/billing/payram/charge-store.ts +109 -0
- package/src/billing/payram/checkout.test.ts +99 -0
- package/src/billing/payram/checkout.ts +40 -0
- package/src/billing/payram/client.test.ts +62 -0
- package/src/billing/payram/client.ts +21 -0
- package/src/billing/payram/index.ts +14 -0
- package/src/billing/payram/types.ts +44 -0
- package/src/billing/payram/webhook.test.ts +318 -0
- package/src/billing/payram/webhook.ts +97 -0
- package/src/billing/stripe/cents-credits-boundary.test.ts +70 -0
- package/src/billing/stripe/checkout.test.ts +186 -0
- package/src/billing/stripe/checkout.ts +82 -0
- package/src/billing/stripe/client.test.ts +64 -0
- package/src/billing/stripe/client.ts +39 -0
- package/src/billing/stripe/credit-prices.test.ts +114 -0
- package/src/billing/stripe/credit-prices.ts +113 -0
- package/src/billing/stripe/index.ts +14 -0
- package/src/billing/stripe/payment-methods-detach-all.test.ts +53 -0
- package/src/billing/stripe/payment-methods.test.ts +157 -0
- package/src/billing/stripe/payment-methods.ts +76 -0
- package/src/billing/stripe/portal.test.ts +63 -0
- package/src/billing/stripe/portal.ts +25 -0
- package/src/billing/stripe/setup-intent.test.ts +78 -0
- package/src/billing/stripe/setup-intent.ts +34 -0
- package/src/billing/stripe/stripe-payment-processor.test.ts +517 -0
- package/src/billing/stripe/stripe-payment-processor.ts +255 -0
- package/src/billing/stripe/tenant-store.test.ts +124 -0
- package/src/billing/stripe/tenant-store.ts +151 -0
- package/src/billing/stripe/types.ts +53 -0
- package/src/billing/webhook-seen-repository.ts +24 -0
- package/src/config/billing-env.test.ts +54 -0
- package/src/config/index.ts +44 -0
- package/src/config/logger.ts +12 -0
- package/src/config/provider-endpoints.ts +14 -0
- package/src/credits/auto-topup-charge.test.ts +292 -0
- package/src/credits/auto-topup-charge.ts +171 -0
- package/src/credits/auto-topup-event-log-repository.test.ts +99 -0
- package/src/credits/auto-topup-event-log-repository.ts +30 -0
- package/src/credits/auto-topup-schedule.test.ts +179 -0
- package/src/credits/auto-topup-schedule.ts +93 -0
- package/src/credits/auto-topup-settings-repository.test.ts +123 -0
- package/src/credits/auto-topup-settings-repository.ts +245 -0
- package/src/credits/auto-topup-usage.test.ts +220 -0
- package/src/credits/auto-topup-usage.ts +68 -0
- package/src/credits/credit-expiry-cron.test.ts +125 -0
- package/src/credits/credit-expiry-cron.ts +76 -0
- package/src/credits/credit-ledger-extra.test.ts +57 -0
- package/src/credits/credit-ledger.bench.ts +56 -0
- package/src/credits/credit-ledger.test.ts +276 -0
- package/src/credits/credit-ledger.ts +450 -0
- package/src/credits/credit-transaction-repository.test.ts +274 -0
- package/src/credits/credit-transaction-repository.ts +62 -0
- package/src/credits/credit.test.ts +234 -0
- package/src/credits/credit.ts +160 -0
- package/src/credits/dividend-cron.test.ts +158 -0
- package/src/credits/dividend-cron.ts +127 -0
- package/src/credits/dividend-repository.test.ts +223 -0
- package/src/credits/dividend-repository.ts +182 -0
- package/src/credits/index.ts +25 -0
- package/src/credits/repository-types.ts +33 -0
- package/src/credits/signup-grant.test.ts +63 -0
- package/src/credits/signup-grant.ts +44 -0
- package/src/credits/tenant-customer-repository.ts +28 -0
- package/src/db/auth-user-repository.ts +124 -0
- package/src/db/credit-column.ts +17 -0
- package/src/db/index.ts +21 -0
- package/src/db/schema/account-deletion-requests.ts +41 -0
- package/src/db/schema/account-export-requests.ts +24 -0
- package/src/db/schema/admin-audit.ts +26 -0
- package/src/db/schema/admin-users.ts +31 -0
- package/src/db/schema/affiliate-fraud.ts +23 -0
- package/src/db/schema/affiliate.ts +38 -0
- package/src/db/schema/coupon-codes.ts +22 -0
- package/src/db/schema/credit-auto-topup-settings.ts +32 -0
- package/src/db/schema/credit-auto-topup.ts +26 -0
- package/src/db/schema/credits.ts +44 -0
- package/src/db/schema/dividend-distributions.ts +24 -0
- package/src/db/schema/email-notifications.ts +26 -0
- package/src/db/schema/index.ts +33 -0
- package/src/db/schema/meter-events.ts +70 -0
- package/src/db/schema/notification-preferences.ts +19 -0
- package/src/db/schema/notification-queue.ts +45 -0
- package/src/db/schema/org-memberships.ts +20 -0
- package/src/db/schema/organization-members.ts +37 -0
- package/src/db/schema/payram.ts +26 -0
- package/src/db/schema/platform-api-keys.ts +25 -0
- package/src/db/schema/promotion-redemptions.ts +23 -0
- package/src/db/schema/promotions.ts +57 -0
- package/src/db/schema/provider-credentials.ts +41 -0
- package/src/db/schema/rate-limit-entries.ts +12 -0
- package/src/db/schema/secret-audit-log.ts +20 -0
- package/src/db/schema/session-usage.ts +24 -0
- package/src/db/schema/spending-limits.ts +9 -0
- package/src/db/schema/tenant-addons.ts +14 -0
- package/src/db/schema/tenant-api-keys.ts +26 -0
- package/src/db/schema/tenant-capability-settings.ts +17 -0
- package/src/db/schema/tenant-customers.ts +35 -0
- package/src/db/schema/tenants.ts +23 -0
- package/src/db/schema/user-roles.ts +23 -0
- package/src/db/schema/webhook-seen-events.ts +14 -0
- package/src/email/billing-emails.test.ts +198 -0
- package/src/email/billing-emails.ts +211 -0
- package/src/email/client.test.ts +149 -0
- package/src/email/client.ts +137 -0
- package/src/email/drizzle-billing-email-repository.test.ts +52 -0
- package/src/email/drizzle-billing-email-repository.ts +59 -0
- package/src/email/index.ts +57 -0
- package/src/email/notification-preferences-store.test.ts +102 -0
- package/src/email/notification-preferences-store.ts +90 -0
- package/src/email/notification-queue-store.test.ts +215 -0
- package/src/email/notification-queue-store.ts +127 -0
- package/src/email/notification-repository-types.ts +101 -0
- package/src/email/notification-service.test.ts +178 -0
- package/src/email/notification-service.ts +265 -0
- package/src/email/notification-templates.test.ts +261 -0
- package/src/email/notification-templates.ts +727 -0
- package/src/email/notification-worker.test.ts +189 -0
- package/src/email/notification-worker.ts +133 -0
- package/src/email/require-verified.ts +65 -0
- package/src/email/resend-adapter.test.ts +253 -0
- package/src/email/resend-adapter.ts +157 -0
- package/src/email/templates.test.ts +217 -0
- package/src/email/templates.ts +469 -0
- package/src/email/verification.test.ts +185 -0
- package/src/email/verification.ts +110 -0
- package/src/index.ts +51 -0
- package/src/metering/aggregator.test.ts +239 -0
- package/src/metering/aggregator.ts +160 -0
- package/src/metering/dlq.test.ts +134 -0
- package/src/metering/dlq.ts +102 -0
- package/src/metering/drizzle-usage-summary-repository.ts +167 -0
- package/src/metering/emitter.test.ts +202 -0
- package/src/metering/emitter.ts +227 -0
- package/src/metering/index.ts +21 -0
- package/src/metering/load-test.bench.ts +130 -0
- package/src/metering/meter-event-repository.ts +87 -0
- package/src/metering/meter-repositories.test.ts +491 -0
- package/src/metering/metering.test.ts +1317 -0
- package/src/metering/reconciliation-cron.test.ts +202 -0
- package/src/metering/reconciliation-cron.ts +134 -0
- package/src/metering/reconciliation-repository.test.ts +196 -0
- package/src/metering/reconciliation-repository.ts +83 -0
- package/src/metering/types.ts +93 -0
- package/src/metering/wal.test.ts +222 -0
- package/src/metering/wal.ts +139 -0
- package/src/middleware/csrf.test.ts +178 -0
- package/src/middleware/csrf.ts +101 -0
- package/src/middleware/drizzle-rate-limit-repository.test.ts +97 -0
- package/src/middleware/drizzle-rate-limit-repository.ts +57 -0
- package/src/middleware/get-client-ip.test.ts +49 -0
- package/src/middleware/get-client-ip.ts +62 -0
- package/src/middleware/index.ts +12 -0
- package/src/middleware/rate-limit-repository.ts +22 -0
- package/src/middleware/rate-limit.test.ts +338 -0
- package/src/middleware/rate-limit.ts +169 -0
- package/src/security/credential-vault/audit-repository.test.ts +91 -0
- package/src/security/credential-vault/audit-repository.ts +64 -0
- package/src/security/credential-vault/credential-repository.test.ts +264 -0
- package/src/security/credential-vault/credential-repository.ts +233 -0
- package/src/security/credential-vault/index.ts +26 -0
- package/src/security/credential-vault/key-rotation.test.ts +139 -0
- package/src/security/credential-vault/key-rotation.ts +70 -0
- package/src/security/credential-vault/migrate-plaintext.test.ts +138 -0
- package/src/security/credential-vault/migrate-plaintext.ts +101 -0
- package/src/security/credential-vault/migration-check.test.ts +533 -0
- package/src/security/credential-vault/migration-check.ts +88 -0
- package/src/security/credential-vault/store.test.ts +569 -0
- package/src/security/credential-vault/store.ts +284 -0
- package/src/security/encryption.test.ts +114 -0
- package/src/security/encryption.ts +65 -0
- package/src/security/host-validation.test.ts +136 -0
- package/src/security/host-validation.ts +116 -0
- package/src/security/index.ts +59 -0
- package/src/security/key-audit.test.ts +57 -0
- package/src/security/key-audit.ts +45 -0
- package/src/security/key-injection.test.ts +131 -0
- package/src/security/key-injection.ts +71 -0
- package/src/security/key-validation.test.ts +111 -0
- package/src/security/key-validation.ts +84 -0
- package/src/security/redirect-allowlist.test.ts +70 -0
- package/src/security/redirect-allowlist.ts +35 -0
- package/src/security/tenant-keys/capability-settings-store.test.ts +98 -0
- package/src/security/tenant-keys/capability-settings-store.ts +53 -0
- package/src/security/tenant-keys/index.ts +10 -0
- package/src/security/tenant-keys/key-resolution-repository.test.ts +95 -0
- package/src/security/tenant-keys/key-resolution-repository.ts +31 -0
- package/src/security/tenant-keys/key-resolution.test.ts +173 -0
- package/src/security/tenant-keys/key-resolution.ts +87 -0
- package/src/security/tenant-keys/org-key-resolution.test.ts +217 -0
- package/src/security/tenant-keys/org-key-resolution.ts +76 -0
- package/src/security/tenant-keys/tenant-key-repository.test.ts +143 -0
- package/src/security/tenant-keys/tenant-key-repository.ts +130 -0
- package/src/security/types.ts +43 -0
- package/src/tenancy/drizzle-org-repository.ts +169 -0
- package/src/tenancy/index.ts +6 -0
- package/src/tenancy/org-member-repository.ts +159 -0
- package/src/tenancy/org-repository.test.ts +172 -0
- package/src/tenancy/org-service.test.ts +634 -0
- package/src/tenancy/org-service.ts +290 -0
- package/src/test/db.ts +97 -0
- package/src/trpc/index.ts +11 -0
- package/src/trpc/init.test.ts +196 -0
- package/src/trpc/init.ts +138 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WOPR Credit price points and bonus tiers.
|
|
3
|
+
*
|
|
4
|
+
* One Stripe Product ("WOPR Credits") with 5 preset Price objects.
|
|
5
|
+
* Each price maps to a dollar amount and a credit value (with optional bonus).
|
|
6
|
+
*
|
|
7
|
+
* Price IDs are loaded from environment variables so they can differ
|
|
8
|
+
* between Stripe test/live modes.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* A preset credit purchase option.
|
|
12
|
+
*
|
|
13
|
+
* IMPORTANT — naming convention (WOP-1058):
|
|
14
|
+
* - amountCents: USD cents charged to Stripe (monetary — DO NOT rename to _credits).
|
|
15
|
+
* This value is passed to Stripe's checkout session and PaymentIntent APIs.
|
|
16
|
+
* - creditCents: USD cents worth of platform credits granted (includes bonus).
|
|
17
|
+
* Named _cents because the platform credit unit = 1 USD cent.
|
|
18
|
+
* Passed to Credit.fromCents() — NOT a raw Credit nanodollar value.
|
|
19
|
+
*/
|
|
20
|
+
export interface CreditPricePoint {
|
|
21
|
+
/** Human-readable label. */
|
|
22
|
+
label: string;
|
|
23
|
+
/** Amount charged in USD cents (Stripe monetary — DO NOT rename to _credits). */
|
|
24
|
+
amountCents: number;
|
|
25
|
+
/** Platform credits granted in USD cents (includes bonus — DO NOT rename to _credits). */
|
|
26
|
+
creditCents: number;
|
|
27
|
+
/** Bonus percentage (0 for no bonus). */
|
|
28
|
+
bonusPercent: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The 5 preset credit tiers.
|
|
32
|
+
*
|
|
33
|
+
* Bonus logic:
|
|
34
|
+
* $5 -> $5.00 credit (0% bonus)
|
|
35
|
+
* $10 -> $10.00 credit (0% bonus)
|
|
36
|
+
* $25 -> $25.50 credit (2% bonus)
|
|
37
|
+
* $50 -> $52.50 credit (5% bonus)
|
|
38
|
+
* $100 -> $110.00 credit (10% bonus)
|
|
39
|
+
*/
|
|
40
|
+
export declare const CREDIT_PRICE_POINTS: readonly CreditPricePoint[];
|
|
41
|
+
/** Mapping from Stripe Price ID -> CreditPricePoint. */
|
|
42
|
+
export type CreditPriceMap = ReadonlyMap<string, CreditPricePoint>;
|
|
43
|
+
/**
|
|
44
|
+
* Load credit price mappings from environment variables.
|
|
45
|
+
*
|
|
46
|
+
* Returns a Map from Stripe Price ID -> CreditPricePoint.
|
|
47
|
+
* Only includes entries where the env var is set.
|
|
48
|
+
*/
|
|
49
|
+
export declare function loadCreditPriceMap(): CreditPriceMap;
|
|
50
|
+
/**
|
|
51
|
+
* Get the credit amount (in cents) for a given purchase amount (in cents).
|
|
52
|
+
*
|
|
53
|
+
* Uses the bonus tiers to determine the credit value.
|
|
54
|
+
* Falls back to 1:1 if no matching tier is found.
|
|
55
|
+
*/
|
|
56
|
+
export declare function getCreditAmountForPurchase(amountCents: number): number;
|
|
57
|
+
/**
|
|
58
|
+
* Look up a CreditPricePoint by Stripe Price ID using the price map.
|
|
59
|
+
* Returns null if the price ID is not recognized.
|
|
60
|
+
*/
|
|
61
|
+
export declare function lookupCreditPrice(priceMap: CreditPriceMap, priceId: string): CreditPricePoint | null;
|
|
62
|
+
/** Get all configured Stripe Price IDs (for validation). */
|
|
63
|
+
export declare function getConfiguredPriceIds(): string[];
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WOPR Credit price points and bonus tiers.
|
|
3
|
+
*
|
|
4
|
+
* One Stripe Product ("WOPR Credits") with 5 preset Price objects.
|
|
5
|
+
* Each price maps to a dollar amount and a credit value (with optional bonus).
|
|
6
|
+
*
|
|
7
|
+
* Price IDs are loaded from environment variables so they can differ
|
|
8
|
+
* between Stripe test/live modes.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The 5 preset credit tiers.
|
|
12
|
+
*
|
|
13
|
+
* Bonus logic:
|
|
14
|
+
* $5 -> $5.00 credit (0% bonus)
|
|
15
|
+
* $10 -> $10.00 credit (0% bonus)
|
|
16
|
+
* $25 -> $25.50 credit (2% bonus)
|
|
17
|
+
* $50 -> $52.50 credit (5% bonus)
|
|
18
|
+
* $100 -> $110.00 credit (10% bonus)
|
|
19
|
+
*/
|
|
20
|
+
export const CREDIT_PRICE_POINTS = [
|
|
21
|
+
{ label: "$5", amountCents: 500, creditCents: 500, bonusPercent: 0 },
|
|
22
|
+
{ label: "$10", amountCents: 1000, creditCents: 1000, bonusPercent: 0 },
|
|
23
|
+
{ label: "$25", amountCents: 2500, creditCents: 2550, bonusPercent: 2 },
|
|
24
|
+
{ label: "$50", amountCents: 5000, creditCents: 5250, bonusPercent: 5 },
|
|
25
|
+
{ label: "$100", amountCents: 10000, creditCents: 11000, bonusPercent: 10 },
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* Map of env var name -> index into CREDIT_PRICE_POINTS.
|
|
29
|
+
* Each env var holds the Stripe Price ID for that tier.
|
|
30
|
+
*/
|
|
31
|
+
const PRICE_ENV_VARS = [
|
|
32
|
+
"STRIPE_CREDIT_PRICE_5",
|
|
33
|
+
"STRIPE_CREDIT_PRICE_10",
|
|
34
|
+
"STRIPE_CREDIT_PRICE_25",
|
|
35
|
+
"STRIPE_CREDIT_PRICE_50",
|
|
36
|
+
"STRIPE_CREDIT_PRICE_100",
|
|
37
|
+
];
|
|
38
|
+
/**
|
|
39
|
+
* Load credit price mappings from environment variables.
|
|
40
|
+
*
|
|
41
|
+
* Returns a Map from Stripe Price ID -> CreditPricePoint.
|
|
42
|
+
* Only includes entries where the env var is set.
|
|
43
|
+
*/
|
|
44
|
+
export function loadCreditPriceMap() {
|
|
45
|
+
const map = new Map();
|
|
46
|
+
for (let i = 0; i < PRICE_ENV_VARS.length; i++) {
|
|
47
|
+
const priceId = process.env[PRICE_ENV_VARS[i]];
|
|
48
|
+
if (priceId) {
|
|
49
|
+
map.set(priceId, CREDIT_PRICE_POINTS[i]);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return map;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get the credit amount (in cents) for a given purchase amount (in cents).
|
|
56
|
+
*
|
|
57
|
+
* Uses the bonus tiers to determine the credit value.
|
|
58
|
+
* Falls back to 1:1 if no matching tier is found.
|
|
59
|
+
*/
|
|
60
|
+
export function getCreditAmountForPurchase(amountCents) {
|
|
61
|
+
const tier = CREDIT_PRICE_POINTS.find((p) => p.amountCents === amountCents);
|
|
62
|
+
return tier ? tier.creditCents : amountCents;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Look up a CreditPricePoint by Stripe Price ID using the price map.
|
|
66
|
+
* Returns null if the price ID is not recognized.
|
|
67
|
+
*/
|
|
68
|
+
export function lookupCreditPrice(priceMap, priceId) {
|
|
69
|
+
return priceMap.get(priceId) ?? null;
|
|
70
|
+
}
|
|
71
|
+
/** Get all configured Stripe Price IDs (for validation). */
|
|
72
|
+
export function getConfiguredPriceIds() {
|
|
73
|
+
const ids = [];
|
|
74
|
+
for (const envVar of PRICE_ENV_VARS) {
|
|
75
|
+
const priceId = process.env[envVar];
|
|
76
|
+
if (priceId) {
|
|
77
|
+
ids.push(priceId);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return ids;
|
|
81
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { CREDIT_PRICE_POINTS, getConfiguredPriceIds, getCreditAmountForPurchase, loadCreditPriceMap, lookupCreditPrice, } from "./credit-prices.js";
|
|
3
|
+
describe("CREDIT_PRICE_POINTS", () => {
|
|
4
|
+
it("has 5 tiers", () => {
|
|
5
|
+
expect(CREDIT_PRICE_POINTS).toHaveLength(5);
|
|
6
|
+
});
|
|
7
|
+
it("tiers have correct bonus percentages", () => {
|
|
8
|
+
expect(CREDIT_PRICE_POINTS[0].bonusPercent).toBe(0);
|
|
9
|
+
expect(CREDIT_PRICE_POINTS[1].bonusPercent).toBe(0);
|
|
10
|
+
expect(CREDIT_PRICE_POINTS[2].bonusPercent).toBe(2);
|
|
11
|
+
expect(CREDIT_PRICE_POINTS[3].bonusPercent).toBe(5);
|
|
12
|
+
expect(CREDIT_PRICE_POINTS[4].bonusPercent).toBe(10);
|
|
13
|
+
});
|
|
14
|
+
it("credit amounts include bonus correctly", () => {
|
|
15
|
+
expect(CREDIT_PRICE_POINTS[0].creditCents).toBe(500);
|
|
16
|
+
expect(CREDIT_PRICE_POINTS[2].creditCents).toBe(2550);
|
|
17
|
+
expect(CREDIT_PRICE_POINTS[4].creditCents).toBe(11000);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe("getCreditAmountForPurchase", () => {
|
|
21
|
+
it("returns bonus credits for matching tier", () => {
|
|
22
|
+
expect(getCreditAmountForPurchase(2500)).toBe(2550);
|
|
23
|
+
expect(getCreditAmountForPurchase(5000)).toBe(5250);
|
|
24
|
+
expect(getCreditAmountForPurchase(10000)).toBe(11000);
|
|
25
|
+
});
|
|
26
|
+
it("returns 1:1 for non-matching amount", () => {
|
|
27
|
+
expect(getCreditAmountForPurchase(7777)).toBe(7777);
|
|
28
|
+
expect(getCreditAmountForPurchase(1)).toBe(1);
|
|
29
|
+
expect(getCreditAmountForPurchase(99999)).toBe(99999);
|
|
30
|
+
});
|
|
31
|
+
it("returns exact amount for tiers without bonus", () => {
|
|
32
|
+
expect(getCreditAmountForPurchase(500)).toBe(500);
|
|
33
|
+
expect(getCreditAmountForPurchase(1000)).toBe(1000);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe("loadCreditPriceMap", () => {
|
|
37
|
+
it("loads prices from environment variables", () => {
|
|
38
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_5", "price_5_test");
|
|
39
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_25", "price_25_test");
|
|
40
|
+
const map = loadCreditPriceMap();
|
|
41
|
+
expect(map.get("price_5_test")).toEqual(expect.objectContaining({ amountCents: 500, creditCents: 500 }));
|
|
42
|
+
expect(map.get("price_25_test")).toEqual(expect.objectContaining({ amountCents: 2500, creditCents: 2550 }));
|
|
43
|
+
vi.unstubAllEnvs();
|
|
44
|
+
});
|
|
45
|
+
it("skips unset environment variables", () => {
|
|
46
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_5", "");
|
|
47
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_10", "");
|
|
48
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_25", "");
|
|
49
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_50", "");
|
|
50
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_100", "");
|
|
51
|
+
const map = loadCreditPriceMap();
|
|
52
|
+
expect(map.size).toBe(0);
|
|
53
|
+
vi.unstubAllEnvs();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe("lookupCreditPrice", () => {
|
|
57
|
+
it("returns price point for known price ID", () => {
|
|
58
|
+
const priceMap = new Map([["price_abc", CREDIT_PRICE_POINTS[2]]]);
|
|
59
|
+
const point = lookupCreditPrice(priceMap, "price_abc");
|
|
60
|
+
expect(point).not.toBeNull();
|
|
61
|
+
expect(point?.amountCents).toBe(2500);
|
|
62
|
+
});
|
|
63
|
+
it("returns null for unknown price ID", () => {
|
|
64
|
+
const priceMap = new Map();
|
|
65
|
+
expect(lookupCreditPrice(priceMap, "price_unknown")).toBeNull();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe("getConfiguredPriceIds", () => {
|
|
69
|
+
it("returns configured price IDs from env", () => {
|
|
70
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_5", "price_5_id");
|
|
71
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_100", "price_100_id");
|
|
72
|
+
const ids = getConfiguredPriceIds();
|
|
73
|
+
expect(ids).toContain("price_5_id");
|
|
74
|
+
expect(ids).toContain("price_100_id");
|
|
75
|
+
vi.unstubAllEnvs();
|
|
76
|
+
});
|
|
77
|
+
it("returns empty array when no prices configured", () => {
|
|
78
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_5", "");
|
|
79
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_10", "");
|
|
80
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_25", "");
|
|
81
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_50", "");
|
|
82
|
+
vi.stubEnv("STRIPE_CREDIT_PRICE_100", "");
|
|
83
|
+
const ids = getConfiguredPriceIds();
|
|
84
|
+
expect(ids).toHaveLength(0);
|
|
85
|
+
vi.unstubAllEnvs();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { createCreditCheckoutSession, createVpsCheckoutSession } from "./checkout.js";
|
|
2
|
+
export { createStripeClient, loadStripeConfig } from "./client.js";
|
|
3
|
+
export type { CreditPricePoint, CreditPriceMap } from "./credit-prices.js";
|
|
4
|
+
export { CREDIT_PRICE_POINTS, loadCreditPriceMap, getCreditAmountForPurchase, lookupCreditPrice, getConfiguredPriceIds } from "./credit-prices.js";
|
|
5
|
+
export { detachPaymentMethod, detachAllPaymentMethods } from "./payment-methods.js";
|
|
6
|
+
export type { DetachPaymentMethodOpts } from "./payment-methods.js";
|
|
7
|
+
export { createPortalSession } from "./portal.js";
|
|
8
|
+
export type { SetupIntentOpts } from "./setup-intent.js";
|
|
9
|
+
export { createSetupIntent } from "./setup-intent.js";
|
|
10
|
+
export type { StripeWebhookHandlerResult, StripePaymentProcessorDeps } from "./stripe-payment-processor.js";
|
|
11
|
+
export { StripePaymentProcessor } from "./stripe-payment-processor.js";
|
|
12
|
+
export type { ITenantCustomerRepository } from "./tenant-store.js";
|
|
13
|
+
export { DrizzleTenantCustomerRepository, TenantCustomerRepository } from "./tenant-store.js";
|
|
14
|
+
export type { TenantCustomerRow, CreditCheckoutOpts, PortalSessionOpts, VpsCheckoutOpts, StripeBillingConfig } from "./types.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createCreditCheckoutSession, createVpsCheckoutSession } from "./checkout.js";
|
|
2
|
+
export { createStripeClient, loadStripeConfig } from "./client.js";
|
|
3
|
+
export { CREDIT_PRICE_POINTS, loadCreditPriceMap, getCreditAmountForPurchase, lookupCreditPrice, getConfiguredPriceIds } from "./credit-prices.js";
|
|
4
|
+
export { detachPaymentMethod, detachAllPaymentMethods } from "./payment-methods.js";
|
|
5
|
+
export { createPortalSession } from "./portal.js";
|
|
6
|
+
export { createSetupIntent } from "./setup-intent.js";
|
|
7
|
+
export { StripePaymentProcessor } from "./stripe-payment-processor.js";
|
|
8
|
+
export { DrizzleTenantCustomerRepository, TenantCustomerRepository } from "./tenant-store.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { detachAllPaymentMethods } from "./payment-methods.js";
|
|
3
|
+
function mockStripe(paymentMethods = []) {
|
|
4
|
+
return {
|
|
5
|
+
customers: {
|
|
6
|
+
listPaymentMethods: vi.fn().mockResolvedValue({ data: paymentMethods }),
|
|
7
|
+
},
|
|
8
|
+
paymentMethods: {
|
|
9
|
+
detach: vi.fn().mockResolvedValue({}),
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function mockTenantStore(customerId = "cus_123") {
|
|
14
|
+
return {
|
|
15
|
+
getByTenant: vi.fn().mockResolvedValue(customerId ? { tenant: "t1", processor_customer_id: customerId } : null),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
describe("detachAllPaymentMethods", () => {
|
|
19
|
+
it("detaches all payment methods for a tenant", async () => {
|
|
20
|
+
const stripe = mockStripe([{ id: "pm_1" }, { id: "pm_2" }]);
|
|
21
|
+
const tenantRepo = mockTenantStore();
|
|
22
|
+
const count = await detachAllPaymentMethods(stripe, tenantRepo, "t1");
|
|
23
|
+
expect(count).toBe(2);
|
|
24
|
+
expect(stripe.paymentMethods.detach).toHaveBeenCalledWith("pm_1");
|
|
25
|
+
expect(stripe.paymentMethods.detach).toHaveBeenCalledWith("pm_2");
|
|
26
|
+
});
|
|
27
|
+
it("returns 0 when tenant has no Stripe customer", async () => {
|
|
28
|
+
const stripe = mockStripe();
|
|
29
|
+
const tenantRepo = mockTenantStore(null);
|
|
30
|
+
const count = await detachAllPaymentMethods(stripe, tenantRepo, "t1");
|
|
31
|
+
expect(count).toBe(0);
|
|
32
|
+
expect(stripe.paymentMethods.detach).not.toHaveBeenCalled();
|
|
33
|
+
});
|
|
34
|
+
it("returns 0 when tenant has no payment methods", async () => {
|
|
35
|
+
const stripe = mockStripe([]);
|
|
36
|
+
const tenantRepo = mockTenantStore();
|
|
37
|
+
const count = await detachAllPaymentMethods(stripe, tenantRepo, "t1");
|
|
38
|
+
expect(count).toBe(0);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type Stripe from "stripe";
|
|
2
|
+
import type { ITenantCustomerRepository, TenantCustomerRepository } from "./tenant-store.js";
|
|
3
|
+
export declare class PaymentMethodOwnershipError extends Error {
|
|
4
|
+
readonly code: "PAYMENT_METHOD_NOT_OWNED";
|
|
5
|
+
constructor();
|
|
6
|
+
}
|
|
7
|
+
export interface DetachPaymentMethodOpts {
|
|
8
|
+
tenant: string;
|
|
9
|
+
paymentMethodId: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Detach a payment method from a Stripe customer.
|
|
13
|
+
*
|
|
14
|
+
* Validates the payment method belongs to the tenant's Stripe customer
|
|
15
|
+
* before detaching to prevent cross-tenant attacks.
|
|
16
|
+
*/
|
|
17
|
+
export declare function detachPaymentMethod(stripe: Stripe, tenantRepo: TenantCustomerRepository, opts: DetachPaymentMethodOpts): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Detach ALL payment methods from a tenant's Stripe customer.
|
|
20
|
+
*
|
|
21
|
+
* Used during ban cascade to prevent any future charges.
|
|
22
|
+
* Returns the number of payment methods detached.
|
|
23
|
+
* Returns 0 (does not throw) if tenant has no Stripe customer or no methods.
|
|
24
|
+
*/
|
|
25
|
+
export declare function detachAllPaymentMethods(stripe: Stripe, tenantRepo: ITenantCustomerRepository, tenantId: string): Promise<number>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export class PaymentMethodOwnershipError extends Error {
|
|
2
|
+
code = "PAYMENT_METHOD_NOT_OWNED";
|
|
3
|
+
constructor() {
|
|
4
|
+
super("Payment method does not belong to this tenant");
|
|
5
|
+
this.name = "PaymentMethodOwnershipError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Detach a payment method from a Stripe customer.
|
|
10
|
+
*
|
|
11
|
+
* Validates the payment method belongs to the tenant's Stripe customer
|
|
12
|
+
* before detaching to prevent cross-tenant attacks.
|
|
13
|
+
*/
|
|
14
|
+
export async function detachPaymentMethod(stripe, tenantRepo, opts) {
|
|
15
|
+
const mapping = await tenantRepo.getByTenant(opts.tenant);
|
|
16
|
+
if (!mapping) {
|
|
17
|
+
throw new Error(`No Stripe customer found for tenant: ${opts.tenant}`);
|
|
18
|
+
}
|
|
19
|
+
// Retrieve the payment method to verify it belongs to this customer
|
|
20
|
+
const pm = await stripe.paymentMethods.retrieve(opts.paymentMethodId);
|
|
21
|
+
if (!pm.customer || pm.customer !== mapping.processor_customer_id) {
|
|
22
|
+
throw new PaymentMethodOwnershipError();
|
|
23
|
+
}
|
|
24
|
+
await stripe.paymentMethods.detach(opts.paymentMethodId);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Detach ALL payment methods from a tenant's Stripe customer.
|
|
28
|
+
*
|
|
29
|
+
* Used during ban cascade to prevent any future charges.
|
|
30
|
+
* Returns the number of payment methods detached.
|
|
31
|
+
* Returns 0 (does not throw) if tenant has no Stripe customer or no methods.
|
|
32
|
+
*/
|
|
33
|
+
export async function detachAllPaymentMethods(stripe, tenantRepo, tenantId) {
|
|
34
|
+
const mapping = await tenantRepo.getByTenant(tenantId);
|
|
35
|
+
if (!mapping)
|
|
36
|
+
return 0;
|
|
37
|
+
let detached = 0;
|
|
38
|
+
let startingAfter;
|
|
39
|
+
while (true) {
|
|
40
|
+
const methods = await stripe.customers.listPaymentMethods(mapping.processor_customer_id, {
|
|
41
|
+
limit: 100,
|
|
42
|
+
...(startingAfter ? { starting_after: startingAfter } : {}),
|
|
43
|
+
});
|
|
44
|
+
for (const pm of methods.data) {
|
|
45
|
+
await stripe.paymentMethods.detach(pm.id);
|
|
46
|
+
detached++;
|
|
47
|
+
}
|
|
48
|
+
if (!methods.has_more)
|
|
49
|
+
break;
|
|
50
|
+
startingAfter = methods.data[methods.data.length - 1].id;
|
|
51
|
+
}
|
|
52
|
+
return detached;
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { beginTestTransaction, createTestDb, endTestTransaction, rollbackTestTransaction } from "../../test/db.js";
|
|
3
|
+
import { detachAllPaymentMethods, detachPaymentMethod } from "./payment-methods.js";
|
|
4
|
+
import { TenantCustomerRepository } from "./tenant-store.js";
|
|
5
|
+
function mockStripe(overrides = {}) {
|
|
6
|
+
return {
|
|
7
|
+
paymentMethods: {
|
|
8
|
+
retrieve: overrides.paymentMethodRetrieve ??
|
|
9
|
+
vi.fn().mockResolvedValue({
|
|
10
|
+
id: "pm_test_123",
|
|
11
|
+
customer: "cus_abc123",
|
|
12
|
+
}),
|
|
13
|
+
detach: overrides.paymentMethodDetach ?? vi.fn().mockResolvedValue({ id: "pm_test_123" }),
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
// TOP OF FILE - shared across ALL describes
|
|
18
|
+
let pool;
|
|
19
|
+
let db;
|
|
20
|
+
beforeAll(async () => {
|
|
21
|
+
({ db, pool } = await createTestDb());
|
|
22
|
+
await beginTestTransaction(pool);
|
|
23
|
+
});
|
|
24
|
+
afterAll(async () => {
|
|
25
|
+
await endTestTransaction(pool);
|
|
26
|
+
await pool.close();
|
|
27
|
+
});
|
|
28
|
+
describe("detachPaymentMethod", () => {
|
|
29
|
+
let store;
|
|
30
|
+
beforeEach(async () => {
|
|
31
|
+
await rollbackTestTransaction(pool);
|
|
32
|
+
store = new TenantCustomerRepository(db);
|
|
33
|
+
});
|
|
34
|
+
it("calls stripe.paymentMethods.detach with the correct ID", async () => {
|
|
35
|
+
await store.upsert({ tenant: "t-1", processorCustomerId: "cus_abc123" });
|
|
36
|
+
const stripe = mockStripe();
|
|
37
|
+
await detachPaymentMethod(stripe, store, {
|
|
38
|
+
tenant: "t-1",
|
|
39
|
+
paymentMethodId: "pm_test_123",
|
|
40
|
+
});
|
|
41
|
+
expect(stripe.paymentMethods.retrieve).toHaveBeenCalledWith("pm_test_123");
|
|
42
|
+
expect(stripe.paymentMethods.detach).toHaveBeenCalledWith("pm_test_123");
|
|
43
|
+
});
|
|
44
|
+
it("throws when tenant has no Stripe customer mapping", async () => {
|
|
45
|
+
const stripe = mockStripe();
|
|
46
|
+
await expect(detachPaymentMethod(stripe, store, {
|
|
47
|
+
tenant: "t-unknown",
|
|
48
|
+
paymentMethodId: "pm_test_123",
|
|
49
|
+
})).rejects.toThrow("No Stripe customer found for tenant: t-unknown");
|
|
50
|
+
});
|
|
51
|
+
it("throws when payment method belongs to a different customer (cross-tenant guard)", async () => {
|
|
52
|
+
await store.upsert({ tenant: "t-1", processorCustomerId: "cus_abc123" });
|
|
53
|
+
const paymentMethodRetrieve = vi.fn().mockResolvedValue({
|
|
54
|
+
id: "pm_other",
|
|
55
|
+
customer: "cus_other_customer",
|
|
56
|
+
});
|
|
57
|
+
const stripe = mockStripe({ paymentMethodRetrieve });
|
|
58
|
+
await expect(detachPaymentMethod(stripe, store, {
|
|
59
|
+
tenant: "t-1",
|
|
60
|
+
paymentMethodId: "pm_other",
|
|
61
|
+
})).rejects.toThrow("Payment method does not belong to this tenant");
|
|
62
|
+
expect(stripe.paymentMethods.detach).not.toHaveBeenCalled();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe("detachAllPaymentMethods", () => {
|
|
66
|
+
let store;
|
|
67
|
+
beforeEach(async () => {
|
|
68
|
+
await rollbackTestTransaction(pool);
|
|
69
|
+
store = new TenantCustomerRepository(db);
|
|
70
|
+
});
|
|
71
|
+
it("returns 0 when tenant has no Stripe customer mapping", async () => {
|
|
72
|
+
const stripe = {
|
|
73
|
+
customers: { listPaymentMethods: vi.fn() },
|
|
74
|
+
paymentMethods: { detach: vi.fn() },
|
|
75
|
+
};
|
|
76
|
+
const result = await detachAllPaymentMethods(stripe, store, "t-unknown");
|
|
77
|
+
expect(result).toBe(0);
|
|
78
|
+
expect(stripe.customers.listPaymentMethods).not.toHaveBeenCalled();
|
|
79
|
+
});
|
|
80
|
+
it("detaches all payment methods on a single page", async () => {
|
|
81
|
+
await store.upsert({ tenant: "t-1", processorCustomerId: "cus_abc" });
|
|
82
|
+
const listPaymentMethods = vi.fn().mockResolvedValue({
|
|
83
|
+
data: [{ id: "pm_1" }, { id: "pm_2" }],
|
|
84
|
+
has_more: false,
|
|
85
|
+
});
|
|
86
|
+
const detach = vi.fn().mockResolvedValue({});
|
|
87
|
+
const stripe = {
|
|
88
|
+
customers: { listPaymentMethods },
|
|
89
|
+
paymentMethods: { detach },
|
|
90
|
+
};
|
|
91
|
+
const result = await detachAllPaymentMethods(stripe, store, "t-1");
|
|
92
|
+
expect(result).toBe(2);
|
|
93
|
+
expect(detach).toHaveBeenCalledWith("pm_1");
|
|
94
|
+
expect(detach).toHaveBeenCalledWith("pm_2");
|
|
95
|
+
expect(listPaymentMethods).toHaveBeenCalledTimes(1);
|
|
96
|
+
});
|
|
97
|
+
it("paginates when has_more is true", async () => {
|
|
98
|
+
await store.upsert({ tenant: "t-1", processorCustomerId: "cus_abc" });
|
|
99
|
+
const listPaymentMethods = vi
|
|
100
|
+
.fn()
|
|
101
|
+
.mockResolvedValueOnce({
|
|
102
|
+
data: [{ id: "pm_1" }, { id: "pm_2" }],
|
|
103
|
+
has_more: true,
|
|
104
|
+
})
|
|
105
|
+
.mockResolvedValueOnce({
|
|
106
|
+
data: [{ id: "pm_3" }],
|
|
107
|
+
has_more: false,
|
|
108
|
+
});
|
|
109
|
+
const detach = vi.fn().mockResolvedValue({});
|
|
110
|
+
const stripe = {
|
|
111
|
+
customers: { listPaymentMethods },
|
|
112
|
+
paymentMethods: { detach },
|
|
113
|
+
};
|
|
114
|
+
const result = await detachAllPaymentMethods(stripe, store, "t-1");
|
|
115
|
+
expect(result).toBe(3);
|
|
116
|
+
expect(listPaymentMethods).toHaveBeenCalledTimes(2);
|
|
117
|
+
expect(listPaymentMethods).toHaveBeenNthCalledWith(2, "cus_abc", { limit: 100, starting_after: "pm_2" });
|
|
118
|
+
expect(detach).toHaveBeenCalledWith("pm_1");
|
|
119
|
+
expect(detach).toHaveBeenCalledWith("pm_2");
|
|
120
|
+
expect(detach).toHaveBeenCalledWith("pm_3");
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type Stripe from "stripe";
|
|
2
|
+
import type { TenantCustomerRepository } from "./tenant-store.js";
|
|
3
|
+
import type { PortalSessionOpts } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Create a Stripe Customer Portal session so the user can manage their billing.
|
|
6
|
+
*
|
|
7
|
+
* Users can update payment methods, view invoices, cancel subscriptions, etc.
|
|
8
|
+
* All via Stripe's hosted UI -- zero billing UI code in WOPR.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createPortalSession(stripe: Stripe, tenantRepo: TenantCustomerRepository, opts: PortalSessionOpts): Promise<Stripe.BillingPortal.Session>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a Stripe Customer Portal session so the user can manage their billing.
|
|
3
|
+
*
|
|
4
|
+
* Users can update payment methods, view invoices, cancel subscriptions, etc.
|
|
5
|
+
* All via Stripe's hosted UI -- zero billing UI code in WOPR.
|
|
6
|
+
*/
|
|
7
|
+
export async function createPortalSession(stripe, tenantRepo, opts) {
|
|
8
|
+
const mapping = await tenantRepo.getByTenant(opts.tenant);
|
|
9
|
+
if (!mapping) {
|
|
10
|
+
throw new Error(`No Stripe customer found for tenant: ${opts.tenant}`);
|
|
11
|
+
}
|
|
12
|
+
return stripe.billingPortal.sessions.create({
|
|
13
|
+
customer: mapping.processor_customer_id,
|
|
14
|
+
return_url: opts.returnUrl,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { createPortalSession } from "./portal.js";
|
|
3
|
+
describe("createPortalSession", () => {
|
|
4
|
+
function mockStripe(portalResult = { url: "https://billing.stripe.com/session_xyz" }) {
|
|
5
|
+
return {
|
|
6
|
+
billingPortal: {
|
|
7
|
+
sessions: {
|
|
8
|
+
create: vi.fn().mockResolvedValue(portalResult),
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function mockTenantStore(mapping = null) {
|
|
14
|
+
return {
|
|
15
|
+
getByTenant: vi.fn().mockReturnValue(mapping),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
it("creates a portal session for an existing customer", async () => {
|
|
19
|
+
const stripe = mockStripe();
|
|
20
|
+
const store = mockTenantStore({ processor_customer_id: "cus_abc" });
|
|
21
|
+
const session = await createPortalSession(stripe, store, {
|
|
22
|
+
tenant: "t-1",
|
|
23
|
+
returnUrl: "https://example.com/billing",
|
|
24
|
+
});
|
|
25
|
+
expect(session.url).toBe("https://billing.stripe.com/session_xyz");
|
|
26
|
+
expect(stripe.billingPortal.sessions.create).toHaveBeenCalledWith({
|
|
27
|
+
customer: "cus_abc",
|
|
28
|
+
return_url: "https://example.com/billing",
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
it("throws when no Stripe customer mapping exists", async () => {
|
|
32
|
+
const stripe = mockStripe();
|
|
33
|
+
const store = mockTenantStore(null);
|
|
34
|
+
await expect(createPortalSession(stripe, store, {
|
|
35
|
+
tenant: "unknown-tenant",
|
|
36
|
+
returnUrl: "https://example.com/billing",
|
|
37
|
+
})).rejects.toThrow("No Stripe customer found for tenant: unknown-tenant");
|
|
38
|
+
});
|
|
39
|
+
it("propagates Stripe API errors", async () => {
|
|
40
|
+
const stripe = mockStripe();
|
|
41
|
+
stripe.billingPortal.sessions.create.mockRejectedValue(new Error("Portal API error"));
|
|
42
|
+
const store = mockTenantStore({ processor_customer_id: "cus_abc" });
|
|
43
|
+
await expect(createPortalSession(stripe, store, {
|
|
44
|
+
tenant: "t-1",
|
|
45
|
+
returnUrl: "https://example.com/billing",
|
|
46
|
+
})).rejects.toThrow("Portal API error");
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type Stripe from "stripe";
|
|
2
|
+
import type { TenantCustomerRepository } from "./tenant-store.js";
|
|
3
|
+
export interface SetupIntentOpts {
|
|
4
|
+
tenant: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Create a Stripe SetupIntent so the user can save a payment method.
|
|
8
|
+
*
|
|
9
|
+
* Requires an existing Stripe customer (created during credit checkout).
|
|
10
|
+
* Returns the client_secret for use with Stripe Elements on the frontend.
|
|
11
|
+
*
|
|
12
|
+
* Note: payment_method_types is omitted to allow dynamic payment methods
|
|
13
|
+
* configured in the Stripe Dashboard. This enables the best payment options
|
|
14
|
+
* for each user's location and preferences.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createSetupIntent(stripe: Stripe, tenantRepo: TenantCustomerRepository, opts: SetupIntentOpts): Promise<Stripe.SetupIntent>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a Stripe SetupIntent so the user can save a payment method.
|
|
3
|
+
*
|
|
4
|
+
* Requires an existing Stripe customer (created during credit checkout).
|
|
5
|
+
* Returns the client_secret for use with Stripe Elements on the frontend.
|
|
6
|
+
*
|
|
7
|
+
* Note: payment_method_types is omitted to allow dynamic payment methods
|
|
8
|
+
* configured in the Stripe Dashboard. This enables the best payment options
|
|
9
|
+
* for each user's location and preferences.
|
|
10
|
+
*/
|
|
11
|
+
export async function createSetupIntent(stripe, tenantRepo, opts) {
|
|
12
|
+
const mapping = await tenantRepo.getByTenant(opts.tenant);
|
|
13
|
+
if (!mapping) {
|
|
14
|
+
throw new Error(`No Stripe customer found for tenant: ${opts.tenant}`);
|
|
15
|
+
}
|
|
16
|
+
return stripe.setupIntents.create({
|
|
17
|
+
customer: mapping.processor_customer_id,
|
|
18
|
+
metadata: {
|
|
19
|
+
wopr_tenant: opts.tenant,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|