@baruk/core 0.1.0 → 0.2.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 (429) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/auth/password-reset.d.ts.map +1 -1
  3. package/dist/auth/password-reset.js +2 -4
  4. package/dist/auth/password-reset.js.map +1 -1
  5. package/dist/auth/password.d.ts +2 -18
  6. package/dist/auth/password.d.ts.map +1 -1
  7. package/dist/auth/password.js +12 -51
  8. package/dist/auth/password.js.map +1 -1
  9. package/dist/auth/service.d.ts +0 -12
  10. package/dist/auth/service.d.ts.map +1 -1
  11. package/dist/auth/service.js +3 -36
  12. package/dist/auth/service.js.map +1 -1
  13. package/dist/auth/session-token.d.ts +5 -2
  14. package/dist/auth/session-token.d.ts.map +1 -1
  15. package/dist/auth/session-token.js +7 -12
  16. package/dist/auth/session-token.js.map +1 -1
  17. package/dist/billing/dunning.d.ts +33 -0
  18. package/dist/billing/dunning.d.ts.map +1 -0
  19. package/dist/billing/dunning.js +49 -0
  20. package/dist/billing/dunning.js.map +1 -0
  21. package/dist/billing/provider/asaas.d.ts +15 -0
  22. package/dist/billing/provider/asaas.d.ts.map +1 -0
  23. package/dist/billing/provider/asaas.js +121 -0
  24. package/dist/billing/provider/asaas.js.map +1 -0
  25. package/dist/billing/provider/manual.d.ts +14 -0
  26. package/dist/billing/provider/manual.d.ts.map +1 -0
  27. package/dist/billing/provider/manual.js +24 -0
  28. package/dist/billing/provider/manual.js.map +1 -0
  29. package/dist/billing/provider/registry.d.ts +12 -0
  30. package/dist/billing/provider/registry.d.ts.map +1 -0
  31. package/dist/billing/provider/registry.js +22 -0
  32. package/dist/billing/provider/registry.js.map +1 -0
  33. package/dist/billing/provider/types.d.ts +76 -0
  34. package/dist/billing/provider/types.d.ts.map +1 -0
  35. package/dist/billing/provider/types.js +9 -0
  36. package/dist/billing/provider/types.js.map +1 -0
  37. package/dist/billing/receivable.d.ts +108 -0
  38. package/dist/billing/receivable.d.ts.map +1 -0
  39. package/dist/billing/receivable.js +2 -0
  40. package/dist/billing/receivable.js.map +1 -0
  41. package/dist/billing/repository.d.ts +97 -0
  42. package/dist/billing/repository.d.ts.map +1 -0
  43. package/dist/billing/repository.js +167 -0
  44. package/dist/billing/repository.js.map +1 -0
  45. package/dist/cjs/audit/audit.js +31 -0
  46. package/dist/cjs/audit/audit.js.map +1 -0
  47. package/dist/cjs/audit/repository.js +32 -0
  48. package/dist/cjs/audit/repository.js.map +1 -0
  49. package/dist/cjs/auth/password-reset.js +58 -0
  50. package/dist/cjs/auth/password-reset.js.map +1 -0
  51. package/dist/cjs/auth/password.js +32 -0
  52. package/dist/cjs/auth/password.js.map +1 -0
  53. package/dist/cjs/auth/service.js +191 -0
  54. package/dist/cjs/auth/service.js.map +1 -0
  55. package/dist/cjs/auth/session-token.js +18 -0
  56. package/dist/cjs/auth/session-token.js.map +1 -0
  57. package/dist/cjs/billing/dunning.js +54 -0
  58. package/dist/cjs/billing/dunning.js.map +1 -0
  59. package/dist/cjs/billing/provider/asaas.js +125 -0
  60. package/dist/cjs/billing/provider/asaas.js.map +1 -0
  61. package/dist/cjs/billing/provider/manual.js +28 -0
  62. package/dist/cjs/billing/provider/manual.js.map +1 -0
  63. package/dist/cjs/billing/provider/registry.js +27 -0
  64. package/dist/cjs/billing/provider/registry.js.map +1 -0
  65. package/dist/cjs/billing/provider/types.js +10 -0
  66. package/dist/cjs/billing/provider/types.js.map +1 -0
  67. package/dist/cjs/billing/receivable.js +3 -0
  68. package/dist/cjs/billing/receivable.js.map +1 -0
  69. package/dist/cjs/billing/repository.js +180 -0
  70. package/dist/cjs/billing/repository.js.map +1 -0
  71. package/dist/cjs/clients/repository.js +123 -0
  72. package/dist/cjs/clients/repository.js.map +1 -0
  73. package/dist/cjs/commercial-catalog/catalog.js +108 -0
  74. package/dist/cjs/commercial-catalog/catalog.js.map +1 -0
  75. package/dist/cjs/commercial-catalog/consumption-contracts.js +10 -0
  76. package/dist/cjs/commercial-catalog/consumption-contracts.js.map +1 -0
  77. package/dist/cjs/commercial-catalog/coupon.js +53 -0
  78. package/dist/cjs/commercial-catalog/coupon.js.map +1 -0
  79. package/dist/cjs/commercial-catalog/index.js +16 -0
  80. package/dist/cjs/commercial-catalog/index.js.map +1 -0
  81. package/dist/cjs/commercial-catalog/offer-application.js +53 -0
  82. package/dist/cjs/commercial-catalog/offer-application.js.map +1 -0
  83. package/dist/cjs/commercial-catalog/types.js +26 -0
  84. package/dist/cjs/commercial-catalog/types.js.map +1 -0
  85. package/dist/cjs/crm/repository.js +195 -0
  86. package/dist/cjs/crm/repository.js.map +1 -0
  87. package/dist/cjs/dashboard/repository.js +39 -0
  88. package/dist/cjs/dashboard/repository.js.map +1 -0
  89. package/dist/cjs/db/client.js +27 -0
  90. package/dist/cjs/db/client.js.map +1 -0
  91. package/dist/cjs/db/context.js +43 -0
  92. package/dist/cjs/db/context.js.map +1 -0
  93. package/dist/cjs/db/load-env.js +47 -0
  94. package/dist/cjs/db/load-env.js.map +1 -0
  95. package/dist/cjs/db/seed.js +69 -0
  96. package/dist/cjs/db/seed.js.map +1 -0
  97. package/dist/cjs/entitlements/decision.js +203 -0
  98. package/dist/cjs/entitlements/decision.js.map +1 -0
  99. package/dist/cjs/entitlements/derive.js +46 -0
  100. package/dist/cjs/entitlements/derive.js.map +1 -0
  101. package/dist/cjs/entitlements/index.js +23 -0
  102. package/dist/cjs/entitlements/index.js.map +1 -0
  103. package/dist/cjs/entitlements/sources.js +60 -0
  104. package/dist/cjs/entitlements/sources.js.map +1 -0
  105. package/dist/cjs/entitlements/standing.js +42 -0
  106. package/dist/cjs/entitlements/standing.js.map +1 -0
  107. package/dist/cjs/entitlements/types.js +16 -0
  108. package/dist/cjs/entitlements/types.js.map +1 -0
  109. package/dist/cjs/import/clients-import.js +147 -0
  110. package/dist/cjs/import/clients-import.js.map +1 -0
  111. package/dist/cjs/index.js +335 -0
  112. package/dist/cjs/index.js.map +1 -0
  113. package/dist/cjs/mail/dedup-store.js +56 -0
  114. package/dist/cjs/mail/dedup-store.js.map +1 -0
  115. package/dist/cjs/mail/index.js +24 -0
  116. package/dist/cjs/mail/index.js.map +1 -0
  117. package/dist/cjs/mail/provider.js +251 -0
  118. package/dist/cjs/mail/provider.js.map +1 -0
  119. package/dist/cjs/mail/resend-client.js +103 -0
  120. package/dist/cjs/mail/resend-client.js.map +1 -0
  121. package/dist/cjs/mail/resend.js +21 -0
  122. package/dist/cjs/mail/resend.js.map +1 -0
  123. package/dist/cjs/mail/sanitize.js +56 -0
  124. package/dist/cjs/mail/sanitize.js.map +1 -0
  125. package/dist/cjs/mail/sender-resolver.js +15 -0
  126. package/dist/cjs/mail/sender-resolver.js.map +1 -0
  127. package/dist/cjs/mail/template-renderer.js +21 -0
  128. package/dist/cjs/mail/template-renderer.js.map +1 -0
  129. package/dist/cjs/mail/types.js +38 -0
  130. package/dist/cjs/mail/types.js.map +1 -0
  131. package/dist/cjs/notifications/repository.js +49 -0
  132. package/dist/cjs/notifications/repository.js.map +1 -0
  133. package/dist/cjs/onboarding/domain.js +370 -0
  134. package/dist/cjs/onboarding/domain.js.map +1 -0
  135. package/dist/cjs/onboarding/interaction-review.js +425 -0
  136. package/dist/cjs/onboarding/interaction-review.js.map +1 -0
  137. package/dist/cjs/onboarding/persistence.js +444 -0
  138. package/dist/cjs/onboarding/persistence.js.map +1 -0
  139. package/dist/cjs/onboarding/portal-actions.js +157 -0
  140. package/dist/cjs/onboarding/portal-actions.js.map +1 -0
  141. package/dist/cjs/onboarding/portal.js +177 -0
  142. package/dist/cjs/onboarding/portal.js.map +1 -0
  143. package/dist/cjs/package.json +3 -0
  144. package/dist/cjs/portal/repository.js +37 -0
  145. package/dist/cjs/portal/repository.js.map +1 -0
  146. package/dist/cjs/pricing-engine/bundle.js +49 -0
  147. package/dist/cjs/pricing-engine/bundle.js.map +1 -0
  148. package/dist/cjs/pricing-engine/errors.js +48 -0
  149. package/dist/cjs/pricing-engine/errors.js.map +1 -0
  150. package/dist/cjs/pricing-engine/index.js +30 -0
  151. package/dist/cjs/pricing-engine/index.js.map +1 -0
  152. package/dist/cjs/pricing-engine/money.js +69 -0
  153. package/dist/cjs/pricing-engine/money.js.map +1 -0
  154. package/dist/cjs/pricing-engine/pipeline.js +330 -0
  155. package/dist/cjs/pricing-engine/pipeline.js.map +1 -0
  156. package/dist/cjs/pricing-engine/proration.js +53 -0
  157. package/dist/cjs/pricing-engine/proration.js.map +1 -0
  158. package/dist/cjs/pricing-engine/shadow.js +58 -0
  159. package/dist/cjs/pricing-engine/shadow.js.map +1 -0
  160. package/dist/cjs/pricing-engine/types.js +34 -0
  161. package/dist/cjs/pricing-engine/types.js.map +1 -0
  162. package/dist/cjs/products/catalog.js +10 -0
  163. package/dist/cjs/products/catalog.js.map +1 -0
  164. package/dist/cjs/products/repository.js +61 -0
  165. package/dist/cjs/products/repository.js.map +1 -0
  166. package/dist/cjs/rbac/authorize.js +36 -0
  167. package/dist/cjs/rbac/authorize.js.map +1 -0
  168. package/dist/cjs/rbac/catalog.js +455 -0
  169. package/dist/cjs/rbac/catalog.js.map +1 -0
  170. package/dist/cjs/requests/repository.js +114 -0
  171. package/dist/cjs/requests/repository.js.map +1 -0
  172. package/dist/cjs/subscription-billing/dunning.js +26 -0
  173. package/dist/cjs/subscription-billing/dunning.js.map +1 -0
  174. package/dist/cjs/subscription-billing/events.js +45 -0
  175. package/dist/cjs/subscription-billing/events.js.map +1 -0
  176. package/dist/cjs/subscription-billing/index.js +62 -0
  177. package/dist/cjs/subscription-billing/index.js.map +1 -0
  178. package/dist/cjs/subscription-billing/plans.js +88 -0
  179. package/dist/cjs/subscription-billing/plans.js.map +1 -0
  180. package/dist/cjs/subscription-billing/product-config.js +54 -0
  181. package/dist/cjs/subscription-billing/product-config.js.map +1 -0
  182. package/dist/cjs/subscription-billing/provider/asaas.js +233 -0
  183. package/dist/cjs/subscription-billing/provider/asaas.js.map +1 -0
  184. package/dist/cjs/subscription-billing/provider/disabled.js +46 -0
  185. package/dist/cjs/subscription-billing/provider/disabled.js.map +1 -0
  186. package/dist/cjs/subscription-billing/provider/registry.js +29 -0
  187. package/dist/cjs/subscription-billing/provider/registry.js.map +1 -0
  188. package/dist/cjs/subscription-billing/reconcile.js +198 -0
  189. package/dist/cjs/subscription-billing/reconcile.js.map +1 -0
  190. package/dist/cjs/subscription-billing/repository.js +359 -0
  191. package/dist/cjs/subscription-billing/repository.js.map +1 -0
  192. package/dist/cjs/subscription-billing/service.js +838 -0
  193. package/dist/cjs/subscription-billing/service.js.map +1 -0
  194. package/dist/cjs/subscription-billing/types.js +28 -0
  195. package/dist/cjs/subscription-billing/types.js.map +1 -0
  196. package/dist/cjs/tenancy/provisioning.js +126 -0
  197. package/dist/cjs/tenancy/provisioning.js.map +1 -0
  198. package/dist/cjs/users/repository.js +83 -0
  199. package/dist/cjs/users/repository.js.map +1 -0
  200. package/dist/cjs/vault/crypto.js +94 -0
  201. package/dist/cjs/vault/crypto.js.map +1 -0
  202. package/dist/cjs/vault/repository.js +120 -0
  203. package/dist/cjs/vault/repository.js.map +1 -0
  204. package/dist/commercial-catalog/catalog.d.ts +27 -0
  205. package/dist/commercial-catalog/catalog.d.ts.map +1 -0
  206. package/dist/commercial-catalog/catalog.js +104 -0
  207. package/dist/commercial-catalog/catalog.js.map +1 -0
  208. package/dist/commercial-catalog/consumption-contracts.d.ts +33 -0
  209. package/dist/commercial-catalog/consumption-contracts.d.ts.map +1 -0
  210. package/dist/commercial-catalog/consumption-contracts.js +9 -0
  211. package/dist/commercial-catalog/consumption-contracts.js.map +1 -0
  212. package/dist/commercial-catalog/coupon.d.ts +27 -0
  213. package/dist/commercial-catalog/coupon.d.ts.map +1 -0
  214. package/dist/commercial-catalog/coupon.js +47 -0
  215. package/dist/commercial-catalog/coupon.js.map +1 -0
  216. package/dist/commercial-catalog/index.d.ts +10 -0
  217. package/dist/commercial-catalog/index.d.ts.map +1 -0
  218. package/dist/commercial-catalog/index.js +5 -0
  219. package/dist/commercial-catalog/index.js.map +1 -0
  220. package/dist/commercial-catalog/offer-application.d.ts +39 -0
  221. package/dist/commercial-catalog/offer-application.d.ts.map +1 -0
  222. package/dist/commercial-catalog/offer-application.js +50 -0
  223. package/dist/commercial-catalog/offer-application.js.map +1 -0
  224. package/dist/commercial-catalog/types.d.ts +155 -0
  225. package/dist/commercial-catalog/types.d.ts.map +1 -0
  226. package/dist/commercial-catalog/types.js +23 -0
  227. package/dist/commercial-catalog/types.js.map +1 -0
  228. package/dist/crm/repository.d.ts +109 -0
  229. package/dist/crm/repository.d.ts.map +1 -0
  230. package/dist/crm/repository.js +181 -0
  231. package/dist/crm/repository.js.map +1 -0
  232. package/dist/db/context.d.ts +2 -0
  233. package/dist/db/context.d.ts.map +1 -1
  234. package/dist/db/context.js +3 -0
  235. package/dist/db/context.js.map +1 -1
  236. package/dist/db/migrate.js +7 -2
  237. package/dist/db/migrate.js.map +1 -1
  238. package/dist/db/seed.js +4 -1
  239. package/dist/db/seed.js.map +1 -1
  240. package/dist/entitlements/decision.d.ts +86 -0
  241. package/dist/entitlements/decision.d.ts.map +1 -0
  242. package/dist/entitlements/decision.js +196 -0
  243. package/dist/entitlements/decision.js.map +1 -0
  244. package/dist/entitlements/derive.d.ts +30 -0
  245. package/dist/entitlements/derive.d.ts.map +1 -0
  246. package/dist/entitlements/derive.js +42 -0
  247. package/dist/entitlements/derive.js.map +1 -0
  248. package/dist/entitlements/index.d.ts +10 -0
  249. package/dist/entitlements/index.d.ts.map +1 -0
  250. package/dist/entitlements/index.js +7 -0
  251. package/dist/entitlements/index.js.map +1 -0
  252. package/dist/entitlements/sources.d.ts +30 -0
  253. package/dist/entitlements/sources.d.ts.map +1 -0
  254. package/dist/entitlements/sources.js +55 -0
  255. package/dist/entitlements/sources.js.map +1 -0
  256. package/dist/entitlements/standing.d.ts +22 -0
  257. package/dist/entitlements/standing.d.ts.map +1 -0
  258. package/dist/entitlements/standing.js +38 -0
  259. package/dist/entitlements/standing.js.map +1 -0
  260. package/dist/entitlements/types.d.ts +26 -0
  261. package/dist/entitlements/types.d.ts.map +1 -0
  262. package/dist/entitlements/types.js +15 -0
  263. package/dist/entitlements/types.js.map +1 -0
  264. package/dist/index.d.ts +74 -3
  265. package/dist/index.d.ts.map +1 -1
  266. package/dist/index.js +51 -4
  267. package/dist/index.js.map +1 -1
  268. package/dist/mail/dedup-store.d.ts +25 -0
  269. package/dist/mail/dedup-store.d.ts.map +1 -0
  270. package/dist/mail/dedup-store.js +51 -0
  271. package/dist/mail/dedup-store.js.map +1 -0
  272. package/dist/mail/index.d.ts +10 -0
  273. package/dist/mail/index.d.ts.map +1 -0
  274. package/dist/mail/index.js +9 -0
  275. package/dist/mail/index.js.map +1 -0
  276. package/dist/mail/provider.d.ts +31 -0
  277. package/dist/mail/provider.d.ts.map +1 -0
  278. package/dist/mail/provider.js +248 -0
  279. package/dist/mail/provider.js.map +1 -0
  280. package/dist/mail/resend-client.d.ts +16 -0
  281. package/dist/mail/resend-client.d.ts.map +1 -0
  282. package/dist/mail/resend-client.js +100 -0
  283. package/dist/mail/resend-client.js.map +1 -0
  284. package/dist/mail/resend.d.ts.map +1 -1
  285. package/dist/mail/resend.js +2 -0
  286. package/dist/mail/resend.js.map +1 -1
  287. package/dist/mail/sanitize.d.ts +17 -0
  288. package/dist/mail/sanitize.d.ts.map +1 -0
  289. package/dist/mail/sanitize.js +51 -0
  290. package/dist/mail/sanitize.js.map +1 -0
  291. package/dist/mail/sender-resolver.d.ts +8 -0
  292. package/dist/mail/sender-resolver.d.ts.map +1 -0
  293. package/dist/mail/sender-resolver.js +11 -0
  294. package/dist/mail/sender-resolver.js.map +1 -0
  295. package/dist/mail/template-renderer.d.ts +10 -0
  296. package/dist/mail/template-renderer.d.ts.map +1 -0
  297. package/dist/mail/template-renderer.js +17 -0
  298. package/dist/mail/template-renderer.js.map +1 -0
  299. package/dist/mail/types.d.ts +143 -0
  300. package/dist/mail/types.d.ts.map +1 -0
  301. package/dist/mail/types.js +34 -0
  302. package/dist/mail/types.js.map +1 -0
  303. package/dist/onboarding/domain.d.ts +146 -0
  304. package/dist/onboarding/domain.d.ts.map +1 -0
  305. package/dist/onboarding/domain.js +348 -0
  306. package/dist/onboarding/domain.js.map +1 -0
  307. package/dist/onboarding/interaction-review.d.ts +123 -0
  308. package/dist/onboarding/interaction-review.d.ts.map +1 -0
  309. package/dist/onboarding/interaction-review.js +413 -0
  310. package/dist/onboarding/interaction-review.js.map +1 -0
  311. package/dist/onboarding/persistence.d.ts +106 -0
  312. package/dist/onboarding/persistence.d.ts.map +1 -0
  313. package/dist/onboarding/persistence.js +430 -0
  314. package/dist/onboarding/persistence.js.map +1 -0
  315. package/dist/onboarding/portal-actions.d.ts +48 -0
  316. package/dist/onboarding/portal-actions.d.ts.map +1 -0
  317. package/dist/onboarding/portal-actions.js +153 -0
  318. package/dist/onboarding/portal-actions.js.map +1 -0
  319. package/dist/onboarding/portal.d.ts +58 -0
  320. package/dist/onboarding/portal.d.ts.map +1 -0
  321. package/dist/onboarding/portal.js +170 -0
  322. package/dist/onboarding/portal.js.map +1 -0
  323. package/dist/pricing-engine/bundle.d.ts +22 -0
  324. package/dist/pricing-engine/bundle.d.ts.map +1 -0
  325. package/dist/pricing-engine/bundle.js +44 -0
  326. package/dist/pricing-engine/bundle.js.map +1 -0
  327. package/dist/pricing-engine/errors.d.ts +32 -0
  328. package/dist/pricing-engine/errors.d.ts.map +1 -0
  329. package/dist/pricing-engine/errors.js +44 -0
  330. package/dist/pricing-engine/errors.js.map +1 -0
  331. package/dist/pricing-engine/index.d.ts +12 -0
  332. package/dist/pricing-engine/index.d.ts.map +1 -0
  333. package/dist/pricing-engine/index.js +8 -0
  334. package/dist/pricing-engine/index.js.map +1 -0
  335. package/dist/pricing-engine/money.d.ts +32 -0
  336. package/dist/pricing-engine/money.d.ts.map +1 -0
  337. package/dist/pricing-engine/money.js +58 -0
  338. package/dist/pricing-engine/money.js.map +1 -0
  339. package/dist/pricing-engine/pipeline.d.ts +3 -0
  340. package/dist/pricing-engine/pipeline.d.ts.map +1 -0
  341. package/dist/pricing-engine/pipeline.js +327 -0
  342. package/dist/pricing-engine/pipeline.js.map +1 -0
  343. package/dist/pricing-engine/proration.d.ts +3 -0
  344. package/dist/pricing-engine/proration.d.ts.map +1 -0
  345. package/dist/pricing-engine/proration.js +50 -0
  346. package/dist/pricing-engine/proration.js.map +1 -0
  347. package/dist/pricing-engine/shadow.d.ts +51 -0
  348. package/dist/pricing-engine/shadow.d.ts.map +1 -0
  349. package/dist/pricing-engine/shadow.js +54 -0
  350. package/dist/pricing-engine/shadow.js.map +1 -0
  351. package/dist/pricing-engine/types.d.ts +184 -0
  352. package/dist/pricing-engine/types.d.ts.map +1 -0
  353. package/dist/pricing-engine/types.js +31 -0
  354. package/dist/pricing-engine/types.js.map +1 -0
  355. package/dist/rbac/authorize.d.ts +3 -6
  356. package/dist/rbac/authorize.d.ts.map +1 -1
  357. package/dist/rbac/authorize.js +4 -8
  358. package/dist/rbac/authorize.js.map +1 -1
  359. package/dist/rbac/catalog.d.ts.map +1 -1
  360. package/dist/rbac/catalog.js +414 -21
  361. package/dist/rbac/catalog.js.map +1 -1
  362. package/dist/subscription-billing/dunning.d.ts +18 -0
  363. package/dist/subscription-billing/dunning.d.ts.map +1 -0
  364. package/dist/subscription-billing/dunning.js +28 -0
  365. package/dist/subscription-billing/dunning.js.map +1 -0
  366. package/dist/subscription-billing/events.d.ts +58 -0
  367. package/dist/subscription-billing/events.d.ts.map +1 -0
  368. package/dist/subscription-billing/events.js +38 -0
  369. package/dist/subscription-billing/events.js.map +1 -0
  370. package/dist/subscription-billing/index.d.ts +18 -0
  371. package/dist/subscription-billing/index.d.ts.map +1 -0
  372. package/dist/subscription-billing/index.js +16 -0
  373. package/dist/subscription-billing/index.js.map +1 -0
  374. package/dist/subscription-billing/plans.d.ts +34 -0
  375. package/dist/subscription-billing/plans.d.ts.map +1 -0
  376. package/dist/subscription-billing/plans.js +85 -0
  377. package/dist/subscription-billing/plans.js.map +1 -0
  378. package/dist/subscription-billing/product-config.d.ts +61 -0
  379. package/dist/subscription-billing/product-config.d.ts.map +1 -0
  380. package/dist/subscription-billing/product-config.js +50 -0
  381. package/dist/subscription-billing/product-config.js.map +1 -0
  382. package/dist/subscription-billing/provider/asaas.d.ts +26 -0
  383. package/dist/subscription-billing/provider/asaas.d.ts.map +1 -0
  384. package/dist/subscription-billing/provider/asaas.js +229 -0
  385. package/dist/subscription-billing/provider/asaas.js.map +1 -0
  386. package/dist/subscription-billing/provider/disabled.d.ts +30 -0
  387. package/dist/subscription-billing/provider/disabled.d.ts.map +1 -0
  388. package/dist/subscription-billing/provider/disabled.js +42 -0
  389. package/dist/subscription-billing/provider/disabled.js.map +1 -0
  390. package/dist/subscription-billing/provider/registry.d.ts +10 -0
  391. package/dist/subscription-billing/provider/registry.d.ts.map +1 -0
  392. package/dist/subscription-billing/provider/registry.js +26 -0
  393. package/dist/subscription-billing/provider/registry.js.map +1 -0
  394. package/dist/subscription-billing/reconcile.d.ts +45 -0
  395. package/dist/subscription-billing/reconcile.d.ts.map +1 -0
  396. package/dist/subscription-billing/reconcile.js +192 -0
  397. package/dist/subscription-billing/reconcile.js.map +1 -0
  398. package/dist/subscription-billing/repository.d.ts +168 -0
  399. package/dist/subscription-billing/repository.d.ts.map +1 -0
  400. package/dist/subscription-billing/repository.js +336 -0
  401. package/dist/subscription-billing/repository.js.map +1 -0
  402. package/dist/subscription-billing/service.d.ts +175 -0
  403. package/dist/subscription-billing/service.d.ts.map +1 -0
  404. package/dist/subscription-billing/service.js +818 -0
  405. package/dist/subscription-billing/service.js.map +1 -0
  406. package/dist/subscription-billing/types.d.ts +161 -0
  407. package/dist/subscription-billing/types.d.ts.map +1 -0
  408. package/dist/subscription-billing/types.js +24 -0
  409. package/dist/subscription-billing/types.js.map +1 -0
  410. package/dist/vault/crypto.d.ts +22 -0
  411. package/dist/vault/crypto.d.ts.map +1 -0
  412. package/dist/vault/crypto.js +88 -0
  413. package/dist/vault/crypto.js.map +1 -0
  414. package/dist/vault/repository.d.ts +45 -0
  415. package/dist/vault/repository.d.ts.map +1 -0
  416. package/dist/vault/repository.js +111 -0
  417. package/dist/vault/repository.js.map +1 -0
  418. package/package.json +23 -13
  419. package/src/db/migrations/0012_vault.sql +51 -0
  420. package/src/db/migrations/0013_crm.sql +67 -0
  421. package/src/db/migrations/0014_billing.sql +146 -0
  422. package/src/db/migrations/0015_merchant_billing_ar.sql +201 -0
  423. package/src/db/migrations/0016_office_onboarding.sql +162 -0
  424. package/src/db/migrations/0017_office_onboarding_api.sql +21 -0
  425. package/src/db/migrations/0018_office_onboarding_portal_actions.sql +60 -0
  426. package/src/db/migrations/0019_office_onboarding_interaction_review.sql +134 -0
  427. package/src/db/migrations/0020_saas_billing.sql +157 -0
  428. package/src/db/migrations/0021_saas_billing_indexes.sql +23 -0
  429. package/src/db/migrations/0022_saas_billing_multi_product.sql +75 -0
