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.
Files changed (414) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +335 -0
  3. data/LICENSE +21 -0
  4. data/README.md +104 -0
  5. data/lib/generators/seams/accounts/accounts_generator.rb +272 -0
  6. data/lib/generators/seams/accounts/templates/README.md.tt +219 -0
  7. data/lib/generators/seams/accounts/templates/app/models/account.rb.tt +124 -0
  8. data/lib/generators/seams/accounts/templates/app/models/application_record.rb.tt +7 -0
  9. data/lib/generators/seams/accounts/templates/app/models/current.rb.tt +38 -0
  10. data/lib/generators/seams/accounts/templates/app/models/membership.rb.tt +114 -0
  11. data/lib/generators/seams/accounts/templates/config/routes.rb.tt +8 -0
  12. data/lib/generators/seams/accounts/templates/db/migrate/create_accounts.rb.tt +29 -0
  13. data/lib/generators/seams/accounts/templates/db/migrate/create_accounts_memberships.rb.tt +49 -0
  14. data/lib/generators/seams/accounts/templates/lib/accounts.rb.tt +21 -0
  15. data/lib/generators/seams/accounts/templates/lib/concerns/account_scoped.rb.tt +97 -0
  16. data/lib/generators/seams/accounts/templates/lib/concerns/authorization.rb.tt +105 -0
  17. data/lib/generators/seams/accounts/templates/lib/configuration.rb.tt +26 -0
  18. data/lib/generators/seams/accounts/templates/lib/engine.rb.tt +55 -0
  19. data/lib/generators/seams/accounts/templates/spec/factories/accounts.rb.tt +49 -0
  20. data/lib/generators/seams/accounts/templates/spec/models/accounts/account_spec.rb.tt +64 -0
  21. data/lib/generators/seams/accounts/templates/spec/models/accounts/membership_spec.rb.tt +99 -0
  22. data/lib/generators/seams/accounts/templates/spec/runtime/accounts_boot_spec.rb.tt +181 -0
  23. data/lib/generators/seams/admin/admin_generator.rb +852 -0
  24. data/lib/generators/seams/admin/templates/README.md.tt +266 -0
  25. data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_controller.rb.tt +16 -0
  26. data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_memberships_controller.rb.tt +16 -0
  27. data/lib/generators/seams/admin/templates/app/controllers/admin/application_controller.rb.tt +282 -0
  28. data/lib/generators/seams/admin/templates/app/controllers/admin/identities_controller.rb.tt +26 -0
  29. data/lib/generators/seams/admin/templates/app/controllers/admin/invitations_controller.rb.tt +14 -0
  30. data/lib/generators/seams/admin/templates/app/controllers/admin/invoices_controller.rb.tt +14 -0
  31. data/lib/generators/seams/admin/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +14 -0
  32. data/lib/generators/seams/admin/templates/app/controllers/admin/notification_preferences_controller.rb.tt +15 -0
  33. data/lib/generators/seams/admin/templates/app/controllers/admin/notifications_controller.rb.tt +18 -0
  34. data/lib/generators/seams/admin/templates/app/controllers/admin/plans_controller.rb.tt +14 -0
  35. data/lib/generators/seams/admin/templates/app/controllers/admin/subscriptions_controller.rb.tt +14 -0
  36. data/lib/generators/seams/admin/templates/app/controllers/admin/teams_controller.rb.tt +14 -0
  37. data/lib/generators/seams/admin/templates/app/controllers/admin/teams_memberships_controller.rb.tt +16 -0
  38. data/lib/generators/seams/admin/templates/app/dashboards/admin/account_dashboard.rb.tt +50 -0
  39. data/lib/generators/seams/admin/templates/app/dashboards/admin/accounts_membership_dashboard.rb.tt +58 -0
  40. data/lib/generators/seams/admin/templates/app/dashboards/admin/identity_dashboard.rb.tt +48 -0
  41. data/lib/generators/seams/admin/templates/app/dashboards/admin/invitation_dashboard.rb.tt +51 -0
  42. data/lib/generators/seams/admin/templates/app/dashboards/admin/invoice_dashboard.rb.tt +67 -0
  43. data/lib/generators/seams/admin/templates/app/dashboards/admin/lifetime_pass_dashboard.rb.tt +65 -0
  44. data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_dashboard.rb.tt +58 -0
  45. data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_preference_dashboard.rb.tt +43 -0
  46. data/lib/generators/seams/admin/templates/app/dashboards/admin/plan_dashboard.rb.tt +72 -0
  47. data/lib/generators/seams/admin/templates/app/dashboards/admin/subscription_dashboard.rb.tt +59 -0
  48. data/lib/generators/seams/admin/templates/app/dashboards/admin/team_dashboard.rb.tt +39 -0
  49. data/lib/generators/seams/admin/templates/app/dashboards/admin/teams_membership_dashboard.rb.tt +43 -0
  50. data/lib/generators/seams/admin/templates/app/policies/admin/platform/account_policy.rb.tt +10 -0
  51. data/lib/generators/seams/admin/templates/app/policies/admin/platform/accounts_membership_policy.rb.tt +10 -0
  52. data/lib/generators/seams/admin/templates/app/policies/admin/platform/application_policy.rb.tt +85 -0
  53. data/lib/generators/seams/admin/templates/app/policies/admin/platform/identity_policy.rb.tt +18 -0
  54. data/lib/generators/seams/admin/templates/app/policies/admin/platform/invitation_policy.rb.tt +9 -0
  55. data/lib/generators/seams/admin/templates/app/policies/admin/platform/invoice_policy.rb.tt +9 -0
  56. data/lib/generators/seams/admin/templates/app/policies/admin/platform/lifetime_pass_policy.rb.tt +9 -0
  57. data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_policy.rb.tt +9 -0
  58. data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_preference_policy.rb.tt +9 -0
  59. data/lib/generators/seams/admin/templates/app/policies/admin/platform/plan_policy.rb.tt +11 -0
  60. data/lib/generators/seams/admin/templates/app/policies/admin/platform/subscription_policy.rb.tt +9 -0
  61. data/lib/generators/seams/admin/templates/app/policies/admin/platform/team_policy.rb.tt +9 -0
  62. data/lib/generators/seams/admin/templates/app/policies/admin/platform/teams_membership_policy.rb.tt +9 -0
  63. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/account_policy.rb.tt +33 -0
  64. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/accounts_membership_policy.rb.tt +24 -0
  65. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/application_policy.rb.tt +169 -0
  66. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/identity_policy.rb.tt +67 -0
  67. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invitation_policy.rb.tt +24 -0
  68. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invoice_policy.rb.tt +21 -0
  69. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/lifetime_pass_policy.rb.tt +21 -0
  70. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_policy.rb.tt +25 -0
  71. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_preference_policy.rb.tt +23 -0
  72. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/plan_policy.rb.tt +47 -0
  73. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/subscription_policy.rb.tt +22 -0
  74. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/team_policy.rb.tt +28 -0
  75. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/teams_membership_policy.rb.tt +24 -0
  76. data/lib/generators/seams/admin/templates/config/routes.rb.tt +38 -0
  77. data/lib/generators/seams/admin/templates/lib/admin.rb.tt +36 -0
  78. data/lib/generators/seams/admin/templates/lib/concerns/authenticator.rb.tt +66 -0
  79. data/lib/generators/seams/admin/templates/lib/configuration.rb.tt +90 -0
  80. data/lib/generators/seams/admin/templates/lib/context.rb.tt +44 -0
  81. data/lib/generators/seams/admin/templates/lib/engine.rb.tt +68 -0
  82. data/lib/generators/seams/admin/templates/spec/factories/admin.rb.tt +10 -0
  83. data/lib/generators/seams/admin/templates/spec/runtime/admin_boot_spec.rb.tt +604 -0
  84. data/lib/generators/seams/auth/add_oauth_provider/add_oauth_provider_generator.rb +157 -0
  85. data/lib/generators/seams/auth/add_oauth_provider/templates/adapter.rb.tt +95 -0
  86. data/lib/generators/seams/auth/add_oauth_provider/templates/adapter_spec.rb.tt +58 -0
  87. data/lib/generators/seams/auth/auth_generator.rb +311 -0
  88. data/lib/generators/seams/auth/templates/README.md.tt +289 -0
  89. data/lib/generators/seams/auth/templates/app/controllers/oauth/callbacks_controller.rb.tt +80 -0
  90. data/lib/generators/seams/auth/templates/app/controllers/password_resets_controller.rb.tt +44 -0
  91. data/lib/generators/seams/auth/templates/app/controllers/registrations_controller.rb.tt +34 -0
  92. data/lib/generators/seams/auth/templates/app/controllers/sessions_controller.rb.tt +49 -0
  93. data/lib/generators/seams/auth/templates/app/jobs/application_job.rb.tt +7 -0
  94. data/lib/generators/seams/auth/templates/app/jobs/cleanup_expired_sessions_job.rb.tt +30 -0
  95. data/lib/generators/seams/auth/templates/app/mailers/passwords_mailer.rb.tt +15 -0
  96. data/lib/generators/seams/auth/templates/app/models/api_token.rb.tt +62 -0
  97. data/lib/generators/seams/auth/templates/app/models/application_record.rb.tt +7 -0
  98. data/lib/generators/seams/auth/templates/app/models/current.rb.tt +15 -0
  99. data/lib/generators/seams/auth/templates/app/models/identity.rb.tt +74 -0
  100. data/lib/generators/seams/auth/templates/app/models/oauth/provider.rb.tt +48 -0
  101. data/lib/generators/seams/auth/templates/app/models/session.rb.tt +28 -0
  102. data/lib/generators/seams/auth/templates/app/services/authenticate_identity.rb.tt +31 -0
  103. data/lib/generators/seams/auth/templates/app/services/generate_api_token.rb.tt +35 -0
  104. data/lib/generators/seams/auth/templates/app/services/oauth/authenticator.rb.tt +94 -0
  105. data/lib/generators/seams/auth/templates/app/services/register_identity.rb.tt +57 -0
  106. data/lib/generators/seams/auth/templates/app/services/reset_password.rb.tt +41 -0
  107. data/lib/generators/seams/auth/templates/app/services/revoke_api_token.rb.tt +38 -0
  108. data/lib/generators/seams/auth/templates/app/views/password_resets/edit.html.erb.tt +12 -0
  109. data/lib/generators/seams/auth/templates/app/views/password_resets/new.html.erb.tt +11 -0
  110. data/lib/generators/seams/auth/templates/app/views/passwords_mailer/reset_email.html.erb.tt +7 -0
  111. data/lib/generators/seams/auth/templates/app/views/registrations/new.html.erb.tt +26 -0
  112. data/lib/generators/seams/auth/templates/app/views/sessions/_oauth_buttons.html.erb.tt +18 -0
  113. data/lib/generators/seams/auth/templates/app/views/sessions/new.html.erb.tt +17 -0
  114. data/lib/generators/seams/auth/templates/config/routes.rb.tt +21 -0
  115. data/lib/generators/seams/auth/templates/db/migrate/create_auth_api_tokens.rb.tt +26 -0
  116. data/lib/generators/seams/auth/templates/db/migrate/create_auth_identities.rb.tt +29 -0
  117. data/lib/generators/seams/auth/templates/db/migrate/create_auth_oauth_providers.rb.tt +35 -0
  118. data/lib/generators/seams/auth/templates/db/migrate/create_auth_sessions.rb.tt +19 -0
  119. data/lib/generators/seams/auth/templates/lib/auth.rb.tt +39 -0
  120. data/lib/generators/seams/auth/templates/lib/concerns/api_authenticatable.rb.tt +58 -0
  121. data/lib/generators/seams/auth/templates/lib/concerns/authenticatable.rb.tt +32 -0
  122. data/lib/generators/seams/auth/templates/lib/concerns/authentication.rb.tt +60 -0
  123. data/lib/generators/seams/auth/templates/lib/configuration.rb.tt +45 -0
  124. data/lib/generators/seams/auth/templates/lib/engine.rb.tt +46 -0
  125. data/lib/generators/seams/auth/templates/lib/oauth/abstract.rb.tt +87 -0
  126. data/lib/generators/seams/auth/templates/lib/oauth/github.rb.tt +112 -0
  127. data/lib/generators/seams/auth/templates/lib/oauth/google.rb.tt +78 -0
  128. data/lib/generators/seams/auth/templates/lib/tasks/auth_pii.rake.tt +68 -0
  129. data/lib/generators/seams/auth/templates/spec/factories/auth.rb.tt +38 -0
  130. data/lib/generators/seams/auth/templates/spec/mailers/passwords_mailer_spec.rb.tt +37 -0
  131. data/lib/generators/seams/auth/templates/spec/models/api_token_spec.rb.tt +84 -0
  132. data/lib/generators/seams/auth/templates/spec/models/identity_spec.rb.tt +56 -0
  133. data/lib/generators/seams/auth/templates/spec/models/oauth/provider_spec.rb.tt +64 -0
  134. data/lib/generators/seams/auth/templates/spec/models/session_spec.rb.tt +34 -0
  135. data/lib/generators/seams/auth/templates/spec/runtime/boot_spec.rb.tt +30 -0
  136. data/lib/generators/seams/auth/templates/spec/runtime/event_payload_spec.rb.tt +29 -0
  137. data/lib/generators/seams/auth/templates/spec/runtime/login_flow_spec.rb.tt +45 -0
  138. data/lib/generators/seams/billing/billing_generator.rb +476 -0
  139. data/lib/generators/seams/billing/templates/README.md.tt +355 -0
  140. data/lib/generators/seams/billing/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +84 -0
  141. data/lib/generators/seams/billing/templates/app/controllers/checkout_controller.rb.tt +92 -0
  142. data/lib/generators/seams/billing/templates/app/controllers/invoices_controller.rb.tt +63 -0
  143. data/lib/generators/seams/billing/templates/app/controllers/plans_controller.rb.tt +14 -0
  144. data/lib/generators/seams/billing/templates/app/controllers/portal_controller.rb.tt +45 -0
  145. data/lib/generators/seams/billing/templates/app/controllers/subscriptions_controller.rb.tt +119 -0
  146. data/lib/generators/seams/billing/templates/app/controllers/webhooks_controller.rb.tt +98 -0
  147. data/lib/generators/seams/billing/templates/app/helpers/currency_helper.rb.tt +44 -0
  148. data/lib/generators/seams/billing/templates/app/jobs/application_job.rb.tt +6 -0
  149. data/lib/generators/seams/billing/templates/app/jobs/cancel_subscription_job.rb.tt +39 -0
  150. data/lib/generators/seams/billing/templates/app/jobs/start_subscription_job.rb.tt +32 -0
  151. data/lib/generators/seams/billing/templates/app/jobs/webhooks/process_event_job.rb.tt +37 -0
  152. data/lib/generators/seams/billing/templates/app/models/application_record.rb.tt +7 -0
  153. data/lib/generators/seams/billing/templates/app/models/invoice.rb.tt +35 -0
  154. data/lib/generators/seams/billing/templates/app/models/lifetime_pass.rb.tt +60 -0
  155. data/lib/generators/seams/billing/templates/app/models/plan.rb.tt +95 -0
  156. data/lib/generators/seams/billing/templates/app/models/subscription.rb.tt +31 -0
  157. data/lib/generators/seams/billing/templates/app/models/webhook_event.rb.tt +13 -0
  158. data/lib/generators/seams/billing/templates/app/services/checkout_session_service.rb.tt +25 -0
  159. data/lib/generators/seams/billing/templates/app/services/customers/find_or_create_service.rb.tt +73 -0
  160. data/lib/generators/seams/billing/templates/app/services/invoices/sync_service.rb.tt +50 -0
  161. data/lib/generators/seams/billing/templates/app/services/lifetime/create_lifetime_session_service.rb.tt +82 -0
  162. data/lib/generators/seams/billing/templates/app/services/lifetime/create_pass_from_checkout_service.rb.tt +88 -0
  163. data/lib/generators/seams/billing/templates/app/services/lifetime/grant_pass_service.rb.tt +80 -0
  164. data/lib/generators/seams/billing/templates/app/services/lifetime/revoke_pass_service.rb.tt +59 -0
  165. data/lib/generators/seams/billing/templates/app/services/portal_session_service.rb.tt +23 -0
  166. data/lib/generators/seams/billing/templates/app/services/service_result.rb.tt +38 -0
  167. data/lib/generators/seams/billing/templates/app/services/stripe_service.rb.tt +67 -0
  168. data/lib/generators/seams/billing/templates/app/services/subscriptions/cancel_service.rb.tt +42 -0
  169. data/lib/generators/seams/billing/templates/app/services/subscriptions/change_plan_service.rb.tt +48 -0
  170. data/lib/generators/seams/billing/templates/app/services/subscriptions/reactivate_service.rb.tt +28 -0
  171. data/lib/generators/seams/billing/templates/app/services/webhooks/event_router.rb.tt +54 -0
  172. data/lib/generators/seams/billing/templates/app/services/webhooks/handler.rb.tt +93 -0
  173. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/charge_refunded_handler.rb.tt +18 -0
  174. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/checkout_session_completed_handler.rb.tt +58 -0
  175. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_created_handler.rb.tt +16 -0
  176. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_finalized_handler.rb.tt +14 -0
  177. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_handler_base.rb.tt +80 -0
  178. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_paid_handler.rb.tt +12 -0
  179. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_payment_failed_handler.rb.tt +12 -0
  180. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_voided_handler.rb.tt +12 -0
  181. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_failed_handler.rb.tt +15 -0
  182. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_succeeded_handler.rb.tt +19 -0
  183. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_created_handler.rb.tt +11 -0
  184. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_deleted_handler.rb.tt +15 -0
  185. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_handler_base.rb.tt +92 -0
  186. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_trial_will_end_handler.rb.tt +15 -0
  187. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_updated_handler.rb.tt +11 -0
  188. data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/index.html.erb.tt +36 -0
  189. data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/new.html.erb.tt +37 -0
  190. data/lib/generators/seams/billing/templates/app/views/checkout/success.html.erb.tt +5 -0
  191. data/lib/generators/seams/billing/templates/app/views/invoices/index.html.erb.tt +22 -0
  192. data/lib/generators/seams/billing/templates/app/views/invoices/show.html.erb.tt +14 -0
  193. data/lib/generators/seams/billing/templates/app/views/plans/index.html.erb.tt +51 -0
  194. data/lib/generators/seams/billing/templates/app/views/subscriptions/index.html.erb.tt +16 -0
  195. data/lib/generators/seams/billing/templates/app/views/subscriptions/show.html.erb.tt +25 -0
  196. data/lib/generators/seams/billing/templates/config/routes.rb.tt +39 -0
  197. data/lib/generators/seams/billing/templates/db/migrate/create_billing_invoices.rb.tt +32 -0
  198. data/lib/generators/seams/billing/templates/db/migrate/create_billing_lifetime_passes.rb.tt +43 -0
  199. data/lib/generators/seams/billing/templates/db/migrate/create_billing_plans.rb.tt +31 -0
  200. data/lib/generators/seams/billing/templates/db/migrate/create_billing_subscriptions.rb.tt +33 -0
  201. data/lib/generators/seams/billing/templates/db/migrate/create_billing_webhook_events.rb.tt +24 -0
  202. data/lib/generators/seams/billing/templates/lib/billing.rb.tt +34 -0
  203. data/lib/generators/seams/billing/templates/lib/concerns/billable.rb.tt +100 -0
  204. data/lib/generators/seams/billing/templates/lib/configuration.rb.tt +52 -0
  205. data/lib/generators/seams/billing/templates/lib/engine.rb.tt +72 -0
  206. data/lib/generators/seams/billing/templates/lib/gateways/abstract.rb.tt +65 -0
  207. data/lib/generators/seams/billing/templates/lib/gateways/adyen.rb.tt +16 -0
  208. data/lib/generators/seams/billing/templates/lib/gateways/paddle.rb.tt +22 -0
  209. data/lib/generators/seams/billing/templates/lib/gateways/stripe.rb.tt +155 -0
  210. data/lib/generators/seams/billing/templates/lib/stripe/client.rb.tt +101 -0
  211. data/lib/generators/seams/billing/templates/lib/stripe/webhook_signature.rb.tt +43 -0
  212. data/lib/generators/seams/billing/templates/lib/tasks/billing_check.rake.tt +34 -0
  213. data/lib/generators/seams/billing/templates/spec/factories/billing.rb.tt +65 -0
  214. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/charge_refunded.json.tt +19 -0
  215. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/checkout_session_completed.json.tt +17 -0
  216. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_created.json.tt +25 -0
  217. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_deleted.json.tt +17 -0
  218. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_trial_will_end.json.tt +17 -0
  219. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_updated.json.tt +28 -0
  220. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_created.json.tt +18 -0
  221. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_finalized.json.tt +18 -0
  222. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_paid.json.tt +19 -0
  223. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_payment_failed.json.tt +20 -0
  224. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_voided.json.tt +18 -0
  225. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_payment_failed.json.tt +21 -0
  226. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_succeeded.json.tt +17 -0
  227. data/lib/generators/seams/billing/templates/spec/gateways/contract_spec.rb.tt +11 -0
  228. data/lib/generators/seams/billing/templates/spec/gateways/stripe_spec.rb.tt +53 -0
  229. data/lib/generators/seams/billing/templates/spec/models/plan_spec.rb.tt +81 -0
  230. data/lib/generators/seams/billing/templates/spec/models/subscription_spec.rb.tt +43 -0
  231. data/lib/generators/seams/billing/templates/spec/runtime/boot_spec.rb.tt +38 -0
  232. data/lib/generators/seams/billing/templates/spec/runtime/webhook_handlers_spec.rb.tt +382 -0
  233. data/lib/generators/seams/billing/templates/spec/support/shared_examples/a_billing_gateway.rb.tt +100 -0
  234. data/lib/generators/seams/billing/templates/spec/support/stripe_helpers.rb.tt +59 -0
  235. data/lib/generators/seams/core/core_generator.rb +191 -0
  236. data/lib/generators/seams/core/templates/README.md.tt +45 -0
  237. data/lib/generators/seams/core/templates/app/controllers/concerns/has_current_attributes.rb.tt +71 -0
  238. data/lib/generators/seams/core/templates/app/models/application_record.rb.tt +7 -0
  239. data/lib/generators/seams/core/templates/app/models/audit_log.rb.tt +19 -0
  240. data/lib/generators/seams/core/templates/app/models/concerns/auditable.rb.tt +64 -0
  241. data/lib/generators/seams/core/templates/app/models/concerns/sluggable.rb.tt +53 -0
  242. data/lib/generators/seams/core/templates/app/models/concerns/soft_deletable.rb.tt +37 -0
  243. data/lib/generators/seams/core/templates/app/models/concerns/tenant_scoped.rb.tt +39 -0
  244. data/lib/generators/seams/core/templates/app/models/current.rb.tt +16 -0
  245. data/lib/generators/seams/core/templates/app/services/event_publisher.rb.tt +23 -0
  246. data/lib/generators/seams/core/templates/app/validators/email_format_validator.rb.tt +21 -0
  247. data/lib/generators/seams/core/templates/db/migrate/create_core_audit_logs.rb.tt +29 -0
  248. data/lib/generators/seams/core/templates/lib/core.rb.tt +8 -0
  249. data/lib/generators/seams/core/templates/lib/engine.rb.tt +28 -0
  250. data/lib/generators/seams/core/templates/spec/concerns/auditable_spec.rb.tt +39 -0
  251. data/lib/generators/seams/core/templates/spec/concerns/sluggable_spec.rb.tt +29 -0
  252. data/lib/generators/seams/core/templates/spec/models/audit_log_spec.rb.tt +22 -0
  253. data/lib/generators/seams/core/templates/spec/runtime/boot_spec.rb.tt +25 -0
  254. data/lib/generators/seams/core/templates/spec/validators/email_format_validator_spec.rb.tt +29 -0
  255. data/lib/generators/seams/engine/engine_generator.rb +165 -0
  256. data/lib/generators/seams/engine/templates/Gemfile.tt +19 -0
  257. data/lib/generators/seams/engine/templates/LICENSE.tt +21 -0
  258. data/lib/generators/seams/engine/templates/README.md.tt +40 -0
  259. data/lib/generators/seams/engine/templates/Rakefile.tt +14 -0
  260. data/lib/generators/seams/engine/templates/app/application_controller.rb.tt +6 -0
  261. data/lib/generators/seams/engine/templates/app/application_record.rb.tt +16 -0
  262. data/lib/generators/seams/engine/templates/config/locales/en.yml.tt +14 -0
  263. data/lib/generators/seams/engine/templates/config/routes.rb.tt +4 -0
  264. data/lib/generators/seams/engine/templates/gemspec.tt +20 -0
  265. data/lib/generators/seams/engine/templates/host_initializer.rb.tt +13 -0
  266. data/lib/generators/seams/engine/templates/lib/engine.rb.tt +27 -0
  267. data/lib/generators/seams/engine/templates/lib/root.rb.tt +7 -0
  268. data/lib/generators/seams/engine/templates/lib/version.rb.tt +5 -0
  269. data/lib/generators/seams/engine/templates/rubocop.yml.tt +55 -0
  270. data/lib/generators/seams/engine/templates/spec/example_spec.rb.tt +16 -0
  271. data/lib/generators/seams/engine/templates/spec/spec_helper.rb.tt +23 -0
  272. data/lib/generators/seams/install/install_generator.rb +211 -0
  273. data/lib/generators/seams/install/templates/Dockerfile.tt +52 -0
  274. data/lib/generators/seams/install/templates/Procfile.tt +14 -0
  275. data/lib/generators/seams/install/templates/bin_seams.tt +107 -0
  276. data/lib/generators/seams/install/templates/ci.yml.tt +123 -0
  277. data/lib/generators/seams/install/templates/deploy.yml.tt +63 -0
  278. data/lib/generators/seams/install/templates/doc/ARCHITECTURE.md.tt +86 -0
  279. data/lib/generators/seams/install/templates/docker-entrypoint.tt +27 -0
  280. data/lib/generators/seams/install/templates/rubocop.yml.tt +33 -0
  281. data/lib/generators/seams/install/templates/ruby-version.tt +1 -0
  282. data/lib/generators/seams/install/templates/script/collate_coverage.rb.tt +33 -0
  283. data/lib/generators/seams/install/templates/script/run_affected_tests.sh.tt +64 -0
  284. data/lib/generators/seams/install/templates/seams.rake.tt +65 -0
  285. data/lib/generators/seams/install/templates/seams.rb.tt +9 -0
  286. data/lib/generators/seams/install/templates/seams_engines.rb.tt +15 -0
  287. data/lib/generators/seams/notifications/notifications_generator.rb +395 -0
  288. data/lib/generators/seams/notifications/templates/README.md.tt +269 -0
  289. data/lib/generators/seams/notifications/templates/app/channels/notification_channel.rb.tt +36 -0
  290. data/lib/generators/seams/notifications/templates/app/controllers/notifications_controller.rb.tt +58 -0
  291. data/lib/generators/seams/notifications/templates/app/controllers/preferences_controller.rb.tt +54 -0
  292. data/lib/generators/seams/notifications/templates/app/javascript/controllers/notification_bell_controller.js.tt +34 -0
  293. data/lib/generators/seams/notifications/templates/app/jobs/application_job.rb.tt +6 -0
  294. data/lib/generators/seams/notifications/templates/app/jobs/create_notification_job.rb.tt +31 -0
  295. data/lib/generators/seams/notifications/templates/app/jobs/send_due_notifications_job.rb.tt +22 -0
  296. data/lib/generators/seams/notifications/templates/app/jobs/send_notification_job.rb.tt +13 -0
  297. data/lib/generators/seams/notifications/templates/app/mailers/application_mailer.rb.tt +12 -0
  298. data/lib/generators/seams/notifications/templates/app/mailers/notification_mailer.rb.tt +23 -0
  299. data/lib/generators/seams/notifications/templates/app/models/application_record.rb.tt +7 -0
  300. data/lib/generators/seams/notifications/templates/app/models/delivery.rb.tt +13 -0
  301. data/lib/generators/seams/notifications/templates/app/models/notification.rb.tt +218 -0
  302. data/lib/generators/seams/notifications/templates/app/models/notification_preference.rb.tt +29 -0
  303. data/lib/generators/seams/notifications/templates/app/models/strategies/email.rb.tt +38 -0
  304. data/lib/generators/seams/notifications/templates/app/models/strategies/in_app.rb.tt +26 -0
  305. data/lib/generators/seams/notifications/templates/app/models/strategies/sms.rb.tt +33 -0
  306. data/lib/generators/seams/notifications/templates/app/subscribers/auth_subscriber.rb.tt +71 -0
  307. data/lib/generators/seams/notifications/templates/app/subscribers/billing_subscriber.rb.tt +127 -0
  308. data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.html.erb.tt +22 -0
  309. data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.text.erb.tt +4 -0
  310. data/lib/generators/seams/notifications/templates/app/views/notifications/_bell.html.erb.tt +15 -0
  311. data/lib/generators/seams/notifications/templates/app/views/notifications/index.html.erb.tt +15 -0
  312. data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.html.erb.tt +4 -0
  313. data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.text.erb.tt +4 -0
  314. data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.html.erb.tt +3 -0
  315. data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.text.erb.tt +3 -0
  316. data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.html.erb.tt +5 -0
  317. data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.text.erb.tt +5 -0
  318. data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.html.erb.tt +5 -0
  319. data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.text.erb.tt +5 -0
  320. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.html.erb.tt +4 -0
  321. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.text.erb.tt +4 -0
  322. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.html.erb.tt +4 -0
  323. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.text.erb.tt +5 -0
  324. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.html.erb.tt +3 -0
  325. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.text.erb.tt +3 -0
  326. data/lib/generators/seams/notifications/templates/app/views/templates/default.html.erb.tt +10 -0
  327. data/lib/generators/seams/notifications/templates/app/views/templates/default.text.erb.tt +11 -0
  328. data/lib/generators/seams/notifications/templates/app/views/templates/welcome.html.erb.tt +6 -0
  329. data/lib/generators/seams/notifications/templates/app/views/templates/welcome.text.erb.tt +6 -0
  330. data/lib/generators/seams/notifications/templates/config/initializers/notifications.rb.tt +58 -0
  331. data/lib/generators/seams/notifications/templates/config/routes.rb.tt +17 -0
  332. data/lib/generators/seams/notifications/templates/db/migrate/create_notification_deliveries.rb.tt +16 -0
  333. data/lib/generators/seams/notifications/templates/db/migrate/create_notification_preferences.rb.tt +25 -0
  334. data/lib/generators/seams/notifications/templates/db/migrate/create_notifications.rb.tt +35 -0
  335. data/lib/generators/seams/notifications/templates/lib/adapters/abstract.rb.tt +20 -0
  336. data/lib/generators/seams/notifications/templates/lib/adapters/action_mailer.rb.tt +17 -0
  337. data/lib/generators/seams/notifications/templates/lib/adapters/null_sms.rb.tt +23 -0
  338. data/lib/generators/seams/notifications/templates/lib/concerns/notifiable.rb.tt +135 -0
  339. data/lib/generators/seams/notifications/templates/lib/configuration.rb.tt +24 -0
  340. data/lib/generators/seams/notifications/templates/lib/engine.rb.tt +35 -0
  341. data/lib/generators/seams/notifications/templates/lib/notifications.rb.tt +75 -0
  342. data/lib/generators/seams/notifications/templates/lib/type_registry.rb.tt +74 -0
  343. data/lib/generators/seams/notifications/templates/spec/factories/notifications.rb.tt +53 -0
  344. data/lib/generators/seams/notifications/templates/spec/models/delivery_spec.rb.tt +28 -0
  345. data/lib/generators/seams/notifications/templates/spec/models/notification_preference_spec.rb.tt +46 -0
  346. data/lib/generators/seams/notifications/templates/spec/models/notification_spec.rb.tt +60 -0
  347. data/lib/generators/seams/notifications/templates/spec/runtime/bell_broadcast_spec.rb.tt +59 -0
  348. data/lib/generators/seams/notifications/templates/spec/runtime/billing_subscriber_skip_spec.rb.tt +87 -0
  349. data/lib/generators/seams/notifications/templates/spec/runtime/boot_spec.rb.tt +39 -0
  350. data/lib/generators/seams/notifications/templates/spec/runtime/schedule_round_trip_spec.rb.tt +55 -0
  351. data/lib/generators/seams/remove/remove_generator.rb +259 -0
  352. data/lib/generators/seams/teams/teams_generator.rb +298 -0
  353. data/lib/generators/seams/teams/templates/README.md.tt +88 -0
  354. data/lib/generators/seams/teams/templates/app/controllers/invitations_controller.rb.tt +102 -0
  355. data/lib/generators/seams/teams/templates/app/controllers/memberships_controller.rb.tt +54 -0
  356. data/lib/generators/seams/teams/templates/app/controllers/teams_controller.rb.tt +68 -0
  357. data/lib/generators/seams/teams/templates/app/jobs/application_job.rb.tt +6 -0
  358. data/lib/generators/seams/teams/templates/app/mailers/invitation_mailer.rb.tt +34 -0
  359. data/lib/generators/seams/teams/templates/app/models/application_record.rb.tt +7 -0
  360. data/lib/generators/seams/teams/templates/app/models/current.rb.tt +30 -0
  361. data/lib/generators/seams/teams/templates/app/models/invitation.rb.tt +36 -0
  362. data/lib/generators/seams/teams/templates/app/models/membership.rb.tt +36 -0
  363. data/lib/generators/seams/teams/templates/app/models/team.rb.tt +32 -0
  364. data/lib/generators/seams/teams/templates/app/subscribers/invitation_subscriber.rb.tt +36 -0
  365. data/lib/generators/seams/teams/templates/app/views/invitation_mailer/invite.text.erb.tt +8 -0
  366. data/lib/generators/seams/teams/templates/app/views/invitations/index.html.erb.tt +44 -0
  367. data/lib/generators/seams/teams/templates/app/views/memberships/index.html.erb.tt +32 -0
  368. data/lib/generators/seams/teams/templates/app/views/teams/edit.html.erb.tt +28 -0
  369. data/lib/generators/seams/teams/templates/app/views/teams/index.html.erb.tt +15 -0
  370. data/lib/generators/seams/teams/templates/app/views/teams/new.html.erb.tt +24 -0
  371. data/lib/generators/seams/teams/templates/app/views/teams/show.html.erb.tt +17 -0
  372. data/lib/generators/seams/teams/templates/config/routes.rb.tt +19 -0
  373. data/lib/generators/seams/teams/templates/db/migrate/create_team_invitations.rb.tt +24 -0
  374. data/lib/generators/seams/teams/templates/db/migrate/create_team_memberships.rb.tt +25 -0
  375. data/lib/generators/seams/teams/templates/db/migrate/create_teams.rb.tt +18 -0
  376. data/lib/generators/seams/teams/templates/lib/concerns/account_scoped.rb.tt +79 -0
  377. data/lib/generators/seams/teams/templates/lib/concerns/authorization.rb.tt +55 -0
  378. data/lib/generators/seams/teams/templates/lib/configuration.rb.tt +45 -0
  379. data/lib/generators/seams/teams/templates/lib/engine.rb.tt +51 -0
  380. data/lib/generators/seams/teams/templates/lib/teams.rb.tt +22 -0
  381. data/lib/generators/seams/teams/templates/spec/factories/teams.rb.tt +47 -0
  382. data/lib/generators/seams/teams/templates/spec/models/invitation_spec.rb.tt +25 -0
  383. data/lib/generators/seams/teams/templates/spec/models/membership_spec.rb.tt +29 -0
  384. data/lib/generators/seams/teams/templates/spec/models/team_spec.rb.tt +23 -0
  385. data/lib/generators/seams/teams/templates/spec/runtime/boot_spec.rb.tt +32 -0
  386. data/lib/seams/cli/list.rb +111 -0
  387. data/lib/seams/cli/quality.rb +99 -0
  388. data/lib/seams/cli/resolve.rb +276 -0
  389. data/lib/seams/cli/test_changed.rb +116 -0
  390. data/lib/seams/cli.rb +48 -0
  391. data/lib/seams/configuration.rb +19 -0
  392. data/lib/seams/cops/known_queue_names.rb +42 -0
  393. data/lib/seams/cops/migration_comments.rb +68 -0
  394. data/lib/seams/cops/no_cross_engine_dependency.rb +58 -0
  395. data/lib/seams/cops/no_cross_engine_model_access.rb +153 -0
  396. data/lib/seams/cops.rb +18 -0
  397. data/lib/seams/event_registry.rb +49 -0
  398. data/lib/seams/events/adapter.rb +24 -0
  399. data/lib/seams/events/adapters/active_support.rb +31 -0
  400. data/lib/seams/events/publisher.rb +178 -0
  401. data/lib/seams/events.rb +39 -0
  402. data/lib/seams/generators/dummy_app_writer.rb +424 -0
  403. data/lib/seams/generators/eject_aware.rb +102 -0
  404. data/lib/seams/generators/follow_up_generator.rb +148 -0
  405. data/lib/seams/generators/host_injector.rb +124 -0
  406. data/lib/seams/generators/sibling_rubocop_writer.rb +77 -0
  407. data/lib/seams/generators/splicer.rb +217 -0
  408. data/lib/seams/observability/adapter.rb +33 -0
  409. data/lib/seams/observability/adapters/rails_logger.rb +59 -0
  410. data/lib/seams/observability.rb +34 -0
  411. data/lib/seams/runtime.rb +23 -0
  412. data/lib/seams/version.rb +5 -0
  413. data/lib/seams.rb +23 -0
  414. metadata +493 -0
