seams 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +335 -0
- data/LICENSE +21 -0
- data/README.md +104 -0
- data/lib/generators/seams/accounts/accounts_generator.rb +272 -0
- data/lib/generators/seams/accounts/templates/README.md.tt +219 -0
- data/lib/generators/seams/accounts/templates/app/models/account.rb.tt +124 -0
- data/lib/generators/seams/accounts/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/accounts/templates/app/models/current.rb.tt +38 -0
- data/lib/generators/seams/accounts/templates/app/models/membership.rb.tt +114 -0
- data/lib/generators/seams/accounts/templates/config/routes.rb.tt +8 -0
- data/lib/generators/seams/accounts/templates/db/migrate/create_accounts.rb.tt +29 -0
- data/lib/generators/seams/accounts/templates/db/migrate/create_accounts_memberships.rb.tt +49 -0
- data/lib/generators/seams/accounts/templates/lib/accounts.rb.tt +21 -0
- data/lib/generators/seams/accounts/templates/lib/concerns/account_scoped.rb.tt +97 -0
- data/lib/generators/seams/accounts/templates/lib/concerns/authorization.rb.tt +105 -0
- data/lib/generators/seams/accounts/templates/lib/configuration.rb.tt +26 -0
- data/lib/generators/seams/accounts/templates/lib/engine.rb.tt +55 -0
- data/lib/generators/seams/accounts/templates/spec/factories/accounts.rb.tt +49 -0
- data/lib/generators/seams/accounts/templates/spec/models/accounts/account_spec.rb.tt +64 -0
- data/lib/generators/seams/accounts/templates/spec/models/accounts/membership_spec.rb.tt +99 -0
- data/lib/generators/seams/accounts/templates/spec/runtime/accounts_boot_spec.rb.tt +181 -0
- data/lib/generators/seams/admin/admin_generator.rb +852 -0
- data/lib/generators/seams/admin/templates/README.md.tt +266 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_memberships_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/application_controller.rb.tt +282 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/identities_controller.rb.tt +26 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/invitations_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/invoices_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/notification_preferences_controller.rb.tt +15 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/notifications_controller.rb.tt +18 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/plans_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/subscriptions_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/teams_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/teams_memberships_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/account_dashboard.rb.tt +50 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/accounts_membership_dashboard.rb.tt +58 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/identity_dashboard.rb.tt +48 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/invitation_dashboard.rb.tt +51 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/invoice_dashboard.rb.tt +67 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/lifetime_pass_dashboard.rb.tt +65 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_dashboard.rb.tt +58 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_preference_dashboard.rb.tt +43 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/plan_dashboard.rb.tt +72 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/subscription_dashboard.rb.tt +59 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/team_dashboard.rb.tt +39 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/teams_membership_dashboard.rb.tt +43 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/account_policy.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/accounts_membership_policy.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/application_policy.rb.tt +85 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/identity_policy.rb.tt +18 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/invitation_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/invoice_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/lifetime_pass_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_preference_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/plan_policy.rb.tt +11 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/subscription_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/team_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/teams_membership_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/account_policy.rb.tt +33 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/accounts_membership_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/application_policy.rb.tt +169 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/identity_policy.rb.tt +67 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invitation_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invoice_policy.rb.tt +21 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/lifetime_pass_policy.rb.tt +21 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_policy.rb.tt +25 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_preference_policy.rb.tt +23 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/plan_policy.rb.tt +47 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/subscription_policy.rb.tt +22 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/team_policy.rb.tt +28 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/teams_membership_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/config/routes.rb.tt +38 -0
- data/lib/generators/seams/admin/templates/lib/admin.rb.tt +36 -0
- data/lib/generators/seams/admin/templates/lib/concerns/authenticator.rb.tt +66 -0
- data/lib/generators/seams/admin/templates/lib/configuration.rb.tt +90 -0
- data/lib/generators/seams/admin/templates/lib/context.rb.tt +44 -0
- data/lib/generators/seams/admin/templates/lib/engine.rb.tt +68 -0
- data/lib/generators/seams/admin/templates/spec/factories/admin.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/spec/runtime/admin_boot_spec.rb.tt +604 -0
- data/lib/generators/seams/auth/add_oauth_provider/add_oauth_provider_generator.rb +157 -0
- data/lib/generators/seams/auth/add_oauth_provider/templates/adapter.rb.tt +95 -0
- data/lib/generators/seams/auth/add_oauth_provider/templates/adapter_spec.rb.tt +58 -0
- data/lib/generators/seams/auth/auth_generator.rb +311 -0
- data/lib/generators/seams/auth/templates/README.md.tt +289 -0
- data/lib/generators/seams/auth/templates/app/controllers/oauth/callbacks_controller.rb.tt +80 -0
- data/lib/generators/seams/auth/templates/app/controllers/password_resets_controller.rb.tt +44 -0
- data/lib/generators/seams/auth/templates/app/controllers/registrations_controller.rb.tt +34 -0
- data/lib/generators/seams/auth/templates/app/controllers/sessions_controller.rb.tt +49 -0
- data/lib/generators/seams/auth/templates/app/jobs/application_job.rb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/jobs/cleanup_expired_sessions_job.rb.tt +30 -0
- data/lib/generators/seams/auth/templates/app/mailers/passwords_mailer.rb.tt +15 -0
- data/lib/generators/seams/auth/templates/app/models/api_token.rb.tt +62 -0
- data/lib/generators/seams/auth/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/models/current.rb.tt +15 -0
- data/lib/generators/seams/auth/templates/app/models/identity.rb.tt +74 -0
- data/lib/generators/seams/auth/templates/app/models/oauth/provider.rb.tt +48 -0
- data/lib/generators/seams/auth/templates/app/models/session.rb.tt +28 -0
- data/lib/generators/seams/auth/templates/app/services/authenticate_identity.rb.tt +31 -0
- data/lib/generators/seams/auth/templates/app/services/generate_api_token.rb.tt +35 -0
- data/lib/generators/seams/auth/templates/app/services/oauth/authenticator.rb.tt +94 -0
- data/lib/generators/seams/auth/templates/app/services/register_identity.rb.tt +57 -0
- data/lib/generators/seams/auth/templates/app/services/reset_password.rb.tt +41 -0
- data/lib/generators/seams/auth/templates/app/services/revoke_api_token.rb.tt +38 -0
- data/lib/generators/seams/auth/templates/app/views/password_resets/edit.html.erb.tt +12 -0
- data/lib/generators/seams/auth/templates/app/views/password_resets/new.html.erb.tt +11 -0
- data/lib/generators/seams/auth/templates/app/views/passwords_mailer/reset_email.html.erb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/views/registrations/new.html.erb.tt +26 -0
- data/lib/generators/seams/auth/templates/app/views/sessions/_oauth_buttons.html.erb.tt +18 -0
- data/lib/generators/seams/auth/templates/app/views/sessions/new.html.erb.tt +17 -0
- data/lib/generators/seams/auth/templates/config/routes.rb.tt +21 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_api_tokens.rb.tt +26 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_identities.rb.tt +29 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_oauth_providers.rb.tt +35 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_sessions.rb.tt +19 -0
- data/lib/generators/seams/auth/templates/lib/auth.rb.tt +39 -0
- data/lib/generators/seams/auth/templates/lib/concerns/api_authenticatable.rb.tt +58 -0
- data/lib/generators/seams/auth/templates/lib/concerns/authenticatable.rb.tt +32 -0
- data/lib/generators/seams/auth/templates/lib/concerns/authentication.rb.tt +60 -0
- data/lib/generators/seams/auth/templates/lib/configuration.rb.tt +45 -0
- data/lib/generators/seams/auth/templates/lib/engine.rb.tt +46 -0
- data/lib/generators/seams/auth/templates/lib/oauth/abstract.rb.tt +87 -0
- data/lib/generators/seams/auth/templates/lib/oauth/github.rb.tt +112 -0
- data/lib/generators/seams/auth/templates/lib/oauth/google.rb.tt +78 -0
- data/lib/generators/seams/auth/templates/lib/tasks/auth_pii.rake.tt +68 -0
- data/lib/generators/seams/auth/templates/spec/factories/auth.rb.tt +38 -0
- data/lib/generators/seams/auth/templates/spec/mailers/passwords_mailer_spec.rb.tt +37 -0
- data/lib/generators/seams/auth/templates/spec/models/api_token_spec.rb.tt +84 -0
- data/lib/generators/seams/auth/templates/spec/models/identity_spec.rb.tt +56 -0
- data/lib/generators/seams/auth/templates/spec/models/oauth/provider_spec.rb.tt +64 -0
- data/lib/generators/seams/auth/templates/spec/models/session_spec.rb.tt +34 -0
- data/lib/generators/seams/auth/templates/spec/runtime/boot_spec.rb.tt +30 -0
- data/lib/generators/seams/auth/templates/spec/runtime/event_payload_spec.rb.tt +29 -0
- data/lib/generators/seams/auth/templates/spec/runtime/login_flow_spec.rb.tt +45 -0
- data/lib/generators/seams/billing/billing_generator.rb +476 -0
- data/lib/generators/seams/billing/templates/README.md.tt +355 -0
- data/lib/generators/seams/billing/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +84 -0
- data/lib/generators/seams/billing/templates/app/controllers/checkout_controller.rb.tt +92 -0
- data/lib/generators/seams/billing/templates/app/controllers/invoices_controller.rb.tt +63 -0
- data/lib/generators/seams/billing/templates/app/controllers/plans_controller.rb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/controllers/portal_controller.rb.tt +45 -0
- data/lib/generators/seams/billing/templates/app/controllers/subscriptions_controller.rb.tt +119 -0
- data/lib/generators/seams/billing/templates/app/controllers/webhooks_controller.rb.tt +98 -0
- data/lib/generators/seams/billing/templates/app/helpers/currency_helper.rb.tt +44 -0
- data/lib/generators/seams/billing/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/billing/templates/app/jobs/cancel_subscription_job.rb.tt +39 -0
- data/lib/generators/seams/billing/templates/app/jobs/start_subscription_job.rb.tt +32 -0
- data/lib/generators/seams/billing/templates/app/jobs/webhooks/process_event_job.rb.tt +37 -0
- data/lib/generators/seams/billing/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/billing/templates/app/models/invoice.rb.tt +35 -0
- data/lib/generators/seams/billing/templates/app/models/lifetime_pass.rb.tt +60 -0
- data/lib/generators/seams/billing/templates/app/models/plan.rb.tt +95 -0
- data/lib/generators/seams/billing/templates/app/models/subscription.rb.tt +31 -0
- data/lib/generators/seams/billing/templates/app/models/webhook_event.rb.tt +13 -0
- data/lib/generators/seams/billing/templates/app/services/checkout_session_service.rb.tt +25 -0
- data/lib/generators/seams/billing/templates/app/services/customers/find_or_create_service.rb.tt +73 -0
- data/lib/generators/seams/billing/templates/app/services/invoices/sync_service.rb.tt +50 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/create_lifetime_session_service.rb.tt +82 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/create_pass_from_checkout_service.rb.tt +88 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/grant_pass_service.rb.tt +80 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/revoke_pass_service.rb.tt +59 -0
- data/lib/generators/seams/billing/templates/app/services/portal_session_service.rb.tt +23 -0
- data/lib/generators/seams/billing/templates/app/services/service_result.rb.tt +38 -0
- data/lib/generators/seams/billing/templates/app/services/stripe_service.rb.tt +67 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/cancel_service.rb.tt +42 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/change_plan_service.rb.tt +48 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/reactivate_service.rb.tt +28 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/event_router.rb.tt +54 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handler.rb.tt +93 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/charge_refunded_handler.rb.tt +18 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/checkout_session_completed_handler.rb.tt +58 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_created_handler.rb.tt +16 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_finalized_handler.rb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_handler_base.rb.tt +80 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_paid_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_payment_failed_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_voided_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_failed_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_succeeded_handler.rb.tt +19 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_created_handler.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_deleted_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_handler_base.rb.tt +92 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_trial_will_end_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_updated_handler.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/index.html.erb.tt +36 -0
- data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/new.html.erb.tt +37 -0
- data/lib/generators/seams/billing/templates/app/views/checkout/success.html.erb.tt +5 -0
- data/lib/generators/seams/billing/templates/app/views/invoices/index.html.erb.tt +22 -0
- data/lib/generators/seams/billing/templates/app/views/invoices/show.html.erb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/views/plans/index.html.erb.tt +51 -0
- data/lib/generators/seams/billing/templates/app/views/subscriptions/index.html.erb.tt +16 -0
- data/lib/generators/seams/billing/templates/app/views/subscriptions/show.html.erb.tt +25 -0
- data/lib/generators/seams/billing/templates/config/routes.rb.tt +39 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_invoices.rb.tt +32 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_lifetime_passes.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_plans.rb.tt +31 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_subscriptions.rb.tt +33 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_webhook_events.rb.tt +24 -0
- data/lib/generators/seams/billing/templates/lib/billing.rb.tt +34 -0
- data/lib/generators/seams/billing/templates/lib/concerns/billable.rb.tt +100 -0
- data/lib/generators/seams/billing/templates/lib/configuration.rb.tt +52 -0
- data/lib/generators/seams/billing/templates/lib/engine.rb.tt +72 -0
- data/lib/generators/seams/billing/templates/lib/gateways/abstract.rb.tt +65 -0
- data/lib/generators/seams/billing/templates/lib/gateways/adyen.rb.tt +16 -0
- data/lib/generators/seams/billing/templates/lib/gateways/paddle.rb.tt +22 -0
- data/lib/generators/seams/billing/templates/lib/gateways/stripe.rb.tt +155 -0
- data/lib/generators/seams/billing/templates/lib/stripe/client.rb.tt +101 -0
- data/lib/generators/seams/billing/templates/lib/stripe/webhook_signature.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/lib/tasks/billing_check.rake.tt +34 -0
- data/lib/generators/seams/billing/templates/spec/factories/billing.rb.tt +65 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/charge_refunded.json.tt +19 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/checkout_session_completed.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_created.json.tt +25 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_deleted.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_trial_will_end.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_updated.json.tt +28 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_created.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_finalized.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_paid.json.tt +19 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_payment_failed.json.tt +20 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_voided.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_payment_failed.json.tt +21 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_succeeded.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/gateways/contract_spec.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/spec/gateways/stripe_spec.rb.tt +53 -0
- data/lib/generators/seams/billing/templates/spec/models/plan_spec.rb.tt +81 -0
- data/lib/generators/seams/billing/templates/spec/models/subscription_spec.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/spec/runtime/boot_spec.rb.tt +38 -0
- data/lib/generators/seams/billing/templates/spec/runtime/webhook_handlers_spec.rb.tt +382 -0
- data/lib/generators/seams/billing/templates/spec/support/shared_examples/a_billing_gateway.rb.tt +100 -0
- data/lib/generators/seams/billing/templates/spec/support/stripe_helpers.rb.tt +59 -0
- data/lib/generators/seams/core/core_generator.rb +191 -0
- data/lib/generators/seams/core/templates/README.md.tt +45 -0
- data/lib/generators/seams/core/templates/app/controllers/concerns/has_current_attributes.rb.tt +71 -0
- data/lib/generators/seams/core/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/core/templates/app/models/audit_log.rb.tt +19 -0
- data/lib/generators/seams/core/templates/app/models/concerns/auditable.rb.tt +64 -0
- data/lib/generators/seams/core/templates/app/models/concerns/sluggable.rb.tt +53 -0
- data/lib/generators/seams/core/templates/app/models/concerns/soft_deletable.rb.tt +37 -0
- data/lib/generators/seams/core/templates/app/models/concerns/tenant_scoped.rb.tt +39 -0
- data/lib/generators/seams/core/templates/app/models/current.rb.tt +16 -0
- data/lib/generators/seams/core/templates/app/services/event_publisher.rb.tt +23 -0
- data/lib/generators/seams/core/templates/app/validators/email_format_validator.rb.tt +21 -0
- data/lib/generators/seams/core/templates/db/migrate/create_core_audit_logs.rb.tt +29 -0
- data/lib/generators/seams/core/templates/lib/core.rb.tt +8 -0
- data/lib/generators/seams/core/templates/lib/engine.rb.tt +28 -0
- data/lib/generators/seams/core/templates/spec/concerns/auditable_spec.rb.tt +39 -0
- data/lib/generators/seams/core/templates/spec/concerns/sluggable_spec.rb.tt +29 -0
- data/lib/generators/seams/core/templates/spec/models/audit_log_spec.rb.tt +22 -0
- data/lib/generators/seams/core/templates/spec/runtime/boot_spec.rb.tt +25 -0
- data/lib/generators/seams/core/templates/spec/validators/email_format_validator_spec.rb.tt +29 -0
- data/lib/generators/seams/engine/engine_generator.rb +165 -0
- data/lib/generators/seams/engine/templates/Gemfile.tt +19 -0
- data/lib/generators/seams/engine/templates/LICENSE.tt +21 -0
- data/lib/generators/seams/engine/templates/README.md.tt +40 -0
- data/lib/generators/seams/engine/templates/Rakefile.tt +14 -0
- data/lib/generators/seams/engine/templates/app/application_controller.rb.tt +6 -0
- data/lib/generators/seams/engine/templates/app/application_record.rb.tt +16 -0
- data/lib/generators/seams/engine/templates/config/locales/en.yml.tt +14 -0
- data/lib/generators/seams/engine/templates/config/routes.rb.tt +4 -0
- data/lib/generators/seams/engine/templates/gemspec.tt +20 -0
- data/lib/generators/seams/engine/templates/host_initializer.rb.tt +13 -0
- data/lib/generators/seams/engine/templates/lib/engine.rb.tt +27 -0
- data/lib/generators/seams/engine/templates/lib/root.rb.tt +7 -0
- data/lib/generators/seams/engine/templates/lib/version.rb.tt +5 -0
- data/lib/generators/seams/engine/templates/rubocop.yml.tt +55 -0
- data/lib/generators/seams/engine/templates/spec/example_spec.rb.tt +16 -0
- data/lib/generators/seams/engine/templates/spec/spec_helper.rb.tt +23 -0
- data/lib/generators/seams/install/install_generator.rb +211 -0
- data/lib/generators/seams/install/templates/Dockerfile.tt +52 -0
- data/lib/generators/seams/install/templates/Procfile.tt +14 -0
- data/lib/generators/seams/install/templates/bin_seams.tt +107 -0
- data/lib/generators/seams/install/templates/ci.yml.tt +123 -0
- data/lib/generators/seams/install/templates/deploy.yml.tt +63 -0
- data/lib/generators/seams/install/templates/doc/ARCHITECTURE.md.tt +86 -0
- data/lib/generators/seams/install/templates/docker-entrypoint.tt +27 -0
- data/lib/generators/seams/install/templates/rubocop.yml.tt +33 -0
- data/lib/generators/seams/install/templates/ruby-version.tt +1 -0
- data/lib/generators/seams/install/templates/script/collate_coverage.rb.tt +33 -0
- data/lib/generators/seams/install/templates/script/run_affected_tests.sh.tt +64 -0
- data/lib/generators/seams/install/templates/seams.rake.tt +65 -0
- data/lib/generators/seams/install/templates/seams.rb.tt +9 -0
- data/lib/generators/seams/install/templates/seams_engines.rb.tt +15 -0
- data/lib/generators/seams/notifications/notifications_generator.rb +395 -0
- data/lib/generators/seams/notifications/templates/README.md.tt +269 -0
- data/lib/generators/seams/notifications/templates/app/channels/notification_channel.rb.tt +36 -0
- data/lib/generators/seams/notifications/templates/app/controllers/notifications_controller.rb.tt +58 -0
- data/lib/generators/seams/notifications/templates/app/controllers/preferences_controller.rb.tt +54 -0
- data/lib/generators/seams/notifications/templates/app/javascript/controllers/notification_bell_controller.js.tt +34 -0
- data/lib/generators/seams/notifications/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/notifications/templates/app/jobs/create_notification_job.rb.tt +31 -0
- data/lib/generators/seams/notifications/templates/app/jobs/send_due_notifications_job.rb.tt +22 -0
- data/lib/generators/seams/notifications/templates/app/jobs/send_notification_job.rb.tt +13 -0
- data/lib/generators/seams/notifications/templates/app/mailers/application_mailer.rb.tt +12 -0
- data/lib/generators/seams/notifications/templates/app/mailers/notification_mailer.rb.tt +23 -0
- data/lib/generators/seams/notifications/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/notifications/templates/app/models/delivery.rb.tt +13 -0
- data/lib/generators/seams/notifications/templates/app/models/notification.rb.tt +218 -0
- data/lib/generators/seams/notifications/templates/app/models/notification_preference.rb.tt +29 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/email.rb.tt +38 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/in_app.rb.tt +26 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/sms.rb.tt +33 -0
- data/lib/generators/seams/notifications/templates/app/subscribers/auth_subscriber.rb.tt +71 -0
- data/lib/generators/seams/notifications/templates/app/subscribers/billing_subscriber.rb.tt +127 -0
- data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.html.erb.tt +22 -0
- data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/notifications/_bell.html.erb.tt +15 -0
- data/lib/generators/seams/notifications/templates/app/views/notifications/index.html.erb.tt +15 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.html.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.text.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.html.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.html.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.html.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.text.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/default.html.erb.tt +10 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/default.text.erb.tt +11 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/welcome.html.erb.tt +6 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/welcome.text.erb.tt +6 -0
- data/lib/generators/seams/notifications/templates/config/initializers/notifications.rb.tt +58 -0
- data/lib/generators/seams/notifications/templates/config/routes.rb.tt +17 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notification_deliveries.rb.tt +16 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notification_preferences.rb.tt +25 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notifications.rb.tt +35 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/abstract.rb.tt +20 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/action_mailer.rb.tt +17 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/null_sms.rb.tt +23 -0
- data/lib/generators/seams/notifications/templates/lib/concerns/notifiable.rb.tt +135 -0
- data/lib/generators/seams/notifications/templates/lib/configuration.rb.tt +24 -0
- data/lib/generators/seams/notifications/templates/lib/engine.rb.tt +35 -0
- data/lib/generators/seams/notifications/templates/lib/notifications.rb.tt +75 -0
- data/lib/generators/seams/notifications/templates/lib/type_registry.rb.tt +74 -0
- data/lib/generators/seams/notifications/templates/spec/factories/notifications.rb.tt +53 -0
- data/lib/generators/seams/notifications/templates/spec/models/delivery_spec.rb.tt +28 -0
- data/lib/generators/seams/notifications/templates/spec/models/notification_preference_spec.rb.tt +46 -0
- data/lib/generators/seams/notifications/templates/spec/models/notification_spec.rb.tt +60 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/bell_broadcast_spec.rb.tt +59 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/billing_subscriber_skip_spec.rb.tt +87 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/boot_spec.rb.tt +39 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/schedule_round_trip_spec.rb.tt +55 -0
- data/lib/generators/seams/remove/remove_generator.rb +259 -0
- data/lib/generators/seams/teams/teams_generator.rb +298 -0
- data/lib/generators/seams/teams/templates/README.md.tt +88 -0
- data/lib/generators/seams/teams/templates/app/controllers/invitations_controller.rb.tt +102 -0
- data/lib/generators/seams/teams/templates/app/controllers/memberships_controller.rb.tt +54 -0
- data/lib/generators/seams/teams/templates/app/controllers/teams_controller.rb.tt +68 -0
- data/lib/generators/seams/teams/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/teams/templates/app/mailers/invitation_mailer.rb.tt +34 -0
- data/lib/generators/seams/teams/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/teams/templates/app/models/current.rb.tt +30 -0
- data/lib/generators/seams/teams/templates/app/models/invitation.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/models/membership.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/models/team.rb.tt +32 -0
- data/lib/generators/seams/teams/templates/app/subscribers/invitation_subscriber.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/views/invitation_mailer/invite.text.erb.tt +8 -0
- data/lib/generators/seams/teams/templates/app/views/invitations/index.html.erb.tt +44 -0
- data/lib/generators/seams/teams/templates/app/views/memberships/index.html.erb.tt +32 -0
- data/lib/generators/seams/teams/templates/app/views/teams/edit.html.erb.tt +28 -0
- data/lib/generators/seams/teams/templates/app/views/teams/index.html.erb.tt +15 -0
- data/lib/generators/seams/teams/templates/app/views/teams/new.html.erb.tt +24 -0
- data/lib/generators/seams/teams/templates/app/views/teams/show.html.erb.tt +17 -0
- data/lib/generators/seams/teams/templates/config/routes.rb.tt +19 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_team_invitations.rb.tt +24 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_team_memberships.rb.tt +25 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_teams.rb.tt +18 -0
- data/lib/generators/seams/teams/templates/lib/concerns/account_scoped.rb.tt +79 -0
- data/lib/generators/seams/teams/templates/lib/concerns/authorization.rb.tt +55 -0
- data/lib/generators/seams/teams/templates/lib/configuration.rb.tt +45 -0
- data/lib/generators/seams/teams/templates/lib/engine.rb.tt +51 -0
- data/lib/generators/seams/teams/templates/lib/teams.rb.tt +22 -0
- data/lib/generators/seams/teams/templates/spec/factories/teams.rb.tt +47 -0
- data/lib/generators/seams/teams/templates/spec/models/invitation_spec.rb.tt +25 -0
- data/lib/generators/seams/teams/templates/spec/models/membership_spec.rb.tt +29 -0
- data/lib/generators/seams/teams/templates/spec/models/team_spec.rb.tt +23 -0
- data/lib/generators/seams/teams/templates/spec/runtime/boot_spec.rb.tt +32 -0
- data/lib/seams/cli/list.rb +111 -0
- data/lib/seams/cli/quality.rb +99 -0
- data/lib/seams/cli/resolve.rb +276 -0
- data/lib/seams/cli/test_changed.rb +116 -0
- data/lib/seams/cli.rb +48 -0
- data/lib/seams/configuration.rb +19 -0
- data/lib/seams/cops/known_queue_names.rb +42 -0
- data/lib/seams/cops/migration_comments.rb +68 -0
- data/lib/seams/cops/no_cross_engine_dependency.rb +58 -0
- data/lib/seams/cops/no_cross_engine_model_access.rb +153 -0
- data/lib/seams/cops.rb +18 -0
- data/lib/seams/event_registry.rb +49 -0
- data/lib/seams/events/adapter.rb +24 -0
- data/lib/seams/events/adapters/active_support.rb +31 -0
- data/lib/seams/events/publisher.rb +178 -0
- data/lib/seams/events.rb +39 -0
- data/lib/seams/generators/dummy_app_writer.rb +424 -0
- data/lib/seams/generators/eject_aware.rb +102 -0
- data/lib/seams/generators/follow_up_generator.rb +148 -0
- data/lib/seams/generators/host_injector.rb +124 -0
- data/lib/seams/generators/sibling_rubocop_writer.rb +77 -0
- data/lib/seams/generators/splicer.rb +217 -0
- data/lib/seams/observability/adapter.rb +33 -0
- data/lib/seams/observability/adapters/rails_logger.rb +59 -0
- data/lib/seams/observability.rb +34 -0
- data/lib/seams/runtime.rb +23 -0
- data/lib/seams/version.rb +5 -0
- data/lib/seams.rb +23 -0
- metadata +493 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<%%# Read-only invoice history for the signed-in user. %>
|
|
2
|
+
<h1>Billing history</h1>
|
|
3
|
+
|
|
4
|
+
<%% if @invoices.empty? %>
|
|
5
|
+
<p>No invoices yet.</p>
|
|
6
|
+
<%% else %>
|
|
7
|
+
<table>
|
|
8
|
+
<thead>
|
|
9
|
+
<tr><th>Date</th><th>Amount</th><th>Status</th><th></th></tr>
|
|
10
|
+
</thead>
|
|
11
|
+
<tbody>
|
|
12
|
+
<%% @invoices.each do |invoice| %>
|
|
13
|
+
<tr>
|
|
14
|
+
<td><%%= invoice.created_at.to_date %></td>
|
|
15
|
+
<td><%%= Billing::CurrencyHelper.format_money(invoice.amount_cents, invoice.currency) %></td>
|
|
16
|
+
<td><%%= invoice.status.titleize %></td>
|
|
17
|
+
<td><%%= link_to "View", invoice_path(invoice) %></td>
|
|
18
|
+
</tr>
|
|
19
|
+
<%% end %>
|
|
20
|
+
</tbody>
|
|
21
|
+
</table>
|
|
22
|
+
<%% end %>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<%%# Single invoice view. The Stripe-hosted PDF lives off the gateway %>
|
|
2
|
+
<%%# (we do not proxy it); add a `hosted_invoice_url` link in your host %>
|
|
3
|
+
<%%# override if you want one. %>
|
|
4
|
+
<h1>Invoice <%%= @invoice.gateway_ref %></h1>
|
|
5
|
+
|
|
6
|
+
<dl>
|
|
7
|
+
<dt>Amount</dt> <dd><%%= Billing::CurrencyHelper.format_money(@invoice.amount_cents, @invoice.currency) %></dd>
|
|
8
|
+
<dt>Status</dt> <dd><%%= @invoice.status.titleize %></dd>
|
|
9
|
+
<dt>Customer</dt> <dd><%%= @invoice.customer_ref %></dd>
|
|
10
|
+
<dt>Subscription</dt><dd><%%= @invoice.subscription_ref %></dd>
|
|
11
|
+
<dt>Paid at</dt> <dd><%%= @invoice.paid_at %></dd>
|
|
12
|
+
</dl>
|
|
13
|
+
|
|
14
|
+
<%%= link_to "Back to billing history", invoices_path %>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<h1>Plans</h1>
|
|
2
|
+
|
|
3
|
+
<%% if @recurring_plans.any? %>
|
|
4
|
+
<section class="plans plans-recurring">
|
|
5
|
+
<h2>Subscriptions</h2>
|
|
6
|
+
<%% @recurring_plans.each do |plan| %>
|
|
7
|
+
<article class="plan">
|
|
8
|
+
<h3><%%= plan.name %></h3>
|
|
9
|
+
<p class="plan-price">
|
|
10
|
+
<%%= number_to_currency(plan.amount_cents / 100.0, unit: plan.currency.upcase + " ") %> /
|
|
11
|
+
<%%= plan.interval %>
|
|
12
|
+
</p>
|
|
13
|
+
<%% if plan.has_trial? %>
|
|
14
|
+
<p><%%= plan.trial_period_days %>-day free trial</p>
|
|
15
|
+
<%% end %>
|
|
16
|
+
<%%= button_to "Subscribe", billing.checkout_path(plan: plan.gateway_ref), method: :post %>
|
|
17
|
+
</article>
|
|
18
|
+
<%% end %>
|
|
19
|
+
</section>
|
|
20
|
+
<%% end %>
|
|
21
|
+
|
|
22
|
+
<%% if @lifetime_plans.any? %>
|
|
23
|
+
<section class="plans plans-lifetime">
|
|
24
|
+
<h2>Lifetime — buy once, own forever</h2>
|
|
25
|
+
<p class="plans-lifetime-disclaimer">
|
|
26
|
+
One payment, permanent access. No recurring billing.
|
|
27
|
+
</p>
|
|
28
|
+
<%% @lifetime_plans.each do |plan| %>
|
|
29
|
+
<article class="plan plan-lifetime">
|
|
30
|
+
<h3><%%= plan.name %></h3>
|
|
31
|
+
<p class="plan-price">
|
|
32
|
+
<%%= number_to_currency(plan.amount_cents / 100.0, unit: plan.currency.upcase + " ") %> ·
|
|
33
|
+
one-time
|
|
34
|
+
</p>
|
|
35
|
+
<%% remaining = plan.lifetime_inventory_remaining %>
|
|
36
|
+
<%% if remaining.nil? %>
|
|
37
|
+
<p class="plan-inventory">Unlimited</p>
|
|
38
|
+
<%% elsif remaining.zero? %>
|
|
39
|
+
<p class="plan-inventory plan-sold-out">Sold out</p>
|
|
40
|
+
<%% else %>
|
|
41
|
+
<p class="plan-inventory">Only <%%= remaining %> left at this price</p>
|
|
42
|
+
<%% end %>
|
|
43
|
+
<%% if plan.lifetime_sold_out? %>
|
|
44
|
+
<button disabled>Sold out</button>
|
|
45
|
+
<%% else %>
|
|
46
|
+
<%%= button_to "Buy lifetime", billing.lifetime_checkout_path(plan: plan.gateway_ref), method: :post %>
|
|
47
|
+
<%% end %>
|
|
48
|
+
</article>
|
|
49
|
+
<%% end %>
|
|
50
|
+
</section>
|
|
51
|
+
<%% end %>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<%%# Lists the signed-in user's subscriptions. Override at %>
|
|
2
|
+
<%%# app/views/billing/subscriptions/index.html.erb in your host. %>
|
|
3
|
+
<h1>Your subscriptions</h1>
|
|
4
|
+
|
|
5
|
+
<%% if @subscriptions.empty? %>
|
|
6
|
+
<p>You do not have any subscriptions yet. <%%= link_to "View plans", plans_path %>.</p>
|
|
7
|
+
<%% else %>
|
|
8
|
+
<ul>
|
|
9
|
+
<%% @subscriptions.each do |subscription| %>
|
|
10
|
+
<li>
|
|
11
|
+
<%%= link_to subscription.plan_ref, subscription_path(subscription) %>
|
|
12
|
+
— <%%= subscription.status.titleize %>
|
|
13
|
+
</li>
|
|
14
|
+
<%% end %>
|
|
15
|
+
</ul>
|
|
16
|
+
<%% end %>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<%%# Single subscription view with cancel / reactivate / change_plan %>
|
|
2
|
+
<%%# controls. Override in your host to brand it. %>
|
|
3
|
+
<h1>Subscription <%%= @subscription.gateway_ref %></h1>
|
|
4
|
+
|
|
5
|
+
<dl>
|
|
6
|
+
<dt>Status</dt> <dd><%%= @subscription.status.titleize %></dd>
|
|
7
|
+
<dt>Plan</dt> <dd><%%= @subscription.plan_ref %></dd>
|
|
8
|
+
<dt>Customer</dt> <dd><%%= @subscription.customer_ref %></dd>
|
|
9
|
+
<dt>Period ends</dt> <dd><%%= @subscription.current_period_end %></dd>
|
|
10
|
+
</dl>
|
|
11
|
+
|
|
12
|
+
<%% if @subscription.active? %>
|
|
13
|
+
<%%= button_to "Cancel at period end", subscription_path(@subscription),
|
|
14
|
+
method: :delete,
|
|
15
|
+
data: { confirm: "Cancel this subscription at the end of the billing period?" } %>
|
|
16
|
+
|
|
17
|
+
<h2>Change plan</h2>
|
|
18
|
+
<%%= form_with url: change_plan_subscription_path(@subscription), method: :post do |form| %>
|
|
19
|
+
<%%= form.label :price_ref, "New plan id" %>
|
|
20
|
+
<%%= form.text_field :price_ref %>
|
|
21
|
+
<%%= form.submit "Change plan" %>
|
|
22
|
+
<%% end %>
|
|
23
|
+
<%% else %>
|
|
24
|
+
<%%= button_to "Reactivate", reactivate_subscription_path(@subscription), method: :post %>
|
|
25
|
+
<%% end %>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Billing::Engine.routes.draw do
|
|
4
|
+
resources :plans, only: %i[index]
|
|
5
|
+
|
|
6
|
+
# Self-service subscription management — Phase 3 (4/4). Backed by
|
|
7
|
+
# the Phase 3 (2/4) service objects; member actions map onto
|
|
8
|
+
# CancelService / ReactivateService / ChangePlanService.
|
|
9
|
+
resources :subscriptions, only: %i[index show] do
|
|
10
|
+
member do
|
|
11
|
+
delete :cancel
|
|
12
|
+
post :reactivate
|
|
13
|
+
post :change_plan
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Read-only billing history. Stripe hosts the PDF; the show view
|
|
18
|
+
# links to hosted_invoice_url if your host wires it up.
|
|
19
|
+
resources :invoices, only: %i[index show]
|
|
20
|
+
|
|
21
|
+
post "/checkout", to: "checkout#create", as: :checkout
|
|
22
|
+
get "/checkout/success", to: "checkout#success", as: :checkout_success
|
|
23
|
+
# Lifetime Deal purchase — Stripe Checkout `mode: "payment"`.
|
|
24
|
+
# Namespaced under /checkout/ alongside the recurring-checkout flow
|
|
25
|
+
# so the surface stays grouped. See issue #2 section 3A.LTD.
|
|
26
|
+
post "/checkout/lifetime", to: "checkout#lifetime", as: :lifetime_checkout
|
|
27
|
+
|
|
28
|
+
post "/portal", to: "portal#create", as: :portal
|
|
29
|
+
|
|
30
|
+
# Admin-side LTD management. Mount behind your admin gate; see
|
|
31
|
+
# Billing::Admin::LifetimePassesController for the contract.
|
|
32
|
+
namespace :admin do
|
|
33
|
+
resources :lifetime_passes, only: %i[index new create destroy]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Follow-up generators that add new gateway-flow routes (Adyen webhook, Paddle webhook, alternative checkout) splice their routes here, ahead of the existing Stripe webhook.
|
|
37
|
+
# seams:insertion-point billing.routes.before_webhook
|
|
38
|
+
post "/webhooks/stripe", to: "webhooks#stripe", as: :stripe_webhook
|
|
39
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# What: creates the billing_invoices table.
|
|
4
|
+
# Why: per-invoice audit trail for support and finance — when a
|
|
5
|
+
# customer asks "did you charge me on the 3rd?" we don't have to
|
|
6
|
+
# round-trip the gateway every time. The invoice belongs to an
|
|
7
|
+
# Accounts::Account (the tenant the subscription was billed on),
|
|
8
|
+
# not to an individual Identity.
|
|
9
|
+
# Risk: append-only writes from webhook handlers; no live-traffic SQL.
|
|
10
|
+
class CreateBillingInvoices < ActiveRecord::Migration[7.1]
|
|
11
|
+
def change
|
|
12
|
+
create_table :billing_invoices do |t|
|
|
13
|
+
# Local tenant FK. UUID to match Accounts::Account#id; no DB-level
|
|
14
|
+
# FK across engines — see create_billing_subscriptions for why.
|
|
15
|
+
t.uuid :account_id, null: false
|
|
16
|
+
t.string :gateway_ref, null: false
|
|
17
|
+
t.string :customer_ref, null: false # Stripe customer id (cus_*)
|
|
18
|
+
t.string :subscription_ref # Stripe subscription id (sub_*); nil for one-off invoices
|
|
19
|
+
t.integer :amount_cents, null: false
|
|
20
|
+
t.string :currency, null: false, default: "USD"
|
|
21
|
+
t.string :status, null: false, default: "open"
|
|
22
|
+
t.datetime :paid_at
|
|
23
|
+
t.timestamps
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
add_index :billing_invoices, :account_id
|
|
27
|
+
add_index :billing_invoices, :gateway_ref, unique: true
|
|
28
|
+
add_index :billing_invoices, :customer_ref
|
|
29
|
+
add_index :billing_invoices, :subscription_ref, where: "subscription_ref IS NOT NULL"
|
|
30
|
+
add_index :billing_invoices, %i[status created_at]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# What: creates billing_lifetime_passes — the one-time-paid OR privately
|
|
4
|
+
# granted permanent-access record. Lives in its own table because
|
|
5
|
+
# Subscription's renewal/period semantics don't apply.
|
|
6
|
+
# Why: Lifetime Deals (LTDs) are a real revenue + early-adopter feedback
|
|
7
|
+
# channel; the engine ships the mechanism so hosts can offer them
|
|
8
|
+
# without rolling their own model. See README LTD section for the
|
|
9
|
+
# trade-off (indefinite support, no recurring revenue). Bound to
|
|
10
|
+
# an Accounts::Account — the entitlement belongs to the tenant.
|
|
11
|
+
# `granted_by_identity_id` is the human (Auth::Identity) who
|
|
12
|
+
# pressed the "grant lifetime" button — Identity, not Account,
|
|
13
|
+
# because that column tracks human action rather than tenant data.
|
|
14
|
+
# Risk: append-mostly (revoke is a soft delete via revoked_at, not a row
|
|
15
|
+
# delete, so audit history survives). Unique index on
|
|
16
|
+
# (account_id, plan_ref) prevents accidentally double-granting.
|
|
17
|
+
class CreateBillingLifetimePasses < ActiveRecord::Migration[7.1]
|
|
18
|
+
def change
|
|
19
|
+
create_table :billing_lifetime_passes do |t|
|
|
20
|
+
# Local tenant FK. UUID to match Accounts::Account#id.
|
|
21
|
+
t.uuid :account_id, null: false
|
|
22
|
+
t.string :customer_ref, null: false # gateway-side customer id (cus_*)
|
|
23
|
+
t.string :plan_ref, null: false # billing_plans.gateway_ref
|
|
24
|
+
t.string :gateway_ref # Stripe Checkout Session id; nil for private grants
|
|
25
|
+
# The Identity (auth_identities row) of the admin who granted
|
|
26
|
+
# the pass; nil for paid LTDs purchased through Stripe Checkout.
|
|
27
|
+
# bigint to match auth_identities' default integer PK.
|
|
28
|
+
t.bigint :granted_by_identity_id
|
|
29
|
+
t.datetime :granted_at, null: false
|
|
30
|
+
t.datetime :revoked_at # soft-revoke (refund / ToS violation)
|
|
31
|
+
t.bigint :revoked_by_identity_id # admin Identity who revoked
|
|
32
|
+
t.text :notes
|
|
33
|
+
t.timestamps
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
add_index :billing_lifetime_passes, :account_id
|
|
37
|
+
add_index :billing_lifetime_passes, %i[account_id plan_ref], unique: true,
|
|
38
|
+
name: "index_billing_ltd_unique"
|
|
39
|
+
add_index :billing_lifetime_passes, :gateway_ref, unique: true,
|
|
40
|
+
where: "gateway_ref IS NOT NULL"
|
|
41
|
+
add_index :billing_lifetime_passes, :revoked_at, where: "revoked_at IS NULL"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# What: creates billing_plans, the local mirror of Stripe Prices.
|
|
4
|
+
# Why: the pricing page renders from this table without a per-request
|
|
5
|
+
# Stripe round-trip; webhooks keep it in sync if a plan changes
|
|
6
|
+
# upstream.
|
|
7
|
+
# Risk: small table, append-mostly. Unique index on gateway_ref.
|
|
8
|
+
class CreateBillingPlans < ActiveRecord::Migration[7.1]
|
|
9
|
+
def change
|
|
10
|
+
create_table :billing_plans do |t|
|
|
11
|
+
t.string :gateway_ref, null: false # price_* from Stripe
|
|
12
|
+
t.string :name, null: false
|
|
13
|
+
t.text :description
|
|
14
|
+
t.integer :amount_cents, null: false, default: 0
|
|
15
|
+
t.string :currency, null: false, default: "usd"
|
|
16
|
+
t.string :interval, null: false, default: "month"
|
|
17
|
+
t.integer :trial_period_days
|
|
18
|
+
t.boolean :active, null: false, default: true
|
|
19
|
+
t.jsonb :features, null: false, default: {}
|
|
20
|
+
# LTD inventory cap. nil = unlimited. Only meaningful when
|
|
21
|
+
# interval == "lifetime"; ignored otherwise. The pricing page +
|
|
22
|
+
# admin grant flow read Plan#lifetime_inventory_remaining to
|
|
23
|
+
# gate purchases / grants.
|
|
24
|
+
t.integer :max_lifetime_units
|
|
25
|
+
t.timestamps
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
add_index :billing_plans, :gateway_ref, unique: true
|
|
29
|
+
add_index :billing_plans, :active
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# What: creates the billing_subscriptions table.
|
|
4
|
+
# Why: source of truth for "is this customer paying us right now?"
|
|
5
|
+
# Synced from gateway webhooks (status, current_period_end). Bound
|
|
6
|
+
# to an Accounts::Account (the tenant) — not to an Identity. The
|
|
7
|
+
# Stripe customer is the workspace, not the human; a single
|
|
8
|
+
# human (Identity) with memberships in two Accounts has two
|
|
9
|
+
# independent billing relationships.
|
|
10
|
+
# Risk: empty on creation, append-mostly thereafter. Status updates
|
|
11
|
+
# happen in transactions of one row — no locking concerns.
|
|
12
|
+
class CreateBillingSubscriptions < ActiveRecord::Migration[7.1]
|
|
13
|
+
def change
|
|
14
|
+
create_table :billing_subscriptions do |t|
|
|
15
|
+
# Local tenant FK. UUID to match Accounts::Account#id; no DB-level
|
|
16
|
+
# foreign key because billing and accounts may live in different
|
|
17
|
+
# schemas / databases — cross-engine integrity is enforced at the
|
|
18
|
+
# application layer (the AccountScoped concern + service objects).
|
|
19
|
+
t.uuid :account_id, null: false
|
|
20
|
+
t.string :customer_ref, null: false # Stripe customer id (cus_*) for the Account
|
|
21
|
+
t.string :plan_ref, null: false
|
|
22
|
+
t.string :gateway_ref, null: false
|
|
23
|
+
t.string :status, null: false, default: "incomplete"
|
|
24
|
+
t.datetime :current_period_end
|
|
25
|
+
t.timestamps
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
add_index :billing_subscriptions, :account_id
|
|
29
|
+
add_index :billing_subscriptions, :customer_ref
|
|
30
|
+
add_index :billing_subscriptions, :gateway_ref, unique: true
|
|
31
|
+
add_index :billing_subscriptions, :status
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# What: creates billing_webhook_events for idempotent webhook handling.
|
|
4
|
+
# Why: Stripe explicitly recommends deduping by event id because they
|
|
5
|
+
# retry indefinitely until the endpoint returns 2xx
|
|
6
|
+
# (https://docs.stripe.com/webhooks#handle-duplicate-events).
|
|
7
|
+
# Without this table the welcome-email subscriber would re-send
|
|
8
|
+
# on every retry.
|
|
9
|
+
# Risk: empty on creation, append-only thereafter. Unique index on
|
|
10
|
+
# gateway_event_id is the deduper.
|
|
11
|
+
class CreateBillingWebhookEvents < ActiveRecord::Migration[7.1]
|
|
12
|
+
def change
|
|
13
|
+
create_table :billing_webhook_events do |t|
|
|
14
|
+
t.string :gateway, null: false # "stripe" | "paddle" | ...
|
|
15
|
+
t.string :gateway_event_id, null: false # provider's evt_* id
|
|
16
|
+
t.string :event_type, null: false
|
|
17
|
+
t.boolean :livemode, null: false, default: false
|
|
18
|
+
t.timestamps
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
add_index :billing_webhook_events, %i[gateway gateway_event_id], unique: true
|
|
22
|
+
add_index :billing_webhook_events, :event_type
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "billing/version"
|
|
4
|
+
require "billing/configuration"
|
|
5
|
+
require "billing/engine"
|
|
6
|
+
require "billing/concerns/billable"
|
|
7
|
+
require "billing/gateways/abstract"
|
|
8
|
+
require "billing/gateways/stripe"
|
|
9
|
+
# Follow-up generators that ship pre-wired gateway adapters (seams:billing:add_gateway paddle) splice their `require "billing/gateways/paddle"` line here.
|
|
10
|
+
# seams:insertion-point billing.gateways.adapters
|
|
11
|
+
|
|
12
|
+
module Billing
|
|
13
|
+
class Error < StandardError; end
|
|
14
|
+
class GatewayError < Error; end
|
|
15
|
+
class WebhookError < Error; end
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def configuration
|
|
19
|
+
@configuration ||= Configuration.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def configure
|
|
23
|
+
yield configuration
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def gateway
|
|
27
|
+
configuration.gateway.constantize.new
|
|
28
|
+
rescue NameError => e
|
|
29
|
+
raise Billing::Error,
|
|
30
|
+
"Billing.configuration.gateway is set to #{configuration.gateway.inspect}, " \
|
|
31
|
+
"which could not be resolved: #{e.message}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
5
|
+
module Billing
|
|
6
|
+
# Mix into the host's Accounts::Account model — the tenant — to gain
|
|
7
|
+
# billing helpers. The Account is the Stripe customer; subscriptions,
|
|
8
|
+
# invoices, and lifetime passes all belong to the tenant, not to an
|
|
9
|
+
# individual human (Identity). A single Identity who is a member of
|
|
10
|
+
# two Accounts has TWO independent billing relationships.
|
|
11
|
+
#
|
|
12
|
+
# Wiring is config-driven (see Billing::Configuration#billable_class).
|
|
13
|
+
# The default `"Accounts::Account"` matches the canonical accounts
|
|
14
|
+
# engine; hosts on a different tenant model point the config at
|
|
15
|
+
# their own class.
|
|
16
|
+
#
|
|
17
|
+
# Hosts that want to wire it manually instead can do so:
|
|
18
|
+
#
|
|
19
|
+
# class Accounts::Account < ApplicationRecord
|
|
20
|
+
# include Billing::Billable
|
|
21
|
+
# end
|
|
22
|
+
module Billable
|
|
23
|
+
extend ActiveSupport::Concern
|
|
24
|
+
|
|
25
|
+
included do
|
|
26
|
+
# Associations are bare `account_id` keyed — the cross-engine
|
|
27
|
+
# model-access cop forbids `belongs_to :account` from inside
|
|
28
|
+
# billing pointing at Accounts::Account, so we use foreign_key
|
|
29
|
+
# without a class_name on the back-edge: account.billing_*
|
|
30
|
+
# queries the billing tables by the account's id.
|
|
31
|
+
has_many :billing_subscriptions, class_name: "Billing::Subscription",
|
|
32
|
+
foreign_key: :account_id, primary_key: :id
|
|
33
|
+
has_many :billing_lifetime_passes, class_name: "Billing::LifetimePass",
|
|
34
|
+
foreign_key: :account_id, primary_key: :id
|
|
35
|
+
has_many :billing_invoices, class_name: "Billing::Invoice",
|
|
36
|
+
foreign_key: :account_id, primary_key: :id
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Enqueues a Stripe subscription. The `email:` arg is the contact
|
|
40
|
+
# address Stripe attaches to the customer record on first creation
|
|
41
|
+
# — typically the Account owner's Identity#email_address, but the
|
|
42
|
+
# host decides (it might be a billing-specific contact instead).
|
|
43
|
+
# Subsequent calls reuse the existing Stripe customer.
|
|
44
|
+
def start_subscription!(plan_ref:, email:)
|
|
45
|
+
Billing::StartSubscriptionJob.perform_later(
|
|
46
|
+
account_id: id,
|
|
47
|
+
customer_ref: stripe_customer_ref!(email: email),
|
|
48
|
+
plan_ref: plan_ref
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def cancel_subscription!(subscription_ref:)
|
|
53
|
+
Billing::CancelSubscriptionJob.perform_later(subscription_ref: subscription_ref)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Lifetime Deal helpers (issue #2 section 3A.LTD).
|
|
57
|
+
# `lifetime?` is true if this Account holds an active LifetimePass
|
|
58
|
+
# for ANY plan; `has_lifetime_for?(plan_ref:)` narrows to one.
|
|
59
|
+
def lifetime?
|
|
60
|
+
billing_lifetime_passes.where(revoked_at: nil).exists?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def has_lifetime_for?(plan_ref:)
|
|
64
|
+
billing_lifetime_passes.where(revoked_at: nil, plan_ref: plan_ref).exists?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# The "is this Account a paying / entitled customer right now?"
|
|
68
|
+
# check. True for active or trialing recurring subscriptions OR
|
|
69
|
+
# for any active lifetime pass. Use this in host code instead of
|
|
70
|
+
# querying subscriptions directly so LTD-only accounts are
|
|
71
|
+
# accounted for.
|
|
72
|
+
def has_active_billing?
|
|
73
|
+
billing_subscriptions.where(status: %w[active trialing]).exists? || lifetime?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Lazily resolves (and caches) the Stripe customer id for this
|
|
77
|
+
# Account. First call creates a Stripe customer keyed on the
|
|
78
|
+
# Account#name + the supplied billing email; subsequent calls
|
|
79
|
+
# return the cached value.
|
|
80
|
+
#
|
|
81
|
+
# The `customer_ref` is stored on every billing_subscriptions /
|
|
82
|
+
# billing_invoices / billing_lifetime_passes row, so once an
|
|
83
|
+
# Account has any billing activity we can read the ref off any
|
|
84
|
+
# existing row and skip the Stripe API call.
|
|
85
|
+
def stripe_customer_ref!(email:)
|
|
86
|
+
existing = billing_subscriptions.first&.customer_ref ||
|
|
87
|
+
billing_invoices.first&.customer_ref ||
|
|
88
|
+
billing_lifetime_passes.first&.customer_ref
|
|
89
|
+
return existing if existing
|
|
90
|
+
|
|
91
|
+
result = Billing::Customers::FindOrCreateService.call(
|
|
92
|
+
email: email,
|
|
93
|
+
metadata: { account_id: id, account_name: respond_to?(:name) ? name : nil }
|
|
94
|
+
)
|
|
95
|
+
raise Billing::Error, "Could not resolve Stripe customer: #{result.error}" unless result.ok?
|
|
96
|
+
|
|
97
|
+
result.value
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Billing
|
|
4
|
+
# Engine-scoped configuration. Override in
|
|
5
|
+
# config/initializers/billing.rb of the host application:
|
|
6
|
+
#
|
|
7
|
+
# Billing.configure do |c|
|
|
8
|
+
# c.gateway = "Billing::Gateways::Stripe"
|
|
9
|
+
# c.api_key = ENV.fetch("STRIPE_SECRET_KEY")
|
|
10
|
+
# c.webhook_secret = ENV.fetch("STRIPE_WEBHOOK_SECRET")
|
|
11
|
+
# # The host class that represents the billable tenant. The
|
|
12
|
+
# # engine includes `Billing::Billable` into this class at boot
|
|
13
|
+
# # so `account.start_subscription!(plan_ref:, email:)` etc are
|
|
14
|
+
# # available wherever you handle Account rows. Default is the
|
|
15
|
+
# # canonical `Accounts::Account` from the seams accounts engine;
|
|
16
|
+
# # set to a different constant name if your tenant lives on a
|
|
17
|
+
# # different model.
|
|
18
|
+
# c.billable_class = "Accounts::Account"
|
|
19
|
+
# end
|
|
20
|
+
class Configuration
|
|
21
|
+
attr_accessor :gateway, :api_key, :webhook_secret, :default_currency,
|
|
22
|
+
:process_webhooks_async, :billable_class
|
|
23
|
+
# Follow-up generators that add knobs (proration mode, dunning retries, tax adapters) declare their attr_accessor here.
|
|
24
|
+
# seams:insertion-point billing.configuration.attributes
|
|
25
|
+
|
|
26
|
+
def initialize
|
|
27
|
+
@gateway = "<%= gateway_class_name %>"
|
|
28
|
+
@api_key = ENV.fetch("<%= gateway_env_prefix %>_SECRET_KEY", nil)
|
|
29
|
+
@webhook_secret = ENV.fetch("<%= gateway_env_prefix %>_WEBHOOK_SECRET", nil)
|
|
30
|
+
@default_currency = "usd"
|
|
31
|
+
# Default to synchronous so failed handlers roll back the
|
|
32
|
+
# WebhookEvent insert and Stripe gets to retry. Hosts on Solid
|
|
33
|
+
# Queue (or similar) can flip to async + accept the trade-off.
|
|
34
|
+
@process_webhooks_async = false
|
|
35
|
+
# The Account-as-billable contract — see class comment.
|
|
36
|
+
@billable_class = "Accounts::Account"
|
|
37
|
+
# Follow-up generators that add defaults for new attributes (matching billing.configuration.attributes) splice them here.
|
|
38
|
+
# seams:insertion-point billing.configuration.defaults
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Short identifier used as the `gateway:` field in canonical event
|
|
42
|
+
# payloads. Derived from `gateway` (e.g. "Billing::Gateways::Stripe"
|
|
43
|
+
# → "stripe"). Override directly if your class name doesn't follow
|
|
44
|
+
# the convention. NOT memoized — `gateway` is mutable, so
|
|
45
|
+
# gateway_name re-derives on every read.
|
|
46
|
+
def gateway_name
|
|
47
|
+
@gateway_name || gateway.to_s.split("::").last.to_s.downcase
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
attr_writer :gateway_name
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Billing
|
|
4
|
+
class Engine < ::Rails::Engine
|
|
5
|
+
isolate_namespace Billing
|
|
6
|
+
|
|
7
|
+
config.generators do |g|
|
|
8
|
+
g.test_framework :rspec
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
initializer "billing.register_events" do
|
|
12
|
+
# Subscription lifecycle.
|
|
13
|
+
Seams::EventRegistry.register("subscription.created.billing", emitted_by: "Billing")
|
|
14
|
+
Seams::EventRegistry.register("subscription.updated.billing", emitted_by: "Billing")
|
|
15
|
+
Seams::EventRegistry.register("subscription.canceled.billing", emitted_by: "Billing")
|
|
16
|
+
Seams::EventRegistry.register("subscription.trial_will_end.billing", emitted_by: "Billing")
|
|
17
|
+
# Invoice lifecycle.
|
|
18
|
+
Seams::EventRegistry.register("invoice.created.billing", emitted_by: "Billing")
|
|
19
|
+
Seams::EventRegistry.register("invoice.paid.billing", emitted_by: "Billing")
|
|
20
|
+
Seams::EventRegistry.register("invoice.failed.billing", emitted_by: "Billing")
|
|
21
|
+
Seams::EventRegistry.register("invoice.finalized.billing", emitted_by: "Billing")
|
|
22
|
+
Seams::EventRegistry.register("invoice.voided.billing", emitted_by: "Billing")
|
|
23
|
+
# One-off payment + refund signals.
|
|
24
|
+
Seams::EventRegistry.register("payment.succeeded.billing", emitted_by: "Billing")
|
|
25
|
+
Seams::EventRegistry.register("payment.failed.billing", emitted_by: "Billing")
|
|
26
|
+
Seams::EventRegistry.register("charge.refunded.billing", emitted_by: "Billing")
|
|
27
|
+
Seams::EventRegistry.register("checkout.session_completed.billing", emitted_by: "Billing")
|
|
28
|
+
# Lifetime Deal events — see issue #2 section 3A.LTD.
|
|
29
|
+
Seams::EventRegistry.register("lifetime.granted.billing", emitted_by: "Billing")
|
|
30
|
+
Seams::EventRegistry.register("lifetime.purchased.billing", emitted_by: "Billing")
|
|
31
|
+
Seams::EventRegistry.register("lifetime.revoked.billing", emitted_by: "Billing")
|
|
32
|
+
# Follow-up generators that ship new billing events (subscription.discount_applied.billing, dispute.created.billing) register them here.
|
|
33
|
+
# seams:insertion-point billing.engine.events
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
initializer "billing.append_migrations" do |app|
|
|
37
|
+
unless app.root == root
|
|
38
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
|
39
|
+
app.config.paths["db/migrate"] << expanded_path
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Follow-up generators that need their own initializer block (custom Billable include, gateway feature-flag wiring) declare it here.
|
|
45
|
+
# seams:insertion-point billing.engine.initializers
|
|
46
|
+
|
|
47
|
+
# Auto-include Billing::Billable into the configured tenant class
|
|
48
|
+
# (default: Accounts::Account). Wired via a to_prepare hook so the
|
|
49
|
+
# host's accounts engine has had a chance to autoload its model
|
|
50
|
+
# first; safe to re-run because Module#include is idempotent. The
|
|
51
|
+
# host can opt out by setting `billable_class = nil`.
|
|
52
|
+
config.to_prepare do
|
|
53
|
+
target = Billing.configuration.billable_class
|
|
54
|
+
next if target.nil? || (target.respond_to?(:empty?) && target.empty?)
|
|
55
|
+
|
|
56
|
+
begin
|
|
57
|
+
# Accept either a String ("Accounts::Account") or a Class
|
|
58
|
+
# (Accounts::Account). String is the documented shape — late
|
|
59
|
+
# constantize sidesteps autoload-order issues — but a host
|
|
60
|
+
# that hardwires the class object (e.g. via `c.billable_class
|
|
61
|
+
# = User`) shouldn't blow up at boot just because Class
|
|
62
|
+
# doesn't respond to `.constantize`.
|
|
63
|
+
klass = target.is_a?(String) ? target.constantize : target
|
|
64
|
+
klass.include(Billing::Billable)
|
|
65
|
+
rescue NameError
|
|
66
|
+
# Tenant class not yet loaded (e.g. accounts engine not
|
|
67
|
+
# installed). Hosts that wire Billable manually will still
|
|
68
|
+
# work; this is a best-effort auto-include.
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|