@@ -0,0 +1,838 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ensureSaasPlanCatalog = exports.getPlan = exports.listPublicPlans = void 0;
4
+ exports.seedSaasPlans = seedSaasPlans;
5
+ exports.signupSelfService = signupSelfService;
6
+ exports.standingFor = standingFor;
7
+ exports.entitlementsForPlan = entitlementsForPlan;
8
+ exports.subscriptionOverview = subscriptionOverview;
9
+ exports.startCheckout = startCheckout;
10
+ exports.processWebhook = processWebhook;
11
+ exports.changePlanForTenant = changePlanForTenant;
12
+ exports.cancelForTenant = cancelForTenant;
13
+ exports.reactivateForTenant = reactivateForTenant;
14
+ exports.backofficeListSubscriptions = backofficeListSubscriptions;
15
+ exports.backofficeOverview = backofficeOverview;
16
+ exports.backofficeListWebhookEvents = backofficeListWebhookEvents;
17
+ exports.backofficeActivateSubscription = backofficeActivateSubscription;
18
+ exports.retryFailedWebhookEvents = retryFailedWebhookEvents;
19
+ exports.runBillingCron = runBillingCron;
20
+ const shared_1 = require("@baruk/shared");
21
+ const context_js_1 = require("../db/context.js");
22
+ const audit_js_1 = require("../audit/audit.js");
23
+ const password_js_1 = require("../auth/password.js");
24
+ const provisioning_js_1 = require("../tenancy/provisioning.js");
25
+ const repository_js_1 = require("../products/repository.js");
26
+ const derive_js_1 = require("../entitlements/derive.js");
27
+ const resend_js_1 = require("../mail/resend.js");
28
+ const reconcile_js_1 = require("./reconcile.js");
29
+ const dunning_js_1 = require("./dunning.js");
30
+ const registry_js_1 = require("./provider/registry.js");
31
+ const repository_js_2 = require("./repository.js");
32
+ Object.defineProperty(exports, "ensureSaasPlanCatalog", { enumerable: true, get: function () { return repository_js_2.ensureSaasPlanCatalog; } });
33
+ Object.defineProperty(exports, "getPlan", { enumerable: true, get: function () { return repository_js_2.getPlan; } });
34
+ Object.defineProperty(exports, "listPublicPlans", { enumerable: true, get: function () { return repository_js_2.listPublicPlans; } });
35
+ const plans_js_1 = require("./plans.js");
36
+ const product_config_js_1 = require("./product-config.js");
37
+ function asRegistry(source) {
38
+ return 'resolve' in source ? source : (0, product_config_js_1.singleProductRegistry)(source);
39
+ }
40
+ function providerFor(cfg) {
41
+ return (0, registry_js_1.resolveSubscriptionBillingProvider)(cfg.provider, {
42
+ apiKey: cfg.apiKey,
43
+ webhookSecret: cfg.webhookSecret,
44
+ mode: cfg.mode,
45
+ baseUrl: cfg.baseUrl,
46
+ fetchImpl: cfg.fetchImpl,
47
+ });
48
+ }
49
+ function todayOf(nowIso) {
50
+ return nowIso.slice(0, 10);
51
+ }
52
+ function addDays(nowIso, days) {
53
+ const d = new Date(nowIso);
54
+ d.setUTCDate(d.getUTCDate() + days);
55
+ return d.toISOString().slice(0, 10);
56
+ }
57
+ function addMonths(nowIso, months) {
58
+ const d = new Date(nowIso);
59
+ d.setUTCMonth(d.getUTCMonth() + months);
60
+ return d.toISOString().slice(0, 10);
61
+ }
62
+ function daysBetween(fromDate, toDate) {
63
+ return Math.floor((new Date(`${toDate}T00:00:00Z`).getTime() - new Date(`${fromDate}T00:00:00Z`).getTime()) /
64
+ 86_400_000);
65
+ }
66
+ /** Mapeia o status da assinatura para o status do tenant (suspensão é decisão do cron). */
67
+ function tenantStatusFor(status) {
68
+ switch (status) {
69
+ case 'trialing':
70
+ return 'trial';
71
+ case 'active':
72
+ return 'active';
73
+ case 'past_due':
74
+ return 'active'; // acesso segue no grace period; cron decide suspender
75
+ case 'canceled':
76
+ return 'churned';
77
+ default:
78
+ return null;
79
+ }
80
+ }
81
+ async function syncTenantStatus(tx, tenantId, status) {
82
+ const next = tenantStatusFor(status);
83
+ if (!next)
84
+ return;
85
+ await tx `UPDATE tenant SET status = ${next}, updated_at = now() WHERE id = ${tenantId} AND status <> 'suspended'`;
86
+ }
87
+ /** Seed do catálogo padrão (chamado pelo seed do core). */
88
+ async function seedSaasPlans(db) {
89
+ await (0, repository_js_1.ensureProductCatalog)(db);
90
+ await (0, repository_js_2.ensureSaasPlanCatalog)(db, plans_js_1.SAAS_PLANS);
91
+ }
92
+ function slugify(name) {
93
+ return name
94
+ .normalize('NFD')
95
+ .replace(/[\u0300-\u036f]/g, '')
96
+ .toLowerCase()
97
+ .replace(/[^a-z0-9]+/g, '-')
98
+ .replace(/^-+|-+$/g, '')
99
+ .slice(0, 40);
100
+ }
101
+ /**
102
+ * Cadastro self-service (Fase 17): cria usuário + tenant em TRIAL + organização +
103
+ * membership com papel `owner` + entitlement do produto + assinatura `trialing`.
104
+ * Idempotência: e-mail já cadastrado → 409 (nunca sobrescreve credencial existente).
105
+ */
106
+ async function signupSelfService(db, input, nowIso) {
107
+ const email = input.email.trim().toLowerCase();
108
+ if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email))
109
+ throw new shared_1.ValidationError('E-mail inválido');
110
+ if (input.password.length < 8)
111
+ throw new shared_1.ValidationError('Senha deve ter ao menos 8 caracteres');
112
+ if (!input.companyName.trim())
113
+ throw new shared_1.ValidationError('Nome da empresa é obrigatório');
114
+ const plan = await (0, repository_js_2.getPlan)(db, input.planKey);
115
+ if (!plan || !plan.isActive || !plan.isPublic) {
116
+ throw new shared_1.ValidationError(`Plano indisponível: ${input.planKey}`);
117
+ }
118
+ const existing = await db `
119
+ SELECT id FROM "user" WHERE email = ${email} AND deleted_at IS NULL
120
+ `;
121
+ if (existing[0]) {
122
+ throw new shared_1.ConflictError('Já existe uma conta com este e-mail. Faça login para continuar.');
123
+ }
124
+ // Código único do tenant a partir do nome (sufixo em caso de colisão).
125
+ const base = slugify(input.companyName) || 'escritorio';
126
+ let code = base;
127
+ for (let i = 2;; i += 1) {
128
+ const clash = await db `
129
+ SELECT id FROM tenant WHERE lower(code) = lower(${code}) AND deleted_at IS NULL
130
+ `;
131
+ if (!clash[0])
132
+ break;
133
+ code = `${base}-${i}`;
134
+ if (i > 50) {
135
+ code = `${base}-${(0, shared_1.uuidv7)().slice(-6)}`;
136
+ break;
137
+ }
138
+ }
139
+ // INSERT puro (não upsert): o papel de app pode CRIAR credencial, nunca
140
+ // sobrescrever (REVOKE UPDATE em user_credential — anti account-takeover).
141
+ // A checagem de e-mail acima já garante que o usuário é novo.
142
+ const userRows = await db `
143
+ INSERT INTO "user" (id, email, full_name, user_type, auth_provider)
144
+ VALUES (${(0, shared_1.uuidv7)()}, ${email}, ${input.fullName.trim()}, 'staff', 'password')
145
+ RETURNING id
146
+ `;
147
+ const user = { id: userRows[0].id, email };
148
+ await db `
149
+ INSERT INTO user_credential (user_id, password_hash)
150
+ VALUES (${user.id}, ${(0, password_js_1.hashPassword)(input.password)})
151
+ `;
152
+ const tenantId = await (0, provisioning_js_1.ensureTenant)(db, {
153
+ code,
154
+ displayName: input.companyName.trim(),
155
+ status: 'trial',
156
+ primaryProduct: 'contabil',
157
+ });
158
+ await (0, provisioning_js_1.ensureOrganization)(db, tenantId, input.companyName.trim());
159
+ await (0, provisioning_js_1.ensureMembership)(db, tenantId, user.id);
160
+ await (0, provisioning_js_1.assignSystemRole)(db, tenantId, user.id, 'owner');
161
+ await (0, repository_js_1.ensureProductCatalog)(db);
162
+ await (0, repository_js_1.ensureTenantProduct)(db, tenantId, 'contabil');
163
+ const trialEndsAt = addDays(nowIso, plan.trialDays > 0 ? plan.trialDays : 14);
164
+ const { subscription, domainEvents } = (0, reconcile_js_1.startTrial)(tenantId, plan.key, trialEndsAt, nowIso);
165
+ await (0, context_js_1.withContext)(db, { tenantId, userId: user.id }, async (tx) => {
166
+ const row = await (0, repository_js_2.upsertSubscription)(tx, { tenantId, planKey: plan.key, subscription });
167
+ await (0, repository_js_2.appendDomainEvents)(tx, row.rowId, domainEvents);
168
+ await (0, audit_js_1.appendAudit)(tx, {
169
+ actorId: user.id,
170
+ action: 'signup',
171
+ resourceType: 'saas_subscription',
172
+ resourceId: row.rowId,
173
+ metadata: { planKey: plan.key, trialEndsAt },
174
+ ip: input.ip ?? null,
175
+ userAgent: input.userAgent ?? null,
176
+ });
177
+ });
178
+ return { tenantId, tenantCode: code, userId: user.id, planKey: plan.key, trialEndsAt };
179
+ }
180
+ function standingFor(sub) {
181
+ if (!sub)
182
+ return { ok: false, reason: 'none' };
183
+ switch (sub.status) {
184
+ case 'active':
185
+ return { ok: true, reason: 'ok' };
186
+ case 'trialing':
187
+ return { ok: true, reason: 'trialing' };
188
+ case 'past_due':
189
+ return { ok: false, reason: 'past_due' };
190
+ case 'paused':
191
+ return { ok: false, reason: 'paused' };
192
+ default:
193
+ return { ok: false, reason: 'canceled' };
194
+ }
195
+ }
196
+ /** Entitlements finos derivados do plano (EntitlementSource 'PLAN', v0.1 stateless). */
197
+ function entitlementsForPlan(sub, plan, nowIso) {
198
+ if (!sub || !plan)
199
+ return [];
200
+ const standing = standingFor(sub);
201
+ const targetGrants = standing.ok
202
+ ? plan.modules.map((moduleCode) => ({ moduleCode, source: 'PLAN', expiresAt: null }))
203
+ : [];
204
+ return (0, derive_js_1.deriveEntitlements)({
205
+ tenantId: sub.tenantId,
206
+ subscriptionId: sub.subscriptionId,
207
+ targetGrants,
208
+ current: [],
209
+ now: nowIso,
210
+ });
211
+ }
212
+ async function subscriptionOverview(db, ctx, nowIso) {
213
+ const { sub, invoices, events } = await (0, context_js_1.withContext)(db, ctx, async (tx) => {
214
+ const sub0 = await (0, repository_js_2.getSubscription)(tx);
215
+ return {
216
+ sub: sub0,
217
+ invoices: sub0 ? await (0, repository_js_2.listInvoices)(tx) : [],
218
+ events: sub0 ? await (0, repository_js_2.listBillingEvents)(tx, 20) : [],
219
+ };
220
+ });
221
+ // Fora da transação: getPlan usa outra conexão do pool — dentro dela seria
222
+ // um segundo checkout de conexão com transação aberta (deadlock de pool).
223
+ const plan = sub ? await (0, repository_js_2.getPlan)(db, sub.planKey) : null;
224
+ return {
225
+ subscription: sub,
226
+ plan,
227
+ standing: standingFor(sub),
228
+ invoices,
229
+ entitlements: entitlementsForPlan(sub, plan, nowIso),
230
+ events,
231
+ };
232
+ }
233
+ /**
234
+ * Inicia o checkout do plano no gateway da plataforma. A atribuição do webhook
235
+ * de volta ao tenant usa `externalReference = "<tenantId>:<planKey>"` (RFC §9).
236
+ * Em modo 'disabled' não há gateway: o fluxo comercial segue e a ativação é
237
+ * manual (backoffice) — nunca cobra por falta de configuração.
238
+ */
239
+ async function startCheckout(db, ctx, source, planKey, _nowIso, productKey = plans_js_1.DEFAULT_PRODUCT_KEY) {
240
+ const plan = await (0, repository_js_2.getPlan)(db, planKey, productKey);
241
+ if (!plan || !plan.isActive || !plan.isPublic) {
242
+ throw new shared_1.ValidationError(`Plano indisponível: ${planKey}`);
243
+ }
244
+ if (plan.periodicity !== 'monthly') {
245
+ // O checkout certificado cria link RECURRENT MENSAL; vender outra
246
+ // periodicidade cobraria errado. Suporte a ciclo anual é v0.2.
247
+ throw new shared_1.ValidationError('Este plano ainda não está disponível para contratação online.');
248
+ }
249
+ // A conta de gateway e a URL de retorno saem do PRODUTO do plano — nunca de
250
+ // uma configuração global. É isso que impede o checkout de um produto voltar
251
+ // para o domínio de outro, ou cobrar na conta errada.
252
+ const cfg = asRegistry(source).resolve({ productKey: plan.productKey, tenantId: ctx.tenantId });
253
+ const provider = providerFor(cfg);
254
+ if (provider.mode === 'disabled') {
255
+ return { mode: 'disabled', provider: provider.name, url: null };
256
+ }
257
+ // 1) Leitura curta (transação própria) — nada de rede com transação aberta.
258
+ const snapshot = await (0, context_js_1.withContext)(db, ctx, async (tx) => {
259
+ const tenantRows = await tx `
260
+ SELECT display_name FROM tenant WHERE id = ${ctx.tenantId}
261
+ `;
262
+ const userRows = await tx `
263
+ SELECT email FROM "user" WHERE id = ${ctx.userId}
264
+ `;
265
+ const sub = await (0, repository_js_2.getSubscription)(tx);
266
+ return {
267
+ tenantName: tenantRows[0]?.display_name ?? 'Tenant Baruk',
268
+ email: userRows[0]?.email ?? null,
269
+ existingCustomerId: sub?.providerCustomerId ?? null,
270
+ subRowId: sub?.rowId ?? null,
271
+ };
272
+ });
273
+ // 2) Gateway (até ~24s de rede) — sem transação e sem conexão do pool presa.
274
+ const customer = snapshot.existingCustomerId
275
+ ? { externalId: snapshot.existingCustomerId }
276
+ : await provider.ensureCustomer({
277
+ tenantId: ctx.tenantId,
278
+ name: snapshot.tenantName,
279
+ email: snapshot.email,
280
+ });
281
+ const checkout = await provider.createCheckout({
282
+ tenantId: ctx.tenantId,
283
+ planKey: plan.key,
284
+ amountBRL: plan.amountCents / 100,
285
+ customerExternalId: customer.externalId,
286
+ returnUrl: `${cfg.webBaseUrl}/checkout/retorno`,
287
+ description: `Assinatura Baruk — plano ${plan.name}`,
288
+ });
289
+ // 3) Persistência curta: SOMENTE as referências de provider (nunca o estado
290
+ // da assinatura — um webhook pode tê-la mudado durante a chamada de rede).
291
+ await (0, context_js_1.withContext)(db, ctx, async (tx) => {
292
+ await (0, repository_js_2.lockTenantBilling)(tx, ctx.tenantId);
293
+ await (0, repository_js_2.setSubscriptionProviderRefs)(tx, ctx.tenantId, provider.name, customer.externalId);
294
+ await (0, audit_js_1.appendAudit)(tx, {
295
+ actorId: ctx.userId,
296
+ action: 'checkout_started',
297
+ resourceType: 'saas_subscription',
298
+ resourceId: snapshot.subRowId,
299
+ metadata: { planKey: plan.key, provider: provider.name, mode: provider.mode },
300
+ });
301
+ });
302
+ return { mode: provider.mode, provider: provider.name, url: checkout.url };
303
+ }
304
+ /**
305
+ * Lê o `externalReference` que voltou do gateway.
306
+ *
307
+ * Formato canônico: `"<tenantId>:<productKey>:<planKey>"`. O formato antigo de
308
+ * duas partes (`"<tenantId>:<planKey>"`) continua aceito para os payment links
309
+ * emitidos antes do multi-produto — nesses casos o produto é o Contábil, o
310
+ * único que existia. Sem o produto explícito, um evento de um produto poderia
311
+ * reconciliar a assinatura de outro no mesmo tenant.
312
+ */
313
+ function parseAttribution(event) {
314
+ const ref = event.externalReference;
315
+ if (!ref)
316
+ return null;
317
+ const parts = ref.split(':');
318
+ if (parts.length === 3) {
319
+ const [tenantId, productKey, planKey] = parts;
320
+ if (!tenantId || !productKey || !planKey)
321
+ return null;
322
+ return { tenantId, productKey, planKey };
323
+ }
324
+ if (parts.length === 2) {
325
+ const [tenantId, planKey] = parts;
326
+ if (!tenantId || !planKey)
327
+ return null;
328
+ return { tenantId, productKey: plans_js_1.DEFAULT_PRODUCT_KEY, planKey };
329
+ }
330
+ return null;
331
+ }
332
+ /**
333
+ * Processa um webhook do gateway: verifica autenticidade → dedup por
334
+ * `externalEventId` → resolve tenant via `externalReference` → reconcilia via
335
+ * domínio puro → espelha fatura → sincroniza tenant.status → audita.
336
+ * A rota SEMPRE responde 200 após o dedup (regra dos 15 não-200 do ASAAS).
337
+ */
338
+ async function processWebhook(db, source, headers, rawBody, payload, nowIso, productKey = plans_js_1.DEFAULT_PRODUCT_KEY) {
339
+ // O segredo do webhook é da CONTA do produto. Como a verificação acontece
340
+ // antes de qualquer parsing, o produto tem de vir de fora — na prática, da
341
+ // própria URL cadastrada no gateway (uma por conta).
342
+ const cfg = asRegistry(source).resolve({ productKey });
343
+ const provider = providerFor(cfg);
344
+ if (!provider.verifyWebhook(headers, rawBody)) {
345
+ return { outcome: 'unauthorized' };
346
+ }
347
+ const event = provider.parseWebhookEvent(payload);
348
+ if (!event)
349
+ return { outcome: 'ignored' };
350
+ const webhookId = await (0, repository_js_2.insertWebhookEvent)(db, {
351
+ provider: provider.name,
352
+ externalEventId: event.externalEventId,
353
+ eventType: event.eventType,
354
+ payload,
355
+ });
356
+ if (!webhookId)
357
+ return { outcome: 'duplicate' };
358
+ return reconcileStoredWebhookEvent(db, provider, event, webhookId, nowIso, productKey);
359
+ }
360
+ /**
361
+ * Reconcilia um evento JÁ persistido em `saas_webhook_event` (caminho comum do
362
+ * webhook inbound e do reprocessamento de eventos `failed` pelo cron).
363
+ */
364
+ async function reconcileStoredWebhookEvent(db, provider, event, webhookId, nowIso, expectedProductKey = plans_js_1.DEFAULT_PRODUCT_KEY) {
365
+ const attribution = parseAttribution(event);
366
+ if (!attribution) {
367
+ await (0, repository_js_2.markWebhookEvent)(db, webhookId, 'ignored', null, 'externalReference ausente/inválida');
368
+ return { outcome: 'ignored' };
369
+ }
370
+ // ISOLAMENTO FINANCEIRO: a conta que assinou o webhook é a conta de UM
371
+ // produto. Se o evento alega pertencer a outro, alguém configurou a URL
372
+ // errada no gateway — ou é tentativa de mover dinheiro entre produtos.
373
+ // Recusar é obrigatório: reconciliar aqui escreveria na assinatura errada.
374
+ if (attribution.productKey !== expectedProductKey) {
375
+ await (0, repository_js_2.markWebhookEvent)(db, webhookId, 'ignored', null, `produto divergente: evento=${attribution.productKey} conta=${expectedProductKey}`);
376
+ return { outcome: 'ignored' };
377
+ }
378
+ try {
379
+ const plan = await (0, repository_js_2.getPlan)(db, attribution.planKey, attribution.productKey);
380
+ const { tenantId } = attribution;
381
+ const applied = await (0, context_js_1.withContext)(db, { tenantId }, async (tx) => {
382
+ await (0, repository_js_2.lockTenantBilling)(tx, tenantId);
383
+ const current = await (0, repository_js_2.getSubscription)(tx);
384
+ // Guarda de ordenação (webhook atrasado): um OVERDUE que chega DEPOIS do
385
+ // pagamento do mesmo ciclo (período corrente ainda vigente) é obsoleto —
386
+ // aplicá-lo marcaria past_due uma assinatura em dia. Política de
387
+ // orquestração; o domínio puro certificado permanece intocado.
388
+ if (event.invoice?.status === 'overdue' &&
389
+ current?.status === 'active' &&
390
+ current.currentPeriodEnd != null &&
391
+ current.currentPeriodEnd >= todayOf(nowIso)) {
392
+ return 'overdue obsoleto — período corrente já pago';
393
+ }
394
+ // Sem plano conhecido E sem assinatura existente não há o que reconciliar —
395
+ // persistir criaria uma linha órfã com plano arbitrário.
396
+ if (!plan && !current) {
397
+ return `plano desconhecido (${attribution.planKey}) sem assinatura existente`;
398
+ }
399
+ // Guarda anti-ressurreição: pagamento ANTERIOR ao cancelamento não desfaz
400
+ // um cancelamento explícito — reativação exige checkout novo.
401
+ if (current?.status === 'canceled' &&
402
+ current.canceledAt != null &&
403
+ (event.invoice?.status === 'received' || event.invoice?.status === 'confirmed') &&
404
+ event.invoice.paidAt != null &&
405
+ new Date(event.invoice.paidAt).getTime() < new Date(current.canceledAt).getTime()) {
406
+ return 'pagamento anterior ao cancelamento — reativação exige novo checkout';
407
+ }
408
+ // Atribuição de plano: em assinatura ATIVA o plano vem do estado interno
409
+ // (fonte de verdade) — payment link antigo com externalReference de plano
410
+ // anterior NÃO reverte um upgrade. Em ativação (trial/dormente), vale o
411
+ // plano efetivamente pago (externalReference).
412
+ const effectivePlanKey = current?.status === 'active' ? current.planKey : (plan?.key ?? null);
413
+ const { subscription, domainEvents } = (0, reconcile_js_1.applySubscriptionEvent)({
414
+ current,
415
+ tenantId,
416
+ planKey: effectivePlanKey,
417
+ event,
418
+ now: nowIso,
419
+ });
420
+ // Evento informativo (ex.: PENDING) para tenant sem assinatura: nada a
421
+ // persistir — gravar o placeholder criaria uma assinatura 'canceled'
422
+ // fantasma e marcaria o tenant como churned.
423
+ if (!current && domainEvents.length === 0) {
424
+ return 'evento sem transição para tenant sem assinatura';
425
+ }
426
+ let next = subscription;
427
+ // Backfill: past_due nascido de trial não tem currentPeriodEnd — a régua
428
+ // ancora no fim do trial (senão ficaria em limbo, sem régua nem suspensão).
429
+ if (next.status === 'past_due' && next.currentPeriodEnd == null) {
430
+ next = { ...next, currentPeriodEnd: current?.trialEndsAt ?? todayOf(nowIso) };
431
+ }
432
+ // Periodicidade: o domínio v0.1 renova sempre +1 mês; planos não mensais
433
+ // (ativação manual/backoffice) seguem o ciclo do plano.
434
+ if (next.status === 'active' &&
435
+ plan &&
436
+ plan.periodicity !== 'monthly' &&
437
+ next.currentPeriodEnd != null) {
438
+ next = {
439
+ ...next,
440
+ currentPeriodEnd: addMonths(nowIso, plan.periodicity === 'yearly' ? 12 : 3),
441
+ };
442
+ }
443
+ const row = await (0, repository_js_2.upsertSubscription)(tx, {
444
+ tenantId,
445
+ planKey: effectivePlanKey ?? current?.planKey ?? next.items[0]?.planKey ?? 'starter',
446
+ subscription: next,
447
+ provider: provider.name,
448
+ providerCustomerId: event.externalCustomerId ?? null,
449
+ providerSubscriptionId: event.subscription?.externalId ?? null,
450
+ });
451
+ await (0, repository_js_2.appendDomainEvents)(tx, row.rowId, domainEvents);
452
+ if (event.invoice) {
453
+ await (0, repository_js_2.upsertInvoice)(tx, {
454
+ tenantId,
455
+ subscriptionRowId: row.rowId,
456
+ provider: provider.name,
457
+ externalId: event.invoice.externalId,
458
+ status: event.invoice.status,
459
+ amountCents: Math.abs(Math.round((event.invoice.value ?? 0) * 100)),
460
+ dueDate: event.invoice.dueDate ?? null,
461
+ paidAt: event.invoice.paidAt ?? null,
462
+ invoiceUrl: event.invoice.invoiceUrl ?? null,
463
+ description: event.invoice.description ?? null,
464
+ });
465
+ }
466
+ await syncTenantStatus(tx, tenantId, next.status);
467
+ await (0, audit_js_1.appendAudit)(tx, {
468
+ actorType: 'system',
469
+ action: `webhook_${event.eventType}`,
470
+ resourceType: 'saas_subscription',
471
+ resourceId: row.rowId,
472
+ metadata: {
473
+ provider: provider.name,
474
+ externalEventId: event.externalEventId,
475
+ status: next.status,
476
+ },
477
+ });
478
+ return null;
479
+ });
480
+ if (applied !== null) {
481
+ await (0, repository_js_2.markWebhookEvent)(db, webhookId, 'ignored', attribution.tenantId, applied);
482
+ return { outcome: 'ignored', tenantId: attribution.tenantId };
483
+ }
484
+ await (0, repository_js_2.markWebhookEvent)(db, webhookId, 'processed', attribution.tenantId);
485
+ return { outcome: 'processed', tenantId: attribution.tenantId };
486
+ }
487
+ catch (err) {
488
+ await (0, repository_js_2.markWebhookEvent)(db, webhookId, 'failed', attribution.tenantId, err instanceof Error ? err.message : String(err));
489
+ return { outcome: 'failed', tenantId: attribution.tenantId };
490
+ }
491
+ }
492
+ // ── Gestão da assinatura (tenant) ────────────────────────────────────────────
493
+ async function changePlanForTenant(db, ctx, newPlanKey, nowIso) {
494
+ const plan = await (0, repository_js_2.getPlan)(db, newPlanKey);
495
+ if (!plan || !plan.isActive)
496
+ throw new shared_1.ValidationError(`Plano desconhecido: ${newPlanKey}`);
497
+ return (0, context_js_1.withContext)(db, ctx, async (tx) => {
498
+ await (0, repository_js_2.lockTenantBilling)(tx, ctx.tenantId);
499
+ const current = await (0, repository_js_2.getSubscription)(tx);
500
+ if (!current)
501
+ throw new shared_1.NotFoundError('Nenhuma assinatura para este tenant');
502
+ if (current.status === 'canceled') {
503
+ throw new shared_1.ValidationError('Assinatura cancelada — reative antes de trocar de plano.');
504
+ }
505
+ if (current.planKey === plan.key)
506
+ return current;
507
+ const { subscription, domainEvents } = (0, reconcile_js_1.changePlan)(current, plan.key, nowIso);
508
+ const row = await (0, repository_js_2.upsertSubscription)(tx, {
509
+ tenantId: ctx.tenantId,
510
+ planKey: plan.key,
511
+ subscription,
512
+ });
513
+ await (0, repository_js_2.appendDomainEvents)(tx, row.rowId, domainEvents);
514
+ await (0, audit_js_1.appendAudit)(tx, {
515
+ actorId: ctx.userId,
516
+ action: 'plan_changed',
517
+ resourceType: 'saas_subscription',
518
+ resourceId: row.rowId,
519
+ diff: { from: current.planKey, to: plan.key },
520
+ });
521
+ return row;
522
+ });
523
+ }
524
+ async function cancelForTenant(db, ctx, nowIso) {
525
+ return (0, context_js_1.withContext)(db, ctx, async (tx) => {
526
+ await (0, repository_js_2.lockTenantBilling)(tx, ctx.tenantId);
527
+ const current = await (0, repository_js_2.getSubscription)(tx);
528
+ if (!current)
529
+ throw new shared_1.NotFoundError('Nenhuma assinatura para este tenant');
530
+ if (current.status === 'canceled')
531
+ return current;
532
+ const { subscription, domainEvents } = (0, reconcile_js_1.cancelSubscription)(current, nowIso);
533
+ const row = await (0, repository_js_2.upsertSubscription)(tx, {
534
+ tenantId: ctx.tenantId,
535
+ planKey: current.planKey,
536
+ subscription,
537
+ });
538
+ await (0, repository_js_2.appendDomainEvents)(tx, row.rowId, domainEvents);
539
+ await syncTenantStatus(tx, ctx.tenantId, subscription.status);
540
+ await (0, audit_js_1.appendAudit)(tx, {
541
+ actorId: ctx.userId,
542
+ action: 'subscription_canceled',
543
+ resourceType: 'saas_subscription',
544
+ resourceId: row.rowId,
545
+ });
546
+ return row;
547
+ });
548
+ }
549
+ /**
550
+ * Reativação: assinatura cancelada/past_due volta pelo PAGAMENTO (checkout →
551
+ * webhook `received` reativa via reconcile). Este método só valida o estado e
552
+ * devolve o checkout; em modo 'disabled' a reativação é manual (backoffice).
553
+ */
554
+ async function reactivateForTenant(db, ctx, source, nowIso, productKey = plans_js_1.DEFAULT_PRODUCT_KEY) {
555
+ const sub = await (0, context_js_1.withContext)(db, ctx, (tx) => (0, repository_js_2.getSubscription)(tx, productKey));
556
+ if (!sub)
557
+ throw new shared_1.NotFoundError('Nenhuma assinatura para este tenant');
558
+ if (sub.status === 'active' || sub.status === 'trialing') {
559
+ throw new shared_1.ValidationError('Assinatura já está ativa.');
560
+ }
561
+ return startCheckout(db, ctx, source, sub.planKey, nowIso, sub.productKey);
562
+ }
563
+ // ── Backoffice (operadora) ───────────────────────────────────────────────────
564
+ async function backofficeListSubscriptions(db, filter = {}) {
565
+ return (0, repository_js_2.withOperatorContext)(db, (tx) => (0, repository_js_2.listAllSubscriptions)(tx, filter));
566
+ }
567
+ async function backofficeOverview(db) {
568
+ return (0, repository_js_2.withOperatorContext)(db, (tx) => (0, repository_js_2.backofficeSummary)(tx));
569
+ }
570
+ async function backofficeListWebhookEvents(db, limit = 100) {
571
+ const rows = await db `
572
+ SELECT id, tenant_id, provider, external_event_id, event_type, status, error, processed_at, created_at
573
+ FROM saas_webhook_event ORDER BY created_at DESC LIMIT ${limit}
574
+ `;
575
+ return rows.map((r) => ({
576
+ id: r.id,
577
+ tenantId: r.tenant_id ?? null,
578
+ provider: r.provider,
579
+ externalEventId: r.external_event_id,
580
+ eventType: r.event_type,
581
+ status: r.status,
582
+ error: r.error ?? null,
583
+ processedAt: r.processed_at ? new Date(r.processed_at).toISOString() : null,
584
+ createdAt: new Date(r.created_at).toISOString(),
585
+ }));
586
+ }
587
+ /**
588
+ * Ativação manual pela operadora (modo 'disabled' ou pagamento fora do gateway):
589
+ * simula uma fatura `received` e reconcilia pelo MESMO caminho do webhook —
590
+ * nenhuma transição de estado fora do domínio puro.
591
+ */
592
+ async function backofficeActivateSubscription(db, operatorEmail, tenantId, planKey, nowIso) {
593
+ const plan = await (0, repository_js_2.getPlan)(db, planKey);
594
+ if (!plan)
595
+ throw new shared_1.ValidationError(`Plano desconhecido: ${planKey}`);
596
+ return (0, context_js_1.withContext)(db, { tenantId }, async (tx) => {
597
+ await (0, repository_js_2.lockTenantBilling)(tx, tenantId);
598
+ const current = await (0, repository_js_2.getSubscription)(tx);
599
+ const manualEvent = {
600
+ externalEventId: `manual_${(0, shared_1.uuidv7)()}`,
601
+ eventType: 'manual.activation',
602
+ kind: 'invoice',
603
+ invoice: {
604
+ externalId: `manual_${(0, shared_1.uuidv7)()}`,
605
+ status: 'received',
606
+ value: plan.amountCents / 100,
607
+ paidAt: nowIso,
608
+ description: `Ativação manual (${operatorEmail})`,
609
+ },
610
+ externalReference: `${tenantId}:${plan.productKey ?? plans_js_1.DEFAULT_PRODUCT_KEY}:${plan.key}`,
611
+ };
612
+ const { subscription, domainEvents } = (0, reconcile_js_1.applySubscriptionEvent)({
613
+ current,
614
+ tenantId,
615
+ planKey: plan.key,
616
+ event: manualEvent,
617
+ now: nowIso,
618
+ });
619
+ const row = await (0, repository_js_2.upsertSubscription)(tx, {
620
+ tenantId,
621
+ planKey: plan.key,
622
+ subscription,
623
+ provider: 'manual',
624
+ });
625
+ await (0, repository_js_2.appendDomainEvents)(tx, row.rowId, domainEvents);
626
+ await (0, repository_js_2.upsertInvoice)(tx, {
627
+ tenantId,
628
+ subscriptionRowId: row.rowId,
629
+ provider: 'manual',
630
+ externalId: manualEvent.invoice.externalId,
631
+ status: 'received',
632
+ amountCents: plan.amountCents,
633
+ paidAt: nowIso,
634
+ description: manualEvent.invoice.description ?? null,
635
+ });
636
+ await syncTenantStatus(tx, tenantId, subscription.status);
637
+ await (0, audit_js_1.appendAudit)(tx, {
638
+ actorType: 'user',
639
+ action: 'backoffice_manual_activation',
640
+ resourceType: 'saas_subscription',
641
+ resourceId: row.rowId,
642
+ metadata: { operatorEmail, planKey: plan.key },
643
+ });
644
+ return row;
645
+ });
646
+ }
647
+ /**
648
+ * Reprocessa eventos de webhook `failed` recentes (rede de segurança para o
649
+ * caso "falhou após o dedup e respondemos 200"). Idempotente: reconciliar um
650
+ * efeito já refletido é no-op de domínio; o passo é chamado pelo cron.
651
+ */
652
+ async function retryFailedWebhookEvents(db, source, nowIso, limit = 25, productKey = plans_js_1.DEFAULT_PRODUCT_KEY) {
653
+ const cfg = asRegistry(source).resolve({ productKey });
654
+ const provider = providerFor(cfg);
655
+ const failed = await (0, repository_js_2.listFailedWebhookEvents)(db, limit);
656
+ let recovered = 0;
657
+ for (const row of failed) {
658
+ if (row.provider !== provider.name)
659
+ continue;
660
+ const event = provider.parseWebhookEvent(row.payload);
661
+ if (!event) {
662
+ await (0, repository_js_2.markWebhookEvent)(db, row.id, 'ignored', null, 'payload não parseável no reprocessamento');
663
+ continue;
664
+ }
665
+ const result = await reconcileStoredWebhookEvent(db, provider, event, row.id, nowIso, productKey);
666
+ if (result.outcome === 'processed' || result.outcome === 'ignored')
667
+ recovered += 1;
668
+ }
669
+ return { retried: failed.length, recovered };
670
+ }
671
+ async function ownerEmailForTenant(tx, tenantId) {
672
+ const rows = await tx `
673
+ SELECT u.email FROM membership m
674
+ JOIN "user" u ON u.id = m.user_id
675
+ WHERE m.tenant_id = ${tenantId} AND m.deleted_at IS NULL AND m.status = 'active'
676
+ ORDER BY m.created_at ASC LIMIT 1
677
+ `;
678
+ return rows[0]?.email ?? null;
679
+ }
680
+ /**
681
+ * Executor de política temporal — idempotente, seguro para rodar a cada hora
682
+ * (Render Cron). Trial vencido → `past_due`; período vencido → `past_due`;
683
+ * `past_due` → régua de inadimplência (stepKey idempotente) + e-mail best-effort;
684
+ * atraso ≥ `suspendAfterDays` → tenant `suspended`.
685
+ */
686
+ async function runBillingCron(db, source, nowIso) {
687
+ const registry = asRegistry(source);
688
+ const today = todayOf(nowIso);
689
+ const report = {
690
+ examined: 0,
691
+ trialsExpired: 0,
692
+ periodsExpired: 0,
693
+ dunningStepsExecuted: 0,
694
+ tenantsSuspended: 0,
695
+ emailsSent: 0,
696
+ webhooksRetried: 0,
697
+ webhooksRecovered: 0,
698
+ };
699
+ // Rede de segurança: reprocessa webhooks que falharam após o dedup/200.
700
+ // Um passe por produto configurado — cada conta de gateway tem a sua fila.
701
+ for (const productKey of registry.productKeys()) {
702
+ const retry = await retryFailedWebhookEvents(db, registry, nowIso, 25, productKey);
703
+ report.webhooksRetried += retry.retried;
704
+ report.webhooksRecovered += retry.recovered;
705
+ }
706
+ const subs = await (0, repository_js_2.withOperatorContext)(db, (tx) => (0, repository_js_2.listSubscriptionsForCron)(tx, today));
707
+ report.examined = subs.length;
708
+ for (const sub of subs) {
709
+ // Isolamento de falha: um tenant quebrado não pode derrubar a régua dos demais.
710
+ try {
711
+ let dunningEmailTo = null;
712
+ // Política de suspensão, remetente e base pública são DO PRODUTO desta
713
+ // assinatura — nunca de uma configuração global compartilhada.
714
+ const cfg = registry.resolve({ productKey: sub.productKey, tenantId: sub.tenantId });
715
+ await (0, context_js_1.withContext)(db, { tenantId: sub.tenantId }, async (tx) => {
716
+ await (0, repository_js_2.lockTenantBilling)(tx, sub.tenantId);
717
+ const loaded = await (0, repository_js_2.getSubscription)(tx, sub.productKey);
718
+ if (!loaded)
719
+ return; // sumiu entre o scan e o lock (ex.: tenant removido)
720
+ let current = loaded;
721
+ // 1. Trial vencido ou período vencido → past_due (evento canônico).
722
+ const trialExpired = current.status === 'trialing' &&
723
+ current.trialEndsAt != null &&
724
+ current.trialEndsAt < today;
725
+ const periodExpired = current.status === 'active' &&
726
+ current.currentPeriodEnd != null &&
727
+ current.currentPeriodEnd < today;
728
+ if (trialExpired || periodExpired) {
729
+ const overdueEvent = {
730
+ externalEventId: `cron_${today}_${current.subscriptionId}_overdue`,
731
+ eventType: trialExpired ? 'cron.trial_expired' : 'cron.period_expired',
732
+ kind: 'invoice',
733
+ invoice: {
734
+ externalId: `cron_${today}_${current.subscriptionId}`,
735
+ status: 'overdue',
736
+ value: 0,
737
+ },
738
+ externalReference: `${current.tenantId}:${current.productKey}:${current.planKey}`,
739
+ };
740
+ // Trial não tem `current` past_due-ável no reconcile? Tem: overdue exige assinatura existente — ok.
741
+ const { subscription, domainEvents } = (0, reconcile_js_1.applySubscriptionEvent)({
742
+ current,
743
+ tenantId: current.tenantId,
744
+ planKey: current.planKey,
745
+ event: overdueEvent,
746
+ now: nowIso,
747
+ });
748
+ if (subscription.status === 'past_due' && current.status !== 'past_due') {
749
+ if (trialExpired)
750
+ report.trialsExpired += 1;
751
+ else
752
+ report.periodsExpired += 1;
753
+ // Referência da régua: fim do trial ou do período.
754
+ const reference = current.trialEndsAt ?? current.currentPeriodEnd ?? today;
755
+ const next = {
756
+ ...subscription,
757
+ currentPeriodEnd: subscription.currentPeriodEnd ?? reference,
758
+ };
759
+ const row = await (0, repository_js_2.upsertSubscription)(tx, {
760
+ tenantId: current.tenantId,
761
+ planKey: current.planKey,
762
+ subscription: next,
763
+ });
764
+ await (0, repository_js_2.appendDomainEvents)(tx, row.rowId, domainEvents);
765
+ await (0, audit_js_1.appendAudit)(tx, {
766
+ actorType: 'automation',
767
+ action: trialExpired ? 'trial_expired' : 'period_expired',
768
+ resourceType: 'saas_subscription',
769
+ resourceId: row.rowId,
770
+ });
771
+ current = (await (0, repository_js_2.getSubscription)(tx));
772
+ }
773
+ }
774
+ // 2. Régua de inadimplência (idempotente por stepKey + reference_date).
775
+ if (current.status === 'past_due') {
776
+ const reference = current.currentPeriodEnd ?? current.trialEndsAt ?? today;
777
+ const executed = await (0, repository_js_2.listExecutedDunningSteps)(tx, current.rowId, reference);
778
+ const events = (0, dunning_js_1.dunningStepsAsEvents)({ ...current, currentPeriodEnd: reference }, today, executed);
779
+ for (const ev of events) {
780
+ const stepKey = ev.data.dunningStep?.stepKey ??
781
+ `step_${ev.sequence}`;
782
+ await (0, repository_js_2.recordDunningStep)(tx, {
783
+ tenantId: current.tenantId,
784
+ subscriptionRowId: current.rowId,
785
+ stepKey,
786
+ referenceDate: reference,
787
+ });
788
+ report.dunningStepsExecuted += 1;
789
+ }
790
+ if (events.length > 0) {
791
+ await (0, repository_js_2.appendDomainEvents)(tx, current.rowId, events);
792
+ dunningEmailTo = await ownerEmailForTenant(tx, current.tenantId);
793
+ await (0, repository_js_2.upsertSubscription)(tx, {
794
+ tenantId: current.tenantId,
795
+ planKey: current.planKey,
796
+ subscription: { ...current, sequence: current.sequence + events.length },
797
+ });
798
+ }
799
+ // 3. Suspensão automática após o grace period.
800
+ const overdueDays = daysBetween(reference, today);
801
+ if (overdueDays >= cfg.suspendAfterDays) {
802
+ const updated = await tx `
803
+ UPDATE tenant SET status = 'suspended', updated_at = now()
804
+ WHERE id = ${current.tenantId} AND status <> 'suspended'
805
+ `;
806
+ if (updated.count > 0) {
807
+ report.tenantsSuspended += 1;
808
+ await (0, audit_js_1.appendAudit)(tx, {
809
+ actorType: 'automation',
810
+ action: 'tenant_suspended_overdue',
811
+ resourceType: 'tenant',
812
+ resourceId: current.tenantId,
813
+ metadata: { overdueDays },
814
+ });
815
+ }
816
+ }
817
+ }
818
+ });
819
+ // Envio FORA da transação (best-effort com timeout): rede nunca segura o banco.
820
+ if (dunningEmailTo) {
821
+ const sent = await (0, resend_js_1.sendEmail)(cfg.resendApiKey, {
822
+ from: cfg.emailFrom,
823
+ to: dunningEmailTo,
824
+ subject: 'Sua assinatura Baruk está com pagamento pendente',
825
+ html: `<p>Olá,</p><p>Identificamos uma pendência na assinatura da sua conta Baruk.</p><p>Regularize para manter seus serviços ativos: <a href="${cfg.webBaseUrl}/assinatura">gerenciar assinatura</a>.</p><p>Se o pagamento já foi feito, desconsidere este aviso.</p>`,
826
+ });
827
+ if (sent)
828
+ report.emailsSent += 1;
829
+ }
830
+ }
831
+ catch (err) {
832
+ // Registrado e seguimos: a assinatura problemática fica para a próxima execução.
833
+ console.error(`billing-cron: falha ao processar tenant ${sub.tenantId}:`, err instanceof Error ? err.message : err);
834
+ }
835
+ }
836
+ return report;
837
+ }
838
+ //# sourceMappingURL=service.js.map