@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,767 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test: payment transition detection
|
|
3
|
+
* Unit tests for detectSubscriptionTransition() — a pure function over the
|
|
4
|
+
* before/after unified subscription plus the webhook's event type.
|
|
5
|
+
*
|
|
6
|
+
* Covers the two things the event type alone decides:
|
|
7
|
+
* 1. Refund detection, which must know EVERY provider's refund event string
|
|
8
|
+
* (the strings are pinned against each provider's own webhook parser here,
|
|
9
|
+
* so a parser that renames one fails this test instead of silently dropping
|
|
10
|
+
* the customer's refund email)
|
|
11
|
+
* 2. Refund idempotency — a webhook doc that already completed once must not
|
|
12
|
+
* dispatch payment-refunded (and its email) a second time
|
|
13
|
+
* 3. The one-time side of the same question — detectOneTimeTransition() reads
|
|
14
|
+
* the event type alone, so EVERY one-time transition needs that same
|
|
15
|
+
* idempotency guard, not just the refund
|
|
16
|
+
*
|
|
17
|
+
* And the state rules, where ORDER inside the array is the behavior: each rule
|
|
18
|
+
* added here is proved to fire AND proved not to shadow the rule it sits in
|
|
19
|
+
* front of ([#212](https://github.com/Omega-JS-Stack/omega/issues/212)).
|
|
20
|
+
*/
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const transitions = require('../../../dist/manager/events/firestore/payments-webhooks/transitions/index.js');
|
|
24
|
+
const stripeProvider = require('../../../dist/manager/routes/payments/webhook/providers/stripe.js');
|
|
25
|
+
const paypalProvider = require('../../../dist/manager/routes/payments/webhook/providers/paypal.js');
|
|
26
|
+
const chargebeeProvider = require('../../../dist/manager/routes/payments/webhook/providers/chargebee.js');
|
|
27
|
+
const coinbaseProvider = require('../../../dist/manager/routes/payments/webhook/providers/coinbase.js');
|
|
28
|
+
const defineCases = require('../../../dist/vendor/devkit/test/define-cases.js');
|
|
29
|
+
|
|
30
|
+
// Every transition name the detector can return, and the category whose folder
|
|
31
|
+
// holds its handler file
|
|
32
|
+
const HANDLED_TRANSITIONS = [
|
|
33
|
+
['subscription', 'new-subscription'],
|
|
34
|
+
['subscription', 'subscription-winback'],
|
|
35
|
+
['subscription', 'checkout-declined'],
|
|
36
|
+
['subscription', 'payment-failed'],
|
|
37
|
+
['subscription', 'payment-recovered'],
|
|
38
|
+
['subscription', 'cancellation-requested'],
|
|
39
|
+
['subscription', 'cancellation-removed'],
|
|
40
|
+
['subscription', 'subscription-cancelled'],
|
|
41
|
+
['subscription', 'plan-changed'],
|
|
42
|
+
['subscription', 'payment-refunded'],
|
|
43
|
+
['one-time', 'purchase-completed'],
|
|
44
|
+
['one-time', 'purchase-failed'],
|
|
45
|
+
['one-time', 'purchase-refunded'],
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// A paid, active unified subscription — the shape a refund webhook arrives against
|
|
49
|
+
function paidSubscription() {
|
|
50
|
+
return {
|
|
51
|
+
product: { id: 'premium', name: 'Premium' },
|
|
52
|
+
status: 'active',
|
|
53
|
+
cancellation: { pending: false },
|
|
54
|
+
payment: { frequency: 'monthly', price: 9.99 },
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// A user doc's birth state: active on basic, never subscribed to anything
|
|
59
|
+
function basicSubscription() {
|
|
60
|
+
return {
|
|
61
|
+
product: { id: 'basic', name: 'Basic' },
|
|
62
|
+
status: 'active',
|
|
63
|
+
cancellation: { pending: false },
|
|
64
|
+
payment: {},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// A fully cancelled paid subscriber — the state a win-back starts from
|
|
69
|
+
function cancelledSubscription() {
|
|
70
|
+
return {
|
|
71
|
+
product: { id: 'premium', name: 'Premium' },
|
|
72
|
+
status: 'cancelled',
|
|
73
|
+
cancellation: { pending: false },
|
|
74
|
+
payment: { frequency: 'monthly', price: 9.99 },
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = defineCases({
|
|
79
|
+
description: 'Payment transition detection (state rules, refund events + idempotency)',
|
|
80
|
+
type: 'group',
|
|
81
|
+
|
|
82
|
+
tests: [
|
|
83
|
+
// ─── State rules: win-back ───
|
|
84
|
+
|
|
85
|
+
{
|
|
86
|
+
name: 'detects-a-win-back',
|
|
87
|
+
async run({ assert }) {
|
|
88
|
+
// A subscriber who cancelled outright and came back: the product id stays
|
|
89
|
+
// paid through the cancellation, so no rule but this one describes it
|
|
90
|
+
const detected = transitions.detectSubscriptionTransition(cancelledSubscription(), paidSubscription(), 'customer.subscription.created');
|
|
91
|
+
|
|
92
|
+
assert.equal(detected, 'subscription-winback', 'cancelled paid → active paid should detect subscription-winback');
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
{
|
|
97
|
+
name: 'a-first-subscription-is-still-new-subscription',
|
|
98
|
+
async run({ assert }) {
|
|
99
|
+
// The win-back rule sits behind new-subscription — a user arriving from
|
|
100
|
+
// basic is still buying for the first time
|
|
101
|
+
const detected = transitions.detectSubscriptionTransition(basicSubscription(), paidSubscription(), 'customer.subscription.created');
|
|
102
|
+
|
|
103
|
+
assert.equal(detected, 'new-subscription', 'basic → active paid should still detect new-subscription');
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
{
|
|
108
|
+
name: 'a-cancelled-basic-user-subscribing-is-still-new-subscription',
|
|
109
|
+
async run({ assert }) {
|
|
110
|
+
// A cancellation that also downgraded the product leaves nothing to win
|
|
111
|
+
// back — that user reads as a first purchase, as it always has
|
|
112
|
+
const before = cancelledSubscription();
|
|
113
|
+
before.product = { id: 'basic', name: 'Basic' };
|
|
114
|
+
|
|
115
|
+
const detected = transitions.detectSubscriptionTransition(before, paidSubscription(), 'customer.subscription.created');
|
|
116
|
+
|
|
117
|
+
assert.equal(detected, 'new-subscription', 'cancelled basic → active paid should still detect new-subscription');
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
// ─── State rules: checkout decline ───
|
|
122
|
+
|
|
123
|
+
{
|
|
124
|
+
name: 'detects-a-first-checkout-decline',
|
|
125
|
+
async run({ assert }) {
|
|
126
|
+
// A user doc is born active on basic, so a declined FIRST checkout is
|
|
127
|
+
// basic active → suspended: a checkout the user is watching fail, not a
|
|
128
|
+
// renewal that needs dunning
|
|
129
|
+
const after = paidSubscription();
|
|
130
|
+
after.status = 'suspended';
|
|
131
|
+
|
|
132
|
+
const detected = transitions.detectSubscriptionTransition(basicSubscription(), after, 'customer.subscription.created');
|
|
133
|
+
|
|
134
|
+
assert.equal(detected, 'checkout-declined', 'basic → suspended should detect checkout-declined');
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
{
|
|
139
|
+
name: 'a-new-user-declining-is-also-a-checkout-decline',
|
|
140
|
+
async run({ assert }) {
|
|
141
|
+
// No user doc at all (no before state) is the same case
|
|
142
|
+
const after = paidSubscription();
|
|
143
|
+
after.status = 'suspended';
|
|
144
|
+
|
|
145
|
+
const detected = transitions.detectSubscriptionTransition(null, after, 'customer.subscription.created');
|
|
146
|
+
|
|
147
|
+
assert.equal(detected, 'checkout-declined', 'null → suspended should detect checkout-declined');
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
{
|
|
152
|
+
name: 'detects-a-declined-win-back-checkout',
|
|
153
|
+
async run({ assert }) {
|
|
154
|
+
// The win-back branch of the same moment: a full cancellation leaves the
|
|
155
|
+
// PAID product id on the user doc, so a returning subscriber whose payment
|
|
156
|
+
// declines is cancelled paid → suspended — not the rule above (before must
|
|
157
|
+
// be basic), not subscription-winback (after must be active), not
|
|
158
|
+
// payment-failed (before must be active). It matched nothing at all, and
|
|
159
|
+
// the webhook completed with no log and no analytics
|
|
160
|
+
// ([#223](https://github.com/Omega-JS-Stack/omega/issues/223))
|
|
161
|
+
const after = paidSubscription();
|
|
162
|
+
after.status = 'suspended';
|
|
163
|
+
|
|
164
|
+
const detected = transitions.detectSubscriptionTransition(cancelledSubscription(), after, 'customer.subscription.created');
|
|
165
|
+
|
|
166
|
+
assert.equal(detected, 'checkout-declined', 'cancelled paid → suspended should detect checkout-declined');
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
{
|
|
171
|
+
name: 'a-returning-subscriber-who-pays-is-still-a-win-back',
|
|
172
|
+
async run({ assert }) {
|
|
173
|
+
// The declined-win-back rule sits BEHIND subscription-winback — the same
|
|
174
|
+
// before state, and only the outcome tells them apart
|
|
175
|
+
const detected = transitions.detectSubscriptionTransition(cancelledSubscription(), paidSubscription(), 'customer.subscription.created');
|
|
176
|
+
|
|
177
|
+
assert.equal(detected, 'subscription-winback', 'cancelled paid → active paid should still detect subscription-winback');
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
{
|
|
182
|
+
name: 'a-paying-subscriber-still-gets-payment-failed',
|
|
183
|
+
async run({ assert }) {
|
|
184
|
+
// The decline rule sits in FRONT of payment-failed, so this is the one
|
|
185
|
+
// that proves it did not swallow the dunning case it precedes
|
|
186
|
+
const after = paidSubscription();
|
|
187
|
+
after.status = 'suspended';
|
|
188
|
+
|
|
189
|
+
const detected = transitions.detectSubscriptionTransition(paidSubscription(), after, 'invoice.payment_failed');
|
|
190
|
+
|
|
191
|
+
assert.equal(detected, 'payment-failed', 'active paid → suspended should still detect payment-failed');
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
{
|
|
196
|
+
name: 'a-second-decline-event-fires-nothing',
|
|
197
|
+
async run({ assert }) {
|
|
198
|
+
// The declined checkout's failed invoice arrives after the subscription
|
|
199
|
+
// event already suspended the user — suspended → suspended, no repeat
|
|
200
|
+
const before = paidSubscription();
|
|
201
|
+
before.status = 'suspended';
|
|
202
|
+
const after = paidSubscription();
|
|
203
|
+
after.status = 'suspended';
|
|
204
|
+
|
|
205
|
+
const detected = transitions.detectSubscriptionTransition(before, after, 'invoice.payment_failed');
|
|
206
|
+
|
|
207
|
+
assert.equal(detected, null, 'suspended → suspended should detect nothing');
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
// ─── State rules: recovery ───
|
|
212
|
+
|
|
213
|
+
{
|
|
214
|
+
name: 'detects-a-plain-recovery',
|
|
215
|
+
async run({ assert }) {
|
|
216
|
+
// Dunning's happy ending, with no cancellation schedule in the way:
|
|
217
|
+
// the retry succeeded and the provider put the subscriber back
|
|
218
|
+
const before = paidSubscription();
|
|
219
|
+
before.status = 'suspended';
|
|
220
|
+
|
|
221
|
+
const detected = transitions.detectSubscriptionTransition(before, paidSubscription(), 'invoice.payment_succeeded');
|
|
222
|
+
|
|
223
|
+
assert.equal(detected, 'payment-recovered', 'suspended → active should detect payment-recovered');
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
// ─── State rules: cancellation withdrawn ───
|
|
228
|
+
|
|
229
|
+
{
|
|
230
|
+
name: 'detects-a-cancellation-being-withdrawn',
|
|
231
|
+
async run({ assert }) {
|
|
232
|
+
const before = paidSubscription();
|
|
233
|
+
before.cancellation = { pending: true };
|
|
234
|
+
|
|
235
|
+
const detected = transitions.detectSubscriptionTransition(before, paidSubscription(), 'customer.subscription.updated');
|
|
236
|
+
|
|
237
|
+
assert.equal(detected, 'cancellation-removed', 'pending true → false while active should detect cancellation-removed');
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
{
|
|
242
|
+
name: 'a-cancellation-being-scheduled-is-still-cancellation-requested',
|
|
243
|
+
async run({ assert }) {
|
|
244
|
+
const after = paidSubscription();
|
|
245
|
+
after.cancellation = { pending: true };
|
|
246
|
+
|
|
247
|
+
const detected = transitions.detectSubscriptionTransition(paidSubscription(), after, 'customer.subscription.updated');
|
|
248
|
+
|
|
249
|
+
assert.equal(detected, 'cancellation-requested', 'pending false → true should still detect cancellation-requested');
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
{
|
|
254
|
+
name: 'a-recovery-that-clears-a-pending-cancellation-is-still-payment-recovered',
|
|
255
|
+
async run({ assert }) {
|
|
256
|
+
// The withdrawal rule sits BEHIND payment-recovered — money moving is the
|
|
257
|
+
// more meaningful half of a suspended subscriber coming back
|
|
258
|
+
const before = paidSubscription();
|
|
259
|
+
before.status = 'suspended';
|
|
260
|
+
before.cancellation = { pending: true };
|
|
261
|
+
|
|
262
|
+
const detected = transitions.detectSubscriptionTransition(before, paidSubscription(), 'invoice.payment_succeeded');
|
|
263
|
+
|
|
264
|
+
assert.equal(detected, 'payment-recovered', 'suspended → active should still detect payment-recovered');
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
{
|
|
269
|
+
name: 'a-plan-change-that-clears-a-pending-cancellation-is-still-plan-changed',
|
|
270
|
+
async run({ assert }) {
|
|
271
|
+
// Same product is part of the rule, so switching plans stays a plan change
|
|
272
|
+
const before = paidSubscription();
|
|
273
|
+
before.cancellation = { pending: true };
|
|
274
|
+
const after = paidSubscription();
|
|
275
|
+
after.product = { id: 'pro', name: 'Pro' };
|
|
276
|
+
|
|
277
|
+
const detected = transitions.detectSubscriptionTransition(before, after, 'customer.subscription.updated');
|
|
278
|
+
|
|
279
|
+
assert.equal(detected, 'plan-changed', 'A different product should still detect plan-changed');
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
// ─── State rules: cancellation ───
|
|
284
|
+
|
|
285
|
+
{
|
|
286
|
+
name: 'detects-a-cancellation',
|
|
287
|
+
async run({ assert }) {
|
|
288
|
+
// The term actually ended (or the provider deleted the subscription):
|
|
289
|
+
// any non-cancelled state arriving at cancelled is the one rule for it
|
|
290
|
+
const after = paidSubscription();
|
|
291
|
+
after.status = 'cancelled';
|
|
292
|
+
|
|
293
|
+
const detected = transitions.detectSubscriptionTransition(paidSubscription(), after, 'customer.subscription.deleted');
|
|
294
|
+
|
|
295
|
+
assert.equal(detected, 'subscription-cancelled', 'active paid → cancelled should detect subscription-cancelled');
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
{
|
|
300
|
+
name: 'a-cancellation-that-also-drops-the-product-is-still-a-cancellation',
|
|
301
|
+
async run({ assert }) {
|
|
302
|
+
// The cancel route writes cancelled AND resets to basic in one go — the
|
|
303
|
+
// product moving must not make this read as a plan change
|
|
304
|
+
const after = cancelledSubscription();
|
|
305
|
+
after.product = { id: 'basic', name: 'Basic' };
|
|
306
|
+
|
|
307
|
+
const detected = transitions.detectSubscriptionTransition(paidSubscription(), after, 'customer.subscription.deleted');
|
|
308
|
+
|
|
309
|
+
assert.equal(detected, 'subscription-cancelled', 'active paid → cancelled basic should still detect subscription-cancelled');
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
{
|
|
314
|
+
name: 'a-second-cancellation-event-fires-nothing',
|
|
315
|
+
async run({ assert }) {
|
|
316
|
+
// The rule reads the EDGE (non-cancelled → cancelled), so a redelivered
|
|
317
|
+
// deletion against an already-cancelled subscriber sends no second email
|
|
318
|
+
const detected = transitions.detectSubscriptionTransition(cancelledSubscription(), cancelledSubscription(), 'customer.subscription.deleted');
|
|
319
|
+
|
|
320
|
+
assert.equal(detected, null, 'cancelled → cancelled should detect nothing');
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
// ─── State rules: plan change ───
|
|
325
|
+
|
|
326
|
+
{
|
|
327
|
+
name: 'detects-a-plan-change',
|
|
328
|
+
async run({ assert }) {
|
|
329
|
+
const after = paidSubscription();
|
|
330
|
+
after.product = { id: 'pro', name: 'Pro' };
|
|
331
|
+
|
|
332
|
+
const detected = transitions.detectSubscriptionTransition(paidSubscription(), after, 'customer.subscription.updated');
|
|
333
|
+
|
|
334
|
+
assert.equal(detected, 'plan-changed', 'active premium → active pro should detect plan-changed');
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
{
|
|
339
|
+
name: 'a-frequency-only-switch-detects-nothing',
|
|
340
|
+
async run({ assert }) {
|
|
341
|
+
// The rule keys on the PRODUCT, so monthly → annually on the same plan
|
|
342
|
+
// has no transition of its own. Recorded here because it is a real gap in
|
|
343
|
+
// the table, not an accident: the plan route's own tests pin the state it
|
|
344
|
+
// writes, and analytics reads the renewal, but no email fires for it
|
|
345
|
+
const after = paidSubscription();
|
|
346
|
+
after.payment = { frequency: 'annually', price: 99.99 };
|
|
347
|
+
|
|
348
|
+
const detected = transitions.detectSubscriptionTransition(paidSubscription(), after, 'customer.subscription.updated');
|
|
349
|
+
|
|
350
|
+
assert.equal(detected, null, 'A frequency-only switch has no transition rule');
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
// ─── The guards around the table ───
|
|
355
|
+
|
|
356
|
+
{
|
|
357
|
+
name: 'no-after-state-detects-nothing',
|
|
358
|
+
async run({ assert }) {
|
|
359
|
+
// The first line of the detector: with nothing to compare against, every
|
|
360
|
+
// rule below would read `undefined.status` and throw
|
|
361
|
+
assert.equal(transitions.detectSubscriptionTransition(paidSubscription(), null, 'customer.subscription.updated'), null, 'A null after state should detect nothing');
|
|
362
|
+
assert.equal(transitions.detectSubscriptionTransition(paidSubscription(), undefined, 'customer.subscription.updated'), null, 'An undefined after state should detect nothing');
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
{
|
|
367
|
+
name: 'an-unknown-category-detects-nothing',
|
|
368
|
+
async run({ assert }) {
|
|
369
|
+
// on-write refuses an unknown category before it ever gets here, so this
|
|
370
|
+
// is the detector's own belt: a category it does not know routes nowhere
|
|
371
|
+
const detected = transitions.detectTransition('donation', null, paidSubscription(), 'charge.refunded');
|
|
372
|
+
|
|
373
|
+
assert.equal(detected, null, 'An unknown category should route to no transition');
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
|
|
377
|
+
{
|
|
378
|
+
name: 'an-unknown-event-type-detects-nothing-on-either-side',
|
|
379
|
+
async run({ assert }) {
|
|
380
|
+
// The one-time side reads the event type ALONE, so an event nothing maps
|
|
381
|
+
// must come back null rather than falling into the last rule it read
|
|
382
|
+
assert.equal(transitions.detectOneTimeTransition('customer.subscription.trial_will_end'), null, 'An unmapped event should detect no one-time transition');
|
|
383
|
+
|
|
384
|
+
// The subscription side ignores the event type unless it is a refund, so
|
|
385
|
+
// an unknown type over an unchanged subscription (a renewal) is also null
|
|
386
|
+
assert.equal(transitions.detectSubscriptionTransition(paidSubscription(), paidSubscription(), 'invoice.payment_succeeded'), null, 'A renewal has no transition of its own');
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
{
|
|
391
|
+
name: 'the-paid-helpers-read-the-product-not-the-status',
|
|
392
|
+
async run({ assert }) {
|
|
393
|
+
// Every rule in the table branches on these two in boolean position, and
|
|
394
|
+
// both answer for a subscription in ANY status — the status is the other
|
|
395
|
+
// half of each rule, never these
|
|
396
|
+
assert.ok(transitions.isBasicOrNull(null), 'No subscription at all is basic-or-null');
|
|
397
|
+
assert.ok(transitions.isBasicOrNull({ status: 'active' }), 'A subscription with no product is basic-or-null');
|
|
398
|
+
assert.ok(transitions.isBasicOrNull(basicSubscription()), 'Basic is basic-or-null');
|
|
399
|
+
assert.ok(!transitions.isBasicOrNull(cancelledSubscription()), 'A cancelled PAID subscription is not basic — that is what makes the win-back rules necessary');
|
|
400
|
+
|
|
401
|
+
assert.ok(!transitions.isPaid(null), 'No subscription at all is not paid');
|
|
402
|
+
assert.ok(!transitions.isPaid(basicSubscription()), 'Basic is not paid');
|
|
403
|
+
assert.ok(transitions.isPaid(cancelledSubscription()), 'A cancelled paid subscription still reads as paid');
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
|
|
407
|
+
// ─── Every rule has somewhere to dispatch ───
|
|
408
|
+
|
|
409
|
+
{
|
|
410
|
+
name: 'every-detectable-transition-has-a-handler-file',
|
|
411
|
+
async run({ assert }) {
|
|
412
|
+
// dispatch() resolves <category>/<name>.js by name — a rule whose handler
|
|
413
|
+
// file is missing logs "not found" and silently sends the customer nothing
|
|
414
|
+
const root = path.join(__dirname, '../../../dist/manager/events/firestore/payments-webhooks/transitions');
|
|
415
|
+
|
|
416
|
+
for (const [category, name] of HANDLED_TRANSITIONS) {
|
|
417
|
+
const handlerPath = path.join(root, category, `${name}.js`);
|
|
418
|
+
|
|
419
|
+
assert.ok(fs.existsSync(handlerPath), `Transition ${category}/${name} should have a handler file`);
|
|
420
|
+
assert.equal(typeof require(handlerPath), 'function', `Transition ${category}/${name} handler should export a function`);
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
|
|
425
|
+
// ─── Refund event strings, per provider ───
|
|
426
|
+
|
|
427
|
+
{
|
|
428
|
+
name: 'detects-stripe-refund',
|
|
429
|
+
async run({ assert }) {
|
|
430
|
+
const detected = transitions.detectSubscriptionTransition(paidSubscription(), paidSubscription(), 'charge.refunded');
|
|
431
|
+
|
|
432
|
+
assert.equal(detected, 'payment-refunded', 'charge.refunded should detect payment-refunded');
|
|
433
|
+
assert.ok(stripeProvider.isSupported('charge.refunded'), 'Stripe provider should accept the same event string');
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
|
|
437
|
+
{
|
|
438
|
+
name: 'detects-paypal-refund',
|
|
439
|
+
async run({ assert }) {
|
|
440
|
+
const detected = transitions.detectSubscriptionTransition(paidSubscription(), paidSubscription(), 'PAYMENT.SALE.REFUNDED');
|
|
441
|
+
|
|
442
|
+
assert.equal(detected, 'payment-refunded', 'PAYMENT.SALE.REFUNDED should detect payment-refunded');
|
|
443
|
+
assert.ok(paypalProvider.isSupported('PAYMENT.SALE.REFUNDED'), 'PayPal provider should accept the same event string');
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
|
|
447
|
+
{
|
|
448
|
+
name: 'detects-chargebee-refund',
|
|
449
|
+
async run({ assert }) {
|
|
450
|
+
const detected = transitions.detectSubscriptionTransition(paidSubscription(), paidSubscription(), 'payment_refunded');
|
|
451
|
+
|
|
452
|
+
assert.equal(detected, 'payment-refunded', 'payment_refunded should detect payment-refunded');
|
|
453
|
+
assert.ok(chargebeeProvider.isSupported('payment_refunded'), 'Chargebee provider should accept the same event string');
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
|
|
457
|
+
{
|
|
458
|
+
name: 'a-refund-outranks-every-state-rule',
|
|
459
|
+
async run({ assert }) {
|
|
460
|
+
// The refund check sits in FRONT of the whole table, deliberately: a full
|
|
461
|
+
// refund usually cancels the subscription in the same breath, and reading
|
|
462
|
+
// that state diff instead would send the cancellation email for a refund
|
|
463
|
+
const after = paidSubscription();
|
|
464
|
+
after.status = 'cancelled';
|
|
465
|
+
|
|
466
|
+
const detected = transitions.detectSubscriptionTransition(paidSubscription(), after, 'charge.refunded');
|
|
467
|
+
|
|
468
|
+
assert.equal(detected, 'payment-refunded', 'A refund that also cancels should still detect payment-refunded');
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
|
|
472
|
+
{
|
|
473
|
+
name: 'every-refund-event-is-a-parsed-event',
|
|
474
|
+
async run({ assert }) {
|
|
475
|
+
// Each refund string must be one a webhook parser actually delivers —
|
|
476
|
+
// an event no provider reports could never fire the transition
|
|
477
|
+
for (const eventType of transitions.REFUND_EVENTS) {
|
|
478
|
+
const supported = stripeProvider.isSupported(eventType)
|
|
479
|
+
|| paypalProvider.isSupported(eventType)
|
|
480
|
+
|| chargebeeProvider.isSupported(eventType);
|
|
481
|
+
|
|
482
|
+
assert.ok(supported, `Refund event ${eventType} should be supported by a webhook provider`);
|
|
483
|
+
}
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
|
|
487
|
+
// ─── One-time refunds ───
|
|
488
|
+
|
|
489
|
+
{
|
|
490
|
+
name: 'detects-a-one-time-refund',
|
|
491
|
+
async run({ assert }) {
|
|
492
|
+
const detected = transitions.detectOneTimeTransition('charge.refunded');
|
|
493
|
+
|
|
494
|
+
assert.equal(detected, 'purchase-refunded', 'A refunded one-time purchase should detect purchase-refunded');
|
|
495
|
+
},
|
|
496
|
+
},
|
|
497
|
+
|
|
498
|
+
{
|
|
499
|
+
name: 'every-refund-event-detects-on-the-one-time-side',
|
|
500
|
+
async run({ assert }) {
|
|
501
|
+
// The one-time side reads the same provider strings the subscription
|
|
502
|
+
// side does — a refund is a refund whichever thing was bought
|
|
503
|
+
for (const eventType of transitions.REFUND_EVENTS) {
|
|
504
|
+
assert.equal(
|
|
505
|
+
transitions.detectOneTimeTransition(eventType),
|
|
506
|
+
'purchase-refunded',
|
|
507
|
+
`${eventType} should detect purchase-refunded for a one-time purchase`,
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
{
|
|
514
|
+
name: 'dispatches-a-one-time-refund-through-detect-transition',
|
|
515
|
+
async run({ assert }) {
|
|
516
|
+
// The path on-write actually calls: category + event type, no before state
|
|
517
|
+
const detected = transitions.detectTransition('one-time', null, { product: { id: 'credits' }, status: 'completed' }, 'charge.refunded');
|
|
518
|
+
|
|
519
|
+
assert.equal(detected, 'purchase-refunded', 'A one-time refund should route to purchase-refunded');
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
|
|
523
|
+
{
|
|
524
|
+
name: 'a-one-time-purchase-still-detects-purchase-completed',
|
|
525
|
+
async run({ assert }) {
|
|
526
|
+
// The refund mapping must not shadow the events already on this side
|
|
527
|
+
assert.equal(transitions.detectOneTimeTransition('checkout.session.completed'), 'purchase-completed', 'A completed checkout is still a purchase');
|
|
528
|
+
assert.equal(transitions.detectOneTimeTransition('CHECKOUT.ORDER.APPROVED'), 'purchase-completed', 'An approved PayPal order is still a purchase');
|
|
529
|
+
assert.equal(transitions.detectOneTimeTransition('invoice.payment_failed'), 'purchase-failed', 'A failed one-time invoice is still a failure');
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
|
|
533
|
+
{
|
|
534
|
+
name: 'a-chargebee-one-time-failure-detects-through-the-provider-fold',
|
|
535
|
+
async run({ assert }) {
|
|
536
|
+
// The fold the pipeline runs: the provider categorizes a
|
|
537
|
+
// subscription-less payment_failed as one-time, and the detector reads
|
|
538
|
+
// the provider's OWN event string off that same parse. Reading only
|
|
539
|
+
// Stripe's string left the Chargebee half of this path detecting
|
|
540
|
+
// nothing, so the failed-payment email never sent
|
|
541
|
+
// ([#714](https://github.com/Omega-JS-Stack/omega/issues/714)).
|
|
542
|
+
const parsed = chargebeeProvider.parseWebhook({
|
|
543
|
+
body: {
|
|
544
|
+
id: 'ev_cb_onetime_fail',
|
|
545
|
+
event_type: 'payment_failed',
|
|
546
|
+
content: {
|
|
547
|
+
invoice: { id: 'inv_cb_onetime_fail' },
|
|
548
|
+
customer: { id: 'cust_cb_onetime', meta_data: '{"uid":"_test-cb-onetime-fail"}' },
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
assert.equal(parsed.category, 'one-time', 'A subscription-less Chargebee payment_failed parses as one-time');
|
|
554
|
+
|
|
555
|
+
const detected = transitions.detectTransition(parsed.category, null, { product: { id: 'credits' }, status: 'failed' }, parsed.eventType);
|
|
556
|
+
|
|
557
|
+
assert.equal(detected, 'purchase-failed', 'Chargebee payment_failed should detect purchase-failed for a one-time purchase');
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
|
|
561
|
+
{
|
|
562
|
+
name: 'a-chargebee-one-time-purchase-detects-through-the-provider-fold',
|
|
563
|
+
async run({ assert }) {
|
|
564
|
+
// The success half of the same fold: a subscription-less
|
|
565
|
+
// payment_succeeded is the honest paid signal for a one-time purchase.
|
|
566
|
+
// Nothing on this side read it, so a paid Chargebee one-time sent no
|
|
567
|
+
// receipt and fired no purchase analytics
|
|
568
|
+
// ([#729](https://github.com/Omega-JS-Stack/omega/issues/729)).
|
|
569
|
+
const parsed = chargebeeProvider.parseWebhook({
|
|
570
|
+
body: {
|
|
571
|
+
id: 'ev_cb_onetime_paid',
|
|
572
|
+
event_type: 'payment_succeeded',
|
|
573
|
+
content: {
|
|
574
|
+
invoice: { id: 'inv_cb_onetime_paid' },
|
|
575
|
+
customer: { id: 'cust_cb_onetime', meta_data: '{"uid":"_test-cb-onetime-paid"}' },
|
|
576
|
+
},
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
assert.equal(parsed.category, 'one-time', 'A subscription-less Chargebee payment_succeeded parses as one-time');
|
|
581
|
+
|
|
582
|
+
const detected = transitions.detectTransition(parsed.category, null, { product: { id: 'credits' }, status: 'completed' }, parsed.eventType);
|
|
583
|
+
|
|
584
|
+
assert.equal(detected, 'purchase-completed', 'Chargebee payment_succeeded should detect purchase-completed for a one-time purchase');
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
|
|
588
|
+
{
|
|
589
|
+
name: 'a-coinbase-charge-detects-through-the-provider-fold',
|
|
590
|
+
async run({ assert }) {
|
|
591
|
+
// Crypto's half of the same fold: every Coinbase Commerce event is about
|
|
592
|
+
// a CHARGE, which is one purchase, so the provider categorizes all three
|
|
593
|
+
// as one-time and the detector reads the provider's own event strings
|
|
594
|
+
// ([#642](https://github.com/Omega-JS-Stack/omega/issues/642)).
|
|
595
|
+
const confirmed = coinbaseProvider.parseWebhook({
|
|
596
|
+
body: {
|
|
597
|
+
id: '_test-delivery',
|
|
598
|
+
event: {
|
|
599
|
+
id: '_test-evt-coinbase-confirmed',
|
|
600
|
+
type: 'charge:confirmed',
|
|
601
|
+
data: { id: 'ch_coinbase_confirmed', metadata: { uid: '_test-coinbase-fold', orderId: 'ord-1', productId: 'credits' } },
|
|
602
|
+
},
|
|
603
|
+
},
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
assert.equal(confirmed.category, 'one-time', 'A confirmed crypto charge parses as one-time');
|
|
607
|
+
assert.equal(
|
|
608
|
+
transitions.detectTransition(confirmed.category, null, { product: { id: 'credits' }, status: 'completed' }, confirmed.eventType),
|
|
609
|
+
'purchase-completed',
|
|
610
|
+
'charge:confirmed should detect purchase-completed — the crypto settled',
|
|
611
|
+
);
|
|
612
|
+
},
|
|
613
|
+
},
|
|
614
|
+
|
|
615
|
+
{
|
|
616
|
+
name: 'an-unsettled-or-expired-crypto-charge-detects-nothing',
|
|
617
|
+
async run({ assert }) {
|
|
618
|
+
// Both write their order (so the account page tells the truth about it)
|
|
619
|
+
// and neither may MAIL: charge:pending is money detected on-chain but not
|
|
620
|
+
// confirmed, and charge:failed is an expired hosted page — the abandoned
|
|
621
|
+
// -checkout population, watched by the person who walked away from it,
|
|
622
|
+
// which is exactly the population checkout-declined refuses to mail. An
|
|
623
|
+
// abandoned Stripe session sends nothing either ([#642]).
|
|
624
|
+
assert.equal(transitions.detectOneTimeTransition('charge:pending'), null, 'Nothing is mailed for money that has not settled');
|
|
625
|
+
assert.equal(transitions.detectOneTimeTransition('charge:failed'), null, 'And nothing is mailed for a checkout nobody paid');
|
|
626
|
+
},
|
|
627
|
+
},
|
|
628
|
+
|
|
629
|
+
{
|
|
630
|
+
name: 'a-chargebee-renewal-charge-never-reaches-the-one-time-side',
|
|
631
|
+
async run({ assert }) {
|
|
632
|
+
// With a subscription present, payment_succeeded is the renewal's charge
|
|
633
|
+
// and `subscription_renewed` already carries it — so the provider gives it
|
|
634
|
+
// no category, no doc is stored, and nothing double-fires ([#729]).
|
|
635
|
+
const parsed = chargebeeProvider.parseWebhook({
|
|
636
|
+
body: {
|
|
637
|
+
id: 'ev_cb_renewal_paid',
|
|
638
|
+
event_type: 'payment_succeeded',
|
|
639
|
+
content: {
|
|
640
|
+
subscription: { id: 'sub_cb_renewal', meta_data: '{"uid":"_test-cb-renewal"}' },
|
|
641
|
+
invoice: { id: 'inv_cb_renewal', subscription_id: 'sub_cb_renewal' },
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
assert.equal(parsed.category, null, 'A Chargebee payment_succeeded carrying a subscription parses as null');
|
|
647
|
+
assert.equal(transitions.detectTransition(parsed.category, null, {}, parsed.eventType), null, 'No category → no transition');
|
|
648
|
+
},
|
|
649
|
+
},
|
|
650
|
+
|
|
651
|
+
{
|
|
652
|
+
name: 'an-unpaid-chargebee-invoice-detects-nothing',
|
|
653
|
+
async run({ assert }) {
|
|
654
|
+
// `invoice_generated` still stores its one-time doc, but an invoice is
|
|
655
|
+
// born UNPAID (the Chargebee library maps `payment_due`/`not_paid` →
|
|
656
|
+
// failed), so it must stay transition-less — mapping it would email a
|
|
657
|
+
// receipt for a purchase nobody paid for ([#729]).
|
|
658
|
+
const parsed = chargebeeProvider.parseWebhook({
|
|
659
|
+
body: {
|
|
660
|
+
id: 'ev_cb_invoice_generated',
|
|
661
|
+
event_type: 'invoice_generated',
|
|
662
|
+
content: {
|
|
663
|
+
invoice: { id: 'inv_cb_unpaid' },
|
|
664
|
+
customer: { id: 'cust_cb_onetime', meta_data: '{"uid":"_test-cb-unpaid"}' },
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
assert.equal(parsed.category, 'one-time', 'A non-recurring invoice_generated still parses as one-time');
|
|
670
|
+
|
|
671
|
+
const detected = transitions.detectTransition(parsed.category, null, { product: { id: 'credits' }, status: 'pending' }, parsed.eventType);
|
|
672
|
+
|
|
673
|
+
assert.equal(detected, null, 'invoice_generated is not a paid signal — no transition, no receipt');
|
|
674
|
+
},
|
|
675
|
+
},
|
|
676
|
+
|
|
677
|
+
{
|
|
678
|
+
name: 'a-redelivered-chargebee-purchase-dispatches-nothing',
|
|
679
|
+
async run({ assert }) {
|
|
680
|
+
// The new arm is event-type-only like the rest of this side, so it needs
|
|
681
|
+
// the same idempotency guard — one purchase, one receipt
|
|
682
|
+
assert.equal(
|
|
683
|
+
transitions.detectOneTimeTransition('payment_succeeded', { previouslyCompleted: true }),
|
|
684
|
+
null,
|
|
685
|
+
'A reprocessed Chargebee purchase should dispatch nothing',
|
|
686
|
+
);
|
|
687
|
+
assert.equal(
|
|
688
|
+
transitions.detectOneTimeTransition('payment_succeeded', { previouslyCompleted: false }),
|
|
689
|
+
'purchase-completed',
|
|
690
|
+
'A first-pass Chargebee purchase should still dispatch',
|
|
691
|
+
);
|
|
692
|
+
},
|
|
693
|
+
},
|
|
694
|
+
|
|
695
|
+
// ─── Refund idempotency ───
|
|
696
|
+
|
|
697
|
+
{
|
|
698
|
+
name: 'refund-fires-on-first-pass',
|
|
699
|
+
async run({ assert }) {
|
|
700
|
+
const detected = transitions.detectSubscriptionTransition(
|
|
701
|
+
paidSubscription(), paidSubscription(), 'charge.refunded', { previouslyCompleted: false },
|
|
702
|
+
);
|
|
703
|
+
|
|
704
|
+
assert.equal(detected, 'payment-refunded', 'A first-pass refund should dispatch');
|
|
705
|
+
},
|
|
706
|
+
},
|
|
707
|
+
|
|
708
|
+
{
|
|
709
|
+
name: 'refund-suppressed-when-doc-already-completed',
|
|
710
|
+
async run({ assert }) {
|
|
711
|
+
const detected = transitions.detectSubscriptionTransition(
|
|
712
|
+
paidSubscription(), paidSubscription(), 'charge.refunded', { previouslyCompleted: true },
|
|
713
|
+
);
|
|
714
|
+
|
|
715
|
+
assert.equal(detected, null, 'A reprocessed refund webhook should dispatch nothing (one refund, one email)');
|
|
716
|
+
},
|
|
717
|
+
},
|
|
718
|
+
|
|
719
|
+
{
|
|
720
|
+
name: 'a-redelivered-one-time-completion-dispatches-nothing',
|
|
721
|
+
async run({ assert }) {
|
|
722
|
+
// The whole one-time side reads the event type alone, so a doc that
|
|
723
|
+
// already completed once would re-dispatch its confirmation email on a
|
|
724
|
+
// redelivery — the same hole the subscription refund path closed
|
|
725
|
+
const detected = transitions.detectOneTimeTransition('checkout.session.completed', { previouslyCompleted: true });
|
|
726
|
+
|
|
727
|
+
assert.equal(detected, null, 'A reprocessed one-time completion should dispatch nothing (one purchase, one email)');
|
|
728
|
+
},
|
|
729
|
+
},
|
|
730
|
+
|
|
731
|
+
{
|
|
732
|
+
name: 'a-first-pass-one-time-completion-still-dispatches',
|
|
733
|
+
async run({ assert }) {
|
|
734
|
+
const detected = transitions.detectOneTimeTransition('checkout.session.completed', { previouslyCompleted: false });
|
|
735
|
+
|
|
736
|
+
assert.equal(detected, 'purchase-completed', 'A first-pass one-time completion should still dispatch');
|
|
737
|
+
},
|
|
738
|
+
},
|
|
739
|
+
|
|
740
|
+
{
|
|
741
|
+
name: 'the-one-time-guard-reaches-detect-transition',
|
|
742
|
+
async run({ assert }) {
|
|
743
|
+
// The path on-write actually calls — the options have to travel through it
|
|
744
|
+
const detected = transitions.detectTransition('one-time', null, { product: { id: 'credits' }, status: 'completed' }, 'checkout.session.completed', { previouslyCompleted: true });
|
|
745
|
+
|
|
746
|
+
assert.equal(detected, null, 'A reprocessed one-time webhook should dispatch nothing through detectTransition');
|
|
747
|
+
},
|
|
748
|
+
},
|
|
749
|
+
|
|
750
|
+
{
|
|
751
|
+
name: 'reprocess-does-not-suppress-other-transitions',
|
|
752
|
+
async run({ assert }) {
|
|
753
|
+
// The guard covers the refund path only — a reprocessed state change is
|
|
754
|
+
// still detected from the before/after diff
|
|
755
|
+
const before = paidSubscription();
|
|
756
|
+
const after = paidSubscription();
|
|
757
|
+
after.status = 'cancelled';
|
|
758
|
+
|
|
759
|
+
const detected = transitions.detectSubscriptionTransition(
|
|
760
|
+
before, after, 'customer.subscription.deleted', { previouslyCompleted: true },
|
|
761
|
+
);
|
|
762
|
+
|
|
763
|
+
assert.equal(detected, 'subscription-cancelled', 'A cancellation should still be detected on a reprocess');
|
|
764
|
+
},
|
|
765
|
+
},
|
|
766
|
+
],
|
|
767
|
+
});
|