@omega.js/manager 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/.claude-plugin/marketplace.json +14 -0
- package/LICENSE +98 -0
- package/README.md +165 -0
- package/bin/omega +2 -0
- package/bin/omg +2 -0
- package/claude-plugin/.claude-plugin/plugin.json +11 -0
- package/claude-plugin/.mcp.json +9 -0
- package/claude-plugin/README.md +139 -0
- package/claude-plugin/hooks/gate/mark.sh +58 -0
- package/claude-plugin/hooks/gate/run.sh +97 -0
- package/claude-plugin/hooks/guard/run.sh +249 -0
- package/claude-plugin/hooks/hooks.json +63 -0
- package/claude-plugin/hooks/inject/run.sh +127 -0
- package/claude-plugin/hooks/lib/omega-gate.sh +20 -0
- package/claude-plugin/hooks/lib/omega-scope.sh +41 -0
- package/claude-plugin/hooks/lib/omega-skills.sh +209 -0
- package/claude-plugin/hooks/quality/run.sh +132 -0
- package/claude-plugin/hooks/shape/run.sh +45 -0
- package/claude-plugin/mcp-router-launch.js +29 -0
- package/claude-plugin/skills/README.md +48 -0
- package/claude-plugin/skills/accessibility/SKILL.md +31 -0
- package/claude-plugin/skills/analytics/SKILL.md +30 -0
- package/claude-plugin/skills/backend/SKILL.md +25 -0
- package/claude-plugin/skills/brandcheck/SKILL.md +29 -0
- package/claude-plugin/skills/browser/SKILL.md +64 -0
- package/claude-plugin/skills/client/SKILL.md +24 -0
- package/claude-plugin/skills/desktop/SKILL.md +25 -0
- package/claude-plugin/skills/extension/SKILL.md +25 -0
- package/claude-plugin/skills/main/SKILL.md +54 -0
- package/claude-plugin/skills/manager/SKILL.md +25 -0
- package/claude-plugin/skills/seo/SKILL.md +36 -0
- package/claude-plugin/skills/theme/SKILL.md +33 -0
- package/claude-plugin/skills/web/SKILL.md +29 -0
- package/dist/cli-run.js +34 -0
- package/dist/cli.js +29 -0
- package/dist/commands/build.js +17 -0
- package/dist/commands/clean.js +16 -0
- package/dist/commands/company.js +46 -0
- package/dist/commands/deploy.js +143 -0
- package/dist/commands/dev.js +437 -0
- package/dist/commands/devlog.js +25 -0
- package/dist/commands/help.js +48 -0
- package/dist/commands/manage.js +53 -0
- package/dist/commands/migrate.js +76 -0
- package/dist/commands/onboard.js +31 -0
- package/dist/commands/pipeline.js +319 -0
- package/dist/commands/test.js +380 -0
- package/dist/commands/update.js +128 -0
- package/dist/commands/version.js +8 -0
- package/dist/company-init.js +167 -0
- package/dist/company.js +283 -0
- package/dist/config.js +1359 -0
- package/dist/devlog/index.js +226 -0
- package/dist/devlog/lib/collect-commits.js +136 -0
- package/dist/devlog/lib/generate-post.js +201 -0
- package/dist/devlog/lib/ghostii.js +122 -0
- package/dist/devlog/lib/project-map.js +98 -0
- package/dist/devlog/lib/publish-website.js +88 -0
- package/dist/index.js +45 -0
- package/dist/lib/agents-md.js +201 -0
- package/dist/lib/analytics-secret.js +22 -0
- package/dist/lib/argv.js +21 -0
- package/dist/lib/auth-admin.js +120 -0
- package/dist/lib/automation-client.js +210 -0
- package/dist/lib/backend-marketing.js +49 -0
- package/dist/lib/brand.js +240 -0
- package/dist/lib/bundle-id.js +53 -0
- package/dist/lib/claude-settings.js +125 -0
- package/dist/lib/company-scaffold.js +213 -0
- package/dist/lib/company.js +227 -0
- package/dist/lib/config-flow.js +350 -0
- package/dist/lib/config-write.js +41 -0
- package/dist/lib/custom-target.js +75 -0
- package/dist/lib/domain-utils.js +19 -0
- package/dist/lib/duration.js +39 -0
- package/dist/lib/env-order.js +236 -0
- package/dist/lib/env-secret.js +78 -0
- package/dist/lib/firestore-rest.js +326 -0
- package/dist/lib/framework-bin.js +156 -0
- package/dist/lib/gitignore.js +63 -0
- package/dist/lib/google-auth.js +495 -0
- package/dist/lib/google-token.js +66 -0
- package/dist/lib/jwt.js +31 -0
- package/dist/lib/legacy-oauth.js +59 -0
- package/dist/lib/node-version.js +127 -0
- package/dist/lib/owner-plan.js +78 -0
- package/dist/lib/package-scripts.js +152 -0
- package/dist/lib/preflight.js +470 -0
- package/dist/lib/product-create.js +144 -0
- package/dist/lib/run-command.js +74 -0
- package/dist/lib/run-gates.js +70 -0
- package/dist/lib/run-output.js +42 -0
- package/dist/lib/run-summary.js +384 -0
- package/dist/lib/scaffold.js +427 -0
- package/dist/lib/service-input.js +207 -0
- package/dist/lib/service-runner.js +466 -0
- package/dist/lib/stale.js +24 -0
- package/dist/lib/target-selection.js +155 -0
- package/dist/lib/verb-fanout.js +128 -0
- package/dist/lib/verify-live.js +188 -0
- package/dist/manage.js +311 -0
- package/dist/omega-bin.js +7 -0
- package/dist/onboard.js +532 -0
- package/dist/services/account/ensure/users.js +259 -0
- package/dist/services/account/index.js +97 -0
- package/dist/services/account/lib/backend-client.js +107 -0
- package/dist/services/account/lib/password.js +36 -0
- package/dist/services/account/lib/resolve-password.js +100 -0
- package/dist/services/advertising/ensure/sites.js +101 -0
- package/dist/services/advertising/index.js +101 -0
- package/dist/services/advertising/lib/adsense-api.js +41 -0
- package/dist/services/ai/ensure/keys.js +21 -0
- package/dist/services/ai/index.js +36 -0
- package/dist/services/analytics/ensure/google-firebase-link.js +230 -0
- package/dist/services/analytics/ensure/google-streams.js +256 -0
- package/dist/services/analytics/ensure/meta-pixel.js +22 -0
- package/dist/services/analytics/ensure/tiktok-pixel.js +22 -0
- package/dist/services/analytics/index.js +134 -0
- package/dist/services/analytics/lib/analytics-api.js +156 -0
- package/dist/services/analytics/lib/meta-api.js +83 -0
- package/dist/services/analytics/lib/pixel-account.js +78 -0
- package/dist/services/analytics/lib/pixel-provision.js +152 -0
- package/dist/services/analytics/lib/pixel-specs.js +57 -0
- package/dist/services/analytics/lib/pixel-token.js +88 -0
- package/dist/services/analytics/lib/property-flow.js +77 -0
- package/dist/services/analytics/lib/tiktok-api.js +157 -0
- package/dist/services/analytics/lib/tiktok-auth.js +153 -0
- package/dist/services/assets/ensure/logo-gen.js +68 -0
- package/dist/services/assets/index.js +109 -0
- package/dist/services/assets/lib/assets-config.js +168 -0
- package/dist/services/assets/lib/brandmark-api.js +160 -0
- package/dist/services/assets/lib/derived.js +113 -0
- package/dist/services/assets/lib/font-loader.js +67 -0
- package/dist/services/assets/lib/reconcile.js +122 -0
- package/dist/services/assets/lib/reset.js +118 -0
- package/dist/services/assets/lib/svg-logo-generator.js +154 -0
- package/dist/services/assets/lib/svg-to-black.js +32 -0
- package/dist/services/assets/write/favicons.js +82 -0
- package/dist/services/assets/write/icons.js +64 -0
- package/dist/services/assets/write/process.js +96 -0
- package/dist/services/assets/write/reconcile.js +30 -0
- package/dist/services/assets/write/social-icons.js +108 -0
- package/dist/services/assets/write/templates.js +345 -0
- package/dist/services/bookmark/ensure/sync.js +255 -0
- package/dist/services/bookmark/index.js +17 -0
- package/dist/services/campaigns/ensure/contact-person.js +40 -0
- package/dist/services/campaigns/ensure/custom-fields.js +84 -0
- package/dist/services/campaigns/ensure/domain-auth.js +92 -0
- package/dist/services/campaigns/ensure/event-webhook.js +89 -0
- package/dist/services/campaigns/ensure/link-branding.js +187 -0
- package/dist/services/campaigns/ensure/list.js +50 -0
- package/dist/services/campaigns/ensure/segments.js +109 -0
- package/dist/services/campaigns/ensure/sender-identity.js +153 -0
- package/dist/services/campaigns/ensure/unsubscribe-groups.js +116 -0
- package/dist/services/campaigns/index.js +79 -0
- package/dist/services/campaigns/lib/dns-sync.js +81 -0
- package/dist/services/campaigns/lib/segment-query.js +138 -0
- package/dist/services/campaigns/lib/sendgrid-api.js +287 -0
- package/dist/services/captcha/ensure/site-key.js +83 -0
- package/dist/services/captcha/index.js +78 -0
- package/dist/services/captcha/lib/console-url.js +45 -0
- package/dist/services/captcha/lib/recaptcha-api.js +36 -0
- package/dist/services/certificates/ensure/api-key.js +22 -0
- package/dist/services/certificates/ensure/bundle-ids.js +115 -0
- package/dist/services/certificates/ensure/certificates.js +187 -0
- package/dist/services/certificates/ensure/profiles.js +143 -0
- package/dist/services/certificates/index.js +225 -0
- package/dist/services/certificates/lib/apple-api.js +183 -0
- package/dist/services/certificates/lib/certificate-manager.js +252 -0
- package/dist/services/certificates/lib/identifier-manager.js +107 -0
- package/dist/services/certificates/lib/keychain.js +135 -0
- package/dist/services/certificates/lib/manual-walkthrough.js +165 -0
- package/dist/services/certificates/lib/profile-manager.js +115 -0
- package/dist/services/chat/data/baseline-knowledge.md +96 -0
- package/dist/services/chat/ensure/chat.js +106 -0
- package/dist/services/chat/ensure/user.js +45 -0
- package/dist/services/chat/index.js +139 -0
- package/dist/services/chat/lib/baseline-knowledge.js +137 -0
- package/dist/services/cloud/ensure/authentication.js +267 -0
- package/dist/services/cloud/ensure/billing.js +120 -0
- package/dist/services/cloud/ensure/cloud-messaging.js +105 -0
- package/dist/services/cloud/ensure/database.js +60 -0
- package/dist/services/cloud/ensure/firestore.js +79 -0
- package/dist/services/cloud/ensure/functions.js +49 -0
- package/dist/services/cloud/ensure/hosting.js +398 -0
- package/dist/services/cloud/ensure/oauth-consent.js +248 -0
- package/dist/services/cloud/ensure/project-settings.js +72 -0
- package/dist/services/cloud/ensure/sdk-config.js +95 -0
- package/dist/services/cloud/ensure/service-account.js +152 -0
- package/dist/services/cloud/ensure/services.js +103 -0
- package/dist/services/cloud/ensure/storage.js +47 -0
- package/dist/services/cloud/index.js +130 -0
- package/dist/services/cloud/lib/access-heal.js +130 -0
- package/dist/services/cloud/lib/firebase-api.js +662 -0
- package/dist/services/cloud/lib/project-flow.js +102 -0
- package/dist/services/directory/ensure/entry.js +48 -0
- package/dist/services/directory/index.js +64 -0
- package/dist/services/directory/lib/blocks.js +104 -0
- package/dist/services/disperse/index.js +45 -0
- package/dist/services/disperse/write/certs.js +117 -0
- package/dist/services/domain/ensure/nameservers.js +179 -0
- package/dist/services/domain/index.js +69 -0
- package/dist/services/domain/lib/namecheap-api.js +167 -0
- package/dist/services/domain/lib/registrars.js +46 -0
- package/dist/services/domain/lib/whitelist-walkthrough.js +76 -0
- package/dist/services/edge/ensure/cache-rules.js +105 -0
- package/dist/services/edge/ensure/dns-records.js +304 -0
- package/dist/services/edge/ensure/email-routing.js +292 -0
- package/dist/services/edge/ensure/rules-configuration.js +98 -0
- package/dist/services/edge/ensure/rules-managed-transforms.js +101 -0
- package/dist/services/edge/ensure/rules-redirect.js +107 -0
- package/dist/services/edge/ensure/rules-response-headers.js +90 -0
- package/dist/services/edge/ensure/rules-security.js +142 -0
- package/dist/services/edge/ensure/speed-scheduled-tests.js +98 -0
- package/dist/services/edge/ensure/workers.js +219 -0
- package/dist/services/edge/ensure/zone-settings.js +108 -0
- package/dist/services/edge/ensure/zone.js +183 -0
- package/dist/services/edge/index.js +73 -0
- package/dist/services/edge/lib/cloudflare-api.js +84 -0
- package/dist/services/edge/lib/dns-records-helpers.js +518 -0
- package/dist/services/edge/lib/read-cache.js +16 -0
- package/dist/services/edge/lib/ruleset-helper.js +108 -0
- package/dist/services/edge/workers/omega-api-proxy.js +43 -0
- package/dist/services/email/data/baseline-filter.md +4 -0
- package/dist/services/email/data/baseline-knowledge.md +86 -0
- package/dist/services/email/ensure/agent.js +107 -0
- package/dist/services/email/ensure/user.js +45 -0
- package/dist/services/email/index.js +138 -0
- package/dist/services/email/lib/baseline.js +82 -0
- package/dist/services/email/lib/knowledge-file.js +53 -0
- package/dist/services/forms/ensure/form.js +46 -0
- package/dist/services/forms/ensure/user.js +45 -0
- package/dist/services/forms/index.js +138 -0
- package/dist/services/migrations/ensure/notifications.js +189 -0
- package/dist/services/migrations/ensure/orders.js +34 -0
- package/dist/services/migrations/ensure/payment-provider.js +132 -0
- package/dist/services/migrations/ensure/payments-intents.js +34 -0
- package/dist/services/migrations/ensure/state-retirement.js +266 -0
- package/dist/services/migrations/ensure/targets-rename.js +124 -0
- package/dist/services/migrations/ensure/users.js +1176 -0
- package/dist/services/migrations/index.js +101 -0
- package/dist/services/migrations/lib/attribution-touch.js +64 -0
- package/dist/services/migrations/lib/ensure-metadata.js +144 -0
- package/dist/services/migrations/lib/migration-runner.js +473 -0
- package/dist/services/migrations/lib/sanitize-strings.js +112 -0
- package/dist/services/migrations/lib/schema-validator.js +154 -0
- package/dist/services/monitoring/ensure/dsn.js +54 -0
- package/dist/services/monitoring/ensure/projects.js +135 -0
- package/dist/services/monitoring/index.js +54 -0
- package/dist/services/monitoring/lib/sentry-api.js +94 -0
- package/dist/services/newsletter/ensure/custom-fields.js +90 -0
- package/dist/services/newsletter/ensure/publication.js +94 -0
- package/dist/services/newsletter/ensure/segments.js +140 -0
- package/dist/services/newsletter/ensure/webhook.js +108 -0
- package/dist/services/newsletter/index.js +64 -0
- package/dist/services/newsletter/lib/beehiiv-api.js +124 -0
- package/dist/services/newsletter/lib/segment-automation.js +577 -0
- package/dist/services/payment/ensure/chargebee-account.js +30 -0
- package/dist/services/payment/ensure/chargebee-products.js +359 -0
- package/dist/services/payment/ensure/chargebee-webhook.js +113 -0
- package/dist/services/payment/ensure/paypal-account.js +36 -0
- package/dist/services/payment/ensure/paypal-products.js +374 -0
- package/dist/services/payment/ensure/paypal-webhook.js +128 -0
- package/dist/services/payment/ensure/stripe-account.js +93 -0
- package/dist/services/payment/ensure/stripe-disputes.js +56 -0
- package/dist/services/payment/ensure/stripe-products.js +245 -0
- package/dist/services/payment/ensure/stripe-radar.js +74 -0
- package/dist/services/payment/ensure/stripe-webhook.js +130 -0
- package/dist/services/payment/index.js +163 -0
- package/dist/services/payment/lib/chargebee-api.js +338 -0
- package/dist/services/payment/lib/payment-utils.js +116 -0
- package/dist/services/payment/lib/paypal-api.js +354 -0
- package/dist/services/payment/lib/provider-setup.js +134 -0
- package/dist/services/payment/lib/stripe-api.js +152 -0
- package/dist/services/repo/ensure/org.js +70 -0
- package/dist/services/repo/ensure/pages.js +72 -0
- package/dist/services/repo/ensure/repo.js +90 -0
- package/dist/services/repo/index.js +52 -0
- package/dist/services/repo/lib/github-api.js +196 -0
- package/dist/services/search/ensure/ga-link.js +57 -0
- package/dist/services/search/ensure/property.js +137 -0
- package/dist/services/search/ensure/sitemaps.js +70 -0
- package/dist/services/search/index.js +61 -0
- package/dist/services/search/lib/search-console-api.js +92 -0
- package/dist/services/seo/ensure/github-repos.js +271 -0
- package/dist/services/seo/index.js +55 -0
- package/dist/services/seo/lib/gh-api.js +161 -0
- package/dist/services/seo/templates/developer-tool/.github/workflows/maintenance.yml +25 -0
- package/dist/services/seo/templates/developer-tool/.nvmrc +1 -0
- package/dist/services/seo/templates/developer-tool/_README.md.js +121 -0
- package/dist/services/seo/templates/developer-tool/_package.json.js +33 -0
- package/dist/services/seo/templates/developer-tool/src/index.js +120 -0
- package/dist/services/seo/templates/index.js +57 -0
- package/dist/services/server/ensure/brands.js +63 -0
- package/dist/services/server/index.js +42 -0
- package/dist/services/testing/ensure/target-checks.js +97 -0
- package/dist/services/testing/index.js +11 -0
- package/dist/services/testing/lib/checks.js +467 -0
- package/dist/services/update/index.js +20 -0
- package/dist/services/update/lib/cache.js +59 -0
- package/dist/services/update/lib/fingerprint.js +157 -0
- package/dist/services/update/write/targets.js +206 -0
- package/dist/services/workspace/ensure/agents.js +42 -0
- package/dist/services/workspace/ensure/claude-settings.js +34 -0
- package/dist/services/workspace/ensure/config.js +54 -0
- package/dist/services/workspace/ensure/defaults.js +60 -0
- package/dist/services/workspace/ensure/env-keys.js +55 -0
- package/dist/services/workspace/ensure/env-order.js +66 -0
- package/dist/services/workspace/ensure/env-rules.js +97 -0
- package/dist/services/workspace/ensure/gitignore.js +18 -0
- package/dist/services/workspace/ensure/scripts.js +126 -0
- package/dist/services/workspace/ensure/structure.js +97 -0
- package/dist/services/workspace/ensure/translation-sdk.js +148 -0
- package/dist/services/workspace/ensure/workflows.js +54 -0
- package/dist/services/workspace/index.js +8 -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/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/certs.js +199 -0
- package/dist/vendor/devkit/ci-workflows.js +520 -0
- package/dist/vendor/devkit/cli-router.js +156 -0
- package/dist/vendor/devkit/command-path.js +46 -0
- package/dist/vendor/devkit/deploy-record.js +180 -0
- package/dist/vendor/devkit/flows.js +317 -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/omega-bin.js +345 -0
- package/dist/vendor/devkit/prompt.js +187 -0
- package/dist/vendor/devkit/safe-install.js +18 -0
- package/dist/vendor/devkit/stop-signals.js +28 -0
- package/dist/vendor/devkit/test/scope.js +162 -0
- package/dist/vendor/devkit/translate/cache.js +84 -0
- package/dist/vendor/devkit/translate/engine.js +243 -0
- package/dist/vendor/devkit/translate/index.js +49 -0
- package/dist/vendor/devkit/translate/languages.js +134 -0
- package/dist/vendor/devkit/translate/providers.js +188 -0
- package/dist/vendor/devkit/update.js +569 -0
- package/docs/AGENTS.md +200 -0
- package/docs/account.md +45 -0
- package/docs/advertising.md +46 -0
- package/docs/ai.md +34 -0
- package/docs/analytics.md +69 -0
- package/docs/assets.md +57 -0
- package/docs/bookmark.md +26 -0
- package/docs/brand.md +151 -0
- package/docs/campaigns.md +96 -0
- package/docs/captcha.md +46 -0
- package/docs/certificates.md +68 -0
- package/docs/chat.md +47 -0
- package/docs/cloud.md +110 -0
- package/docs/company.md +64 -0
- package/docs/directory.md +140 -0
- package/docs/disperse.md +46 -0
- package/docs/domain.md +56 -0
- package/docs/edge.md +234 -0
- package/docs/email.md +42 -0
- package/docs/forms.md +48 -0
- package/docs/index.md +109 -0
- package/docs/migration.md +203 -0
- package/docs/migrations.md +56 -0
- package/docs/monitoring.md +41 -0
- package/docs/newsletter.md +48 -0
- package/docs/payment.md +83 -0
- package/docs/repo.md +48 -0
- package/docs/search.md +44 -0
- package/docs/seo.md +34 -0
- package/docs/server.md +37 -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/testing.md +34 -0
- package/docs/update.md +36 -0
- package/docs/workspace.md +64 -0
- package/package.json +88 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PayPal REST API wrapper — OAuth2 client credentials with live/sandbox
|
|
3
|
+
* auto-detection (first auth probes both endpoints in parallel). Ported from
|
|
4
|
+
* omega-manager minus the subscription-inspection methods (they belong to
|
|
5
|
+
* the migrations port) and the unused plan/webhook helpers.
|
|
6
|
+
*/
|
|
7
|
+
const LIVE_URL = 'https://api-m.paypal.com';
|
|
8
|
+
const SANDBOX_URL = 'https://api-m.sandbox.paypal.com';
|
|
9
|
+
|
|
10
|
+
class PayPalAPI {
|
|
11
|
+
constructor(clientId, clientSecret) {
|
|
12
|
+
this.clientId = clientId;
|
|
13
|
+
this.clientSecret = clientSecret;
|
|
14
|
+
this.baseUrl = null; // Resolved on first auth
|
|
15
|
+
this.accessToken = null;
|
|
16
|
+
this.tokenExpiry = 0;
|
|
17
|
+
this.appId = null; // From token response
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Try to authenticate against a specific PayPal endpoint.
|
|
22
|
+
* Returns the token response data on success, null on auth failure.
|
|
23
|
+
*/
|
|
24
|
+
static async tryAuth(clientId, clientSecret, baseUrl) {
|
|
25
|
+
const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
|
|
26
|
+
|
|
27
|
+
const response = await fetch(`${baseUrl}/v1/oauth2/token`, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: {
|
|
30
|
+
'Authorization': `Basic ${credentials}`,
|
|
31
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
32
|
+
},
|
|
33
|
+
body: 'grant_type=client_credentials',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return response.json();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get OAuth2 access token (cached until expiry).
|
|
45
|
+
* On first call, tries both live and sandbox simultaneously to detect the right endpoint.
|
|
46
|
+
*/
|
|
47
|
+
async getAccessToken() {
|
|
48
|
+
if (this.accessToken && Date.now() < this.tokenExpiry) {
|
|
49
|
+
return this.accessToken;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// First auth — try both endpoints in parallel
|
|
53
|
+
if (!this.baseUrl) {
|
|
54
|
+
const [liveResult, sandboxResult] = await Promise.all([
|
|
55
|
+
PayPalAPI.tryAuth(this.clientId, this.clientSecret, LIVE_URL),
|
|
56
|
+
PayPalAPI.tryAuth(this.clientId, this.clientSecret, SANDBOX_URL),
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
if (liveResult) {
|
|
60
|
+
this._applyToken(liveResult, LIVE_URL);
|
|
61
|
+
return this.accessToken;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (sandboxResult) {
|
|
65
|
+
this._applyToken(sandboxResult, SANDBOX_URL);
|
|
66
|
+
return this.accessToken;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
throw new Error('PayPal auth failed on both live and sandbox — check paypal.clientId and PAYPAL_CLIENT_SECRET');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Subsequent auths — use the resolved endpoint
|
|
73
|
+
const result = await PayPalAPI.tryAuth(this.clientId, this.clientSecret, this.baseUrl);
|
|
74
|
+
if (!result) {
|
|
75
|
+
throw new Error(`PayPal auth failed (${this.baseUrl})`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this._applyToken(result);
|
|
79
|
+
return this.accessToken;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Store token data from an auth response
|
|
84
|
+
*/
|
|
85
|
+
_applyToken(data, baseUrl) {
|
|
86
|
+
if (baseUrl) {
|
|
87
|
+
this.baseUrl = baseUrl;
|
|
88
|
+
}
|
|
89
|
+
this.accessToken = data.access_token;
|
|
90
|
+
this.tokenExpiry = Date.now() + (data.expires_in - 60) * 1000;
|
|
91
|
+
this.appId = data.app_id || this.appId;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get account info derived from the token response.
|
|
96
|
+
* Must call getAccessToken() first.
|
|
97
|
+
*/
|
|
98
|
+
getAccountInfo() {
|
|
99
|
+
return {
|
|
100
|
+
appId: this.appId,
|
|
101
|
+
environment: this.baseUrl === SANDBOX_URL ? 'sandbox' : 'live',
|
|
102
|
+
clientId: this.clientId,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Make an authenticated request to PayPal API
|
|
108
|
+
*/
|
|
109
|
+
async makeRequest(method, path, body, extraHeaders) {
|
|
110
|
+
const token = await this.getAccessToken();
|
|
111
|
+
|
|
112
|
+
const options = {
|
|
113
|
+
method,
|
|
114
|
+
headers: {
|
|
115
|
+
'Authorization': `Bearer ${token}`,
|
|
116
|
+
'Content-Type': 'application/json',
|
|
117
|
+
...extraHeaders,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
if (body) {
|
|
122
|
+
options.body = JSON.stringify(body);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
126
|
+
...options,
|
|
127
|
+
signal: AbortSignal.timeout(30000),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// PATCH returns 204 No Content on success
|
|
131
|
+
if (response.status === 204) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
const text = await response.text();
|
|
137
|
+
throw new Error(`PayPal API error (${response.status} ${method} ${path}): ${text}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return response.json();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Paginate through a PayPal list endpoint, collecting all items.
|
|
145
|
+
*
|
|
146
|
+
* @param {string} basePath - Base API path (e.g. '/v1/catalogs/products')
|
|
147
|
+
* @param {string} itemsKey - Key in response containing items (e.g. 'products')
|
|
148
|
+
* @param {Object} [extraParams] - Additional query params
|
|
149
|
+
* @param {Object} [extraHeaders] - Additional request headers
|
|
150
|
+
* @returns {Array} All items across all pages
|
|
151
|
+
*/
|
|
152
|
+
async paginate(basePath, itemsKey, extraParams = {}, extraHeaders) {
|
|
153
|
+
const allItems = [];
|
|
154
|
+
let page = 1;
|
|
155
|
+
const pageSize = 20;
|
|
156
|
+
|
|
157
|
+
while (true) {
|
|
158
|
+
const params = new URLSearchParams({
|
|
159
|
+
page_size: pageSize,
|
|
160
|
+
page: page,
|
|
161
|
+
total_required: true,
|
|
162
|
+
...extraParams,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const response = await this.makeRequest('GET', `${basePath}?${params}`, null, extraHeaders);
|
|
166
|
+
const items = response?.[itemsKey] || [];
|
|
167
|
+
allItems.push(...items);
|
|
168
|
+
|
|
169
|
+
const total = response?.total_items || 0;
|
|
170
|
+
if (allItems.length >= total || items.length < pageSize) {
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
page++;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return allItems;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// Catalog Products
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* List all catalog products (paginated)
|
|
186
|
+
*/
|
|
187
|
+
async listProducts() {
|
|
188
|
+
return this.paginate('/v1/catalogs/products', 'products');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Retrieve a catalog product by ID
|
|
193
|
+
*/
|
|
194
|
+
async getProduct(productId) {
|
|
195
|
+
return this.makeRequest('GET', `/v1/catalogs/products/${productId}`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Create a catalog product
|
|
200
|
+
*/
|
|
201
|
+
async createProduct({ name, description, type = 'SERVICE', imageUrl, homeUrl }) {
|
|
202
|
+
const body = {
|
|
203
|
+
name,
|
|
204
|
+
type,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
if (description) {
|
|
208
|
+
body.description = description.slice(0, 256); // PayPal limit
|
|
209
|
+
}
|
|
210
|
+
if (imageUrl) {
|
|
211
|
+
body.image_url = imageUrl;
|
|
212
|
+
}
|
|
213
|
+
if (homeUrl) {
|
|
214
|
+
body.home_url = homeUrl;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return this.makeRequest('POST', '/v1/catalogs/products', body);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Update a catalog product using JSON Patch format
|
|
222
|
+
*
|
|
223
|
+
* @param {string} productId - PayPal product ID
|
|
224
|
+
* @param {Array} patches - Array of JSON Patch operations
|
|
225
|
+
* e.g. [{ op: 'replace', path: '/description', value: 'new desc' }]
|
|
226
|
+
*/
|
|
227
|
+
async updateProduct(productId, patches) {
|
|
228
|
+
return this.makeRequest('PATCH', `/v1/catalogs/products/${productId}`, patches);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
// Billing Plans (subscriptions)
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* List billing plans filtered by product ID (with full details including billing_cycles).
|
|
237
|
+
* Paginated — returns ALL plans for the product.
|
|
238
|
+
*/
|
|
239
|
+
async listPlansForProduct(productId) {
|
|
240
|
+
return this.paginate(
|
|
241
|
+
'/v1/billing/plans',
|
|
242
|
+
'plans',
|
|
243
|
+
{ product_id: productId },
|
|
244
|
+
{ 'Prefer': 'return=representation' },
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Deactivate a billing plan (stops new subscriptions, existing ones continue)
|
|
250
|
+
*/
|
|
251
|
+
async deactivatePlan(planId) {
|
|
252
|
+
return this.makeRequest('POST', `/v1/billing/plans/${planId}/deactivate`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Create a billing plan for a product
|
|
257
|
+
*
|
|
258
|
+
* @param {Object} options
|
|
259
|
+
* @param {string} options.productId - PayPal catalog product ID
|
|
260
|
+
* @param {string} options.name - Plan name (e.g. "Chatsy - Plus (Monthly)")
|
|
261
|
+
* @param {string} options.interval - 'monthly' or 'annually'
|
|
262
|
+
* @param {number} options.amount - Price amount (e.g. 28)
|
|
263
|
+
* @param {string} [options.currency='USD'] - Currency code
|
|
264
|
+
* @param {number} [options.trialDays=0] - Trial period in days
|
|
265
|
+
*/
|
|
266
|
+
async createPlan({ productId, name, interval, amount, currency = 'USD', trialDays = 0 }) {
|
|
267
|
+
const billingCycles = [];
|
|
268
|
+
|
|
269
|
+
// Add trial cycle if specified
|
|
270
|
+
if (trialDays > 0) {
|
|
271
|
+
billingCycles.push({
|
|
272
|
+
frequency: {
|
|
273
|
+
interval_unit: 'DAY',
|
|
274
|
+
interval_count: 1,
|
|
275
|
+
},
|
|
276
|
+
tenure_type: 'TRIAL',
|
|
277
|
+
sequence: 1,
|
|
278
|
+
total_cycles: trialDays,
|
|
279
|
+
pricing_scheme: {
|
|
280
|
+
fixed_price: {
|
|
281
|
+
value: '0',
|
|
282
|
+
currency_code: currency,
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Regular billing cycle
|
|
289
|
+
billingCycles.push({
|
|
290
|
+
frequency: {
|
|
291
|
+
interval_unit: { annually: 'YEAR', monthly: 'MONTH', weekly: 'WEEK', daily: 'DAY' }[interval] || 'MONTH',
|
|
292
|
+
interval_count: 1,
|
|
293
|
+
},
|
|
294
|
+
tenure_type: 'REGULAR',
|
|
295
|
+
sequence: trialDays > 0 ? 2 : 1,
|
|
296
|
+
total_cycles: 0, // 0 = infinite
|
|
297
|
+
pricing_scheme: {
|
|
298
|
+
fixed_price: {
|
|
299
|
+
value: amount.toFixed(2),
|
|
300
|
+
currency_code: currency,
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const body = {
|
|
306
|
+
product_id: productId,
|
|
307
|
+
name,
|
|
308
|
+
billing_cycles: billingCycles,
|
|
309
|
+
payment_preferences: {
|
|
310
|
+
auto_bill_outstanding: true,
|
|
311
|
+
payment_failure_threshold: 3,
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
return this.makeRequest('POST', '/v1/billing/plans', body);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ---------------------------------------------------------------------------
|
|
319
|
+
// Webhooks
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* List all webhook endpoints
|
|
324
|
+
*/
|
|
325
|
+
async listWebhooks() {
|
|
326
|
+
return this.makeRequest('GET', '/v1/notifications/webhooks');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Create a webhook endpoint
|
|
331
|
+
*/
|
|
332
|
+
async createWebhook(url, eventTypes) {
|
|
333
|
+
return this.makeRequest('POST', '/v1/notifications/webhooks', {
|
|
334
|
+
url,
|
|
335
|
+
event_types: eventTypes.map((name) => ({ name })),
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Update a webhook endpoint using JSON Patch format
|
|
341
|
+
*/
|
|
342
|
+
async updateWebhook(webhookId, patches) {
|
|
343
|
+
return this.makeRequest('PATCH', `/v1/notifications/webhooks/${webhookId}`, patches);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Delete a webhook endpoint (204 No Content on success)
|
|
348
|
+
*/
|
|
349
|
+
async deleteWebhook(webhookId) {
|
|
350
|
+
return this.makeRequest('DELETE', `/v1/notifications/webhooks/${webhookId}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
module.exports = { PayPalAPI };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive payment-provider credential entry — one provider's instance of
|
|
3
|
+
* the shared setup contract (#608): the same Provide / Skip / Disable gate
|
|
4
|
+
* every service opens with (lib/config-flow.js confirmSetup, the ONE home of
|
|
5
|
+
* that wording), then the provider dashboard and its keys. Public halves
|
|
6
|
+
* (publishable key, client id, site) land in omega.json5 via the
|
|
7
|
+
* comment-preserving writeback — a provider with no public half declares none
|
|
8
|
+
* (coinbase, whose API key is the whole credential) — and the SECRET half rides
|
|
9
|
+
* the shared helper
|
|
10
|
+
* (lib/service-input.js), which persists it to the brand .env and exports it
|
|
11
|
+
* so the current run proceeds immediately. Disable writes
|
|
12
|
+
* `payment.providers.<name>: false` so the run stops asking — permanently.
|
|
13
|
+
*
|
|
14
|
+
* Non-interactive or dry-run sessions return false without prompting — the
|
|
15
|
+
* provider stays unconfigured and its operations print their dim note.
|
|
16
|
+
*/
|
|
17
|
+
const chalk = require('chalk').default;
|
|
18
|
+
const { input } = require('../../../vendor/devkit/prompt.js');
|
|
19
|
+
const { serviceInputSpec } = require('../../../config.js');
|
|
20
|
+
const { writeBrandConfig } = require('../../../lib/config-write.js');
|
|
21
|
+
const { confirmSetup, setAtPath } = require('../../../lib/config-flow.js');
|
|
22
|
+
const { requestServiceInput } = require('../../../lib/service-input.js');
|
|
23
|
+
const { canPrompt } = require('../../../lib/run-gates.js');
|
|
24
|
+
|
|
25
|
+
// Per provider: the dashboard to open, and the PUBLIC halves that land in
|
|
26
|
+
// omega.json5. The secret half's descriptor (name, mint URL, hint) lives once,
|
|
27
|
+
// in the REQUIRES registry — never re-spelled here.
|
|
28
|
+
const PROVIDERS = {
|
|
29
|
+
stripe: {
|
|
30
|
+
label: 'Stripe',
|
|
31
|
+
url: 'https://dashboard.stripe.com/apikeys',
|
|
32
|
+
instructions: 'Create or switch to the brand\'s Stripe account, then go to Developers → API keys.',
|
|
33
|
+
secret: 'STRIPE_SECRET_KEY',
|
|
34
|
+
fields: [
|
|
35
|
+
{
|
|
36
|
+
message: 'Stripe publishable key (pk_live_... or pk_test_...):',
|
|
37
|
+
validate: (val) => (val?.trim().startsWith('pk_') ? true : 'Must start with pk_live_ or pk_test_'),
|
|
38
|
+
configPath: 'payment.providers.stripe.publishableKey',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
paypal: {
|
|
43
|
+
label: 'PayPal',
|
|
44
|
+
url: 'https://developer.paypal.com/dashboard/applications',
|
|
45
|
+
instructions: 'Create or open the brand\'s REST API app (live), then copy its credentials.',
|
|
46
|
+
secret: 'PAYPAL_CLIENT_SECRET',
|
|
47
|
+
fields: [
|
|
48
|
+
{
|
|
49
|
+
message: 'PayPal client ID:',
|
|
50
|
+
validate: (val) => (val?.trim() ? true : 'Required'),
|
|
51
|
+
configPath: 'payment.providers.paypal.clientId',
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
chargebee: {
|
|
56
|
+
label: 'Chargebee',
|
|
57
|
+
url: 'https://app.chargebee.com/',
|
|
58
|
+
instructions: 'Open the brand\'s Chargebee site, then go to Settings → API keys.',
|
|
59
|
+
secret: 'CHARGEBEE_API_KEY',
|
|
60
|
+
fields: [
|
|
61
|
+
{
|
|
62
|
+
message: 'Chargebee site (the {site}.chargebee.com subdomain):',
|
|
63
|
+
validate: (val) => (val?.trim() ? true : 'Required'),
|
|
64
|
+
configPath: 'payment.providers.chargebee.site',
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
// The one provider with NO public half ([#642](https://github.com/Omega-JS-Stack/omega/issues/642)):
|
|
69
|
+
// the Coinbase Commerce API key is the entire credential, so `fields` is empty
|
|
70
|
+
// and nothing is written to omega.json5 — the gate and the secret ask are the
|
|
71
|
+
// whole flow. Its Disable therefore lands on `enabled`, the switch the schema
|
|
72
|
+
// declares and the checkout reads, rather than on the provider block the
|
|
73
|
+
// siblings turn off.
|
|
74
|
+
coinbase: {
|
|
75
|
+
label: 'Coinbase Commerce',
|
|
76
|
+
url: 'https://commerce.coinbase.com/settings/security',
|
|
77
|
+
instructions: 'Open the brand\'s Coinbase Commerce account, then go to Settings → Security → API keys.',
|
|
78
|
+
secret: 'COINBASE_COMMERCE_API_KEY',
|
|
79
|
+
disablePath: 'payment.providers.coinbase.enabled',
|
|
80
|
+
fields: [],
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Run the credential-entry flow for one provider. Returns true when
|
|
86
|
+
* credentials were landed (config + .env + process.env), false otherwise.
|
|
87
|
+
*
|
|
88
|
+
* @param {Object} context - Service context (brandConfig, brandRoot, options)
|
|
89
|
+
* @param {string} name - 'stripe' | 'paypal' | 'chargebee' | 'coinbase'
|
|
90
|
+
*/
|
|
91
|
+
async function providerSetupFlow(context, name) {
|
|
92
|
+
if (!canPrompt(context.options)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const provider = PROVIDERS[name];
|
|
97
|
+
// Where Disable lands `false`. The provider BLOCK is the switch for the ones
|
|
98
|
+
// whose public data enables them; a provider carrying its own switch names it
|
|
99
|
+
// (coinbase's `enabled`, #642), so the off state is the one shape the schema
|
|
100
|
+
// declares and every reader already gates on.
|
|
101
|
+
const disablePath = provider.disablePath || `payment.providers.${name}`;
|
|
102
|
+
|
|
103
|
+
// The uniform gate — Disable lands `payment.providers.<name>: false`
|
|
104
|
+
const action = await confirmSetup(context, {
|
|
105
|
+
label: provider.label,
|
|
106
|
+
instructions: [provider.instructions, `URL: ${chalk.cyan(provider.url)}`],
|
|
107
|
+
disablePath,
|
|
108
|
+
});
|
|
109
|
+
if (action !== 'yes') {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// The public halves land in omega.json5; the dashboard is opened by the
|
|
114
|
+
// secret ask below (Enter-gated, the house rule) — one page, both keys.
|
|
115
|
+
const configEdits = {};
|
|
116
|
+
for (const field of provider.fields) {
|
|
117
|
+
const value = (await input({ message: field.message, validate: field.validate })).trim();
|
|
118
|
+
configEdits[field.configPath] = value;
|
|
119
|
+
setAtPath(context.brandConfig, field.configPath, value);
|
|
120
|
+
}
|
|
121
|
+
writeBrandConfig(context, configEdits);
|
|
122
|
+
|
|
123
|
+
// The secret half through the shared helper — the gate already ran, so it
|
|
124
|
+
// goes straight to the guided paste, the brand .env, and process.env
|
|
125
|
+
const gate = await requestServiceInput(context, serviceInputSpec('payment', {
|
|
126
|
+
names: [provider.secret],
|
|
127
|
+
label: provider.label,
|
|
128
|
+
gate: false,
|
|
129
|
+
}));
|
|
130
|
+
|
|
131
|
+
return gate === null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
module.exports = { providerSetupFlow };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stripe API wrapper — the official SDK behind the method surface the
|
|
3
|
+
* payment operations actually use (accounts, products, prices, webhook
|
|
4
|
+
* endpoints). omega-manager's wrapper also carried test clocks, customers,
|
|
5
|
+
* subscriptions, and Radar-rule methods; the first three ride the
|
|
6
|
+
* testing-live port and the Radar methods were dead code (Stripe has no
|
|
7
|
+
* Radar rules API — the stripe-radar operation is manual guidance).
|
|
8
|
+
*/
|
|
9
|
+
const Stripe = require('stripe');
|
|
10
|
+
|
|
11
|
+
class StripeAPI {
|
|
12
|
+
constructor(secretKey) {
|
|
13
|
+
this.stripe = new Stripe(secretKey);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Retrieve the current Stripe account
|
|
18
|
+
*/
|
|
19
|
+
async getAccount() {
|
|
20
|
+
return this.stripe.accounts.retrieve();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Update a Stripe account's settings
|
|
25
|
+
*/
|
|
26
|
+
async updateAccount(accountId, updates) {
|
|
27
|
+
return this.stripe.accounts.update(accountId, updates);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Retrieve a product by ID
|
|
32
|
+
*/
|
|
33
|
+
async getProduct(productId) {
|
|
34
|
+
return this.stripe.products.retrieve(productId);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create a Stripe product with full brand/product details
|
|
39
|
+
*/
|
|
40
|
+
async createProduct({ name, brandId, productId, description, images, url }) {
|
|
41
|
+
const params = {
|
|
42
|
+
name,
|
|
43
|
+
metadata: { brandId, productId },
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (description) {
|
|
47
|
+
params.description = description;
|
|
48
|
+
}
|
|
49
|
+
if (images?.length) {
|
|
50
|
+
params.images = images;
|
|
51
|
+
}
|
|
52
|
+
if (url) {
|
|
53
|
+
params.url = url;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return this.stripe.products.create(params);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Update an existing Stripe product
|
|
61
|
+
*/
|
|
62
|
+
async updateProduct(stripeProductId, updates) {
|
|
63
|
+
return this.stripe.products.update(stripeProductId, updates);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* List all products (auto-paginates)
|
|
68
|
+
*/
|
|
69
|
+
async listAllProducts() {
|
|
70
|
+
const products = [];
|
|
71
|
+
for await (const product of this.stripe.products.list({ limit: 100 })) {
|
|
72
|
+
products.push(product);
|
|
73
|
+
}
|
|
74
|
+
return products;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* List all prices for a product (auto-paginates)
|
|
79
|
+
*/
|
|
80
|
+
async listPricesForProduct(productId) {
|
|
81
|
+
const prices = [];
|
|
82
|
+
for await (const price of this.stripe.prices.list({ product: productId, limit: 100 })) {
|
|
83
|
+
prices.push(price);
|
|
84
|
+
}
|
|
85
|
+
return prices;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create a recurring price (monthly or annually)
|
|
90
|
+
*/
|
|
91
|
+
async createRecurringPrice(productId, amount, interval, currency = 'usd') {
|
|
92
|
+
return this.stripe.prices.create({
|
|
93
|
+
product: productId,
|
|
94
|
+
unit_amount: Math.round(amount * 100), // Stripe uses cents
|
|
95
|
+
currency,
|
|
96
|
+
recurring: {
|
|
97
|
+
interval: { annually: 'year', monthly: 'month', weekly: 'week', daily: 'day' }[interval] || 'month',
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Create a one-time price
|
|
104
|
+
*/
|
|
105
|
+
async createOneTimePrice(productId, amount, currency = 'usd') {
|
|
106
|
+
return this.stripe.prices.create({
|
|
107
|
+
product: productId,
|
|
108
|
+
unit_amount: Math.round(amount * 100),
|
|
109
|
+
currency,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Archive a price (set active: false). Stripe does not support deleting prices.
|
|
115
|
+
*/
|
|
116
|
+
async archivePrice(priceId) {
|
|
117
|
+
return this.stripe.prices.update(priceId, { active: false });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* List all webhook endpoints on this account
|
|
122
|
+
*/
|
|
123
|
+
async listWebhookEndpoints() {
|
|
124
|
+
return this.stripe.webhookEndpoints.list({ limit: 100 });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Create a webhook endpoint
|
|
129
|
+
*/
|
|
130
|
+
async createWebhookEndpoint(url, enabledEvents) {
|
|
131
|
+
return this.stripe.webhookEndpoints.create({
|
|
132
|
+
url,
|
|
133
|
+
enabled_events: enabledEvents,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Update a webhook endpoint ({ enabled_events }, { disabled }, etc.)
|
|
139
|
+
*/
|
|
140
|
+
async updateWebhookEndpoint(webhookId, params) {
|
|
141
|
+
return this.stripe.webhookEndpoints.update(webhookId, params);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Delete a webhook endpoint
|
|
146
|
+
*/
|
|
147
|
+
async deleteWebhookEndpoint(webhookId) {
|
|
148
|
+
return this.stripe.webhookEndpoints.del(webhookId);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
module.exports = { StripeAPI };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure the GitHub org profile matches the brand — display name, support
|
|
3
|
+
* email, billing email, description (160-char org limit), blog URL, and
|
|
4
|
+
* location (only when github.location is configured; omega-manager hardcoded
|
|
5
|
+
* a country — company/brand config owns that choice now).
|
|
6
|
+
*
|
|
7
|
+
* Skipped entirely for shared orgs (filtered in the service setup).
|
|
8
|
+
*/
|
|
9
|
+
const chalk = require('chalk').default;
|
|
10
|
+
const { dryRunPlan } = require('../../../lib/run-gates.js');
|
|
11
|
+
|
|
12
|
+
module.exports = async function ensureOrg(context) {
|
|
13
|
+
const { brandConfig, options = {}, githubApi: api } = context;
|
|
14
|
+
|
|
15
|
+
const github = brandConfig.repo?.providers?.github;
|
|
16
|
+
const org = api.getOrg(github.org);
|
|
17
|
+
|
|
18
|
+
if (!org) {
|
|
19
|
+
console.log(` ${chalk.dim(`⊘ ${github.org} is not an organization — org-level settings don't apply`)}`);
|
|
20
|
+
return { status: 'success', output: { org: { skipped: 'owner is not an organization' } } };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const name = brandConfig.brand?.name;
|
|
24
|
+
const domain = (brandConfig.brand?.url || '').replace(/^https?:\/\//, '');
|
|
25
|
+
// GitHub org description has a 160 character limit
|
|
26
|
+
const fullDescription = brandConfig.brand?.description || '';
|
|
27
|
+
const description = fullDescription.length > 160
|
|
28
|
+
? fullDescription.slice(0, 157) + '...'
|
|
29
|
+
: fullDescription;
|
|
30
|
+
|
|
31
|
+
const supportEmail = `support@${domain}`;
|
|
32
|
+
const desired = {
|
|
33
|
+
name: name || org.name,
|
|
34
|
+
email: supportEmail,
|
|
35
|
+
billing_email: supportEmail,
|
|
36
|
+
description: description || org.description || '',
|
|
37
|
+
blog: `https://${domain}`,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if (github.location) {
|
|
41
|
+
desired.location = github.location;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Diff current vs desired — only drifted fields get patched
|
|
45
|
+
const updates = {};
|
|
46
|
+
for (const [key, value] of Object.entries(desired)) {
|
|
47
|
+
if (org[key] !== value) {
|
|
48
|
+
updates[key] = value;
|
|
49
|
+
console.log(` ${key}: "${org[key] || '(none)'}" ${chalk.dim('→')} "${chalk.cyan(value)}"`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (Object.keys(updates).length === 0) {
|
|
54
|
+
console.log(` ${chalk.green('✓')} Org settings already configured`);
|
|
55
|
+
return { status: 'success' };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (options.dryRun) {
|
|
59
|
+
return dryRunPlan('update org settings', { status: 'success', output: { org: { planned: Object.keys(updates) } } });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
api.updateOrg(github.org, updates);
|
|
64
|
+
console.log(` ${chalk.green('✓')} Org settings updated`);
|
|
65
|
+
return { status: 'success', output: { org: { updated: Object.keys(updates) } } };
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.log(` ${chalk.red('✗')} Failed to update org${chalk.dim(`: ${error.message}`)}`);
|
|
68
|
+
return { status: 'error', error: error.message };
|
|
69
|
+
}
|
|
70
|
+
};
|