@omega.js/backend 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/LICENSE +98 -0
- package/README.md +1053 -0
- package/bin/omega +2 -0
- package/bin/omega-backend +2 -0
- package/bin/omg +2 -0
- package/cli.js +3 -0
- package/dist/cli/command-table.js +268 -0
- package/dist/cli/commands/auth.js +259 -0
- package/dist/cli/commands/base-command.js +451 -0
- package/dist/cli/commands/build.js +27 -0
- package/dist/cli/commands/clean.js +13 -0
- package/dist/cli/commands/clear.js +11 -0
- package/dist/cli/commands/cwd.js +9 -0
- package/dist/cli/commands/deploy.js +205 -0
- package/dist/cli/commands/emulator-orphans.js +440 -0
- package/dist/cli/commands/emulator.js +1829 -0
- package/dist/cli/commands/firebase-init.js +101 -0
- package/dist/cli/commands/firestore.js +261 -0
- package/dist/cli/commands/indexes.js +51 -0
- package/dist/cli/commands/install.js +151 -0
- package/dist/cli/commands/logs.js +358 -0
- package/dist/cli/commands/mcp.js +41 -0
- package/dist/cli/commands/migrate-markers.js +176 -0
- package/dist/cli/commands/migrate-rules.js +90 -0
- package/dist/cli/commands/migrate.js +59 -0
- package/dist/cli/commands/serve.js +208 -0
- package/dist/cli/commands/setup-tests/base-test.js +174 -0
- package/dist/cli/commands/setup-tests/emulator-config.js +109 -0
- package/dist/cli/commands/setup-tests/env-runtime-config-deprecated.js +47 -0
- package/dist/cli/commands/setup-tests/firebase-admin.js +49 -0
- package/dist/cli/commands/setup-tests/firebase-auth.js +26 -0
- package/dist/cli/commands/setup-tests/firebase-cli.js +26 -0
- package/dist/cli/commands/setup-tests/firebase-functions.js +49 -0
- package/dist/cli/commands/setup-tests/firestore-indexes-file.js +72 -0
- package/dist/cli/commands/setup-tests/firestore-indexes-in-json.js +20 -0
- package/dist/cli/commands/setup-tests/firestore-indexes-required.js +108 -0
- package/dist/cli/commands/setup-tests/firestore-indexes-synced.js +236 -0
- package/dist/cli/commands/setup-tests/firestore-rules-file.js +115 -0
- package/dist/cli/commands/setup-tests/firestore-rules-in-json.js +49 -0
- package/dist/cli/commands/setup-tests/functions-package.js +44 -0
- package/dist/cli/commands/setup-tests/gcloud-cli.js +26 -0
- package/dist/cli/commands/setup-tests/gitignore.js +122 -0
- package/dist/cli/commands/setup-tests/helpers/merge-line-files.js +28 -0
- package/dist/cli/commands/setup-tests/helpers/required-indexes.js +109 -0
- package/dist/cli/commands/setup-tests/helpers/seed-campaigns.js +246 -0
- package/dist/cli/commands/setup-tests/helpers.js +72 -0
- package/dist/cli/commands/setup-tests/hosting-folder.js +23 -0
- package/dist/cli/commands/setup-tests/hosting-rewrites.js +52 -0
- package/dist/cli/commands/setup-tests/index.js +111 -0
- package/dist/cli/commands/setup-tests/is-firebase-project.js +21 -0
- package/dist/cli/commands/setup-tests/java-installed.js +27 -0
- package/dist/cli/commands/setup-tests/legacy-tests-cleanup.js +43 -0
- package/dist/cli/commands/setup-tests/marketing-campaigns-seeded.js +230 -0
- package/dist/cli/commands/setup-tests/node-version.js +60 -0
- package/dist/cli/commands/setup-tests/npm-project-scripts.js +42 -0
- package/dist/cli/commands/setup-tests/nvmrc-version.js +36 -0
- package/dist/cli/commands/setup-tests/omega-backend.js +42 -0
- package/dist/cli/commands/setup-tests/omega-config.js +84 -0
- package/dist/cli/commands/setup-tests/project-directories.js +34 -0
- package/dist/cli/commands/setup-tests/project-id-consistency.js +201 -0
- package/dist/cli/commands/setup-tests/public-html-files.js +22 -0
- package/dist/cli/commands/setup-tests/realtime-rules-file.js +69 -0
- package/dist/cli/commands/setup-tests/realtime-rules-in-json.js +20 -0
- package/dist/cli/commands/setup-tests/remoteconfig-template-file.js +32 -0
- package/dist/cli/commands/setup-tests/remoteconfig-template-in-json.js +31 -0
- package/dist/cli/commands/setup-tests/service-account.js +86 -0
- package/dist/cli/commands/setup-tests/storage-lifecycle-policy.js +81 -0
- package/dist/cli/commands/setup-tests/storage-rules-file.js +32 -0
- package/dist/cli/commands/setup-tests/storage-rules-in-json.js +20 -0
- package/dist/cli/commands/stripe.js +14 -0
- package/dist/cli/commands/test-lanes/stripe-live.js +440 -0
- package/dist/cli/commands/test.js +976 -0
- package/dist/cli/commands/update.js +27 -0
- package/dist/cli/commands/version.js +10 -0
- package/dist/cli/commands/watch.js +196 -0
- package/dist/cli/flags.js +13 -0
- package/dist/cli/index.js +173 -0
- package/dist/cli/run.js +33 -0
- package/dist/cli/utils/attach-log-file.js +4 -0
- package/dist/cli/utils/compile-rules.js +1155 -0
- package/dist/cli/utils/ensure-target.js +314 -0
- package/dist/cli/utils/project-type.js +140 -0
- package/dist/cli/utils/public-files.js +49 -0
- package/dist/cli/utils/safe-install.js +4 -0
- package/dist/cli/utils/spawn-shell.js +94 -0
- package/dist/cli/utils/stage-functions.js +338 -0
- package/dist/cli/utils/stage-local-packages.js +210 -0
- package/dist/cli/utils/target-checks.js +161 -0
- package/dist/cli/utils/target.js +24 -0
- package/dist/cli/utils/ui.js +292 -0
- package/dist/defaults/AGENTS.md +125 -0
- package/dist/defaults/CHANGELOG.md +15 -0
- package/dist/defaults/CLAUDE.md +1 -0
- package/dist/defaults/_.gitignore +65 -0
- package/dist/defaults/docs/README.md +17 -0
- package/dist/defaults/test/README.md +65 -0
- package/dist/defaults/test/_init.js +14 -0
- package/dist/defaults/test/helpers/connect-trap.js +72 -0
- package/dist/defaults/test/unit/registration.test.js +143 -0
- package/dist/defaults/test/unit/rules-posture.test.js +101 -0
- package/dist/defaults/test/unit/socket-free.test.js +43 -0
- package/dist/manager/events/auth/before-create.js +89 -0
- package/dist/manager/events/auth/before-signin.js +76 -0
- package/dist/manager/events/auth/on-create.js +112 -0
- package/dist/manager/events/auth/on-delete.js +141 -0
- package/dist/manager/events/auth/utils.js +93 -0
- package/dist/manager/events/cron/daily/blog-auto-publisher.js +287 -0
- package/dist/manager/events/cron/daily/data-requests.js +57 -0
- package/dist/manager/events/cron/daily/expire-paypal-cancellations.js +191 -0
- package/dist/manager/events/cron/daily/marketing-prune.js +486 -0
- package/dist/manager/events/cron/daily/reset-usage.js +188 -0
- package/dist/manager/events/cron/daily/trial-lapse-sweep.js +355 -0
- package/dist/manager/events/cron/daily.js +3 -0
- package/dist/manager/events/cron/frequent/abandoned-carts.js +204 -0
- package/dist/manager/events/cron/frequent/email-queue.js +71 -0
- package/dist/manager/events/cron/frequent/marketing-campaigns.js +463 -0
- package/dist/manager/events/cron/frequent/retry-failed-webhooks.js +70 -0
- package/dist/manager/events/cron/frequent.js +3 -0
- package/dist/manager/events/cron/runner.js +64 -0
- package/dist/manager/events/firestore/notifications/on-write.js +123 -0
- package/dist/manager/events/firestore/payments-disputes/on-write.js +256 -0
- package/dist/manager/events/firestore/payments-disputes/providers/stripe.js +218 -0
- package/dist/manager/events/firestore/payments-disputes/providers/test.js +231 -0
- package/dist/manager/events/firestore/payments-webhooks/analytics.js +687 -0
- package/dist/manager/events/firestore/payments-webhooks/on-write.js +1224 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/index.js +395 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-completed.js +58 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-failed.js +28 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-refunded.js +30 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/send-email.js +74 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/cancellation-removed.js +14 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/cancellation-requested.js +28 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/checkout-declined.js +18 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/new-subscription.js +67 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-failed.js +25 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-recovered.js +26 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-refunded.js +34 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/plan-changed.js +42 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/subscription-cancelled.js +33 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/subscription-winback.js +17 -0
- package/dist/manager/functions/_legacy/actions/create-post-handler.js +188 -0
- package/dist/manager/functions/_legacy/actions/generate-uuid.js +63 -0
- package/dist/manager/functions/_legacy/actions/sign-up-handler.js +205 -0
- package/dist/manager/functions/_legacy/admin/create-post.js +207 -0
- package/dist/manager/functions/_legacy/admin/firestore-write.js +72 -0
- package/dist/manager/functions/_legacy/admin/get-stats.js +218 -0
- package/dist/manager/functions/_legacy/admin/query.js +198 -0
- package/dist/manager/functions/_legacy/admin/send-notification.js +206 -0
- package/dist/manager/functions/_legacy/template.js +33 -0
- package/dist/manager/functions/_legacy/test/authenticate.js +40 -0
- package/dist/manager/functions/_legacy/test/webhook.js +37 -0
- package/dist/manager/functions/wrappers/mailchimp/addToList.js +25 -0
- package/dist/manager/helpers/analytics.js +498 -0
- package/dist/manager/helpers/api-manager.js +313 -0
- package/dist/manager/helpers/backend-router.js +37 -0
- package/dist/manager/helpers/context/authenticate.js +215 -0
- package/dist/manager/helpers/context/client-info.js +91 -0
- package/dist/manager/helpers/context/index.js +228 -0
- package/dist/manager/helpers/context/logging.js +150 -0
- package/dist/manager/helpers/context/parse.js +160 -0
- package/dist/manager/helpers/context/respond.js +247 -0
- package/dist/manager/helpers/event-middleware.js +109 -0
- package/dist/manager/helpers/metadata.js +32 -0
- package/dist/manager/helpers/middleware.js +541 -0
- package/dist/manager/helpers/redact-secret.js +25 -0
- package/dist/manager/helpers/resolved-config.js +34 -0
- package/dist/manager/helpers/roles.js +69 -0
- package/dist/manager/helpers/safe-compare.js +31 -0
- package/dist/manager/helpers/schema-engine.js +314 -0
- package/dist/manager/helpers/schema-zod.js +203 -0
- package/dist/manager/helpers/settings.js +215 -0
- package/dist/manager/helpers/usage.js +541 -0
- package/dist/manager/helpers/user.js +57 -0
- package/dist/manager/helpers/utilities.js +565 -0
- package/dist/manager/index.js +1404 -0
- package/dist/manager/libraries/abandoned-cart-config.js +12 -0
- package/dist/manager/libraries/ai/index.js +321 -0
- package/dist/manager/libraries/ai/prompt.js +85 -0
- package/dist/manager/libraries/ai/providers/anthropic-format.js +297 -0
- package/dist/manager/libraries/ai/providers/anthropic.js +169 -0
- package/dist/manager/libraries/ai/providers/claude-code.js +183 -0
- package/dist/manager/libraries/ai/providers/openai.js +1272 -0
- package/dist/manager/libraries/ai/providers/test.js +257 -0
- package/dist/manager/libraries/ai/tokens.js +54 -0
- package/dist/manager/libraries/analytics/conversions.js +478 -0
- package/dist/manager/libraries/analytics/match-data.js +573 -0
- package/dist/manager/libraries/analytics/signup.js +120 -0
- package/dist/manager/libraries/auth-user.js +46 -0
- package/dist/manager/libraries/content/feed-parser.js +198 -0
- package/dist/manager/libraries/content/ghostii.js +191 -0
- package/dist/manager/libraries/content/source-resolver.js +629 -0
- package/dist/manager/libraries/email/constants.js +532 -0
- package/dist/manager/libraries/email/data/blocked-local-parts.json +55 -0
- package/dist/manager/libraries/email/data/blocked-local-patterns.js +14 -0
- package/dist/manager/libraries/email/data/corporate-domains.json +23 -0
- package/dist/manager/libraries/email/data/custom-disposable-domains.json +58 -0
- package/dist/manager/libraries/email/data/disposable-domains.json +8184 -0
- package/dist/manager/libraries/email/data/typo-domains.js +83 -0
- package/dist/manager/libraries/email/disposable-domains.js +92 -0
- package/dist/manager/libraries/email/generators/lib/filter.js +179 -0
- package/dist/manager/libraries/email/generators/lib/image-host.js +344 -0
- package/dist/manager/libraries/email/generators/lib/image-illustrator.js +154 -0
- package/dist/manager/libraries/email/generators/lib/markdown-renderer.js +294 -0
- package/dist/manager/libraries/email/generators/lib/mjml-template.js +120 -0
- package/dist/manager/libraries/email/generators/lib/structure.js +299 -0
- package/dist/manager/libraries/email/generators/lib/svg-illustrator.js +197 -0
- package/dist/manager/libraries/email/generators/lib/templates/base.js +229 -0
- package/dist/manager/libraries/email/generators/lib/templates/card.js +33 -0
- package/dist/manager/libraries/email/generators/lib/templates/classic-schema.js +59 -0
- package/dist/manager/libraries/email/generators/lib/templates/clean.js +82 -0
- package/dist/manager/libraries/email/generators/lib/templates/editorial/helpers.js +100 -0
- package/dist/manager/libraries/email/generators/lib/templates/editorial/index.js +318 -0
- package/dist/manager/libraries/email/generators/lib/templates/feedback.js +101 -0
- package/dist/manager/libraries/email/generators/lib/templates/field-report/helpers.js +138 -0
- package/dist/manager/libraries/email/generators/lib/templates/field-report/index.js +494 -0
- package/dist/manager/libraries/email/generators/lib/templates/index.js +47 -0
- package/dist/manager/libraries/email/generators/lib/templates/newsletter-shared.js +610 -0
- package/dist/manager/libraries/email/generators/lib/templates/order.js +461 -0
- package/dist/manager/libraries/email/generators/lib/templates/plain.js +83 -0
- package/dist/manager/libraries/email/generators/lib/templates/shared-campaign.js +64 -0
- package/dist/manager/libraries/email/generators/newsletter.js +899 -0
- package/dist/manager/libraries/email/index.js +146 -0
- package/dist/manager/libraries/email/marketing/index.js +754 -0
- package/dist/manager/libraries/email/prepare.js +365 -0
- package/dist/manager/libraries/email/providers/beehiiv.js +589 -0
- package/dist/manager/libraries/email/providers/sendgrid.js +845 -0
- package/dist/manager/libraries/email/transactional/index.js +510 -0
- package/dist/manager/libraries/email/utm.js +86 -0
- package/dist/manager/libraries/email/validation-provider-neverbounce.js +70 -0
- package/dist/manager/libraries/email/validation-provider-zerobounce.js +42 -0
- package/dist/manager/libraries/email/validation.js +255 -0
- package/dist/manager/libraries/env.js +198 -0
- package/dist/manager/libraries/infer-contact.js +101 -0
- package/dist/manager/libraries/load-provider.js +65 -0
- package/dist/manager/libraries/notification.js +256 -0
- package/dist/manager/libraries/openai.js +13 -0
- package/dist/manager/libraries/payment/discount-codes.js +204 -0
- package/dist/manager/libraries/payment/fetch-failure.js +56 -0
- package/dist/manager/libraries/payment/license.js +44 -0
- package/dist/manager/libraries/payment/order-id.js +57 -0
- package/dist/manager/libraries/payment/provider-errors.js +71 -0
- package/dist/manager/libraries/payment/providers/chargebee.js +817 -0
- package/dist/manager/libraries/payment/providers/coinbase.js +330 -0
- package/dist/manager/libraries/payment/providers/paypal.js +987 -0
- package/dist/manager/libraries/payment/providers/stripe.js +755 -0
- package/dist/manager/libraries/payment/providers/test.js +250 -0
- package/dist/manager/libraries/payment/refund-linkage.js +78 -0
- package/dist/manager/libraries/payment/refund-policy.js +113 -0
- package/dist/manager/libraries/payment/winback.js +55 -0
- package/dist/manager/libraries/prompts/infer-contact.md +78 -0
- package/dist/manager/libraries/rate-limits.js +17 -0
- package/dist/manager/libraries/recaptcha.js +58 -0
- package/dist/manager/libraries/user-doc.js +234 -0
- package/dist/manager/routes/admin/backup/post.js +106 -0
- package/dist/manager/routes/admin/cron/post.js +37 -0
- package/dist/manager/routes/admin/database/get.js +35 -0
- package/dist/manager/routes/admin/database/post.js +34 -0
- package/dist/manager/routes/admin/email/post.js +74 -0
- package/dist/manager/routes/admin/firestore/get.js +35 -0
- package/dist/manager/routes/admin/firestore/post.js +56 -0
- package/dist/manager/routes/admin/firestore/query/post.js +128 -0
- package/dist/manager/routes/admin/hook/post.js +104 -0
- package/dist/manager/routes/admin/infer-contact/post.js +35 -0
- package/dist/manager/routes/admin/notification/post.js +37 -0
- package/dist/manager/routes/admin/payment/post.js +56 -0
- package/dist/manager/routes/admin/post/deduplicate-image-alts.js +52 -0
- package/dist/manager/routes/admin/post/dispatch-deploy.js +36 -0
- package/dist/manager/routes/admin/post/post.js +487 -0
- package/dist/manager/routes/admin/post/put.js +146 -0
- package/dist/manager/routes/admin/post/templates/post.html +14 -0
- package/dist/manager/routes/admin/repo/content/post.js +106 -0
- package/dist/manager/routes/admin/stats/get.js +222 -0
- package/dist/manager/routes/admin/users/disable/post.js +50 -0
- package/dist/manager/routes/admin/users/list/get.js +110 -0
- package/dist/manager/routes/admin/users/sync/post.js +119 -0
- package/dist/manager/routes/brand/get.js +26 -0
- package/dist/manager/routes/content/post/get.js +110 -0
- package/dist/manager/routes/general/email/post.js +89 -0
- package/dist/manager/routes/general/email/templates/general/download-app-link.js +42 -0
- package/dist/manager/routes/general/uuid/post.js +31 -0
- package/dist/manager/routes/handler/post/post.js +142 -0
- package/dist/manager/routes/health/get.js +43 -0
- package/dist/manager/routes/index.js +11 -0
- package/dist/manager/routes/marketing/campaign/delete.js +45 -0
- package/dist/manager/routes/marketing/campaign/get.js +69 -0
- package/dist/manager/routes/marketing/campaign/post.js +124 -0
- package/dist/manager/routes/marketing/campaign/put.js +86 -0
- package/dist/manager/routes/marketing/campaign/utils.js +59 -0
- package/dist/manager/routes/marketing/contact/delete.js +100 -0
- package/dist/manager/routes/marketing/contact/post.js +149 -0
- package/dist/manager/routes/marketing/contact/put.js +37 -0
- package/dist/manager/routes/marketing/email-preferences/post.js +275 -0
- package/dist/manager/routes/marketing/webhook/forward/post.js +170 -0
- package/dist/manager/routes/marketing/webhook/post.js +123 -0
- package/dist/manager/routes/marketing/webhook/providers/beehiiv.js +197 -0
- package/dist/manager/routes/marketing/webhook/providers/sendgrid.js +194 -0
- package/dist/manager/routes/payments/cancel/_is-trialing.js +40 -0
- package/dist/manager/routes/payments/cancel/post.js +149 -0
- package/dist/manager/routes/payments/cancel/providers/chargebee.js +41 -0
- package/dist/manager/routes/payments/cancel/providers/paypal.js +56 -0
- package/dist/manager/routes/payments/cancel/providers/stripe.js +33 -0
- package/dist/manager/routes/payments/cancel/providers/test.js +118 -0
- package/dist/manager/routes/payments/discount/get.js +25 -0
- package/dist/manager/routes/payments/dispute-alert/post.js +117 -0
- package/dist/manager/routes/payments/dispute-alert/providers/chargeblast.js +55 -0
- package/dist/manager/routes/payments/intent/post.js +341 -0
- package/dist/manager/routes/payments/intent/providers/chargebee.js +197 -0
- package/dist/manager/routes/payments/intent/providers/coinbase.js +94 -0
- package/dist/manager/routes/payments/intent/providers/paypal.js +244 -0
- package/dist/manager/routes/payments/intent/providers/stripe.js +141 -0
- package/dist/manager/routes/payments/intent/providers/test.js +341 -0
- package/dist/manager/routes/payments/plan/post.js +147 -0
- package/dist/manager/routes/payments/plan/providers/chargebee.js +62 -0
- package/dist/manager/routes/payments/plan/providers/paypal.js +62 -0
- package/dist/manager/routes/payments/plan/providers/stripe.js +59 -0
- package/dist/manager/routes/payments/plan/providers/test.js +113 -0
- package/dist/manager/routes/payments/portal/post.js +113 -0
- package/dist/manager/routes/payments/portal/providers/chargebee.js +62 -0
- package/dist/manager/routes/payments/portal/providers/paypal.js +25 -0
- package/dist/manager/routes/payments/portal/providers/stripe.js +62 -0
- package/dist/manager/routes/payments/portal/providers/test.js +18 -0
- package/dist/manager/routes/payments/refund/post.js +207 -0
- package/dist/manager/routes/payments/refund/providers/chargebee.js +147 -0
- package/dist/manager/routes/payments/refund/providers/coinbase.js +40 -0
- package/dist/manager/routes/payments/refund/providers/paypal.js +239 -0
- package/dist/manager/routes/payments/refund/providers/stripe.js +160 -0
- package/dist/manager/routes/payments/refund/providers/test.js +204 -0
- package/dist/manager/routes/payments/trial-eligibility/get.js +29 -0
- package/dist/manager/routes/payments/uncancel/post.js +106 -0
- package/dist/manager/routes/payments/uncancel/providers/chargebee.js +30 -0
- package/dist/manager/routes/payments/uncancel/providers/paypal.js +25 -0
- package/dist/manager/routes/payments/uncancel/providers/stripe.js +27 -0
- package/dist/manager/routes/payments/uncancel/providers/test.js +114 -0
- package/dist/manager/routes/payments/webhook/post.js +157 -0
- package/dist/manager/routes/payments/webhook/providers/chargebee.js +218 -0
- package/dist/manager/routes/payments/webhook/providers/coinbase.js +91 -0
- package/dist/manager/routes/payments/webhook/providers/paypal.js +214 -0
- package/dist/manager/routes/payments/webhook/providers/stripe.js +182 -0
- package/dist/manager/routes/payments/webhook/providers/test.js +18 -0
- package/dist/manager/routes/payments/winback/post.js +276 -0
- package/dist/manager/routes/payments/winback/providers/chargebee.js +24 -0
- package/dist/manager/routes/payments/winback/providers/paypal.js +22 -0
- package/dist/manager/routes/payments/winback/providers/stripe.js +41 -0
- package/dist/manager/routes/payments/winback/providers/test.js +42 -0
- package/dist/manager/routes/restart/index.js +37 -0
- package/dist/manager/routes/special/electron-client/post.js +59 -0
- package/dist/manager/routes/test/authenticate/get.js +11 -0
- package/dist/manager/routes/test/health/get.js +8 -0
- package/dist/manager/routes/test/index.js +16 -0
- package/dist/manager/routes/test/lab/post.js +17 -0
- package/dist/manager/routes/test/redirect/get.js +27 -0
- package/dist/manager/routes/test/reset-account/post.js +79 -0
- package/dist/manager/routes/test/roster/get.js +40 -0
- package/dist/manager/routes/test/schema/post.js +20 -0
- package/dist/manager/routes/test/usage/post.js +44 -0
- package/dist/manager/routes/test/webhook/post.js +21 -0
- package/dist/manager/routes/user/api-keys/post.js +51 -0
- package/dist/manager/routes/user/connections/_context.js +194 -0
- package/dist/manager/routes/user/connections/_grant.js +247 -0
- package/dist/manager/routes/user/connections/_lease.js +184 -0
- package/dist/manager/routes/user/connections/_providers.js +191 -0
- package/dist/manager/routes/user/connections/_state.js +117 -0
- package/dist/manager/routes/user/connections/delete.js +55 -0
- package/dist/manager/routes/user/connections/get.js +167 -0
- package/dist/manager/routes/user/connections/post.js +370 -0
- package/dist/manager/routes/user/connections/providers/discord.js +41 -0
- package/dist/manager/routes/user/connections/providers/google.js +55 -0
- package/dist/manager/routes/user/connections/providers/kick.js +68 -0
- package/dist/manager/routes/user/connections/providers/spotify.js +40 -0
- package/dist/manager/routes/user/connections/providers/twitch.js +52 -0
- package/dist/manager/routes/user/data-request/delete.js +84 -0
- package/dist/manager/routes/user/data-request/get.js +229 -0
- package/dist/manager/routes/user/data-request/post.js +136 -0
- package/dist/manager/routes/user/delete.js +149 -0
- package/dist/manager/routes/user/feedback/post.js +81 -0
- package/dist/manager/routes/user/get.js +14 -0
- package/dist/manager/routes/user/orders/get.js +95 -0
- package/dist/manager/routes/user/sessions/delete.js +95 -0
- package/dist/manager/routes/user/sessions/get.js +40 -0
- package/dist/manager/routes/user/settings/validate/post.js +86 -0
- package/dist/manager/routes/user/signup/post.js +662 -0
- package/dist/manager/routes/user/subscription/get.js +77 -0
- package/dist/manager/routes/user/token/post.js +30 -0
- package/dist/manager/routes/verts/delete.js +43 -0
- package/dist/manager/routes/verts/get.js +49 -0
- package/dist/manager/routes/verts/post.js +61 -0
- package/dist/manager/routes/verts/put.js +75 -0
- package/dist/manager/routes/verts/redirect/get.js +36 -0
- package/dist/manager/routes/verts/serve/get.js +67 -0
- package/dist/manager/routes/verts/utils.js +436 -0
- package/dist/manager/schemas/admin/backup/post.js +8 -0
- package/dist/manager/schemas/admin/cron/post.js +8 -0
- package/dist/manager/schemas/admin/database/get.js +5 -0
- package/dist/manager/schemas/admin/database/post.js +6 -0
- package/dist/manager/schemas/admin/email/post.js +31 -0
- package/dist/manager/schemas/admin/firestore/get.js +5 -0
- package/dist/manager/schemas/admin/firestore/post.js +8 -0
- package/dist/manager/schemas/admin/firestore/query/post.js +5 -0
- package/dist/manager/schemas/admin/hook/post.js +8 -0
- package/dist/manager/schemas/admin/infer-contact/post.js +6 -0
- package/dist/manager/schemas/admin/notification/post.js +23 -0
- package/dist/manager/schemas/admin/payment/post.js +8 -0
- package/dist/manager/schemas/admin/post/post.js +23 -0
- package/dist/manager/schemas/admin/post/put.js +13 -0
- package/dist/manager/schemas/admin/repo/content/post.js +10 -0
- package/dist/manager/schemas/admin/stats/get.js +5 -0
- package/dist/manager/schemas/admin/users/disable/post.js +9 -0
- package/dist/manager/schemas/admin/users/list/get.js +10 -0
- package/dist/manager/schemas/admin/users/sync/post.js +7 -0
- package/dist/manager/schemas/brand/get.js +3 -0
- package/dist/manager/schemas/content/post/get.js +8 -0
- package/dist/manager/schemas/general/email/post.js +10 -0
- package/dist/manager/schemas/general/uuid/post.js +9 -0
- package/dist/manager/schemas/handler/post/post.js +13 -0
- package/dist/manager/schemas/health/get.js +3 -0
- package/dist/manager/schemas/marketing/campaign/delete.js +8 -0
- package/dist/manager/schemas/marketing/campaign/get.js +13 -0
- package/dist/manager/schemas/marketing/campaign/post.js +43 -0
- package/dist/manager/schemas/marketing/campaign/put.js +37 -0
- package/dist/manager/schemas/marketing/contact/delete.js +8 -0
- package/dist/manager/schemas/marketing/contact/post.js +14 -0
- package/dist/manager/schemas/marketing/contact/put.js +8 -0
- package/dist/manager/schemas/marketing/email-preferences/post.js +15 -0
- package/dist/manager/schemas/marketing/webhook/forward/post.js +8 -0
- package/dist/manager/schemas/marketing/webhook/post.js +7 -0
- package/dist/manager/schemas/payments/cancel/post.js +15 -0
- package/dist/manager/schemas/payments/discount/get.js +8 -0
- package/dist/manager/schemas/payments/dispute-alert/post.js +6 -0
- package/dist/manager/schemas/payments/intent/post.js +28 -0
- package/dist/manager/schemas/payments/plan/post.js +11 -0
- package/dist/manager/schemas/payments/portal/post.js +12 -0
- package/dist/manager/schemas/payments/refund/post.js +14 -0
- package/dist/manager/schemas/payments/trial-eligibility/get.js +7 -0
- package/dist/manager/schemas/payments/uncancel/post.js +9 -0
- package/dist/manager/schemas/payments/webhook/post.js +6 -0
- package/dist/manager/schemas/payments/winback/post.js +12 -0
- package/dist/manager/schemas/restart/index.js +5 -0
- package/dist/manager/schemas/special/electron-client/post.js +11 -0
- package/dist/manager/schemas/test/authenticate/get.js +3 -0
- package/dist/manager/schemas/test/health/get.js +3 -0
- package/dist/manager/schemas/test/index.js +5 -0
- package/dist/manager/schemas/test/lab/post.js +3 -0
- package/dist/manager/schemas/test/redirect/get.js +7 -0
- package/dist/manager/schemas/test/reset-account/post.js +3 -0
- package/dist/manager/schemas/test/roster/get.js +3 -0
- package/dist/manager/schemas/test/schema/post.js +70 -0
- package/dist/manager/schemas/test/usage/post.js +8 -0
- package/dist/manager/schemas/test/webhook/post.js +7 -0
- package/dist/manager/schemas/user/api-keys/post.js +6 -0
- package/dist/manager/schemas/user/connections/delete.js +8 -0
- package/dist/manager/schemas/user/connections/get.js +21 -0
- package/dist/manager/schemas/user/connections/post.js +12 -0
- package/dist/manager/schemas/user/data-request/delete.js +3 -0
- package/dist/manager/schemas/user/data-request/get.js +5 -0
- package/dist/manager/schemas/user/data-request/post.js +6 -0
- package/dist/manager/schemas/user/delete.js +6 -0
- package/dist/manager/schemas/user/feedback/post.js +8 -0
- package/dist/manager/schemas/user/get.js +3 -0
- package/dist/manager/schemas/user/orders/get.js +6 -0
- package/dist/manager/schemas/user/sessions/delete.js +6 -0
- package/dist/manager/schemas/user/sessions/get.js +6 -0
- package/dist/manager/schemas/user/settings/validate/post.js +8 -0
- package/dist/manager/schemas/user/signup/post.js +27 -0
- package/dist/manager/schemas/user/subscription/get.js +5 -0
- package/dist/manager/schemas/user/token/post.js +5 -0
- package/dist/manager/schemas/verts/delete.js +8 -0
- package/dist/manager/schemas/verts/get.js +9 -0
- package/dist/manager/schemas/verts/post.js +23 -0
- package/dist/manager/schemas/verts/put.js +20 -0
- package/dist/manager/schemas/verts/redirect/get.js +10 -0
- package/dist/manager/schemas/verts/serve/get.js +14 -0
- package/dist/manager/server-manager.js +69 -0
- package/dist/mcp/client.js +98 -0
- package/dist/mcp/handler.js +521 -0
- package/dist/mcp/index.js +157 -0
- package/dist/mcp/tools.js +555 -0
- package/dist/mcp/utils.js +108 -0
- package/dist/omega-bin.js +6 -0
- package/dist/require.js +3 -0
- package/dist/test/fixtures/firebase-project/.firebaserc +5 -0
- package/dist/test/fixtures/firebase-project/config/omega.json5 +131 -0
- package/dist/test/fixtures/firebase-project/database.rules.json +6 -0
- package/dist/test/fixtures/firebase-project/firebase.json +49 -0
- package/dist/test/fixtures/firebase-project/firestore.indexes.json +4 -0
- package/dist/test/fixtures/firebase-project/package.json +14 -0
- package/dist/test/fixtures/firebase-project/src/index.js +11 -0
- package/dist/test/fixtures/firebase-project/storage.rules +8 -0
- package/dist/test/parse-audit.js +25 -0
- package/dist/test/run-tests.js +90 -0
- package/dist/test/runner.js +982 -0
- package/dist/test/seed.js +242 -0
- package/dist/test/test-accounts.js +2660 -0
- package/dist/test/utils/assertions.js +189 -0
- package/dist/test/utils/email-capture.js +252 -0
- package/dist/test/utils/extended-mode-warning.js +11 -0
- package/dist/test/utils/firestore-rules-client.js +277 -0
- package/dist/test/utils/http-client.js +228 -0
- package/dist/test/utils/test-mode-file.js +192 -0
- package/dist/utils/merge-line-files.js +6 -0
- package/dist/utils/scaffold-defaults.js +71 -0
- package/dist/utils/test-lanes.js +62 -0
- package/dist/vendor/account/engine.js +182 -0
- package/dist/vendor/account/features.js +220 -0
- package/dist/vendor/account/index.js +53 -0
- package/dist/vendor/account/schema.js +272 -0
- package/dist/vendor/account/subscription.js +38 -0
- package/dist/vendor/analytics/adapters/ga4.js +26 -0
- package/dist/vendor/analytics/adapters/meta.js +26 -0
- package/dist/vendor/analytics/adapters/resolve.js +130 -0
- package/dist/vendor/analytics/adapters/tiktok.js +27 -0
- package/dist/vendor/analytics/catalog.js +908 -0
- package/dist/vendor/analytics/identity.js +136 -0
- package/dist/vendor/config/company.js +31 -0
- package/dist/vendor/config/defaults.js +173 -0
- package/dist/vendor/config/demo.js +18 -0
- package/dist/vendor/config/desktop-artifacts.js +110 -0
- package/dist/vendor/config/edit.js +769 -0
- package/dist/vendor/config/env-delivery.js +145 -0
- package/dist/vendor/config/env-rules.js +93 -0
- package/dist/vendor/config/env-schema.js +1078 -0
- package/dist/vendor/config/env.js +445 -0
- package/dist/vendor/config/hooks.js +97 -0
- package/dist/vendor/config/index.js +237 -0
- package/dist/vendor/config/instances.js +208 -0
- package/dist/vendor/config/load.js +490 -0
- package/dist/vendor/config/merge.js +68 -0
- package/dist/vendor/config/order.js +139 -0
- package/dist/vendor/config/ports.js +374 -0
- package/dist/vendor/config/providers.js +32 -0
- package/dist/vendor/config/repo.js +142 -0
- package/dist/vendor/config/retired-keys.js +430 -0
- package/dist/vendor/config/schema.js +1610 -0
- package/dist/vendor/config/secrets.js +50 -0
- package/dist/vendor/config/seed.js +34 -0
- package/dist/vendor/config/site-global.js +205 -0
- package/dist/vendor/config/validate.js +554 -0
- package/dist/vendor/config/winback.js +61 -0
- package/dist/vendor/devkit/attach-log-file.js +262 -0
- package/dist/vendor/devkit/bare-requires.js +153 -0
- package/dist/vendor/devkit/command-path.js +46 -0
- package/dist/vendor/devkit/defaults-engine.js +372 -0
- package/dist/vendor/devkit/deploy-record.js +180 -0
- package/dist/vendor/devkit/deploy.js +286 -0
- package/dist/vendor/devkit/license.js +155 -0
- package/dist/vendor/devkit/local-https.js +360 -0
- package/dist/vendor/devkit/local.js +1905 -0
- package/dist/vendor/devkit/logger.js +128 -0
- package/dist/vendor/devkit/merge-line-files.js +296 -0
- package/dist/vendor/devkit/npm-registry.js +52 -0
- package/dist/vendor/devkit/omega-bin.js +345 -0
- package/dist/vendor/devkit/parse-audit.js +74 -0
- package/dist/vendor/devkit/safe-install.js +18 -0
- package/dist/vendor/devkit/scaffold-guard.js +96 -0
- package/dist/vendor/devkit/stop-signals.js +28 -0
- package/dist/vendor/devkit/test/assert.js +120 -0
- package/dist/vendor/devkit/test/define-cases.js +104 -0
- package/dist/vendor/devkit/test/runner-core.js +554 -0
- package/dist/vendor/devkit/test/scope.js +162 -0
- package/dist/vendor/devkit/update.js +569 -0
- package/dist/vendor/monitoring/core.js +180 -0
- package/dist/vendor/monitoring/env.js +49 -0
- package/dist/vendor/monitoring/logger.js +39 -0
- package/dist/vendor/monitoring/node.js +72 -0
- package/docs/admin-post-route.md +57 -0
- package/docs/ai-library.md +171 -0
- package/docs/architecture.md +62 -0
- package/docs/audit.md +70 -0
- package/docs/auth-hooks.md +74 -0
- package/docs/build-system.md +65 -0
- package/docs/cdp-debugging.md +29 -0
- package/docs/cli-firestore-auth.md +85 -0
- package/docs/cli-logs.md +67 -0
- package/docs/cli-output.md +146 -0
- package/docs/code-patterns.md +77 -0
- package/docs/common-mistakes.md +12 -0
- package/docs/common-operations.md +60 -0
- package/docs/connections.md +212 -0
- package/docs/consent.md +362 -0
- package/docs/directory-structure.md +150 -0
- package/docs/email-system.md +459 -0
- package/docs/environment-detection.md +93 -0
- package/docs/file-naming.md +10 -0
- package/docs/firestore.md +134 -0
- package/docs/ghostii.md +240 -0
- package/docs/index.md +299 -0
- package/docs/key-files.md +37 -0
- package/docs/logging.md +60 -0
- package/docs/marketing-campaigns.md +407 -0
- package/docs/marketing-fields.md +25 -0
- package/docs/mcp.md +222 -0
- package/docs/migration.md +129 -0
- package/docs/payment-system.md +856 -0
- package/docs/paypal-sandbox-qa.md +105 -0
- package/docs/response-headers.md +9 -0
- package/docs/routes.md +211 -0
- package/docs/sanitization.md +71 -0
- package/docs/schemas.md +163 -0
- package/docs/shared/agent-docs.md +89 -0
- package/docs/shared/analytics.md +612 -0
- package/docs/shared/brands.md +51 -0
- package/docs/shared/breaking-changes.md +497 -0
- package/docs/shared/config.md +1387 -0
- package/docs/shared/deploys.md +215 -0
- package/docs/shared/icons.md +201 -0
- package/docs/shared/local-dev.md +147 -0
- package/docs/shared/logging.md +202 -0
- package/docs/shared/monitoring.md +153 -0
- package/docs/shared/publishing.md +183 -0
- package/docs/shared/rulings.md +34 -0
- package/docs/shared/testing.md +147 -0
- package/docs/shared/theming.md +604 -0
- package/docs/shared/translation.md +291 -0
- package/docs/shared/updates.md +61 -0
- package/docs/stripe-webhook-forwarding.md +20 -0
- package/docs/test-boot-layer.md +67 -0
- package/docs/test-framework.md +583 -0
- package/docs/usage-rate-limiting.md +121 -0
- package/docs/verts.md +29 -0
- package/package.json +143 -0
- package/templates/config/omega.json5 +322 -0
- package/templates/database.rules.json +82 -0
- package/templates/firebase.json +67 -0
- package/templates/firestore.framework.rules +191 -0
- package/templates/firestore.indexes.json +4 -0
- package/templates/firestore.rules +59 -0
- package/templates/index.js +12 -0
- package/templates/public/404.html +26 -0
- package/templates/public/index.html +24 -0
- package/templates/remoteconfig.template.json +1 -0
- package/templates/storage-lifecycle-config-1-day.json +9 -0
- package/templates/storage-lifecycle-config-30-days.json +9 -0
- package/templates/storage.rules +11 -0
- package/test/_init/accounts-validation.js +58 -0
- package/test/ai/tools-live.test.js +171 -0
- package/test/analytics/conversion-delivery.test.js +1013 -0
- package/test/analytics/match-normalization.test.js +238 -0
- package/test/analytics/signup-conversion.test.js +135 -0
- package/test/boot/cli-dispatch.test.js +142 -0
- package/test/boot/defaults-scaffold.test.js +248 -0
- package/test/boot/deploy-staging.test.js +446 -0
- package/test/boot/disposable-domains.test.js +154 -0
- package/test/boot/emulator-boots.test.js +39 -0
- package/test/boot/emulator-port-preflight.test.js +126 -0
- package/test/boot/emulator-ready-timeout.test.js +46 -0
- package/test/boot/project-id-refresh.test.js +73 -0
- package/test/boot/suite-portability.test.js +278 -0
- package/test/boot/update-command.test.js +63 -0
- package/test/cli/cross-platform.test.js +168 -0
- package/test/cli/custom-project-type.test.js +426 -0
- package/test/cli/deploy-license-env.test.js +100 -0
- package/test/cli/emulator-adoption.test.js +254 -0
- package/test/cli/emulator-orphans.test.js +658 -0
- package/test/cli/emulator-ownership.test.js +177 -0
- package/test/cli/emulator-port-retry.test.js +395 -0
- package/test/cli/emulator-shutdown.test.js +788 -0
- package/test/cli/emulator-stale-reap.test.js +574 -0
- package/test/cli/ensure-target.test.js +325 -0
- package/test/cli/flags.test.js +64 -0
- package/test/cli/https-trust.test.js +38 -0
- package/test/cli/install-update-dispatch.test.js +47 -0
- package/test/cli/lane-environments.test.js +159 -0
- package/test/cli/marketing-campaigns-seeded.test.js +194 -0
- package/test/cli/migrate-bare-requires.test.js +128 -0
- package/test/cli/migrate-markers.test.js +530 -0
- package/test/cli/required-indexes.test.js +64 -0
- package/test/cli/rules-compile.test.js +622 -0
- package/test/cli/rules-migration-deferral.test.js +259 -0
- package/test/cli/rules-version.test.js +104 -0
- package/test/cli/setup-load-files.test.js +66 -0
- package/test/cli/setup-offline-mode.test.js +276 -0
- package/test/cli/setup-retired.test.js +64 -0
- package/test/cli/setup-shared-project-indexes.test.js +262 -0
- package/test/cli/setup-tests-requires.test.js +45 -0
- package/test/cli/stage-env-compose.test.js +316 -0
- package/test/cli/stripe-live-lane.test.js +303 -0
- package/test/cli/target.test.js +182 -0
- package/test/cli/templates.test.js +66 -0
- package/test/cli/test-runner-env.test.js +115 -0
- package/test/cli/test-stack-shutdown.test.js +275 -0
- package/test/cli/test-target-no-match.test.js +128 -0
- package/test/cli/verb-logs.test.js +118 -0
- package/test/cli/version-dispatch.test.js +39 -0
- package/test/content/blog-generate.test.js +164 -0
- package/test/email/campaign-config-fault.test.js +376 -0
- package/test/email/campaign-cron-pipeline.test.js +536 -0
- package/test/email/campaign-send.test.js +47 -0
- package/test/email/consent-lifecycle.test.js +258 -0
- package/test/email/content-html-policy.test.js +270 -0
- package/test/email/feedback-and-plain-send.test.js +54 -0
- package/test/email/fixtures/clean.json +30 -0
- package/test/email/fixtures/editorial.json +30 -0
- package/test/email/fixtures/field-report.json +53 -0
- package/test/email/identity.test.js +321 -0
- package/test/email/marketing/consent-gate.test.js +265 -0
- package/test/email/marketing/custom-fields-catalog.test.js +259 -0
- package/test/email/marketing/prune-per-provider.test.js +762 -0
- package/test/email/marketing/remove-log-privacy.test.js +225 -0
- package/test/email/marketing-lifecycle.test.js +139 -0
- package/test/email/newsletter-generate.test.js +853 -0
- package/test/email/newsletter-svg-tokens.test.js +51 -0
- package/test/email/newsletter-templates.test.js +492 -0
- package/test/email/order-one-time-cta.test.js +97 -0
- package/test/email/render-content.test.js +248 -0
- package/test/email/safe-url.test.js +0 -0
- package/test/email/sanitize-images.test.js +66 -0
- package/test/email/send-log-privacy.test.js +107 -0
- package/test/email/templates.test.js +288 -0
- package/test/email/testing-capture.test.js +260 -0
- package/test/email/transactional-send.test.js +36 -0
- package/test/email/transactional.test.js +562 -0
- package/test/email/unsubscribe-groups.test.js +155 -0
- package/test/email/unsubscribe-key.test.js +91 -0
- package/test/email/validation-cases.test.js +152 -0
- package/test/email/validation.test.js +746 -0
- package/test/events/auth-delete-conversion.test.js +138 -0
- package/test/events/auth-delete-race.test.js +211 -0
- package/test/events/auth-on-create-log.test.js +164 -0
- package/test/events/auth-signup-conversion.test.js +141 -0
- package/test/events/auth-signup-limit.test.js +142 -0
- package/test/events/auth-trigger-log-privacy.test.js +256 -0
- package/test/events/cron-job-doc-shape.test.js +101 -0
- package/test/events/cron-reset-usage.test.js +116 -0
- package/test/events/notification-conversion.test.js +182 -0
- package/test/events/payments/_webhook-harness.js +307 -0
- package/test/events/payments/abandoned-cart-activity.test.js +135 -0
- package/test/events/payments/analytics-payment-events.test.js +945 -0
- package/test/events/payments/dispute-email-status.test.js +55 -0
- package/test/events/payments/journey-payments-abandoned.test.js +126 -0
- package/test/events/payments/journey-payments-cancel-endpoint.test.js +102 -0
- package/test/events/payments/journey-payments-cancel-no-order.test.js +76 -0
- package/test/events/payments/journey-payments-cancel.test.js +184 -0
- package/test/events/payments/journey-payments-decline.test.js +225 -0
- package/test/events/payments/journey-payments-discount.test.js +91 -0
- package/test/events/payments/journey-payments-dispute.test.js +190 -0
- package/test/events/payments/journey-payments-failure.test.js +148 -0
- package/test/events/payments/journey-payments-legacy-product.test.js +151 -0
- package/test/events/payments/journey-payments-one-time-decline.test.js +127 -0
- package/test/events/payments/journey-payments-one-time-failure.test.js +113 -0
- package/test/events/payments/journey-payments-one-time-refund.test.js +186 -0
- package/test/events/payments/journey-payments-one-time.test.js +175 -0
- package/test/events/payments/journey-payments-plan-change.test.js +146 -0
- package/test/events/payments/journey-payments-plan-switch-trial.test.js +161 -0
- package/test/events/payments/journey-payments-plan-switch.test.js +182 -0
- package/test/events/payments/journey-payments-refund-no-order.test.js +76 -0
- package/test/events/payments/journey-payments-refund-webhook.test.js +225 -0
- package/test/events/payments/journey-payments-suspend.test.js +183 -0
- package/test/events/payments/journey-payments-trial-cancel.test.js +132 -0
- package/test/events/payments/journey-payments-trial.test.js +173 -0
- package/test/events/payments/journey-payments-uid-resolution.test.js +134 -0
- package/test/events/payments/journey-payments-uncancel.test.js +157 -0
- package/test/events/payments/journey-payments-upgrade.test.js +137 -0
- package/test/events/payments/journey-payments-winback-decline.test.js +171 -0
- package/test/events/payments/journey-payments-winback.test.js +195 -0
- package/test/events/payments/paypal-expiry-cron.test.js +166 -0
- package/test/events/payments/purchase-failed-handler.test.js +80 -0
- package/test/events/payments/purchase-refunded-handler.test.js +108 -0
- package/test/events/payments/test-processor-doc-shape.test.js +162 -0
- package/test/events/payments/transition-order-emails.test.js +185 -0
- package/test/events/payments/transition-promo-lines.test.js +192 -0
- package/test/events/payments/transitions-detect.test.js +767 -0
- package/test/events/payments/trial-lapse-sweep-staleness.test.js +173 -0
- package/test/events/payments/trial-lapse-sweep.test.js +359 -0
- package/test/events/payments/webhook-atomic-writes.test.js +69 -0
- package/test/events/payments/webhook-chargebee-unreachable.test.js +106 -0
- package/test/events/payments/webhook-discount-clear.test.js +189 -0
- package/test/events/payments/webhook-failure-intent.test.js +93 -0
- package/test/events/payments/webhook-hosted-page-uid-trust.test.js +212 -0
- package/test/events/payments/webhook-ordering.test.js +326 -0
- package/test/events/payments/webhook-provider-lookup-trust.test.js +206 -0
- package/test/events/payments/webhook-refund-amount-trust.test.js +223 -0
- package/test/events/payments/webhook-refund-envelope-shape.test.js +148 -0
- package/test/events/payments/webhook-refund-linkage-trust.test.js +404 -0
- package/test/events/payments/webhook-refund-transaction-fallback.test.js +184 -0
- package/test/events/payments/webhook-refund-without-order.test.js +170 -0
- package/test/events/payments/webhook-refusal-reporting.test.js +194 -0
- package/test/events/payments/webhook-refused-intent.test.js +142 -0
- package/test/events/payments/webhook-retry-sweep.test.js +252 -0
- package/test/events/payments/webhook-transition-claim.test.js +389 -0
- package/test/events/payments/webhook-uid-trust.test.js +203 -0
- package/test/events/payments/webhook-user-without-auth.test.js +161 -0
- package/test/fixtures/chargebee/invoice-one-time.json +27 -0
- package/test/fixtures/chargebee/subscription-active.json +44 -0
- package/test/fixtures/chargebee/subscription-cancelled.json +42 -0
- package/test/fixtures/chargebee/subscription-in-trial.json +41 -0
- package/test/fixtures/chargebee/subscription-legacy-plan.json +41 -0
- package/test/fixtures/chargebee/subscription-non-renewing.json +41 -0
- package/test/fixtures/chargebee/subscription-paused.json +42 -0
- package/test/fixtures/chargebee/webhook-payment-failed.json +51 -0
- package/test/fixtures/chargebee/webhook-subscription-created.json +47 -0
- package/test/fixtures/coinbase/charge-confirmed.json +50 -0
- package/test/fixtures/coinbase/charge-failed.json +34 -0
- package/test/fixtures/coinbase/charge-pending.json +34 -0
- package/test/fixtures/migrate/ported-route.js.txt +11 -0
- package/test/fixtures/paypal/capture-completed.json +51 -0
- package/test/fixtures/paypal/capture-refunded.json +43 -0
- package/test/fixtures/paypal/order-approved.json +62 -0
- package/test/fixtures/paypal/order-completed.json +110 -0
- package/test/fixtures/paypal/sale-refunded.json +38 -0
- package/test/fixtures/paypal/subscription-active.json +76 -0
- package/test/fixtures/paypal/subscription-cancelled.json +50 -0
- package/test/fixtures/paypal/subscription-suspended.json +65 -0
- package/test/fixtures/stripe/checkout-session-completed.json +130 -0
- package/test/fixtures/stripe/invoice-payment-failed.json +148 -0
- package/test/fixtures/stripe/invoice-subscription-payment-failed.json +28 -0
- package/test/fixtures/stripe/invoice-subscription-payment-succeeded.json +28 -0
- package/test/fixtures/stripe/subscription-active.json +161 -0
- package/test/fixtures/stripe/subscription-canceled.json +161 -0
- package/test/fixtures/stripe/subscription-trialing.json +161 -0
- package/test/helpers/_shared-config.js +15 -0
- package/test/helpers/ai-request-payload.test.js +619 -0
- package/test/helpers/ai-schema-resolve.test.js +125 -0
- package/test/helpers/ai-test-provider.test.js +227 -0
- package/test/helpers/ai-token-accounting.test.js +150 -0
- package/test/helpers/ai-tools-format.test.js +384 -0
- package/test/helpers/analytics-no-id-notice.test.js +146 -0
- package/test/helpers/analytics-user-data.test.js +194 -0
- package/test/helpers/api-manager.test.js +311 -0
- package/test/helpers/backend-router.test.js +136 -0
- package/test/helpers/content/blog-auto-publisher.test.js +482 -0
- package/test/helpers/content/feed-parser.test.js +529 -0
- package/test/helpers/content/ghostii-blocks.test.js +135 -0
- package/test/helpers/content/ghostii-feed-integration.test.js +405 -0
- package/test/helpers/content/ghostii-write-article.test.js +244 -0
- package/test/helpers/dev-only-routes.test.js +225 -0
- package/test/helpers/env-reader.test.js +362 -0
- package/test/helpers/environment.test.js +263 -0
- package/test/helpers/event-middleware.test.js +407 -0
- package/test/helpers/infer-contact.test.js +157 -0
- package/test/helpers/lane-url.test.js +90 -0
- package/test/helpers/merge-line-files.test.js +272 -0
- package/test/helpers/metadata.test.js +122 -0
- package/test/helpers/middleware-request-log.test.js +134 -0
- package/test/helpers/middleware-user-log.test.js +124 -0
- package/test/helpers/payment/chargebee/parse-webhook.test.js +490 -0
- package/test/helpers/payment/chargebee/refund-details.test.js +198 -0
- package/test/helpers/payment/chargebee/to-unified-one-time.test.js +148 -0
- package/test/helpers/payment/chargebee/to-unified-subscription.test.js +649 -0
- package/test/helpers/payment/coinbase/create-intent.test.js +247 -0
- package/test/helpers/payment/coinbase/parse-webhook.test.js +195 -0
- package/test/helpers/payment/coinbase/refund-unsupported.test.js +141 -0
- package/test/helpers/payment/coinbase/to-unified-one-time.test.js +201 -0
- package/test/helpers/payment/discount-codes.test.js +141 -0
- package/test/helpers/payment/extract-resource.test.js +113 -0
- package/test/helpers/payment/fetch-failure.test.js +215 -0
- package/test/helpers/payment/license-gate.test.js +108 -0
- package/test/helpers/payment/order-id.test.js +99 -0
- package/test/helpers/payment/paypal/create-intent.test.js +382 -0
- package/test/helpers/payment/paypal/fetch-capture.test.js +123 -0
- package/test/helpers/payment/paypal/fetch-sale.test.js +183 -0
- package/test/helpers/payment/paypal/parse-webhook.test.js +678 -0
- package/test/helpers/payment/paypal/refund-details.test.js +202 -0
- package/test/helpers/payment/paypal/resolve-plan-id.test.js +108 -0
- package/test/helpers/payment/paypal/switch-plan.test.js +92 -0
- package/test/helpers/payment/paypal/to-unified-one-time.test.js +383 -0
- package/test/helpers/payment/paypal/to-unified-subscription.test.js +884 -0
- package/test/helpers/payment/stripe/fetch-charge.test.js +147 -0
- package/test/helpers/payment/stripe/parse-webhook.test.js +448 -0
- package/test/helpers/payment/stripe/refund-details.test.js +147 -0
- package/test/helpers/payment/stripe/to-unified-one-time.test.js +307 -0
- package/test/helpers/payment/stripe/to-unified-subscription.test.js +709 -0
- package/test/helpers/persona-domain.test.js +87 -0
- package/test/helpers/recaptcha.test.js +166 -0
- package/test/helpers/resolved-config.test.js +106 -0
- package/test/helpers/response-log-redaction.test.js +262 -0
- package/test/helpers/roles.test.js +155 -0
- package/test/helpers/route-context-debug-gate.test.js +118 -0
- package/test/helpers/route-context-logging.test.js +214 -0
- package/test/helpers/route-context.test.js +129 -0
- package/test/helpers/safe-compare.test.js +114 -0
- package/test/helpers/sanitize.test.js +223 -0
- package/test/helpers/schema-engine.test.js +537 -0
- package/test/helpers/schema-zod.test.js +554 -0
- package/test/helpers/seed-accounts-load.test.js +163 -0
- package/test/helpers/seed-google-personas.test.js +114 -0
- package/test/helpers/seeded-personas.test.js +823 -0
- package/test/helpers/settings.test.js +292 -0
- package/test/helpers/setup-engines-pin.test.js +68 -0
- package/test/helpers/setup-manifest-sync.test.js +116 -0
- package/test/helpers/slugify.test.js +395 -0
- package/test/helpers/storage.test.js +195 -0
- package/test/helpers/test-banner-latch.test.js +139 -0
- package/test/helpers/usage-consume.test.js +460 -0
- package/test/helpers/usage-log-privacy.test.js +121 -0
- package/test/helpers/user-doc-heal.test.js +540 -0
- package/test/helpers/user.test.js +771 -0
- package/test/helpers/webhook-forward.test.js +419 -0
- package/test/helpers/wipe-auth-project.test.js +143 -0
- package/test/mcp/discovery.test.js +52 -0
- package/test/mcp/oauth.test.js +160 -0
- package/test/mcp/protocol.test.js +278 -0
- package/test/mcp/roles.test.js +203 -0
- package/test/mcp/utils.test.js +245 -0
- package/test/notification/identity.test.js +155 -0
- package/test/routes/admin/create-post.test.js +362 -0
- package/test/routes/admin/database.test.js +133 -0
- package/test/routes/admin/deduplicate-image-alts.test.js +191 -0
- package/test/routes/admin/email-content-html.test.js +58 -0
- package/test/routes/admin/email-request-log.test.js +86 -0
- package/test/routes/admin/email.test.js +116 -0
- package/test/routes/admin/firestore-query.test.js +206 -0
- package/test/routes/admin/firestore.test.js +129 -0
- package/test/routes/admin/infer-contact.test.js +220 -0
- package/test/routes/admin/notification.test.js +199 -0
- package/test/routes/admin/post-convert-image.test.js +159 -0
- package/test/routes/admin/post-deploy-flag.test.js +81 -0
- package/test/routes/admin/post-download-error.test.js +90 -0
- package/test/routes/admin/post-resize-image.test.js +185 -0
- package/test/routes/admin/post.test.js +369 -0
- package/test/routes/admin/repo-content.test.js +223 -0
- package/test/routes/admin/stats.test.js +114 -0
- package/test/routes/admin/users-disable.test.js +63 -0
- package/test/routes/admin/users-list.test.js +71 -0
- package/test/routes/content/post.test.js +60 -0
- package/test/routes/general/uuid.test.js +133 -0
- package/test/routes/health.test.js +110 -0
- package/test/routes/marketing/campaign.test.js +184 -0
- package/test/routes/marketing/contact.test.js +416 -0
- package/test/routes/marketing/email-preferences.test.js +293 -0
- package/test/routes/marketing/push-send.test.js +34 -0
- package/test/routes/marketing/webhook-forward.test.js +63 -0
- package/test/routes/marketing/webhook.test.js +641 -0
- package/test/routes/payments/_route-harness.js +145 -0
- package/test/routes/payments/cancel-provider-errors.test.js +227 -0
- package/test/routes/payments/cancel-skip-guards.test.js +126 -0
- package/test/routes/payments/cancel-trialing.test.js +428 -0
- package/test/routes/payments/cancel.test.js +165 -0
- package/test/routes/payments/dedup-race.test.js +186 -0
- package/test/routes/payments/discount.test.js +82 -0
- package/test/routes/payments/dispute-alert.test.js +324 -0
- package/test/routes/payments/intent-discount-amounts.test.js +550 -0
- package/test/routes/payments/intent-discount-coupons.test.js +293 -0
- package/test/routes/payments/intent-one-time-metadata.test.js +122 -0
- package/test/routes/payments/intent-purchaser-guard.test.js +139 -0
- package/test/routes/payments/intent-zero-total.test.js +260 -0
- package/test/routes/payments/intent.test.js +413 -0
- package/test/routes/payments/plan.test.js +367 -0
- package/test/routes/payments/portal-return-url.test.js +107 -0
- package/test/routes/payments/portal.test.js +94 -0
- package/test/routes/payments/refund-one-time.test.js +254 -0
- package/test/routes/payments/refund-paypal-proration.test.js +190 -0
- package/test/routes/payments/refund.test.js +181 -0
- package/test/routes/payments/trial-eligibility.test.js +73 -0
- package/test/routes/payments/uncancel.test.js +270 -0
- package/test/routes/payments/webhook-stripe-invoice.test.js +129 -0
- package/test/routes/payments/webhook-stripe-refund-one-time.test.js +130 -0
- package/test/routes/payments/webhook-test-provider.test.js +156 -0
- package/test/routes/payments/webhook.test.js +115 -0
- package/test/routes/payments/winback-stripe-coupon.test.js +126 -0
- package/test/routes/payments/winback.test.js +597 -0
- package/test/routes/test/authenticate.test.js +79 -0
- package/test/routes/test/redirect.test.js +66 -0
- package/test/routes/test/reset-account.test.js +126 -0
- package/test/routes/test/roster.test.js +101 -0
- package/test/routes/test/schema.test.js +556 -0
- package/test/routes/test/usage.test.js +374 -0
- package/test/routes/user/api-keys.test.js +158 -0
- package/test/routes/user/connections-grant.test.js +759 -0
- package/test/routes/user/connections-identity.test.js +260 -0
- package/test/routes/user/connections-log-privacy.test.js +206 -0
- package/test/routes/user/connections-refresh-lease.test.js +696 -0
- package/test/routes/user/connections-return.test.js +261 -0
- package/test/routes/user/connections-uid.test.js +516 -0
- package/test/routes/user/delete.test.js +138 -0
- package/test/routes/user/feedback.test.js +106 -0
- package/test/routes/user/orders.test.js +233 -0
- package/test/routes/user/sessions.test.js +231 -0
- package/test/routes/user/settings-validate.test.js +84 -0
- package/test/routes/user/signup-emails.test.js +113 -0
- package/test/routes/user/signup-location.test.js +84 -0
- package/test/routes/user/signup-log-privacy.test.js +112 -0
- package/test/routes/user/signup.test.js +579 -0
- package/test/routes/user/subscription.test.js +101 -0
- package/test/routes/user/token.test.js +111 -0
- package/test/routes/user/user.test.js +158 -0
- package/test/routes/verts/cache.test.js +78 -0
- package/test/routes/verts/click-destination.test.js +76 -0
- package/test/routes/verts/crud.test.js +194 -0
- package/test/routes/verts/redirect.test.js +147 -0
- package/test/routes/verts/selection.test.js +237 -0
- package/test/routes/verts/serve.test.js +185 -0
- package/test/routes/verts/unit-document.test.js +117 -0
- package/test/rules/_environment.js +66 -0
- package/test/rules/brand-merge.test.js +195 -0
- package/test/rules/field-helpers.test.js +309 -0
- package/test/rules/notifications.test.js +577 -0
- package/test/rules/payments-carts.test.js +406 -0
- package/test/rules/sessions.test.js +170 -0
- package/test/rules/user.test.js +517 -0
- package/test/rules/verts.test.js +101 -0
- package/test/security/fetch-log-secrets.test.js +124 -0
- package/test/security/primitives.test.js +105 -0
- package/test/security/repo-pinning.test.js +77 -0
- package/test/stripe-live/subscription-lifecycle.test.js +141 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment transition detection and dispatch
|
|
3
|
+
*
|
|
4
|
+
* Compares subscription state before and after a webhook to detect meaningful
|
|
5
|
+
* transitions (e.g., new subscription, payment failed, cancellation).
|
|
6
|
+
* Dispatches to individual handler files for each transition type.
|
|
7
|
+
*/
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
// Webhook event types that mean "money went back to the customer", per provider:
|
|
11
|
+
// PayPal PAYMENT.SALE.REFUNDED (v1 sales) and PAYMENT.CAPTURE.REFUNDED (v2
|
|
12
|
+
// captures — the shape this framework's own one-time purchases refund as,
|
|
13
|
+
// [#240](https://github.com/Omega-JS-Stack/omega/issues/240)), Stripe
|
|
14
|
+
// charge.refunded, Chargebee payment_refunded (each string is the provider's
|
|
15
|
+
// own, as its webhook parser reports it).
|
|
16
|
+
const REFUND_EVENTS = ['PAYMENT.SALE.REFUNDED', 'PAYMENT.CAPTURE.REFUNDED', 'charge.refunded', 'payment_refunded'];
|
|
17
|
+
|
|
18
|
+
// How long a transition claim speaks for. The race it exists to settle is SECONDS
|
|
19
|
+
// wide — two provider events for one checkout, a second apart, both reading a user
|
|
20
|
+
// doc neither has written yet — and the retry that re-runs a failed event is
|
|
21
|
+
// minutes wide. Past that, the same transition name on the same order is a genuinely
|
|
22
|
+
// NEW occurrence, not a duplicate: the order id never changes for the subscription's
|
|
23
|
+
// life, so a second dunning cycle, a re-cancellation after an uncancel, and a second
|
|
24
|
+
// plan change all re-detect a name the order already carries. An unbounded claim
|
|
25
|
+
// suppressed every one of them — no dunning email, no analytics, no marketing sync,
|
|
26
|
+
// forever after the first ([#665](https://github.com/Omega-JS-Stack/omega/issues/665)).
|
|
27
|
+
const CLAIM_WINDOW_MS = 10 * 60 * 1000;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Detect what transition occurred based on category and before/after state
|
|
31
|
+
*
|
|
32
|
+
* @param {string} category - 'subscription' or 'one-time'
|
|
33
|
+
* @param {object|null} before - Previous state (null for new users / one-time)
|
|
34
|
+
* @param {object} after - New unified state about to be written
|
|
35
|
+
* @param {string} eventType - Original webhook event type (used for one-time detection)
|
|
36
|
+
* @param {object} [options] - { previouslyCompleted } — this webhook doc already completed once
|
|
37
|
+
* @returns {string|null} Transition name or null if no meaningful change
|
|
38
|
+
*/
|
|
39
|
+
function detectTransition(category, before, after, eventType, options) {
|
|
40
|
+
if (category === 'subscription') {
|
|
41
|
+
return detectSubscriptionTransition(before, after, eventType, options);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (category === 'one-time') {
|
|
45
|
+
return detectOneTimeTransition(eventType, options);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Detect subscription state transitions by comparing before and after
|
|
53
|
+
*
|
|
54
|
+
* Checks are ordered by specificity — most specific first to avoid misclassification.
|
|
55
|
+
*
|
|
56
|
+
* @param {object|null} before - Previous users/{uid}.subscription (null/undefined for new users)
|
|
57
|
+
* @param {object} after - New unified subscription
|
|
58
|
+
* @param {string} eventType - Original webhook event type
|
|
59
|
+
* @param {object} [options] - { previouslyCompleted } — this webhook doc already completed once
|
|
60
|
+
* @returns {string|null} Transition name
|
|
61
|
+
*/
|
|
62
|
+
function detectSubscriptionTransition(before, after, eventType, options) {
|
|
63
|
+
if (!after) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Refund events take priority — detected by webhook event type rather than state diff
|
|
68
|
+
// because the subscription state may not change meaningfully during a refund
|
|
69
|
+
if (REFUND_EVENTS.includes(eventType)) {
|
|
70
|
+
// Idempotency: refund detection is event-type-only, so a webhook doc that is
|
|
71
|
+
// processed a second time (a redelivery, or a doc put back to pending) would
|
|
72
|
+
// re-dispatch payment-refunded and email the customer about the same refund
|
|
73
|
+
// twice. A doc that already completed once has already sent it.
|
|
74
|
+
if (options?.previouslyCompleted) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return 'payment-refunded';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const beforeStatus = before?.status;
|
|
82
|
+
const afterStatus = after.status;
|
|
83
|
+
|
|
84
|
+
// 1. new-subscription: basic/null → active paid (handler checks after.trial.claimed for trial info)
|
|
85
|
+
if (isBasicOrNull(before) && afterStatus === 'active' && isPaid(after)) {
|
|
86
|
+
return 'new-subscription';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 2. subscription-winback: cancelled paid → active paid. A full cancellation
|
|
90
|
+
// leaves the paid product id in place, so a returning subscriber matched
|
|
91
|
+
// nothing here — no confirmation email, and analytics read the checkout as a
|
|
92
|
+
// renewal ([#218](https://github.com/Omega-JS-Stack/omega/issues/218)).
|
|
93
|
+
if (beforeStatus === 'cancelled' && isPaid(before) && afterStatus === 'active' && isPaid(after)) {
|
|
94
|
+
return 'subscription-winback';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 3. checkout-declined: basic/null → suspended. Users are born active on basic,
|
|
98
|
+
// so a declined FIRST checkout reads as active → suspended and used to send the
|
|
99
|
+
// renewal-dunning email to someone who never had a subscription to dun.
|
|
100
|
+
if (isBasicOrNull(before) && afterStatus === 'suspended') {
|
|
101
|
+
return 'checkout-declined';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 3b. checkout-declined, the win-back branch: cancelled paid → suspended. A full
|
|
105
|
+
// cancellation leaves the paid product id in place, so a returning subscriber
|
|
106
|
+
// whose payment declines matched NOTHING — not the rule above (before must be
|
|
107
|
+
// basic), not subscription-winback (after must be active), not payment-failed
|
|
108
|
+
// (before must be active) — and the webhook completed with no log and no
|
|
109
|
+
// analytics ([#223](https://github.com/Omega-JS-Stack/omega/issues/223)). Same
|
|
110
|
+
// handler as above: the user is standing at the checkout watching it fail, so it
|
|
111
|
+
// logs and sends no dunning email.
|
|
112
|
+
if (beforeStatus === 'cancelled' && isPaid(before) && afterStatus === 'suspended') {
|
|
113
|
+
return 'checkout-declined';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 4. payment-failed: active → suspended
|
|
117
|
+
if (beforeStatus === 'active' && afterStatus === 'suspended') {
|
|
118
|
+
return 'payment-failed';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 5. payment-recovered: suspended → active
|
|
122
|
+
if (beforeStatus === 'suspended' && afterStatus === 'active') {
|
|
123
|
+
return 'payment-recovered';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 6. cancellation-requested: pending flips from false → true while still active
|
|
127
|
+
if (afterStatus === 'active' && !before?.cancellation?.pending && after.cancellation?.pending) {
|
|
128
|
+
return 'cancellation-requested';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 7. cancellation-removed: pending flips from true → false on the same product
|
|
132
|
+
// while still active — the uncancel route's half of the pair above. Same product
|
|
133
|
+
// and behind payment-recovered, so a plan change or a recovery that also clears
|
|
134
|
+
// the schedule keeps its own, more meaningful, name.
|
|
135
|
+
if (
|
|
136
|
+
afterStatus === 'active'
|
|
137
|
+
&& before?.cancellation?.pending
|
|
138
|
+
&& !after.cancellation?.pending
|
|
139
|
+
&& before.product?.id === after.product?.id
|
|
140
|
+
) {
|
|
141
|
+
return 'cancellation-removed';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 8. subscription-cancelled: any non-cancelled → cancelled
|
|
145
|
+
if (beforeStatus !== 'cancelled' && afterStatus === 'cancelled') {
|
|
146
|
+
return 'subscription-cancelled';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 9. plan-changed: both active, both paid, different product
|
|
150
|
+
if (
|
|
151
|
+
beforeStatus === 'active'
|
|
152
|
+
&& afterStatus === 'active'
|
|
153
|
+
&& isPaid(before)
|
|
154
|
+
&& isPaid(after)
|
|
155
|
+
&& before.product.id !== after.product.id
|
|
156
|
+
) {
|
|
157
|
+
return 'plan-changed';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Detect one-time payment transitions from event type
|
|
165
|
+
* Simpler than subscriptions — no before/after comparison needed
|
|
166
|
+
*
|
|
167
|
+
* @param {string} eventType - Webhook event type
|
|
168
|
+
* @param {object} [options] - { previouslyCompleted } — this webhook doc already completed once
|
|
169
|
+
* @returns {string|null} Transition name
|
|
170
|
+
*/
|
|
171
|
+
function detectOneTimeTransition(eventType, options) {
|
|
172
|
+
// Idempotency: EVERY transition on this side is detected from the event type
|
|
173
|
+
// alone, so a webhook doc processed a second time (a redelivery, or a doc put
|
|
174
|
+
// back to pending) would re-dispatch its handler — and email the customer about
|
|
175
|
+
// the same purchase or refund twice. The subscription side guards its
|
|
176
|
+
// event-type-only path the same way.
|
|
177
|
+
if (options?.previouslyCompleted) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Refunds first, and by event type alone — a one-time purchase has no
|
|
182
|
+
// before/after state to diff, the event IS the transition. The strings are the
|
|
183
|
+
// same provider strings the subscription side reads
|
|
184
|
+
// ([#212](https://github.com/Omega-JS-Stack/omega/issues/212)).
|
|
185
|
+
if (REFUND_EVENTS.includes(eventType)) {
|
|
186
|
+
return 'purchase-refunded';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Stripe
|
|
190
|
+
if (eventType === 'checkout.session.completed') {
|
|
191
|
+
return 'purchase-completed';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (eventType === 'invoice.payment_failed') {
|
|
195
|
+
return 'purchase-failed';
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// PayPal
|
|
199
|
+
if (eventType === 'CHECKOUT.ORDER.APPROVED') {
|
|
200
|
+
return 'purchase-completed';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Chargebee. Its provider routes a subscription-less payment_failed to
|
|
204
|
+
// category one-time (routes/payments/webhook/providers/chargebee.js), and
|
|
205
|
+
// reading only Stripe's string left that whole half detecting nothing — no
|
|
206
|
+
// transition, so the failed-payment email never sent
|
|
207
|
+
// ([#714](https://github.com/Omega-JS-Stack/omega/issues/714)). The
|
|
208
|
+
// subscription side never reaches here: it names payment-failed off the
|
|
209
|
+
// active → suspended state diff, not off this string.
|
|
210
|
+
if (eventType === 'payment_failed') {
|
|
211
|
+
return 'purchase-failed';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// The success half of that same pair, and it has to be THIS string rather than
|
|
215
|
+
// `invoice_generated`: an invoice is born unpaid (libraries/payment/providers/
|
|
216
|
+
// chargebee.js maps `payment_due`/`not_paid` → failed), so mapping the generated
|
|
217
|
+
// invoice would send a receipt for a purchase nobody paid for. Reading neither
|
|
218
|
+
// left a paid Chargebee one-time with no receipt and no purchase analytics at all
|
|
219
|
+
// ([#729](https://github.com/Omega-JS-Stack/omega/issues/729)). The subscription
|
|
220
|
+
// side never reaches here either: its provider gives a payment_succeeded carrying
|
|
221
|
+
// a subscription no category at all, because `subscription_renewed` owns that
|
|
222
|
+
// revenue.
|
|
223
|
+
if (eventType === 'payment_succeeded') {
|
|
224
|
+
return 'purchase-completed';
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Coinbase Commerce. A crypto charge is a single payment, so `charge:confirmed`
|
|
228
|
+
// is this provider's whole success half — the coins have settled on-chain and
|
|
229
|
+
// the purchase is real ([#642](https://github.com/Omega-JS-Stack/omega/issues/642)).
|
|
230
|
+
//
|
|
231
|
+
// Its two siblings deliberately map to NOTHING, though both are supported by
|
|
232
|
+
// the parser and both write their order: `charge:pending` is money detected but
|
|
233
|
+
// not yet confirmed, so there is nothing to mail about or book yet, and
|
|
234
|
+
// `charge:failed` is a hosted charge that expired unpaid — the abandoned-
|
|
235
|
+
// checkout population, watched by the person who walked away from it, which is
|
|
236
|
+
// exactly the population `checkout-declined` refuses to mail. (An abandoned
|
|
237
|
+
// Stripe session sends nothing either; `purchase-failed` is for a MANUAL
|
|
238
|
+
// invoice nobody is standing in front of.)
|
|
239
|
+
if (eventType === 'charge:confirmed') {
|
|
240
|
+
return 'purchase-completed';
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Claim a detected transition for one order, so exactly one webhook dispatches it
|
|
248
|
+
*
|
|
249
|
+
* Detection is a DIFF: it compares the user's stored subscription against the
|
|
250
|
+
* unified state this event carries. Two webhooks for the same checkout can be in
|
|
251
|
+
* flight at once (Stripe sent `customer.subscription.created` and
|
|
252
|
+
* `invoice.payment_succeeded` a second apart), and both read the user doc before
|
|
253
|
+
* either one's write landed — so both saw `basic`, both detected
|
|
254
|
+
* `new-subscription`, and both dispatched it: two order emails, two trial_start
|
|
255
|
+
* fires ([#665](https://github.com/Omega-JS-Stack/omega/issues/665)).
|
|
256
|
+
*
|
|
257
|
+
* The claim is the tiebreaker, keyed on the ORDER rather than the event: the
|
|
258
|
+
* transaction's read locks the order document, so exactly one of the racing runs
|
|
259
|
+
* writes `transitions.<name>` and the other is told who beat it. Per order per
|
|
260
|
+
* transition NAME, so a later renewal, cancellation, or a genuinely second
|
|
261
|
+
* subscription on its own order all still fire their own transitions.
|
|
262
|
+
*
|
|
263
|
+
* A claim carries its OUTCOME, not just its holder — `settleClaim()` below writes
|
|
264
|
+
* it — so a `failed` claim is retakeable at any age. A winner that threw after
|
|
265
|
+
* claiming (the webhook doc goes `failed`, and the frequent cron re-runs THAT SAME
|
|
266
|
+
* event id) was otherwise refused by its own claim forever: no order email, no
|
|
267
|
+
* analytics, no marketing sync, for a customer who paid
|
|
268
|
+
* ([#665](https://github.com/Omega-JS-Stack/omega/issues/665)).
|
|
269
|
+
*
|
|
270
|
+
* And a claim only speaks for `CLAIM_WINDOW_MS`. Whatever its status, an older one
|
|
271
|
+
* is retaken: the same transition name reaching the same order ten minutes later is
|
|
272
|
+
* a new occurrence of it, not the duplicate half of a race (see the constant).
|
|
273
|
+
*
|
|
274
|
+
* @param {object} options
|
|
275
|
+
* @param {object} options.admin - The firebase-admin handle
|
|
276
|
+
* @param {string} options.orderId - The order the transition belongs to
|
|
277
|
+
* @param {string} options.transitionName - e.g., 'new-subscription', 'payment-failed'
|
|
278
|
+
* @param {string} options.eventId - The webhook event making the claim
|
|
279
|
+
* @param {string} options.now - ISO timestamp of this run
|
|
280
|
+
* @param {number} options.nowUNIX - UNIX second of this run
|
|
281
|
+
* @returns {Promise<{ claimed: boolean, eventId: string }>} Whether this event won
|
|
282
|
+
* the claim, and the event id that holds it either way
|
|
283
|
+
*/
|
|
284
|
+
async function claim({ admin, orderId, transitionName, eventId, now, nowUNIX }) {
|
|
285
|
+
const orderRef = admin.firestore().doc(`payments-orders/${orderId}`);
|
|
286
|
+
|
|
287
|
+
return admin.firestore().runTransaction(async (transaction) => {
|
|
288
|
+
const orderDoc = await transaction.get(orderRef);
|
|
289
|
+
const existing = orderDoc.data()?.transitions?.[transitionName];
|
|
290
|
+
|
|
291
|
+
// A FAILED claim is retaken at any age — that IS the retry path, the same one
|
|
292
|
+
// the webhook and dispute-alert routes reclaim their own docs from. So is a
|
|
293
|
+
// claim older than the window, whatever it says: that is a repeat of the
|
|
294
|
+
// transition, not the duplicate half of a race. Anything else — a claim still
|
|
295
|
+
// running, or one that finished seconds ago — refuses whoever asks, including
|
|
296
|
+
// the event that holds it. A claim with no stamp cannot be shown to be fresh.
|
|
297
|
+
const spent = existing && (nowUNIX - (existing.timestampUNIX || 0)) * 1000 >= CLAIM_WINDOW_MS;
|
|
298
|
+
|
|
299
|
+
if (existing && existing.status !== 'failed' && !spent) {
|
|
300
|
+
return { claimed: false, eventId: existing.eventId || 'unknown' };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
transaction.set(orderRef, {
|
|
304
|
+
transitions: {
|
|
305
|
+
[transitionName]: {
|
|
306
|
+
eventId: eventId,
|
|
307
|
+
status: 'claimed',
|
|
308
|
+
timestamp: now,
|
|
309
|
+
timestampUNIX: nowUNIX,
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
}, { merge: true });
|
|
313
|
+
|
|
314
|
+
return { claimed: true, eventId: eventId };
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Record how the run that WON a claim ended, so a retry can tell the two apart
|
|
320
|
+
*
|
|
321
|
+
* The claim is taken before anything acts on the transition, and the run holding
|
|
322
|
+
* it can still throw afterwards. `done` is stamped once the dispatch, the
|
|
323
|
+
* analytics fire and the marketing sync are away; `failed` is stamped beside the
|
|
324
|
+
* webhook doc's own failure stamp, and is the only state `claim()` retakes.
|
|
325
|
+
*
|
|
326
|
+
* Only the run that took the claim ever calls this — a run that LOST one settles
|
|
327
|
+
* nothing, and neither does a transition that had no order to claim on.
|
|
328
|
+
*
|
|
329
|
+
* @param {object} options
|
|
330
|
+
* @param {object} options.admin - The firebase-admin handle
|
|
331
|
+
* @param {object} options.claim - The claim this run won: `{ orderId, transitionName }`
|
|
332
|
+
* @param {string} options.status - 'done' or 'failed'
|
|
333
|
+
* @returns {Promise<void>}
|
|
334
|
+
*/
|
|
335
|
+
async function settleClaim({ admin, claim, status }) {
|
|
336
|
+
if (!claim.orderId || !claim.transitionName) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
await admin.firestore().doc(`payments-orders/${claim.orderId}`).set({
|
|
341
|
+
transitions: {
|
|
342
|
+
[claim.transitionName]: {
|
|
343
|
+
status: status,
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
}, { merge: true });
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Dispatch a transition handler (fire-and-forget)
|
|
351
|
+
*
|
|
352
|
+
* @param {string} transitionName - e.g., 'new-subscription', 'payment-failed'
|
|
353
|
+
* @param {string} category - 'subscription' or 'one-time'
|
|
354
|
+
* @param {object} context - Full context passed to the handler
|
|
355
|
+
*/
|
|
356
|
+
function dispatch(transitionName, category, context) {
|
|
357
|
+
const { ctx } = context;
|
|
358
|
+
|
|
359
|
+
try {
|
|
360
|
+
const handlerPath = path.join(__dirname, category, `${transitionName}.js`);
|
|
361
|
+
const handler = require(handlerPath);
|
|
362
|
+
|
|
363
|
+
// Fire-and-forget — don't block the main webhook processing
|
|
364
|
+
Promise.resolve(handler(context)).catch((e) => {
|
|
365
|
+
ctx.error(`Transition handler [${category}/${transitionName}] failed: ${e.message}`, e);
|
|
366
|
+
});
|
|
367
|
+
} catch (e) {
|
|
368
|
+
// Handler file doesn't exist or can't be loaded — log but don't fail
|
|
369
|
+
ctx.error(`Transition handler [${category}/${transitionName}] not found: ${e.message}`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ─── Helpers ───
|
|
374
|
+
|
|
375
|
+
function isBasicOrNull(sub) {
|
|
376
|
+
return !sub || !sub.product || sub.product.id === 'basic';
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function isPaid(sub) {
|
|
380
|
+
return sub && sub.product && sub.product.id !== 'basic';
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
module.exports = {
|
|
384
|
+
detectTransition,
|
|
385
|
+
detectSubscriptionTransition,
|
|
386
|
+
detectOneTimeTransition,
|
|
387
|
+
claim,
|
|
388
|
+
settleClaim,
|
|
389
|
+
dispatch,
|
|
390
|
+
CLAIM_WINDOW_MS,
|
|
391
|
+
// Exported for testing
|
|
392
|
+
REFUND_EVENTS,
|
|
393
|
+
isBasicOrNull,
|
|
394
|
+
isPaid,
|
|
395
|
+
};
|
package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-completed.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transition: purchase-completed
|
|
3
|
+
* Triggered when a one-time payment checkout completes (checkout.session.completed with mode=payment)
|
|
4
|
+
*/
|
|
5
|
+
const { sendOrderEmail, formatDate } = require('../send-email.js');
|
|
6
|
+
const discountCodes = require('../../../../../libraries/payment/discount-codes.js');
|
|
7
|
+
|
|
8
|
+
module.exports = async function ({ before, after, order, uid, userDoc, ctx }) {
|
|
9
|
+
const brandName = ctx.Manager.config.brand?.name || '';
|
|
10
|
+
const productName = after.product?.name || '';
|
|
11
|
+
|
|
12
|
+
// Pre-compute discount values for the email template. The total goes through
|
|
13
|
+
// applyToAmount() — its one home — for BOTH coupon shapes: gating it on a
|
|
14
|
+
// percent quoted the full list price on an amount code's receipt
|
|
15
|
+
// ([#239](https://github.com/Omega-JS-Stack/omega/issues/239)).
|
|
16
|
+
// The promo LINE is keyed off the shape the code actually carries. An order
|
|
17
|
+
// written before the amount field existed comes back valid with NEITHER, and
|
|
18
|
+
// this handler is dispatched fire-and-forget — reading a shape that isn't
|
|
19
|
+
// there would cost the customer their confirmation email, silently.
|
|
20
|
+
const price = parseFloat(after.payment?.price || 0);
|
|
21
|
+
const discount = order.discount;
|
|
22
|
+
const hasPromoDiscount = discount?.valid === true;
|
|
23
|
+
const promoShape = discountCodes.promoShape(discount);
|
|
24
|
+
const firstCharge = discountCodes.applyToAmount(price, discount);
|
|
25
|
+
|
|
26
|
+
ctx.log(`Transition [one-time/purchase-completed]: uid=${uid}, resourceId=${after.payment?.resourceId}, discount=${hasPromoDiscount ? discount.code : 'none'}`);
|
|
27
|
+
|
|
28
|
+
sendOrderEmail({
|
|
29
|
+
template: 'order',
|
|
30
|
+
subject: `Your ${brandName} ${productName} order #${order?.id || ''}`,
|
|
31
|
+
categories: ['order/confirmation'],
|
|
32
|
+
userDoc,
|
|
33
|
+
ctx,
|
|
34
|
+
data: {
|
|
35
|
+
content: { event: 'confirmation',
|
|
36
|
+
...order,
|
|
37
|
+
_computed: {
|
|
38
|
+
date: formatDate(new Date().toISOString()),
|
|
39
|
+
...(promoShape && {
|
|
40
|
+
promoCode: discount.code,
|
|
41
|
+
// The savings line quotes the code's own terms; the flat-dollar one
|
|
42
|
+
// is what it actually took off, which floors at the charge itself
|
|
43
|
+
...(promoShape === 'percent'
|
|
44
|
+
? {
|
|
45
|
+
promoPercent: discount.percent,
|
|
46
|
+
promoSavings: (price * discount.percent / 100).toFixed(2),
|
|
47
|
+
}
|
|
48
|
+
: {
|
|
49
|
+
promoAmount: discount.amount.toFixed(2),
|
|
50
|
+
promoSavings: (price - firstCharge).toFixed(2),
|
|
51
|
+
}),
|
|
52
|
+
}),
|
|
53
|
+
totalToday: firstCharge.toFixed(2),
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
};
|
package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-failed.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transition: purchase-failed
|
|
3
|
+
* Triggered when a one-time payment fails (invoice.payment_failed with a
|
|
4
|
+
* billing_reason no subscription owns — a MANUAL invoice).
|
|
5
|
+
*
|
|
6
|
+
* It sends, where the subscription side's first-checkout twin (checkout-declined)
|
|
7
|
+
* deliberately does not, because the two populations are opposites: a declined
|
|
8
|
+
* checkout is watched by the person who just pressed the button, and a manual
|
|
9
|
+
* invoice that fails is not watched by anybody. The failure only reaches the
|
|
10
|
+
* customer if this handler tells them
|
|
11
|
+
* ([#673](https://github.com/Omega-JS-Stack/omega/issues/673)).
|
|
12
|
+
*
|
|
13
|
+
* The mail IS the subscription twin's (subscription/payment-failed.js) — the same
|
|
14
|
+
* `order` template, the same `payment-failed` event, the same subject and
|
|
15
|
+
* category — so it is sent by that same handler rather than a second copy of it
|
|
16
|
+
* here (the winback delegation precedent). Only the log line names the one-time
|
|
17
|
+
* lane. The copy still reads one-time: the template branches on the ORDER's type,
|
|
18
|
+
* not on which transition sent it.
|
|
19
|
+
*/
|
|
20
|
+
const paymentFailed = require('../subscription/payment-failed.js');
|
|
21
|
+
|
|
22
|
+
module.exports = async function (context) {
|
|
23
|
+
const { after, order, uid, ctx } = context;
|
|
24
|
+
|
|
25
|
+
ctx.log(`Transition [one-time/purchase-failed]: uid=${uid}, orderId=${order?.id}, product=${after?.product?.id}`);
|
|
26
|
+
|
|
27
|
+
return paymentFailed(context);
|
|
28
|
+
};
|
package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-refunded.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transition: purchase-refunded
|
|
3
|
+
* Triggered when a one-time purchase is refunded (charge.refunded with no
|
|
4
|
+
* subscription and no invoice).
|
|
5
|
+
*
|
|
6
|
+
* Webhook-driven, so it fires however the refund originated — the refund
|
|
7
|
+
* endpoint, an admin dashboard, or a direct provider action.
|
|
8
|
+
*
|
|
9
|
+
* Provider-agnostic — refund details are extracted by the provider library's
|
|
10
|
+
* getRefundDetails() and passed as a unified { amount, currency, reason } object,
|
|
11
|
+
* the same ones on-write.js records on the order.
|
|
12
|
+
*
|
|
13
|
+
* The mail IS the subscription twin's (subscription/payment-refunded.js) — the same
|
|
14
|
+
* `order` template, the same `refunded` event, the same computed refund fields —
|
|
15
|
+
* so it is sent by that same handler rather than a second copy of it here (the
|
|
16
|
+
* winback delegation precedent). Only the log line names the one-time lane, and
|
|
17
|
+
* it says more: the amount that came back is this record's whole point. A
|
|
18
|
+
* customer refunded on a one-time purchase used to hear nothing at all — the
|
|
19
|
+
* handler was a log-only stub, so their only notice was the line on their
|
|
20
|
+
* statement ([#673](https://github.com/Omega-JS-Stack/omega/issues/673)).
|
|
21
|
+
*/
|
|
22
|
+
const paymentRefunded = require('../subscription/payment-refunded.js');
|
|
23
|
+
|
|
24
|
+
module.exports = async function (context) {
|
|
25
|
+
const { after, order, uid, ctx, refundDetails } = context;
|
|
26
|
+
|
|
27
|
+
ctx.log(`Transition [one-time/purchase-refunded]: uid=${uid}, orderId=${order?.id}, product=${after?.product?.id}, amount=${refundDetails?.amount || 'unknown'} ${refundDetails?.currency || 'USD'}, reason=${refundDetails?.reason || 'none'}`);
|
|
28
|
+
|
|
29
|
+
return paymentRefunded(context);
|
|
30
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared email helper for payment transition handlers
|
|
3
|
+
* Sends transactional order emails directly via the shared email library (no HTTP round-trip)
|
|
4
|
+
*/
|
|
5
|
+
const moment = require('moment');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Send an order email directly using the shared email library (fire-and-forget)
|
|
9
|
+
*
|
|
10
|
+
* @param {object} options
|
|
11
|
+
* @param {string} options.template - SendGrid dynamic template ID
|
|
12
|
+
* @param {string} options.subject - Email subject line
|
|
13
|
+
* @param {string[]} options.categories - SendGrid categories for filtering
|
|
14
|
+
* @param {object} options.data - Template data (passed as-is to the email)
|
|
15
|
+
* @param {object} options.userDoc - User document data (passed as `to` — email.js extracts email/name and user template data)
|
|
16
|
+
* @param {object} options.ctx - Assistant instance
|
|
17
|
+
*/
|
|
18
|
+
function sendOrderEmail({ template, subject, categories, data, userDoc, ctx, copy, internalOnly, sender = 'orders' }) {
|
|
19
|
+
const email = ctx.Manager.Email(ctx);
|
|
20
|
+
const uid = userDoc?.auth?.uid;
|
|
21
|
+
|
|
22
|
+
if (!internalOnly && !userDoc?.auth?.email) {
|
|
23
|
+
ctx.error(`sendOrderEmail(): No email found for uid=${uid}, skipping`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const brandContact = ctx.Manager.config?.brand?.contact;
|
|
28
|
+
const to = internalOnly
|
|
29
|
+
? { email: brandContact?.email, name: brandContact?.name || ctx.Manager.config?.brand?.name }
|
|
30
|
+
: userDoc;
|
|
31
|
+
|
|
32
|
+
const content = data?.content || {};
|
|
33
|
+
ctx.log(`sendOrderEmail(): template=${template}, price=${content.unified?.payment?.price}, frequency=${content.unified?.payment?.frequency}, totalToday=${content._computed?.totalToday}, firstCharge=${content._computed?.firstChargeAmount}, trial=${content.unified?.trial?.claimed}, productId=${content.unified?.product?.id}`);
|
|
34
|
+
|
|
35
|
+
email.send({
|
|
36
|
+
sender,
|
|
37
|
+
to,
|
|
38
|
+
subject,
|
|
39
|
+
template,
|
|
40
|
+
categories,
|
|
41
|
+
copy: internalOnly ? false : copy !== false,
|
|
42
|
+
data,
|
|
43
|
+
})
|
|
44
|
+
.then((result) => {
|
|
45
|
+
ctx.log(`sendOrderEmail(): Success template=${template}, uid=${uid}, status=${result.status}`);
|
|
46
|
+
})
|
|
47
|
+
.catch((e) => {
|
|
48
|
+
ctx.error(`sendOrderEmail(): Failed template=${template}, uid=${uid}: ${e.message}`);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Format an ISO timestamp or Unix timestamp to display format
|
|
54
|
+
*
|
|
55
|
+
* @param {string|number} timestamp - ISO string or Unix timestamp (seconds)
|
|
56
|
+
* @returns {string} Formatted date (e.g., 'Feb 25, 2026')
|
|
57
|
+
*/
|
|
58
|
+
function formatDate(timestamp) {
|
|
59
|
+
if (!timestamp) {
|
|
60
|
+
return '';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Unix timestamp (number or numeric string)
|
|
64
|
+
if (typeof timestamp === 'number' || /^\d+$/.test(timestamp)) {
|
|
65
|
+
return moment.unix(Number(timestamp)).utc().format('MMM D, YYYY');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return moment(timestamp).utc().format('MMM D, YYYY');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
sendOrderEmail,
|
|
73
|
+
formatDate,
|
|
74
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transition: cancellation-removed
|
|
3
|
+
* Triggered when a scheduled cancellation is withdrawn (cancellation.pending true → false, still active)
|
|
4
|
+
*
|
|
5
|
+
* Log-only. The order template's event variants have no copy for a cancellation
|
|
6
|
+
* being withdrawn, and an unknown event falls back to the 'confirmation' variant —
|
|
7
|
+
* which would tell a subscriber whose plan never lapsed that their subscription is
|
|
8
|
+
* confirmed, with a fresh order summary and a "total paid today" they were not
|
|
9
|
+
* charged. Sending the wrong email is worse than sending none, so this transition
|
|
10
|
+
* stays a record until the template carries the copy ([#212]).
|
|
11
|
+
*/
|
|
12
|
+
module.exports = async function ({ before, after, order, uid, ctx }) {
|
|
13
|
+
ctx.log(`Transition [subscription/cancellation-removed]: uid=${uid}, product=${after.product?.id}, previousCancelDate=${before?.cancellation?.date?.timestamp}, order=${order?.id || 'none'} (no email — the order template has no cancellation-removed variant)`);
|
|
14
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transition: cancellation-requested
|
|
3
|
+
* Triggered when a user requests cancellation at period end (cancellation.pending flips to true)
|
|
4
|
+
*/
|
|
5
|
+
const { sendOrderEmail, formatDate } = require('../send-email.js');
|
|
6
|
+
|
|
7
|
+
module.exports = async function ({ before, after, order, uid, userDoc, ctx }) {
|
|
8
|
+
ctx.log(`Transition [subscription/cancellation-requested]: uid=${uid}, product=${after.product?.id}, cancelDate=${after.cancellation?.date?.timestamp}`);
|
|
9
|
+
|
|
10
|
+
sendOrderEmail({
|
|
11
|
+
template: 'order',
|
|
12
|
+
subject: `Your cancellation is confirmed #${order?.id || ''}`,
|
|
13
|
+
categories: ['order/cancellation-requested'],
|
|
14
|
+
userDoc,
|
|
15
|
+
ctx,
|
|
16
|
+
data: {
|
|
17
|
+
content: { event: 'cancellation-requested',
|
|
18
|
+
...order,
|
|
19
|
+
_computed: {
|
|
20
|
+
date: formatDate(new Date().toISOString()),
|
|
21
|
+
...(after.cancellation?.date?.timestamp && {
|
|
22
|
+
cancellationDate: formatDate(after.cancellation.date.timestamp),
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transition: checkout-declined
|
|
3
|
+
* Triggered when a checkout's payment is declined, from either entry state:
|
|
4
|
+
* - a FIRST checkout (basic/null → suspended) ([#212])
|
|
5
|
+
* - a declined WIN-BACK: a fully cancelled subscriber resubscribing, which keeps
|
|
6
|
+
* the paid product id through the cancellation (cancelled paid → suspended)
|
|
7
|
+
* ([#223](https://github.com/Omega-JS-Stack/omega/issues/223))
|
|
8
|
+
*
|
|
9
|
+
* Log-only, deliberately, for both. This user is standing at the checkout watching
|
|
10
|
+
* the decline happen — the payment-failed email this case used to send is renewal
|
|
11
|
+
* dunning copy ("your access has been suspended", "update your payment method to
|
|
12
|
+
* restore access") addressed to someone who is not mid-subscription and has no
|
|
13
|
+
* access to lose. No analytics either: no money moved, and the resolver tracks
|
|
14
|
+
* payments, not attempts ([#212]).
|
|
15
|
+
*/
|
|
16
|
+
module.exports = async function ({ before, after, order, uid, ctx }) {
|
|
17
|
+
ctx.log(`Transition [subscription/checkout-declined]: uid=${uid}, attemptedProduct=${after.product?.id}, previousProduct=${before?.product?.id || 'none'}, order=${order?.id || 'none'} (no email — the user is at checkout and sees the decline)`);
|
|
18
|
+
};
|