@@ -0,0 +1,476 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "rails/generators"
5
+ require "seams"
6
+ require "generators/seams/engine/engine_generator"
7
+ require "seams/generators/host_injector"
8
+ require "seams/generators/eject_aware"
9
+ require "seams/generators/dummy_app_writer"
10
+
11
+ module Seams
12
+ module Generators
13
+ # Generates a canonical Billing engine on top of the generic engine
14
+ # scaffold. Wires Stripe by default through a swappable Gateway
15
+ # adapter, ships subscription + invoice models, a webhook
16
+ # controller with signature verification, and a Billable concern
17
+ # the host's user model can include.
18
+ #
19
+ # Run with: bin/rails generate seams:billing
20
+ # rubocop:disable Metrics/ClassLength
21
+ class BillingGenerator < Rails::Generators::Base
22
+ include Seams::Generators::HostInjector
23
+ include Seams::Generators::EjectAware
24
+
25
+ source_root File.expand_path("templates", __dir__)
26
+
27
+ ENGINE_NAME = "billing"
28
+ DEFAULT_GATEWAY = "stripe"
29
+ KNOWN_GATEWAYS = %w[stripe paddle adyen].freeze
30
+
31
+ class_option :gateway, type: :string, default: DEFAULT_GATEWAY,
32
+ desc: "Billing gateway: stripe (default), paddle, or adyen (stub)"
33
+
34
+ def create_base_engine
35
+ EngineGenerator.start([ENGINE_NAME], destination_root: destination_root)
36
+ end
37
+
38
+ def overwrite_engine_entry_point
39
+ # engine.rb / lib/billing.rb stay framework-managed.
40
+ template "lib/engine.rb.tt", engine_path("lib/billing/engine.rb"), force: true
41
+ template "lib/billing.rb.tt", engine_path("lib/billing.rb"), force: true
42
+ template_unless_ejected "lib/configuration.rb.tt",
43
+ engine_path("lib/billing/configuration.rb")
44
+ template_unless_ejected "lib/tasks/billing_check.rake.tt",
45
+ engine_path("lib/tasks/billing_check.rake")
46
+ end
47
+
48
+ def overwrite_routes
49
+ template_unless_ejected "config/routes.rb.tt", engine_path("config/routes.rb"), force: true
50
+ end
51
+
52
+ def create_models
53
+ template_unless_ejected "app/models/application_record.rb.tt",
54
+ engine_path("app/models/billing/application_record.rb")
55
+ template_unless_ejected "app/models/subscription.rb.tt",
56
+ engine_path("app/models/billing/subscription.rb")
57
+ template_unless_ejected "app/models/invoice.rb.tt",
58
+ engine_path("app/models/billing/invoice.rb")
59
+ template_unless_ejected "app/models/webhook_event.rb.tt",
60
+ engine_path("app/models/billing/webhook_event.rb")
61
+ template_unless_ejected "app/models/plan.rb.tt",
62
+ engine_path("app/models/billing/plan.rb")
63
+ template_unless_ejected "app/models/lifetime_pass.rb.tt",
64
+ engine_path("app/models/billing/lifetime_pass.rb")
65
+ end
66
+
67
+ def create_gateways
68
+ template_unless_ejected "lib/gateways/abstract.rb.tt",
69
+ engine_path("lib/billing/gateways/abstract.rb")
70
+
71
+ # Stripe ships unconditionally (it's the default + reference
72
+ # implementation that sibling specs reference). Paddle / Adyen
73
+ # are stubs — copied only when --gateway=paddle/adyen.
74
+ template_unless_ejected "lib/gateways/stripe.rb.tt",
75
+ engine_path("lib/billing/gateways/stripe.rb")
76
+ template_unless_ejected "lib/stripe/client.rb.tt",
77
+ engine_path("lib/billing/stripe/client.rb")
78
+ template_unless_ejected "lib/stripe/webhook_signature.rb.tt",
79
+ engine_path("lib/billing/stripe/webhook_signature.rb")
80
+
81
+ return if gateway == "stripe"
82
+
83
+ template_unless_ejected "lib/gateways/#{gateway}.rb.tt",
84
+ engine_path("lib/billing/gateways/#{gateway}.rb")
85
+ end
86
+
87
+ def create_concern
88
+ template_unless_ejected "lib/concerns/billable.rb.tt",
89
+ engine_path("lib/billing/concerns/billable.rb")
90
+ end
91
+
92
+ def create_jobs
93
+ template_unless_ejected "app/jobs/application_job.rb.tt",
94
+ engine_path("app/jobs/billing/application_job.rb")
95
+ template_unless_ejected "app/jobs/start_subscription_job.rb.tt",
96
+ engine_path("app/jobs/billing/start_subscription_job.rb")
97
+ template_unless_ejected "app/jobs/cancel_subscription_job.rb.tt",
98
+ engine_path("app/jobs/billing/cancel_subscription_job.rb")
99
+ end
100
+
101
+ def create_services
102
+ create_service_foundation
103
+ create_session_services
104
+ create_domain_services
105
+ end
106
+
107
+ def create_service_foundation
108
+ # Phase 3 (1/4) — uniform service object foundation.
109
+ template_unless_ejected "app/services/service_result.rb.tt",
110
+ engine_path("app/services/billing/service_result.rb")
111
+ template_unless_ejected "app/services/stripe_service.rb.tt",
112
+ engine_path("app/services/billing/stripe_service.rb")
113
+ end
114
+
115
+ def create_session_services
116
+ template_unless_ejected "app/services/checkout_session_service.rb.tt",
117
+ engine_path("app/services/billing/checkout/create_session_service.rb")
118
+ template_unless_ejected "app/services/portal_session_service.rb.tt",
119
+ engine_path("app/services/billing/portal/create_session_service.rb")
120
+ end
121
+
122
+ def create_domain_services
123
+ create_customer_and_subscription_services
124
+ create_invoice_and_lifetime_services
125
+ end
126
+
127
+ def create_customer_and_subscription_services
128
+ # Phase 3 (2/4) — Customers + Subscriptions service objects.
129
+ template_unless_ejected "app/services/customers/find_or_create_service.rb.tt",
130
+ engine_path("app/services/billing/customers/find_or_create_service.rb")
131
+ template_unless_ejected "app/services/subscriptions/cancel_service.rb.tt",
132
+ engine_path("app/services/billing/subscriptions/cancel_service.rb")
133
+ template_unless_ejected "app/services/subscriptions/change_plan_service.rb.tt",
134
+ engine_path("app/services/billing/subscriptions/change_plan_service.rb")
135
+ template_unless_ejected "app/services/subscriptions/reactivate_service.rb.tt",
136
+ engine_path("app/services/billing/subscriptions/reactivate_service.rb")
137
+ end
138
+
139
+ def create_invoice_and_lifetime_services
140
+ template_unless_ejected "app/services/invoices/sync_service.rb.tt",
141
+ engine_path("app/services/billing/invoices/sync_service.rb")
142
+ # Lifetime Deal services — see issue #2 section 3A.LTD.
143
+ template_unless_ejected "app/services/lifetime/grant_pass_service.rb.tt",
144
+ engine_path("app/services/billing/lifetime/grant_pass_service.rb")
145
+ template_unless_ejected "app/services/lifetime/revoke_pass_service.rb.tt",
146
+ engine_path("app/services/billing/lifetime/revoke_pass_service.rb")
147
+ template_unless_ejected "app/services/lifetime/create_pass_from_checkout_service.rb.tt",
148
+ engine_path("app/services/billing/lifetime/create_pass_from_checkout_service.rb")
149
+ template_unless_ejected "app/services/lifetime/create_lifetime_session_service.rb.tt",
150
+ engine_path("app/services/billing/lifetime/create_lifetime_session_service.rb")
151
+ end
152
+
153
+ # Phase 3 (3/4) — webhook router + 13 handler classes.
154
+ def create_webhook_router_and_handlers
155
+ template_unless_ejected "app/services/webhooks/handler.rb.tt",
156
+ engine_path("app/services/billing/webhooks/handler.rb")
157
+ template_unless_ejected "app/services/webhooks/event_router.rb.tt",
158
+ engine_path("app/services/billing/webhooks/event_router.rb")
159
+
160
+ webhook_handler_templates.each do |basename|
161
+ template_unless_ejected "app/services/webhooks/handlers/#{basename}.rb.tt",
162
+ engine_path("app/services/billing/webhooks/handlers/#{basename}.rb")
163
+ end
164
+ end
165
+
166
+ def create_webhook_process_event_job
167
+ template_unless_ejected "app/jobs/webhooks/process_event_job.rb.tt",
168
+ engine_path("app/jobs/billing/webhooks/process_event_job.rb")
169
+ end
170
+
171
+ def create_controllers_and_views
172
+ template_unless_ejected "app/controllers/webhooks_controller.rb.tt",
173
+ engine_path("app/controllers/billing/webhooks_controller.rb")
174
+ template_unless_ejected "app/controllers/checkout_controller.rb.tt",
175
+ engine_path("app/controllers/billing/checkout_controller.rb")
176
+ template_unless_ejected "app/controllers/portal_controller.rb.tt",
177
+ engine_path("app/controllers/billing/portal_controller.rb")
178
+ template_unless_ejected "app/controllers/plans_controller.rb.tt",
179
+ engine_path("app/controllers/billing/plans_controller.rb")
180
+ template_unless_ejected "app/views/checkout/success.html.erb.tt",
181
+ engine_path("app/views/billing/checkout/success.html.erb")
182
+ template_unless_ejected "app/views/plans/index.html.erb.tt",
183
+ engine_path("app/views/billing/plans/index.html.erb")
184
+ end
185
+
186
+ # Phase 3 (4/4) — self-service subscription management +
187
+ # read-only billing history. Routes live in config/routes.rb.tt.
188
+ def create_subscriptions_and_invoices_ui
189
+ template_unless_ejected "app/controllers/subscriptions_controller.rb.tt",
190
+ engine_path("app/controllers/billing/subscriptions_controller.rb")
191
+ template_unless_ejected "app/controllers/invoices_controller.rb.tt",
192
+ engine_path("app/controllers/billing/invoices_controller.rb")
193
+ template_unless_ejected "app/views/subscriptions/index.html.erb.tt",
194
+ engine_path("app/views/billing/subscriptions/index.html.erb")
195
+ template_unless_ejected "app/views/subscriptions/show.html.erb.tt",
196
+ engine_path("app/views/billing/subscriptions/show.html.erb")
197
+ template_unless_ejected "app/views/invoices/index.html.erb.tt",
198
+ engine_path("app/views/billing/invoices/index.html.erb")
199
+ template_unless_ejected "app/views/invoices/show.html.erb.tt",
200
+ engine_path("app/views/billing/invoices/show.html.erb")
201
+ end
202
+
203
+ # LTD admin controller + views (issue #2 section 3A.LTD). Kept
204
+ # in its own generator method so create_controllers_and_views
205
+ # stays under the AbcSize lint threshold.
206
+ def create_lifetime_admin_controller_and_views
207
+ template_unless_ejected "app/controllers/admin/lifetime_passes_controller.rb.tt",
208
+ engine_path("app/controllers/billing/admin/lifetime_passes_controller.rb")
209
+ template_unless_ejected "app/views/admin/lifetime_passes/index.html.erb.tt",
210
+ engine_path("app/views/billing/admin/lifetime_passes/index.html.erb")
211
+ template_unless_ejected "app/views/admin/lifetime_passes/new.html.erb.tt",
212
+ engine_path("app/views/billing/admin/lifetime_passes/new.html.erb")
213
+ end
214
+
215
+ def create_migrations
216
+ template "db/migrate/create_billing_subscriptions.rb.tt",
217
+ engine_path("db/migrate/#{timestamp(0)}_create_billing_subscriptions.rb")
218
+ template "db/migrate/create_billing_invoices.rb.tt",
219
+ engine_path("db/migrate/#{timestamp(1)}_create_billing_invoices.rb")
220
+ template "db/migrate/create_billing_webhook_events.rb.tt",
221
+ engine_path("db/migrate/#{timestamp(2)}_create_billing_webhook_events.rb")
222
+ template "db/migrate/create_billing_plans.rb.tt",
223
+ engine_path("db/migrate/#{timestamp(3)}_create_billing_plans.rb")
224
+ template "db/migrate/create_billing_lifetime_passes.rb.tt",
225
+ engine_path("db/migrate/#{timestamp(4)}_create_billing_lifetime_passes.rb")
226
+ end
227
+
228
+ def create_specs
229
+ template_unless_ejected "spec/models/subscription_spec.rb.tt",
230
+ engine_path("spec/models/billing/subscription_spec.rb")
231
+ template_unless_ejected "spec/models/plan_spec.rb.tt",
232
+ engine_path("spec/models/billing/plan_spec.rb")
233
+ template_unless_ejected "spec/gateways/stripe_spec.rb.tt",
234
+ engine_path("spec/gateways/billing/stripe_spec.rb")
235
+ # Phase 3 (1/4) — factories + Stripe webmock helpers + event fixtures.
236
+ template_unless_ejected "spec/factories/billing.rb.tt",
237
+ engine_path("spec/factories/billing.rb")
238
+ template_unless_ejected "spec/support/stripe_helpers.rb.tt",
239
+ engine_path("spec/support/stripe_helpers.rb")
240
+ # Phase 3 (4/4) — gateway contract shared_examples.
241
+ template_unless_ejected "spec/support/shared_examples/a_billing_gateway.rb.tt",
242
+ engine_path("spec/support/shared_examples/a_billing_gateway.rb")
243
+ template_unless_ejected "spec/gateways/contract_spec.rb.tt",
244
+ engine_path("spec/gateways/billing/contract_spec.rb")
245
+ create_stripe_event_fixtures
246
+ end
247
+
248
+ def create_stripe_event_fixtures
249
+ %w[
250
+ customer_subscription_created
251
+ customer_subscription_updated
252
+ customer_subscription_deleted
253
+ customer_subscription_trial_will_end
254
+ invoice_created
255
+ invoice_paid
256
+ invoice_payment_failed
257
+ invoice_finalized
258
+ invoice_voided
259
+ payment_intent_succeeded
260
+ payment_intent_payment_failed
261
+ charge_refunded
262
+ checkout_session_completed
263
+ ].each do |name|
264
+ template_unless_ejected "spec/fixtures/stripe/#{name}.json.tt",
265
+ engine_path("spec/fixtures/stripe/#{name}.json")
266
+ end
267
+ end
268
+
269
+ def create_helpers
270
+ # Phase 3 (1/4) — currency formatter for the pricing page +
271
+ # invoice views.
272
+ template_unless_ejected "app/helpers/currency_helper.rb.tt",
273
+ engine_path("app/helpers/billing/currency_helper.rb")
274
+ end
275
+
276
+ def overwrite_readme
277
+ template "README.md.tt", engine_path("README.md"), force: true
278
+ end
279
+
280
+ def update_exposed_concerns
281
+ rubocop_path = engine_path(".rubocop.yml")
282
+ return unless File.exist?(rubocop_path)
283
+
284
+ contents = File.read(rubocop_path)
285
+ replacement = " ExposedConcerns:\n - Billing::Billable"
286
+ contents.sub!(/^ ExposedConcerns: \[\]$/, replacement)
287
+ File.write(rubocop_path, contents)
288
+ end
289
+
290
+ def create_dummy_app
291
+ # Post Wave 9: the dummy app does NOT ship a host User model.
292
+ # Billing addresses an Accounts::Account (the tenant), not a
293
+ # human; the engine specs use the dummy `accounts` table that
294
+ # ships in dummy_schema below. Hosts that keep a User model
295
+ # are unaffected — Billable is included into the configured
296
+ # tenant class (default Accounts::Account), not into User.
297
+ Seams::Generators::DummyAppWriter.write!(
298
+ engine_path: File.join(destination_root, "engines", ENGINE_NAME),
299
+ engine_module: "Billing",
300
+ mount_at: "/billing",
301
+ schema: dummy_schema
302
+ )
303
+ template "spec/runtime/boot_spec.rb.tt",
304
+ engine_path("spec/runtime/billing_boot_spec.rb")
305
+ # Phase 3 (3/4) coverage gap: a runtime spec that instantiates
306
+ # each of the 13 webhook handlers with the matching Stripe
307
+ # fixture and asserts the local DB row + canonical seams event.
308
+ template "spec/runtime/webhook_handlers_spec.rb.tt",
309
+ engine_path("spec/runtime/webhook_handlers_spec.rb")
310
+ end
311
+
312
+ def wire_into_host
313
+ # The Billing engine speaks Stripe via the official `stripe`
314
+ # gem (https://github.com/stripe/stripe-ruby). Wave 8's earlier
315
+ # decision to roll a Faraday client was reversed — Stripe
316
+ # maintains the gem as a first-class deliverable; tracking
317
+ # their API ourselves wasn't worth the centralisation
318
+ # benefit. See feedback_external_apis.md.
319
+ host_inject_gem("stripe", "~> 13.0")
320
+ host_inject_gem("factory_bot_rails", "~> 6.4", group: :test)
321
+ host_inject_gem("webmock", "~> 3.23", group: :test)
322
+ host_inject_mount(engine_class: "Billing::Engine", at: "/billing")
323
+ # Post Wave 9: Billing::Billable is included into the
324
+ # configured tenant class (default Accounts::Account) at
325
+ # engine boot via Billing::Configuration#billable_class —
326
+ # not injected into a host User model. Hosts that customise
327
+ # the tenant class set Billing.configuration.billable_class
328
+ # in config/initializers/billing.rb.
329
+ end
330
+
331
+ def report_summary
332
+ say ""
333
+ say " Billing engine generated at engines/billing/", :green
334
+ say ""
335
+ say " Next steps:", :yellow
336
+ say " 1. bundle install (picks up stripe + Billing::Engine)"
337
+ say " 2. Set STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET in your env"
338
+ say " 3. Configure your Stripe webhook to POST to /billing/webhooks/stripe"
339
+ say " 4. bin/rails db:migrate"
340
+ say ""
341
+ end
342
+
343
+ private
344
+
345
+ # Resolved gateway choice from --gateway. Garbage / unknown
346
+ # values fall back to stripe (no surprising half-installed
347
+ # engine). Memoised so ERB conditionals stay consistent.
348
+ def gateway
349
+ @gateway ||= begin
350
+ requested = options[:gateway].to_s.downcase.strip
351
+ KNOWN_GATEWAYS.include?(requested) ? requested : DEFAULT_GATEWAY
352
+ end
353
+ end
354
+
355
+ def gateway_class_name
356
+ "Billing::Gateways::#{gateway.capitalize}"
357
+ end
358
+
359
+ def gateway_env_prefix
360
+ gateway.upcase
361
+ end
362
+
363
+ def webhook_handler_templates
364
+ %w[
365
+ subscription_handler_base
366
+ subscription_created_handler
367
+ subscription_updated_handler
368
+ subscription_deleted_handler
369
+ subscription_trial_will_end_handler
370
+ invoice_handler_base
371
+ invoice_created_handler
372
+ invoice_paid_handler
373
+ invoice_payment_failed_handler
374
+ invoice_finalized_handler
375
+ invoice_voided_handler
376
+ payment_succeeded_handler
377
+ payment_failed_handler
378
+ charge_refunded_handler
379
+ checkout_session_completed_handler
380
+ ]
381
+ end
382
+
383
+ def engine_path(relative)
384
+ File.join(destination_root, "engines", ENGINE_NAME, relative)
385
+ end
386
+
387
+ def timestamp(offset)
388
+ base = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
389
+ (base + 200 + offset).to_s
390
+ end
391
+
392
+ def dummy_schema
393
+ # Post Wave 9 — every billing table carries `account_id` (UUID)
394
+ # as a bare local FK to Accounts::Account. The dummy schema
395
+ # ships a minimal `accounts` table so the runtime specs can
396
+ # write rows and read them back; we don't ship the full
397
+ # accounts engine schema here because billing's tests don't
398
+ # exercise the accounts engine — they need just a tenant id.
399
+ <<~SCHEMA
400
+ enable_extension "pgcrypto"
401
+
402
+ create_table :accounts, id: :uuid do |t|
403
+ t.string :name, null: false
404
+ t.timestamps
405
+ end
406
+
407
+ create_table :billing_subscriptions do |t|
408
+ t.uuid :account_id, null: false
409
+ t.string :customer_ref, null: false
410
+ t.string :plan_ref, null: false
411
+ t.string :gateway_ref, null: false
412
+ t.string :status, null: false, default: "incomplete"
413
+ t.datetime :current_period_end
414
+ t.timestamps
415
+ end
416
+ add_index :billing_subscriptions, :account_id
417
+ add_index :billing_subscriptions, :gateway_ref, unique: true
418
+
419
+ create_table :billing_invoices do |t|
420
+ t.uuid :account_id, null: false
421
+ t.string :gateway_ref, null: false
422
+ t.string :customer_ref, null: false
423
+ t.string :subscription_ref
424
+ t.integer :amount_cents, null: false
425
+ t.string :currency, null: false, default: "USD"
426
+ t.string :status, null: false, default: "open"
427
+ t.datetime :paid_at
428
+ t.timestamps
429
+ end
430
+ add_index :billing_invoices, :account_id
431
+ add_index :billing_invoices, :gateway_ref, unique: true
432
+
433
+ create_table :billing_webhook_events do |t|
434
+ t.string :gateway, null: false
435
+ t.string :gateway_event_id, null: false
436
+ t.string :event_type, null: false
437
+ t.boolean :livemode, null: false, default: false
438
+ t.timestamps
439
+ end
440
+ add_index :billing_webhook_events, %i[gateway gateway_event_id], unique: true
441
+
442
+ create_table :billing_plans do |t|
443
+ t.string :gateway_ref, null: false
444
+ t.string :name, null: false
445
+ t.text :description
446
+ t.integer :amount_cents, null: false, default: 0
447
+ t.string :currency, null: false, default: "usd"
448
+ t.string :interval, null: false, default: "month"
449
+ t.integer :trial_period_days
450
+ t.boolean :active, null: false, default: true
451
+ t.jsonb :features, null: false, default: {}
452
+ t.integer :max_lifetime_units
453
+ t.timestamps
454
+ end
455
+
456
+ create_table :billing_lifetime_passes do |t|
457
+ t.uuid :account_id, null: false
458
+ t.string :customer_ref, null: false
459
+ t.string :plan_ref, null: false
460
+ t.string :gateway_ref
461
+ t.bigint :granted_by_identity_id
462
+ t.datetime :granted_at, null: false
463
+ t.datetime :revoked_at
464
+ t.bigint :revoked_by_identity_id
465
+ t.text :notes
466
+ t.timestamps
467
+ end
468
+ add_index :billing_lifetime_passes, :account_id
469
+ add_index :billing_lifetime_passes, %i[account_id plan_ref], unique: true,
470
+ name: "index_billing_ltd_unique"
471
+ SCHEMA
472
+ end
473
+ end
474
+ # rubocop:enable Metrics/ClassLength
475
+ end
476
+ end