@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,341 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const loadProvider = require('../../../libraries/load-provider.js');
|
|
3
|
+
const powertools = require('node-powertools');
|
|
4
|
+
const OrderId = require('../../../libraries/payment/order-id.js');
|
|
5
|
+
const recaptcha = require('../../../libraries/recaptcha.js');
|
|
6
|
+
const discountCodes = require('../../../libraries/payment/discount-codes.js');
|
|
7
|
+
const { hasAuthUser } = require('../../../libraries/auth-user.js');
|
|
8
|
+
const { COLLECTION: CART_COLLECTION } = require('../../../libraries/abandoned-cart-config.js');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* POST /payments/intent
|
|
12
|
+
* Creates a payment intent (e.g., Stripe Checkout Session) for subscription or one-time purchase
|
|
13
|
+
* Requires authentication
|
|
14
|
+
*/
|
|
15
|
+
module.exports = async ({ ctx, Manager, user, settings, libraries }) => {
|
|
16
|
+
const { admin } = libraries;
|
|
17
|
+
|
|
18
|
+
// Require authentication
|
|
19
|
+
if (!user.authenticated) {
|
|
20
|
+
return ctx.respond('Authentication required', { code: 401 });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// A purchaser has to BE a user of this project: an auth user AND the user doc a
|
|
24
|
+
// signup creates behind it. The pair only ever comes apart for a uid that lives
|
|
25
|
+
// in ANOTHER project (a local QA checkout run against the emulator) or an
|
|
26
|
+
// account deleted mid-session — and a checkout begun without it ends as a
|
|
27
|
+
// webhook with nowhere to write, which the pipeline then refuses on its own
|
|
28
|
+
// ([#399](https://github.com/Omega-JS-Stack/omega/issues/399)). Refusing HERE
|
|
29
|
+
// means no provider session, no intent doc, and no half-written account.
|
|
30
|
+
//
|
|
31
|
+
// The DOC half is defense in depth on the token lane — authenticate() only calls
|
|
32
|
+
// a JWT caller authenticated once it has read their user doc — and the live gate
|
|
33
|
+
// on the omega-admin-key lane, which authenticates carrying no user at all.
|
|
34
|
+
const missing = await findMissingPurchaser(admin, user.auth.uid);
|
|
35
|
+
|
|
36
|
+
if (missing) {
|
|
37
|
+
ctx.warn(`Checkout refused: uid=${user.auth.uid} has ${missing} in this project — a purchase requires both an auth user and a user doc`);
|
|
38
|
+
return ctx.respond('Your account could not be verified. Please sign out, sign back in, and try again.', { code: 403 });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Verify reCAPTCHA (skip during automated tests). verify() owns the whole
|
|
42
|
+
// decision: no RECAPTCHA_SECRET_KEY configured → pass (unkeyed brands are a
|
|
43
|
+
// sanctioned population; cp257: keys are optional and per-brand); secret +
|
|
44
|
+
// missing/bad token → fail. No pre-check here: an empty-token 403 before
|
|
45
|
+
// verify() would permanently reject every checkout from a brand that never
|
|
46
|
+
// configured reCAPTCHA. Same shape as routes/marketing/contact/post.js.
|
|
47
|
+
if (!ctx.isTesting()) {
|
|
48
|
+
const recaptchaToken = settings.verification?.['g-recaptcha-response'];
|
|
49
|
+
const recaptchaValid = await recaptcha.verify(recaptchaToken);
|
|
50
|
+
if (!recaptchaValid) {
|
|
51
|
+
return ctx.respond('Request could not be verified', { code: 403 });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const uid = user.auth.uid;
|
|
56
|
+
const provider = settings.provider;
|
|
57
|
+
const productId = settings.productId;
|
|
58
|
+
const attribution = settings.attribution;
|
|
59
|
+
const trackingConsent = settings.trackingConsent;
|
|
60
|
+
const discount = settings.discount;
|
|
61
|
+
const supplemental = settings.supplemental;
|
|
62
|
+
const simulate = settings.simulate;
|
|
63
|
+
let trial = settings.trial;
|
|
64
|
+
let frequency = settings.frequency;
|
|
65
|
+
|
|
66
|
+
ctx.log(`Intent request: uid=${uid}, provider=${provider}, product=${productId}, frequency=${frequency}, trial=${trial}, simulate=${simulate || 'none'}`);
|
|
67
|
+
|
|
68
|
+
// Validate product exists in config
|
|
69
|
+
const product = (Manager.config.payment?.products || []).find(p => p.id === productId);
|
|
70
|
+
if (!product) {
|
|
71
|
+
ctx.log(`Product "${productId}" not found (available: ${(Manager.config.payment?.products || []).map(p => p.id).join(', ')})`);
|
|
72
|
+
return ctx.respond(`Product '${productId}' not found`, { code: 400 });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const productType = product.type || 'subscription';
|
|
76
|
+
|
|
77
|
+
ctx.log(`Product resolved: id=${product.id}, name=${product.name}, type=${productType}, trialDays=${product.trial?.days || 'none'}`);
|
|
78
|
+
|
|
79
|
+
// Subscription-specific guards
|
|
80
|
+
if (productType === 'subscription') {
|
|
81
|
+
// Require frequency for subscriptions
|
|
82
|
+
if (!frequency) {
|
|
83
|
+
return ctx.respond('Frequency is required for subscription products', { code: 400 });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Block checkout unless user has no subscription or is fully cancelled
|
|
87
|
+
const subProductId = user.subscription?.product?.id || 'basic';
|
|
88
|
+
const subStatus = user.subscription?.status;
|
|
89
|
+
if (subProductId !== 'basic' && subStatus !== 'cancelled') {
|
|
90
|
+
ctx.log(`User ${uid} has existing subscription: product=${subProductId}, status=${subStatus}, resourceId=${user.subscription.payment?.resourceId}`);
|
|
91
|
+
return ctx.respond('You already have a subscription. Please cancel your existing subscription before purchasing a new one.', { code: 400 });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Resolve trial eligibility: if requested but user has subscription history, silently downgrade
|
|
95
|
+
if (trial) {
|
|
96
|
+
const historySnapshot = await admin.firestore()
|
|
97
|
+
.collection('payments-orders')
|
|
98
|
+
.where('owner', '==', uid)
|
|
99
|
+
.where('type', '==', 'subscription')
|
|
100
|
+
.limit(1)
|
|
101
|
+
.get();
|
|
102
|
+
|
|
103
|
+
if (!historySnapshot.empty) {
|
|
104
|
+
ctx.log(`User ${uid} not eligible for trial (has subscription history), continuing without trial`);
|
|
105
|
+
trial = false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
// One-time purchases don't use trial or frequency. `once` is what a
|
|
110
|
+
// one-time buy bills on, and it is written here rather than taken from the
|
|
111
|
+
// caller: a checkout that asked for `annually` on a one-time product put
|
|
112
|
+
// that word on the intent doc, the provider call and the confirmation URL
|
|
113
|
+
// alike ([#668](https://github.com/Omega-JS-Stack/omega/issues/668)).
|
|
114
|
+
trial = false;
|
|
115
|
+
frequency = 'once';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Validate discount code (if provided)
|
|
119
|
+
let resolvedDiscount = null;
|
|
120
|
+
if (discount) {
|
|
121
|
+
const discountResult = discountCodes.validate(discount, user);
|
|
122
|
+
if (!discountResult.valid) {
|
|
123
|
+
return ctx.respond(`Invalid discount code: ${discount}`, { code: 400 });
|
|
124
|
+
}
|
|
125
|
+
resolvedDiscount = discountResult;
|
|
126
|
+
ctx.log(`Discount validated: code=${resolvedDiscount.code}, percent=${resolvedDiscount.percent}, amount=${resolvedDiscount.amount}, duration=${resolvedDiscount.duration}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Mint the order id against the intents that already exist. The id keys the
|
|
130
|
+
// intent doc written below, so an unchecked repeat overwrote another
|
|
131
|
+
// customer's checkout ([#664](https://github.com/Omega-JS-Stack/omega/issues/664)).
|
|
132
|
+
const orderId = await OrderId.mint({ admin, ctx });
|
|
133
|
+
|
|
134
|
+
ctx.log(`Generated orderId=${orderId}`);
|
|
135
|
+
|
|
136
|
+
// Build redirect URLs
|
|
137
|
+
const confirmationUrl = buildConfirmationUrl(Manager.project.websiteUrl, { product, productId, productType, frequency, provider, trial, orderId, discount: resolvedDiscount });
|
|
138
|
+
const cancelUrl = buildCancelUrl(Manager.project.websiteUrl, { productId, frequency });
|
|
139
|
+
|
|
140
|
+
// Load the provider module
|
|
141
|
+
let providerModule;
|
|
142
|
+
try {
|
|
143
|
+
providerModule = loadProvider(path.join(__dirname, 'providers'), provider);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
return ctx.respond(`Unknown provider: ${provider}`, { code: 400 });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Create the intent via the provider
|
|
149
|
+
let result;
|
|
150
|
+
try {
|
|
151
|
+
result = await providerModule.createIntent({
|
|
152
|
+
uid,
|
|
153
|
+
orderId,
|
|
154
|
+
product,
|
|
155
|
+
productId,
|
|
156
|
+
frequency,
|
|
157
|
+
trial,
|
|
158
|
+
discount: resolvedDiscount,
|
|
159
|
+
simulate,
|
|
160
|
+
confirmationUrl,
|
|
161
|
+
cancelUrl,
|
|
162
|
+
ctx,
|
|
163
|
+
});
|
|
164
|
+
} catch (e) {
|
|
165
|
+
// A REFUSAL the framework itself wrote is the one thing said out loud: a
|
|
166
|
+
// discount code that covers the whole price leaves nothing for PayPal or
|
|
167
|
+
// Coinbase to charge, and that is the buyer's own code and price to hear
|
|
168
|
+
// rather than a fault to hide (`chargeableAmount()` in
|
|
169
|
+
// libraries/payment/discount-codes.js codes it 400,
|
|
170
|
+
// [#786](https://github.com/Omega-JS-Stack/omega/issues/786)).
|
|
171
|
+
if (e.code === 400) {
|
|
172
|
+
return ctx.respond(e.message, { code: 400 });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// The provider's own words stay in the logs — a client gets one neutral
|
|
176
|
+
// sentence, never an SDK message naming our internals ([#212]).
|
|
177
|
+
ctx.error(`Failed to create ${provider} intent: uid=${uid}, product=${productId}, error=${e.message}`);
|
|
178
|
+
return ctx.respond('We could not start your checkout right now. Please try again shortly.', { code: 500 });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
ctx.log(`${provider} intent created: id=${result.id}, url=${result.url}`);
|
|
182
|
+
|
|
183
|
+
// Build timestamps
|
|
184
|
+
const now = powertools.timestamp(new Date(), { output: 'string' });
|
|
185
|
+
const nowUNIX = powertools.timestamp(now, { output: 'unix' });
|
|
186
|
+
|
|
187
|
+
// Save to payments-intents collection (keyed by orderId for consistent lookup with payments-orders)
|
|
188
|
+
await admin.firestore().doc(`payments-intents/${orderId}`).set({
|
|
189
|
+
id: orderId,
|
|
190
|
+
intentId: result.id,
|
|
191
|
+
provider: provider,
|
|
192
|
+
owner: uid,
|
|
193
|
+
status: 'pending',
|
|
194
|
+
productId: productId,
|
|
195
|
+
type: productType,
|
|
196
|
+
frequency: frequency,
|
|
197
|
+
trial: trial,
|
|
198
|
+
attribution: attribution,
|
|
199
|
+
trackingConsent: trackingConsent,
|
|
200
|
+
// The CUSTOMER's request context, captured here because this is the only
|
|
201
|
+
// moment the backend hears from their browser: the webhook that completes
|
|
202
|
+
// the order arrives from the provider's servers. Meta and TikTok match a
|
|
203
|
+
// server conversion to the browsing session on exactly this pair
|
|
204
|
+
// ([#385](https://github.com/Omega-JS-Stack/omega/issues/385)), and the
|
|
205
|
+
// order fold copies it off the intent.
|
|
206
|
+
request: {
|
|
207
|
+
ip: ctx.request.geolocation?.ip || null,
|
|
208
|
+
userAgent: ctx.request.client?.userAgent || null,
|
|
209
|
+
},
|
|
210
|
+
discount: resolvedDiscount,
|
|
211
|
+
supplemental: supplemental,
|
|
212
|
+
raw: result.raw,
|
|
213
|
+
metadata: {
|
|
214
|
+
created: {
|
|
215
|
+
timestamp: now,
|
|
216
|
+
timestampUNIX: nowUNIX,
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
ctx.log(`Saved payments-intents/${orderId}: uid=${uid}, product=${productId}, type=${productType}, frequency=${frequency}, trial=${trial}`);
|
|
222
|
+
|
|
223
|
+
// Tell the abandoned-cart sweep this shopper is mid-checkout. Asking for a
|
|
224
|
+
// provider session is the loudest "buying it right now" the backend ever
|
|
225
|
+
// hears, and the cart's reminder clock was otherwise set once, when the page
|
|
226
|
+
// opened, and never moved — so a shopper still working through the checkout
|
|
227
|
+
// was mailed about the cart they were paying for
|
|
228
|
+
// ([#655](https://github.com/Omega-JS-Stack/omega/issues/655)).
|
|
229
|
+
//
|
|
230
|
+
// Fire-and-forget, and an `update` on purpose: a checkout reached without the
|
|
231
|
+
// page's cart tracker (an unauthenticated page load, a direct link) has no
|
|
232
|
+
// cart, and this must never CREATE one the sweep would then chase.
|
|
233
|
+
admin.firestore().doc(`${CART_COLLECTION}/${uid}`).update({ lastActivityAt: nowUNIX })
|
|
234
|
+
.then(() => ctx.log(`Updated ${CART_COLLECTION}/${uid}: lastActivityAt=${nowUNIX}`))
|
|
235
|
+
.catch((e) => {
|
|
236
|
+
// Not-found is the ordinary case above, not a fault
|
|
237
|
+
if (e.code !== 5) {
|
|
238
|
+
ctx.error(`Failed to stamp checkout activity on ${CART_COLLECTION}/${uid}: ${e.message}`);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
return ctx.respond({
|
|
243
|
+
id: result.id,
|
|
244
|
+
orderId: orderId,
|
|
245
|
+
url: result.url,
|
|
246
|
+
// What the server actually RECEIVED to match this conversion with — key
|
|
247
|
+
// NAMES only, never a value ([#577](https://github.com/Omega-JS-Stack/omega/issues/577)).
|
|
248
|
+
// The dev palette reads the browser's side of the same question off the
|
|
249
|
+
// document; this is the only way to see which of those cookies survived the
|
|
250
|
+
// trip, which is exactly where a blocked pixel shows up.
|
|
251
|
+
attribution: {
|
|
252
|
+
cookies: Object.keys(attribution?.cookies || {}),
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Which half of the purchaser is missing, if either
|
|
259
|
+
*
|
|
260
|
+
* @param {object} admin - The firebase-admin app
|
|
261
|
+
* @param {string} uid - The authenticated caller's uid
|
|
262
|
+
* @returns {Promise<string|null>} What is missing, phrased for the log line, or null when both exist
|
|
263
|
+
*/
|
|
264
|
+
async function findMissingPurchaser(admin, uid) {
|
|
265
|
+
// The omega-admin-key lane authenticates with no user token, so there may be no
|
|
266
|
+
// uid to look up at all — getUser('') throws, which would answer a 500 where the
|
|
267
|
+
// whole point is a loud 403
|
|
268
|
+
if (!uid) {
|
|
269
|
+
return 'no auth user';
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (!await hasAuthUser(admin, uid)) {
|
|
273
|
+
return 'no auth user';
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const userDoc = await admin.firestore().doc(`users/${uid}`).get();
|
|
277
|
+
|
|
278
|
+
return userDoc.exists ? null : 'no user doc';
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Build the confirmation/success redirect URL
|
|
283
|
+
*
|
|
284
|
+
* `amount` is what the customer is charged TODAY, not the list price: the
|
|
285
|
+
* confirmation page hands that param straight to the client's analytics modules,
|
|
286
|
+
* so quoting the list price on a discounted checkout over-reports revenue to
|
|
287
|
+
* GA4/pixels. The discount comes off HERE rather than in a provider, because a
|
|
288
|
+
* real provider applies the coupon on its own hosted page and never revisits
|
|
289
|
+
* this URL — quoting it provider-side would have left every Stripe checkout
|
|
290
|
+
* reporting the full price ([#239](https://github.com/Omega-JS-Stack/omega/issues/239)).
|
|
291
|
+
*/
|
|
292
|
+
function buildConfirmationUrl(baseUrl, { product, productId, productType, frequency, provider, trial, orderId, discount }) {
|
|
293
|
+
const listPrice = productType === 'subscription'
|
|
294
|
+
? (product.prices?.[frequency] || 0)
|
|
295
|
+
: (product.prices?.once || 0);
|
|
296
|
+
|
|
297
|
+
// A trial charges nothing today; otherwise a validated coupon comes off the
|
|
298
|
+
// first charge (every code is duration: 'once' — the renewal stays full price)
|
|
299
|
+
const amount = trial && product.trial?.days
|
|
300
|
+
? 0
|
|
301
|
+
: discountCodes.applyToAmount(listPrice, discount);
|
|
302
|
+
|
|
303
|
+
const url = new URL('/payment/confirmation', baseUrl);
|
|
304
|
+
url.searchParams.set('productId', productId);
|
|
305
|
+
url.searchParams.set('productName', product.name || productId);
|
|
306
|
+
url.searchParams.set('amount', String(amount));
|
|
307
|
+
url.searchParams.set('currency', 'USD');
|
|
308
|
+
// What was BOUGHT, said outright rather than inferred from the cadence beside
|
|
309
|
+
// it: the confirmation page holds nothing else about the product, and a
|
|
310
|
+
// checkout that sent the wrong frequency left it polling the account for a
|
|
311
|
+
// plan a one-time purchase never writes
|
|
312
|
+
// ([#668](https://github.com/Omega-JS-Stack/omega/issues/668)).
|
|
313
|
+
url.searchParams.set('type', productType);
|
|
314
|
+
url.searchParams.set('frequency', frequency || 'once');
|
|
315
|
+
url.searchParams.set('paymentMethod', provider);
|
|
316
|
+
url.searchParams.set('trial', String(!!trial && !!product.trial?.days));
|
|
317
|
+
url.searchParams.set('orderId', orderId);
|
|
318
|
+
url.searchParams.set('track', 'true');
|
|
319
|
+
|
|
320
|
+
return url.toString();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Build the cancel/back redirect URL
|
|
325
|
+
*/
|
|
326
|
+
function buildCancelUrl(baseUrl, { productId, frequency }) {
|
|
327
|
+
const url = new URL('/payment/checkout', baseUrl);
|
|
328
|
+
url.searchParams.set('product', productId);
|
|
329
|
+
|
|
330
|
+
if (frequency) {
|
|
331
|
+
url.searchParams.set('frequency', frequency);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
url.searchParams.set('payment', 'cancelled');
|
|
335
|
+
|
|
336
|
+
return url.toString();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Exported for testing — the discounted `amount` is a provider-independent
|
|
340
|
+
// promise of this route, not of whichever provider happens to run
|
|
341
|
+
module.exports.buildConfirmationUrl = buildConfirmationUrl;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chargebee intent provider
|
|
3
|
+
* Creates Chargebee Hosted Page sessions for subscription and one-time purchases
|
|
4
|
+
*/
|
|
5
|
+
module.exports = {
|
|
6
|
+
/**
|
|
7
|
+
* Create a Chargebee Hosted Page checkout session
|
|
8
|
+
*
|
|
9
|
+
* @param {object} options
|
|
10
|
+
* @param {string} options.uid - User's UID
|
|
11
|
+
* @param {string} options.orderId - @omega.js/backend order ID (XXXX-XXXX-XXXX)
|
|
12
|
+
* @param {object} options.product - Full product object from config
|
|
13
|
+
* @param {string} options.productId - Product ID from config (e.g., 'premium')
|
|
14
|
+
* @param {string} options.frequency - 'monthly' or 'annually' (subscriptions only)
|
|
15
|
+
* @param {boolean} options.trial - Whether to include a trial period
|
|
16
|
+
* @param {string} options.confirmationUrl - Success redirect URL
|
|
17
|
+
* @param {string} options.cancelUrl - Cancel redirect URL
|
|
18
|
+
* @param {object} options.ctx - Assistant instance for logging
|
|
19
|
+
* @returns {object} { id, url, raw }
|
|
20
|
+
*/
|
|
21
|
+
async createIntent({ uid, orderId, product, productId, frequency, trial, discount, confirmationUrl, cancelUrl, ctx }) {
|
|
22
|
+
const ChargebeeLib = require('../../../../libraries/payment/providers/chargebee.js');
|
|
23
|
+
ChargebeeLib.init();
|
|
24
|
+
|
|
25
|
+
const productType = product.type || 'subscription';
|
|
26
|
+
const metaData = ChargebeeLib.buildMetaData(uid, orderId, productType === 'one-time' ? productId : undefined);
|
|
27
|
+
|
|
28
|
+
// Resolve Chargebee coupon if discount is present
|
|
29
|
+
let chargebeeCouponId = null;
|
|
30
|
+
if (discount) {
|
|
31
|
+
chargebeeCouponId = await resolveChargebeeCoupon(ChargebeeLib, discount, ctx);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let hostedPage;
|
|
35
|
+
|
|
36
|
+
if (productType === 'subscription') {
|
|
37
|
+
hostedPage = await createSubscriptionCheckout({ ChargebeeLib, uid, orderId, product, productId, frequency, trial, metaData, chargebeeCouponId, confirmationUrl, cancelUrl, ctx });
|
|
38
|
+
} else {
|
|
39
|
+
hostedPage = await createOneTimeCheckout({ ChargebeeLib, uid, orderId, product, productId, metaData, chargebeeCouponId, confirmationUrl, cancelUrl, ctx });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
ctx.log(`Chargebee hosted page created: id=${hostedPage.id}, type=${productType}, url=${hostedPage.url}`);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
id: hostedPage.id,
|
|
46
|
+
url: hostedPage.url,
|
|
47
|
+
raw: hostedPage,
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Create a Chargebee Hosted Page for a new subscription
|
|
54
|
+
*/
|
|
55
|
+
async function createSubscriptionCheckout({ ChargebeeLib, uid, orderId, product, productId, frequency, trial, metaData, chargebeeCouponId, confirmationUrl, cancelUrl, ctx }) {
|
|
56
|
+
const chargebeeItemId = product.chargebee?.itemId;
|
|
57
|
+
|
|
58
|
+
if (!chargebeeItemId) {
|
|
59
|
+
throw new Error(`No Chargebee item ID configured for product ${productId}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Deterministic item price ID: {itemId}-{frequency}
|
|
63
|
+
const itemPriceId = `${chargebeeItemId}-${frequency}`;
|
|
64
|
+
|
|
65
|
+
// NOTE: subscription[meta_data] is NOT supported by Chargebee's hosted page checkout.
|
|
66
|
+
// We use pass_thru_content to carry our UID/orderId through the checkout flow,
|
|
67
|
+
// then backfill meta_data on the subscription after the webhook resolves the UID.
|
|
68
|
+
const params = {
|
|
69
|
+
subscription_items: {
|
|
70
|
+
item_price_id: [itemPriceId],
|
|
71
|
+
quantity: [1],
|
|
72
|
+
},
|
|
73
|
+
redirect_url: confirmationUrl,
|
|
74
|
+
cancel_url: cancelUrl,
|
|
75
|
+
pass_thru_content: metaData,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Handle trial: if trial requested but product has no trial config in Chargebee,
|
|
79
|
+
// set trial_end explicitly. Otherwise let the item price's trial config handle it.
|
|
80
|
+
if (trial === false && product.trial?.days) {
|
|
81
|
+
// Explicitly skip trial by setting trial_end to 0
|
|
82
|
+
params.subscription = { trial_end: 0 };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Apply discount coupon (first payment only)
|
|
86
|
+
if (chargebeeCouponId) {
|
|
87
|
+
params.coupon_ids = [chargebeeCouponId];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
ctx.log(`Chargebee subscription checkout: itemPriceId=${itemPriceId}, uid=${uid}, trial=${trial}, coupon=${chargebeeCouponId || 'none'}`);
|
|
91
|
+
|
|
92
|
+
const result = await ChargebeeLib.request('/hosted_pages/checkout_new_for_items', {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
body: params,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return result.hosted_page;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Create a Chargebee Hosted Page for a one-time charge
|
|
102
|
+
*/
|
|
103
|
+
async function createOneTimeCheckout({ ChargebeeLib, uid, orderId, product, productId, metaData, chargebeeCouponId, confirmationUrl, cancelUrl, ctx }) {
|
|
104
|
+
const price = product.prices?.once;
|
|
105
|
+
|
|
106
|
+
if (!price) {
|
|
107
|
+
throw new Error(`No one-time price configured for product ${productId}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Amount in cents
|
|
111
|
+
const amountCents = Math.round(price * 100);
|
|
112
|
+
|
|
113
|
+
const params = {
|
|
114
|
+
charges: {
|
|
115
|
+
amount: [amountCents],
|
|
116
|
+
description: [product.name || productId],
|
|
117
|
+
},
|
|
118
|
+
redirect_url: confirmationUrl,
|
|
119
|
+
cancel_url: cancelUrl,
|
|
120
|
+
pass_thru_content: metaData,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Apply discount coupon
|
|
124
|
+
if (chargebeeCouponId) {
|
|
125
|
+
params.coupon_ids = [chargebeeCouponId];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
ctx.log(`Chargebee one-time checkout: amount=${amountCents}, productId=${productId}, uid=${uid}, coupon=${chargebeeCouponId || 'none'}`);
|
|
129
|
+
|
|
130
|
+
const result = await ChargebeeLib.request('/hosted_pages/checkout_one_time_for_items', {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
body: params,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return result.hosted_page;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Resolve or create a Chargebee coupon for a discount code
|
|
140
|
+
* Uses a deterministic ID so the same code always maps to the same coupon
|
|
141
|
+
*
|
|
142
|
+
* A code is percent-based or amount-based, and Chargebee spells the second one
|
|
143
|
+
* `discount_type: 'fixed_amount'` with `discount_amount` in the currency's MINOR
|
|
144
|
+
* unit (cents — the same unit this file's one-time `charges.amount` uses) plus the
|
|
145
|
+
* `currency_code` it applies in. The two shapes carry different ids so a code can
|
|
146
|
+
* never collide with the other form, and the percent id is byte-identical to what
|
|
147
|
+
* it has always been, so coupons already live in a brand's site keep resolving
|
|
148
|
+
* ([#239](https://github.com/Omega-JS-Stack/omega/issues/239)).
|
|
149
|
+
*
|
|
150
|
+
* Verified against the params the API is asked to create, not against Chargebee:
|
|
151
|
+
* live-provider verification is a Stage 3 item, the same trust level as the rest
|
|
152
|
+
* of this provider ([#212](https://github.com/Omega-JS-Stack/omega/issues/212)).
|
|
153
|
+
*/
|
|
154
|
+
async function resolveChargebeeCoupon(ChargebeeLib, discount, ctx) {
|
|
155
|
+
const isAmount = discount.amount > 0;
|
|
156
|
+
const currency = (ctx.Manager?.config?.payment?.currency || 'USD').toUpperCase();
|
|
157
|
+
const couponId = isAmount
|
|
158
|
+
? `BEM_${discount.code}_${discount.amount}AMTOFF_ONCE`
|
|
159
|
+
: `BEM_${discount.code}_${discount.percent}OFF_ONCE`;
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
// Check if coupon already exists
|
|
163
|
+
await ChargebeeLib.request(`/coupons/${couponId}`, { method: 'GET' });
|
|
164
|
+
ctx.log(`Chargebee coupon exists: ${couponId}`);
|
|
165
|
+
return couponId;
|
|
166
|
+
} catch (e) {
|
|
167
|
+
// Chargebee returns 404 for missing resources
|
|
168
|
+
if (e.status !== 404 && e.statusCode !== 404) {
|
|
169
|
+
throw e;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Create the coupon
|
|
174
|
+
await ChargebeeLib.request('/coupons', {
|
|
175
|
+
method: 'POST',
|
|
176
|
+
body: {
|
|
177
|
+
id: couponId,
|
|
178
|
+
duration_type: 'one_time',
|
|
179
|
+
apply_on: 'invoice_amount',
|
|
180
|
+
...(isAmount
|
|
181
|
+
? {
|
|
182
|
+
name: `${discount.code} (${discount.amount.toFixed(2)} ${currency} off first payment)`,
|
|
183
|
+
discount_type: 'fixed_amount',
|
|
184
|
+
discount_amount: Math.round(discount.amount * 100),
|
|
185
|
+
currency_code: currency,
|
|
186
|
+
}
|
|
187
|
+
: {
|
|
188
|
+
name: `${discount.code} (${discount.percent}% off first payment)`,
|
|
189
|
+
discount_type: 'percentage',
|
|
190
|
+
discount_percentage: discount.percent,
|
|
191
|
+
}),
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
ctx.log(`Chargebee coupon created: ${couponId}`);
|
|
196
|
+
return couponId;
|
|
197
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coinbase Commerce intent provider
|
|
3
|
+
* Creates a hosted CHARGE and hands back the page the buyer pays it on
|
|
4
|
+
*
|
|
5
|
+
* ONE-TIME ONLY ([#642](https://github.com/Omega-JS-Stack/omega/issues/642)):
|
|
6
|
+
* a Coinbase Commerce charge is a single payment at a single price. There is no
|
|
7
|
+
* subscription, plan or billing agreement in this API, so a subscription product
|
|
8
|
+
* is refused here rather than half-sold — the checkout hides the crypto button
|
|
9
|
+
* on a plan for the same reason.
|
|
10
|
+
*/
|
|
11
|
+
const { chargeableAmount } = require('../../../../libraries/payment/discount-codes.js');
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
/**
|
|
15
|
+
* Create a Coinbase Commerce payment intent (a hosted charge)
|
|
16
|
+
*
|
|
17
|
+
* @param {object} options
|
|
18
|
+
* @param {string} options.uid - User's UID
|
|
19
|
+
* @param {string} options.orderId - Internal order ID
|
|
20
|
+
* @param {object} options.product - Full product object from config
|
|
21
|
+
* @param {string} options.productId - Product ID from config (e.g., 'launch-kit')
|
|
22
|
+
* @param {object} options.discount - Validated discount result, or null
|
|
23
|
+
* @param {string} options.confirmationUrl - Success redirect URL
|
|
24
|
+
* @param {string} options.cancelUrl - Cancel redirect URL
|
|
25
|
+
* @param {object} options.ctx - Assistant instance for logging
|
|
26
|
+
* @returns {object} { id, url, raw }
|
|
27
|
+
*/
|
|
28
|
+
async createIntent({ uid, orderId, product, productId, discount, confirmationUrl, cancelUrl, ctx }) {
|
|
29
|
+
const CoinbaseLib = require('../../../../libraries/payment/providers/coinbase.js');
|
|
30
|
+
|
|
31
|
+
const productType = product.type || 'subscription';
|
|
32
|
+
|
|
33
|
+
if (productType !== 'one-time') {
|
|
34
|
+
throw new Error(`Coinbase Commerce cannot sell ${product.id}: a crypto charge is a one-time payment and this product is a ${productType}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (product.archived) {
|
|
38
|
+
throw new Error(`Product ${product.id} is archived`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const listPrice = product.prices?.once;
|
|
42
|
+
|
|
43
|
+
if (!listPrice) {
|
|
44
|
+
throw new Error(`No one-time price configured for ${product.id}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// The charge is a FIXED price we compute, so a validated coupon has to come
|
|
48
|
+
// off here: a real provider applies it on its own hosted page, and Coinbase
|
|
49
|
+
// has no coupon object at all. Leaving it out would charge the full amount
|
|
50
|
+
// in crypto while the confirmation page — which discounts the same way, in
|
|
51
|
+
// routes/payments/intent/post.js — reported the discounted one. A code that
|
|
52
|
+
// covers the whole price is refused by that same helper before this charge
|
|
53
|
+
// is ever created: Coinbase Commerce does not take a $0.00 one
|
|
54
|
+
// ([#786](https://github.com/Omega-JS-Stack/omega/issues/786)).
|
|
55
|
+
const amount = chargeableAmount(listPrice, discount, { provider: 'Coinbase Commerce' });
|
|
56
|
+
|
|
57
|
+
const brandName = ctx.Manager?.config?.brand?.name || product.name || productId;
|
|
58
|
+
|
|
59
|
+
const chargeParams = {
|
|
60
|
+
name: product.name || productId,
|
|
61
|
+
description: product.description || brandName,
|
|
62
|
+
pricing_type: 'fixed_price',
|
|
63
|
+
local_price: {
|
|
64
|
+
amount: amount.toFixed(2),
|
|
65
|
+
currency: 'USD',
|
|
66
|
+
},
|
|
67
|
+
// Coinbase Commerce metadata is a flat string map, and it is the ONLY place
|
|
68
|
+
// this purchase's identifiers live — the webhook pipeline reads uid and
|
|
69
|
+
// orderId back off the charge it looks up
|
|
70
|
+
metadata: CoinbaseLib.buildMetadata(uid, orderId, productId),
|
|
71
|
+
redirect_url: confirmationUrl,
|
|
72
|
+
cancel_url: cancelUrl,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const result = await CoinbaseLib.request('/charges', {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
body: chargeParams,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const charge = result.data || result;
|
|
81
|
+
|
|
82
|
+
if (!charge.hosted_url) {
|
|
83
|
+
throw new Error('Coinbase Commerce charge created but no hosted_url returned');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
ctx.log(`Coinbase charge created: id=${charge.id}, code=${charge.code}, amount=${chargeParams.local_price.amount}, url=${charge.hosted_url}`);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
id: charge.id,
|
|
90
|
+
url: charge.hosted_url,
|
|
91
|
+
raw: charge,
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
};
|