alepha 0.19.2 → 0.19.4

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 (241) hide show
  1. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  2. package/dist/api/audits/index.d.ts +8 -8
  3. package/dist/api/invitations/index.d.ts +790 -0
  4. package/dist/api/invitations/index.d.ts.map +1 -0
  5. package/dist/api/invitations/index.js +665 -0
  6. package/dist/api/invitations/index.js.map +1 -0
  7. package/dist/api/jobs/index.browser.js +8 -9
  8. package/dist/api/jobs/index.browser.js.map +1 -1
  9. package/dist/api/jobs/index.d.ts +90 -34
  10. package/dist/api/jobs/index.d.ts.map +1 -1
  11. package/dist/api/jobs/index.js +267 -44
  12. package/dist/api/jobs/index.js.map +1 -1
  13. package/dist/api/notifications/index.browser.js +0 -1
  14. package/dist/api/notifications/index.browser.js.map +1 -1
  15. package/dist/api/notifications/index.d.ts +3 -3
  16. package/dist/api/notifications/index.d.ts.map +1 -1
  17. package/dist/api/notifications/index.js +0 -1
  18. package/dist/api/notifications/index.js.map +1 -1
  19. package/dist/api/parameters/index.browser.js +112 -1
  20. package/dist/api/parameters/index.browser.js.map +1 -1
  21. package/dist/api/parameters/index.d.ts +90 -3
  22. package/dist/api/parameters/index.d.ts.map +1 -1
  23. package/dist/api/parameters/index.js +79 -12
  24. package/dist/api/parameters/index.js.map +1 -1
  25. package/dist/{billing → api/payments}/index.d.ts +67 -49
  26. package/dist/api/payments/index.d.ts.map +1 -0
  27. package/dist/{billing → api/payments}/index.js +108 -74
  28. package/dist/api/payments/index.js.map +1 -0
  29. package/dist/api/subscriptions/index.d.ts +1692 -0
  30. package/dist/api/subscriptions/index.d.ts.map +1 -0
  31. package/dist/api/subscriptions/index.js +1870 -0
  32. package/dist/api/subscriptions/index.js.map +1 -0
  33. package/dist/api/users/index.d.ts +27 -21
  34. package/dist/api/users/index.d.ts.map +1 -1
  35. package/dist/api/users/index.js +167 -34
  36. package/dist/api/users/index.js.map +1 -1
  37. package/dist/api/workflows/index.browser.js +246 -0
  38. package/dist/api/workflows/index.browser.js.map +1 -0
  39. package/dist/api/workflows/index.d.ts +1618 -0
  40. package/dist/api/workflows/index.d.ts.map +1 -0
  41. package/dist/api/workflows/index.js +1504 -0
  42. package/dist/api/workflows/index.js.map +1 -0
  43. package/dist/cli/config/index.d.ts +6 -28
  44. package/dist/cli/config/index.d.ts.map +1 -1
  45. package/dist/cli/config/index.js +5 -10
  46. package/dist/cli/config/index.js.map +1 -1
  47. package/dist/cli/core/index.d.ts +11669 -208
  48. package/dist/cli/core/index.d.ts.map +1 -1
  49. package/dist/cli/core/index.js +60 -69
  50. package/dist/cli/core/index.js.map +1 -1
  51. package/dist/cli/devtools/index.d.ts +5 -0
  52. package/dist/cli/devtools/index.d.ts.map +1 -1
  53. package/dist/cli/devtools/index.js +4 -0
  54. package/dist/cli/devtools/index.js.map +1 -1
  55. package/dist/cli/platform/index.d.ts +69 -64
  56. package/dist/cli/platform/index.d.ts.map +1 -1
  57. package/dist/cli/platform/index.js +6 -2
  58. package/dist/cli/platform/index.js.map +1 -1
  59. package/dist/cli/vendor/index.d.ts +38 -10
  60. package/dist/cli/vendor/index.d.ts.map +1 -1
  61. package/dist/cli/vendor/index.js +85 -26
  62. package/dist/cli/vendor/index.js.map +1 -1
  63. package/dist/core/index.browser.js +21 -2
  64. package/dist/core/index.browser.js.map +1 -1
  65. package/dist/core/index.d.ts +33 -2
  66. package/dist/core/index.d.ts.map +1 -1
  67. package/dist/core/index.js +25 -2
  68. package/dist/core/index.js.map +1 -1
  69. package/dist/core/index.native.js +25 -2
  70. package/dist/core/index.native.js.map +1 -1
  71. package/dist/core/index.workerd.js +25 -2
  72. package/dist/core/index.workerd.js.map +1 -1
  73. package/dist/email/smtp/index.js +24 -8
  74. package/dist/email/smtp/index.js.map +1 -1
  75. package/dist/logger/index.d.ts.map +1 -1
  76. package/dist/logger/index.js +1 -1
  77. package/dist/logger/index.js.map +1 -1
  78. package/dist/orm/core/index.browser.js +0 -18
  79. package/dist/orm/core/index.browser.js.map +1 -1
  80. package/dist/orm/core/index.bun.js +25 -73
  81. package/dist/orm/core/index.bun.js.map +1 -1
  82. package/dist/orm/core/index.d.ts +10 -32
  83. package/dist/orm/core/index.d.ts.map +1 -1
  84. package/dist/orm/core/index.js +25 -73
  85. package/dist/orm/core/index.js.map +1 -1
  86. package/dist/orm/postgres/index.bun.js +3 -3
  87. package/dist/orm/postgres/index.bun.js.map +1 -1
  88. package/dist/orm/postgres/index.d.ts +2 -1
  89. package/dist/orm/postgres/index.d.ts.map +1 -1
  90. package/dist/orm/postgres/index.js +3 -3
  91. package/dist/orm/postgres/index.js.map +1 -1
  92. package/dist/react/router/index.browser.js +25 -3
  93. package/dist/react/router/index.browser.js.map +1 -1
  94. package/dist/react/router/index.d.ts +16 -1
  95. package/dist/react/router/index.d.ts.map +1 -1
  96. package/dist/react/router/index.js +25 -3
  97. package/dist/react/router/index.js.map +1 -1
  98. package/dist/security/index.d.ts +28 -0
  99. package/dist/security/index.d.ts.map +1 -1
  100. package/dist/security/index.js +28 -0
  101. package/dist/security/index.js.map +1 -1
  102. package/package.json +37 -20
  103. package/src/api/invitations/__tests__/InvitationService.spec.ts +439 -0
  104. package/src/api/invitations/controllers/AdminInvitationController.ts +86 -0
  105. package/src/api/invitations/controllers/InvitationController.ts +84 -0
  106. package/src/api/invitations/entities/invitations.ts +33 -0
  107. package/src/api/invitations/index.ts +65 -0
  108. package/src/api/invitations/jobs/InvitationJobs.ts +37 -0
  109. package/src/api/invitations/providers/InvitationProvider.ts +45 -0
  110. package/src/api/invitations/schemas/createInvitationSchema.ts +12 -0
  111. package/src/api/invitations/schemas/invitationConfigAtom.ts +20 -0
  112. package/src/api/invitations/schemas/invitationQuerySchema.ts +15 -0
  113. package/src/api/invitations/schemas/invitationResourceSchema.ts +6 -0
  114. package/src/api/invitations/schemas/invitationWithResourceInfoSchema.ts +22 -0
  115. package/src/api/invitations/schemas/myInvitationsQuerySchema.ts +10 -0
  116. package/src/api/invitations/services/InvitationService.ts +556 -0
  117. package/src/api/jobs/__tests__/$job.spec.ts +876 -0
  118. package/src/api/jobs/controllers/AdminJobController.ts +44 -0
  119. package/src/api/jobs/entities/jobExecutionEntity.ts +0 -2
  120. package/src/api/jobs/index.ts +0 -3
  121. package/src/api/jobs/primitives/$job.ts +22 -11
  122. package/src/api/jobs/providers/JobProvider.ts +239 -25
  123. package/src/api/jobs/schemas/jobConfigAtom.ts +4 -0
  124. package/src/api/jobs/schemas/jobCronInfoSchema.ts +1 -0
  125. package/src/api/jobs/schemas/jobExecutionQuerySchema.ts +0 -1
  126. package/src/api/jobs/schemas/jobQueueDepthSchema.ts +1 -0
  127. package/src/api/jobs/schemas/jobRegistrationSchema.ts +1 -6
  128. package/src/api/jobs/services/JobService.ts +51 -12
  129. package/src/api/notifications/schemas/notificationQuerySchema.ts +0 -1
  130. package/src/api/parameters/__tests__/$parameter.spec.ts +327 -0
  131. package/src/api/parameters/controllers/AdminParameterController.ts +29 -3
  132. package/src/api/parameters/index.browser.ts +12 -0
  133. package/src/api/parameters/primitives/$parameter.ts +20 -3
  134. package/src/api/parameters/services/ParameterProvider.ts +48 -7
  135. package/src/{billing → api/payments}/__tests__/PaymentMethodService.spec.ts +32 -6
  136. package/src/api/payments/__tests__/PaymentService.spec.ts +279 -0
  137. package/src/{billing/controllers/AdminBillingController.ts → api/payments/controllers/AdminPaymentController.ts} +26 -21
  138. package/src/{billing/controllers/BillingController.ts → api/payments/controllers/PaymentController.ts} +23 -11
  139. package/src/{billing → api/payments}/entities/paymentIntents.ts +1 -0
  140. package/src/{billing/errors/BillingError.ts → api/payments/errors/PaymentError.ts} +1 -1
  141. package/src/{billing → api/payments}/index.ts +31 -25
  142. package/src/{billing/providers/MemoryBillingProvider.ts → api/payments/providers/MemoryPaymentProvider.ts} +4 -4
  143. package/src/{billing/providers/BillingProvider.ts → api/payments/providers/PaymentProvider.ts} +9 -2
  144. package/src/{billing → api/payments}/services/PaymentMethodService.ts +5 -5
  145. package/src/{billing/services/BillingService.ts → api/payments/services/PaymentService.ts} +94 -18
  146. package/src/api/subscriptions/__tests__/BillingService.spec.ts +218 -0
  147. package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +278 -0
  148. package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +212 -0
  149. package/src/api/subscriptions/controllers/SubscriptionController.ts +189 -0
  150. package/src/api/subscriptions/entities/subscriptionEvents.ts +54 -0
  151. package/src/api/subscriptions/entities/subscriptions.ts +68 -0
  152. package/src/api/subscriptions/index.ts +144 -0
  153. package/src/api/subscriptions/jobs/SubscriptionJobs.ts +382 -0
  154. package/src/api/subscriptions/middleware/$requireLimit.ts +50 -0
  155. package/src/api/subscriptions/middleware/$requirePlan.ts +49 -0
  156. package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +110 -0
  157. package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +8 -0
  158. package/src/api/subscriptions/schemas/changePlanSchema.ts +9 -0
  159. package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +11 -0
  160. package/src/api/subscriptions/schemas/entitlementsSchema.ts +21 -0
  161. package/src/api/subscriptions/schemas/mrrSchema.ts +13 -0
  162. package/src/api/subscriptions/schemas/planDefinitionSchema.ts +71 -0
  163. package/src/api/subscriptions/schemas/planResourceSchema.ts +25 -0
  164. package/src/api/subscriptions/schemas/subscriptionEventResourceSchema.ts +8 -0
  165. package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +19 -0
  166. package/src/api/subscriptions/schemas/subscriptionResourceSchema.ts +6 -0
  167. package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +32 -0
  168. package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +23 -0
  169. package/src/api/subscriptions/services/BillingService.ts +437 -0
  170. package/src/api/subscriptions/services/SubscriptionConfig.ts +56 -0
  171. package/src/api/subscriptions/services/SubscriptionService.ts +867 -0
  172. package/src/api/subscriptions/services/UsageService.ts +118 -0
  173. package/src/api/users/__tests__/AdminUserController.spec.ts +80 -1
  174. package/src/api/users/__tests__/CredentialService.spec.ts +177 -0
  175. package/src/api/users/__tests__/EmailVerification.spec.ts +29 -18
  176. package/src/api/users/__tests__/PasswordReset.spec.ts +3 -0
  177. package/src/api/users/__tests__/RegistrationService.spec.ts +148 -1
  178. package/src/api/users/__tests__/SessionService.spec.ts +142 -1
  179. package/src/api/users/atoms/realmAuthSettingsAtom.ts +10 -1
  180. package/src/api/users/controllers/UserController.ts +3 -8
  181. package/src/api/users/notifications/UserNotifications.ts +23 -0
  182. package/src/api/users/schemas/loginSchema.ts +1 -1
  183. package/src/api/users/services/CredentialService.ts +51 -4
  184. package/src/api/users/services/RegistrationService.ts +38 -9
  185. package/src/api/users/services/SessionService.ts +62 -9
  186. package/src/api/users/services/UserService.ts +21 -12
  187. package/src/api/workflows/__tests__/$workflow.spec.ts +616 -0
  188. package/src/api/workflows/controllers/AdminWorkflowController.ts +191 -0
  189. package/src/api/workflows/entities/workflowExecutions.ts +74 -0
  190. package/src/api/workflows/entities/workflowStepExecutions.ts +74 -0
  191. package/src/api/workflows/entities/workflowStepLogs.ts +13 -0
  192. package/src/api/workflows/index.browser.ts +22 -0
  193. package/src/api/workflows/index.ts +124 -0
  194. package/src/api/workflows/jobs/WorkflowJobs.ts +77 -0
  195. package/src/api/workflows/primitives/$workflow.ts +202 -0
  196. package/src/api/workflows/providers/WorkflowProvider.ts +1284 -0
  197. package/src/api/workflows/schemas/workflowActivitySchema.ts +15 -0
  198. package/src/api/workflows/schemas/workflowConfigAtom.ts +51 -0
  199. package/src/api/workflows/schemas/workflowExecutionDetailSchema.ts +18 -0
  200. package/src/api/workflows/schemas/workflowExecutionQuerySchema.ts +26 -0
  201. package/src/api/workflows/schemas/workflowExecutionResourceSchema.ts +30 -0
  202. package/src/api/workflows/schemas/workflowRegistrationSchema.ts +26 -0
  203. package/src/api/workflows/schemas/workflowStatsSchema.ts +16 -0
  204. package/src/api/workflows/schemas/workflowStepExecutionResourceSchema.ts +15 -0
  205. package/src/api/workflows/services/WorkflowService.ts +382 -0
  206. package/src/cli/config/defineConfig.ts +17 -46
  207. package/src/cli/core/providers/ViteDevServerProvider.ts +45 -3
  208. package/src/cli/core/services/PackageManagerUtils.ts +3 -1
  209. package/src/cli/core/services/ProjectScaffolder.ts +5 -5
  210. package/src/cli/core/templates/agentMd.ts +14 -5
  211. package/src/cli/core/templates/webAppRouterTs.ts +5 -58
  212. package/src/cli/devtools/index.ts +21 -1
  213. package/src/cli/platform/index.ts +23 -2
  214. package/src/cli/vendor/__tests__/VendorService.spec.ts +283 -178
  215. package/src/cli/vendor/index.ts +20 -3
  216. package/src/cli/vendor/services/VendorService.ts +126 -27
  217. package/src/core/Alepha.ts +10 -0
  218. package/src/core/__tests__/TypeProvider.spec.ts +4 -2
  219. package/src/core/providers/SchemaValidator.ts +1 -1
  220. package/src/core/providers/TypeProvider.ts +46 -3
  221. package/src/logger/index.ts +6 -1
  222. package/src/orm/__tests__/enums.spec.ts +22 -29
  223. package/src/orm/__tests__/orm-showcase-tests.ts +430 -0
  224. package/src/orm/__tests__/orm-showcase.spec.ts +167 -0
  225. package/src/orm/core/providers/DatabaseTypeProvider.ts +0 -29
  226. package/src/orm/core/providers/DrizzleKitProvider.ts +56 -105
  227. package/src/orm/postgres/services/PostgresModelBuilder.ts +3 -6
  228. package/src/react/router/__tests__/$page.browser.spec.tsx +157 -0
  229. package/src/react/router/providers/ReactBrowserProvider.ts +39 -0
  230. package/src/react/router/providers/ReactBrowserRouterProvider.ts +22 -0
  231. package/src/security/__tests__/$secure-combinations.spec.ts +945 -0
  232. package/src/security/primitives/$secure.ts +28 -0
  233. package/tsconfig.base.json +0 -1
  234. package/dist/billing/index.d.ts.map +0 -1
  235. package/dist/billing/index.js.map +0 -1
  236. package/src/billing/__tests__/BillingService.spec.ts +0 -136
  237. /package/src/{billing → api/payments}/entities/paymentMethods.ts +0 -0
  238. /package/src/{billing → api/payments}/entities/refunds.ts +0 -0
  239. /package/src/{billing → api/payments}/schemas/intentSchemas.ts +0 -0
  240. /package/src/{billing → api/payments}/schemas/paymentMethodSchemas.ts +0 -0
  241. /package/src/{billing → api/payments}/schemas/refundSchemas.ts +0 -0
@@ -6,7 +6,7 @@ import { $job } from "alepha/api/jobs";
6
6
  import { DateTimeProvider } from "alepha/datetime";
7
7
  import { $logger } from "alepha/logger";
8
8
  import { randomUUID } from "node:crypto";
9
- //#region ../../src/billing/entities/paymentIntents.ts
9
+ //#region ../../src/api/payments/entities/paymentIntents.ts
10
10
  const paymentIntents = $entity({
11
11
  name: "payment_intents",
12
12
  schema: t.object({
@@ -22,6 +22,7 @@ const paymentIntents = $entity({
22
22
  "processing",
23
23
  "authorized",
24
24
  "captured",
25
+ "partially_refunded",
25
26
  "voided",
26
27
  "failed",
27
28
  "cancelled",
@@ -42,7 +43,7 @@ const paymentIntents = $entity({
42
43
  ]
43
44
  });
44
45
  //#endregion
45
- //#region ../../src/billing/schemas/intentSchemas.ts
46
+ //#region ../../src/api/payments/schemas/intentSchemas.ts
46
47
  const createIntentSchema = t.object({
47
48
  amount: t.integer({ minimum: 1 }),
48
49
  currency: t.text({ size: "short" }),
@@ -74,7 +75,7 @@ const intentQuerySchema = t.extend(pageQuerySchema, {
74
75
  });
75
76
  const intentResourceSchema = paymentIntents.schema;
76
77
  //#endregion
77
- //#region ../../src/billing/entities/refunds.ts
78
+ //#region ../../src/api/payments/entities/refunds.ts
78
79
  const refunds = $entity({
79
80
  name: "refunds",
80
81
  schema: t.object({
@@ -102,23 +103,23 @@ const refunds = $entity({
102
103
  ]
103
104
  });
104
105
  //#endregion
105
- //#region ../../src/billing/schemas/refundSchemas.ts
106
+ //#region ../../src/api/payments/schemas/refundSchemas.ts
106
107
  const refundResourceSchema = refunds.schema;
107
108
  //#endregion
108
- //#region ../../src/billing/errors/BillingError.ts
109
- var BillingError = class extends AlephaError {
109
+ //#region ../../src/api/payments/errors/PaymentError.ts
110
+ var PaymentError = class extends AlephaError {
110
111
  status = 400;
111
112
  };
112
113
  //#endregion
113
- //#region ../../src/billing/providers/BillingProvider.ts
114
- var BillingProvider = class {};
114
+ //#region ../../src/api/payments/providers/PaymentProvider.ts
115
+ var PaymentProvider = class {};
115
116
  //#endregion
116
- //#region ../../src/billing/services/BillingService.ts
117
- var BillingService = class {
117
+ //#region ../../src/api/payments/services/PaymentService.ts
118
+ var PaymentService = class PaymentService {
118
119
  alepha = $inject(Alepha);
119
120
  log = $logger();
120
121
  dateTime = $inject(DateTimeProvider);
121
- provider = $inject(BillingProvider);
122
+ provider = $inject(PaymentProvider);
122
123
  intentRepo = $repository(paymentIntents);
123
124
  refundRepo = $repository(refunds);
124
125
  /**
@@ -150,7 +151,7 @@ var BillingService = class {
150
151
  async createIntent(amount, currency, metadata, options) {
151
152
  return await this.intentRepo.create({
152
153
  amount,
153
- currency,
154
+ currency: currency.toLowerCase(),
154
155
  status: "created",
155
156
  metadata,
156
157
  paymentMethodId: options?.paymentMethodId,
@@ -161,9 +162,11 @@ var BillingService = class {
161
162
  * Create a checkout session with the payment provider and
162
163
  * transition the intent to "processing".
163
164
  */
164
- async createSession(intentId, returnUrl, authorize) {
165
+ async createSession(intentId, returnUrl, authorize, userId) {
165
166
  const intent = await this.getIntent(intentId);
166
167
  this.assertStatus(intent, "created", "createSession");
168
+ if (userId && intent.userId && intent.userId !== userId) throw new PaymentError("Payment intent does not belong to this user");
169
+ if (userId && !intent.userId) await this.intentRepo.updateById(intent.id, { userId });
167
170
  const result = await this.provider.createSession(intent, {
168
171
  returnUrl,
169
172
  authorize
@@ -195,20 +198,36 @@ var BillingService = class {
195
198
  }
196
199
  /**
197
200
  * Process a webhook event by updating the intent status and emitting
198
- * the corresponding billing event.
201
+ * the corresponding payment event.
202
+ */
203
+ /**
204
+ * Valid status transitions from webhook events.
205
+ * Only these transitions are allowed — all others are silently ignored.
199
206
  */
207
+ static VALID_WEBHOOK_TRANSITIONS = {
208
+ processing: [
209
+ "authorized",
210
+ "captured",
211
+ "failed"
212
+ ],
213
+ authorized: ["captured", "failed"]
214
+ };
200
215
  async handleWebhookEvent(intentId, status, raw) {
201
216
  const intent = await this.getIntent(intentId);
202
217
  const eventMap = {
203
- authorized: "billing:authorized",
204
- captured: "billing:captured",
205
- failed: "billing:failed"
218
+ authorized: "payments:authorized",
219
+ captured: "payments:captured",
220
+ failed: "payments:failed"
206
221
  };
207
222
  if (!(status in eventMap)) {
208
223
  this.log.warn(`Unknown webhook status: ${status}`);
209
224
  return;
210
225
  }
211
226
  const webhookStatus = status;
227
+ if (!PaymentService.VALID_WEBHOOK_TRANSITIONS[intent.status]?.includes(webhookStatus)) {
228
+ this.log.warn(`Ignoring webhook: cannot transition ${intent.status} → ${webhookStatus}`, { intentId: intent.id });
229
+ return;
230
+ }
212
231
  await this.intentRepo.updateById(intent.id, {
213
232
  status: webhookStatus,
214
233
  providerRaw: raw
@@ -228,12 +247,13 @@ var BillingService = class {
228
247
  const intent = await this.getIntent(intentId);
229
248
  this.assertStatus(intent, "authorized", "capture");
230
249
  const amount = finalAmount ?? intent.amount;
250
+ if (amount > intent.amount) throw new PaymentError(`Capture amount ${amount} exceeds authorized amount ${intent.amount}`);
231
251
  if (intent.providerRef) await this.provider.capturePayment(intent.providerRef, amount);
232
252
  const updated = await this.intentRepo.updateById(intent.id, {
233
253
  status: "captured",
234
254
  amount
235
255
  });
236
- await this.alepha.events.emit("billing:captured", {
256
+ await this.alepha.events.emit("payments:captured", {
237
257
  intentId: intent.id,
238
258
  amount,
239
259
  currency: intent.currency,
@@ -249,7 +269,7 @@ var BillingService = class {
249
269
  this.assertStatus(intent, "authorized", "void");
250
270
  if (intent.providerRef) await this.provider.voidPayment(intent.providerRef);
251
271
  const updated = await this.intentRepo.updateById(intent.id, { status: "voided" });
252
- await this.alepha.events.emit("billing:voided", {
272
+ await this.alepha.events.emit("payments:voided", {
253
273
  intentId: intent.id,
254
274
  amount: intent.amount,
255
275
  currency: intent.currency,
@@ -262,7 +282,10 @@ var BillingService = class {
262
282
  */
263
283
  async refund(intentId, amount, reason) {
264
284
  const intent = await this.getIntent(intentId);
265
- this.assertStatus(intent, "captured", "refund");
285
+ if (intent.status !== "captured" && intent.status !== "partially_refunded") throw new PaymentError(`Cannot refund: intent ${intent.id} is '${intent.status}', expected 'captured' or 'partially_refunded'`);
286
+ const totalRefunded = (await this.refundRepo.findMany({ where: { intentId: { eq: intent.id } } })).reduce((sum, r) => sum + r.amount, 0);
287
+ const remaining = intent.amount - totalRefunded;
288
+ if (amount > remaining) throw new PaymentError(`Refund amount ${amount} exceeds remaining refundable amount ${remaining}`);
266
289
  let refundProviderRef;
267
290
  if (intent.providerRef) refundProviderRef = (await this.provider.refundPayment(intent.providerRef, amount)).providerRef;
268
291
  const refund = await this.refundRepo.create({
@@ -274,8 +297,9 @@ var BillingService = class {
274
297
  reason,
275
298
  providerRef: refundProviderRef
276
299
  });
277
- await this.intentRepo.updateById(intent.id, { status: "refunded" });
278
- await this.alepha.events.emit("billing:refunded", {
300
+ const newStatus = totalRefunded + amount >= intent.amount ? "refunded" : "partially_refunded";
301
+ await this.intentRepo.updateById(intent.id, { status: newStatus });
302
+ await this.alepha.events.emit("payments:refunded", {
279
303
  intentId: intent.id,
280
304
  refundId: refund.id,
281
305
  amount,
@@ -291,11 +315,11 @@ var BillingService = class {
291
315
  async recordCashPayment(amount, currency, metadata) {
292
316
  const intent = await this.intentRepo.create({
293
317
  amount,
294
- currency,
318
+ currency: currency.toLowerCase(),
295
319
  status: "captured",
296
320
  metadata
297
321
  });
298
- await this.alepha.events.emit("billing:captured", {
322
+ await this.alepha.events.emit("payments:captured", {
299
323
  intentId: intent.id,
300
324
  amount,
301
325
  currency,
@@ -309,7 +333,14 @@ var BillingService = class {
309
333
  async cancel(intentId) {
310
334
  const intent = await this.getIntent(intentId);
311
335
  this.assertStatus(intent, "created", "cancel");
312
- return await this.intentRepo.updateById(intent.id, { status: "cancelled" });
336
+ const cancelled = await this.intentRepo.updateById(intent.id, { status: "cancelled" });
337
+ await this.alepha.events.emit("payments:cancelled", {
338
+ intentId: intent.id,
339
+ amount: intent.amount,
340
+ currency: intent.currency,
341
+ metadata: intent.metadata
342
+ });
343
+ return cancelled;
313
344
  }
314
345
  /**
315
346
  * Get a payment intent by ID. Throws NotFoundError if not found.
@@ -327,28 +358,28 @@ var BillingService = class {
327
358
  return await this.intentRepo.paginate(query, { where }, { count: true });
328
359
  }
329
360
  assertStatus(intent, expected, operation) {
330
- if (intent.status !== expected) throw new BillingError(`Cannot ${operation}: intent ${intent.id} is '${intent.status}', expected '${expected}'`);
361
+ if (intent.status !== expected) throw new PaymentError(`Cannot ${operation}: intent ${intent.id} is '${intent.status}', expected '${expected}'`);
331
362
  }
332
363
  };
333
364
  //#endregion
334
- //#region ../../src/billing/controllers/AdminBillingController.ts
335
- var AdminBillingController = class {
336
- url = "/admin/billing";
337
- group = "admin:billing";
338
- billing = $inject(BillingService);
365
+ //#region ../../src/api/payments/controllers/AdminPaymentController.ts
366
+ var AdminPaymentController = class {
367
+ url = "/admin/payments";
368
+ group = "admin:payments";
369
+ payments = $inject(PaymentService);
339
370
  /**
340
371
  * List payment intents with pagination and filtering.
341
372
  */
342
373
  listIntents = $action({
343
374
  path: `${this.url}/intents`,
344
375
  group: this.group,
345
- use: [$secure({ permissions: ["billing:read"] })],
376
+ use: [$secure({ permissions: ["payments:read"] })],
346
377
  description: "List payment intents",
347
378
  schema: {
348
379
  query: intentQuerySchema,
349
380
  response: t.page(intentResourceSchema)
350
381
  },
351
- handler: ({ query }) => this.billing.findIntents(query)
382
+ handler: ({ query }) => this.payments.findIntents(query)
352
383
  });
353
384
  /**
354
385
  * Get a payment intent by ID.
@@ -356,13 +387,13 @@ var AdminBillingController = class {
356
387
  getIntent = $action({
357
388
  path: `${this.url}/intents/:id`,
358
389
  group: this.group,
359
- use: [$secure({ permissions: ["billing:read"] })],
390
+ use: [$secure({ permissions: ["payments:read"] })],
360
391
  description: "Get payment intent details",
361
392
  schema: {
362
393
  params: t.object({ id: t.uuid() }),
363
394
  response: intentResourceSchema
364
395
  },
365
- handler: ({ params }) => this.billing.getIntent(params.id)
396
+ handler: ({ params }) => this.payments.getIntent(params.id)
366
397
  });
367
398
  /**
368
399
  * Capture an authorized intent.
@@ -371,14 +402,14 @@ var AdminBillingController = class {
371
402
  method: "POST",
372
403
  path: `${this.url}/intents/:id/capture`,
373
404
  group: this.group,
374
- use: [$secure({ permissions: ["billing:write"] })],
405
+ use: [$secure({ permissions: ["payments:write"] })],
375
406
  description: "Capture an authorized payment intent",
376
407
  schema: {
377
408
  params: t.object({ id: t.uuid() }),
378
409
  body: captureIntentSchema,
379
410
  response: intentResourceSchema
380
411
  },
381
- handler: ({ params, body }) => this.billing.capture(params.id, body.amount)
412
+ handler: ({ params, body }) => this.payments.capture(params.id, body.amount)
382
413
  });
383
414
  /**
384
415
  * Void an authorized intent.
@@ -387,13 +418,13 @@ var AdminBillingController = class {
387
418
  method: "POST",
388
419
  path: `${this.url}/intents/:id/void`,
389
420
  group: this.group,
390
- use: [$secure({ permissions: ["billing:write"] })],
421
+ use: [$secure({ permissions: ["payments:write"] })],
391
422
  description: "Void an authorized payment intent",
392
423
  schema: {
393
424
  params: t.object({ id: t.uuid() }),
394
425
  response: intentResourceSchema
395
426
  },
396
- handler: ({ params }) => this.billing.void(params.id)
427
+ handler: ({ params }) => this.payments.void(params.id)
397
428
  });
398
429
  /**
399
430
  * Refund a captured intent.
@@ -402,14 +433,14 @@ var AdminBillingController = class {
402
433
  method: "POST",
403
434
  path: `${this.url}/intents/:id/refund`,
404
435
  group: this.group,
405
- use: [$secure({ permissions: ["billing:write"] })],
436
+ use: [$secure({ permissions: ["payments:write"] })],
406
437
  description: "Issue partial or full refund",
407
438
  schema: {
408
439
  params: t.object({ id: t.uuid() }),
409
440
  body: refundIntentSchema,
410
441
  response: refundResourceSchema
411
442
  },
412
- handler: ({ params, body }) => this.billing.refund(params.id, body.amount, body.reason)
443
+ handler: ({ params, body }) => this.payments.refund(params.id, body.amount, body.reason)
413
444
  });
414
445
  /**
415
446
  * Cancel a created intent.
@@ -418,13 +449,13 @@ var AdminBillingController = class {
418
449
  method: "POST",
419
450
  path: `${this.url}/intents/:id/cancel`,
420
451
  group: this.group,
421
- use: [$secure({ permissions: ["billing:write"] })],
452
+ use: [$secure({ permissions: ["payments:write"] })],
422
453
  description: "Cancel a created payment intent",
423
454
  schema: {
424
455
  params: t.object({ id: t.uuid() }),
425
456
  response: intentResourceSchema
426
457
  },
427
- handler: ({ params }) => this.billing.cancel(params.id)
458
+ handler: ({ params }) => this.payments.cancel(params.id)
428
459
  });
429
460
  /**
430
461
  * Record a cash payment.
@@ -433,31 +464,31 @@ var AdminBillingController = class {
433
464
  method: "POST",
434
465
  path: `${this.url}/cash`,
435
466
  group: this.group,
436
- use: [$secure({ permissions: ["billing:write"] })],
467
+ use: [$secure({ permissions: ["payments:write"] })],
437
468
  description: "Record a cash payment",
438
469
  schema: {
439
470
  body: recordCashSchema,
440
471
  response: intentResourceSchema
441
472
  },
442
- handler: ({ body }) => this.billing.recordCashPayment(body.amount, body.currency, body.metadata)
473
+ handler: ({ body }) => this.payments.recordCashPayment(body.amount, body.currency, body.metadata)
443
474
  });
444
475
  /**
445
476
  * PSP webhook endpoint (not under /admin, no auth — verified by provider).
446
477
  */
447
478
  webhook = $action({
448
479
  method: "POST",
449
- path: "/billing/webhook",
480
+ path: "/payments/webhook",
450
481
  group: this.group,
451
482
  description: "PSP webhook endpoint",
452
483
  schema: { response: okSchema },
453
484
  handler: async (request) => {
454
- await this.billing.handleWebhook(request.raw.web.req);
485
+ await this.payments.handleWebhook(request.raw.web.req);
455
486
  return { ok: true };
456
487
  }
457
488
  });
458
489
  };
459
490
  //#endregion
460
- //#region ../../src/billing/entities/paymentMethods.ts
491
+ //#region ../../src/api/payments/entities/paymentMethods.ts
461
492
  const paymentMethods = $entity({
462
493
  name: "payment_methods",
463
494
  schema: t.object({
@@ -478,14 +509,14 @@ const paymentMethods = $entity({
478
509
  indexes: ["userId", "organizationId"]
479
510
  });
480
511
  //#endregion
481
- //#region ../../src/billing/schemas/paymentMethodSchemas.ts
512
+ //#region ../../src/api/payments/schemas/paymentMethodSchemas.ts
482
513
  const addPaymentMethodSchema = t.object({ token: t.text() });
483
514
  const paymentMethodResourceSchema = paymentMethods.schema;
484
515
  //#endregion
485
- //#region ../../src/billing/services/PaymentMethodService.ts
516
+ //#region ../../src/api/payments/services/PaymentMethodService.ts
486
517
  var PaymentMethodService = class {
487
518
  log = $logger();
488
- provider = $inject(BillingProvider);
519
+ provider = $inject(PaymentProvider);
489
520
  methodRepo = $repository(paymentMethods);
490
521
  async addPaymentMethod(userId, organizationId, token) {
491
522
  const result = await this.provider.createPaymentMethod(userId, token);
@@ -507,24 +538,24 @@ var PaymentMethodService = class {
507
538
  }
508
539
  async removePaymentMethod(methodId, userId) {
509
540
  const method = await this.methodRepo.getById(methodId);
510
- if (method.userId !== userId) throw new BillingError("Cannot remove another user's payment method");
541
+ if (method.userId !== userId) throw new PaymentError("Cannot remove another user's payment method");
511
542
  await this.provider.deletePaymentMethod(method.providerRef);
512
543
  await this.methodRepo.deleteById(method.id);
513
544
  }
514
545
  async setDefault(methodId, userId) {
515
546
  const method = await this.methodRepo.getById(methodId);
516
- if (method.userId !== userId) throw new BillingError("Cannot modify another user's payment method");
547
+ if (method.userId !== userId) throw new PaymentError("Cannot modify another user's payment method");
517
548
  const userMethods = await this.methodRepo.findMany({ where: { userId: { eq: userId } } });
518
549
  for (const m of userMethods) if (m.isDefault) await this.methodRepo.updateById(m.id, { isDefault: false });
519
550
  return await this.methodRepo.updateById(method.id, { isDefault: true });
520
551
  }
521
552
  };
522
553
  //#endregion
523
- //#region ../../src/billing/controllers/BillingController.ts
524
- var BillingController = class {
525
- url = "/billing";
526
- group = "billing";
527
- billing = $inject(BillingService);
554
+ //#region ../../src/api/payments/controllers/PaymentController.ts
555
+ var PaymentController = class {
556
+ url = "/payments";
557
+ group = "payments";
558
+ payments = $inject(PaymentService);
528
559
  paymentMethods = $inject(PaymentMethodService);
529
560
  /**
530
561
  * List the current user's saved payment methods.
@@ -550,7 +581,10 @@ var BillingController = class {
550
581
  body: addPaymentMethodSchema,
551
582
  response: paymentMethodResourceSchema
552
583
  },
553
- handler: ({ body, user }) => this.paymentMethods.addPaymentMethod(user.id, user.organization, body.token)
584
+ handler: ({ body, user }) => {
585
+ if (!user.organization) throw new PaymentError("Organization is required to add a payment method");
586
+ return this.paymentMethods.addPaymentMethod(user.id, user.organization, body.token);
587
+ }
554
588
  });
555
589
  /**
556
590
  * Remove a payment method.
@@ -601,12 +635,12 @@ var BillingController = class {
601
635
  body: createCheckoutSchema,
602
636
  response: checkoutResponseSchema
603
637
  },
604
- handler: ({ body }) => this.billing.createSession(body.intentId, body.returnUrl, body.authorize)
638
+ handler: ({ body, user }) => this.payments.createSession(body.intentId, body.returnUrl, body.authorize, user.id)
605
639
  });
606
640
  };
607
641
  //#endregion
608
- //#region ../../src/billing/providers/MemoryBillingProvider.ts
609
- var MemoryBillingProvider = class {
642
+ //#region ../../src/api/payments/providers/MemoryPaymentProvider.ts
643
+ var MemoryPaymentProvider = class {
610
644
  charges = /* @__PURE__ */ new Map();
611
645
  refundRecords = /* @__PURE__ */ new Map();
612
646
  methods = /* @__PURE__ */ new Map();
@@ -620,7 +654,7 @@ var MemoryBillingProvider = class {
620
654
  status
621
655
  });
622
656
  return {
623
- url: `/billing/mock-checkout/${intent.id}`,
657
+ url: `/payments/mock-checkout/${intent.id}`,
624
658
  providerRef
625
659
  };
626
660
  }
@@ -688,26 +722,26 @@ var MemoryBillingProvider = class {
688
722
  }
689
723
  };
690
724
  //#endregion
691
- //#region ../../src/billing/index.ts
692
- const AlephaBilling = $module({
693
- name: "alepha.billing",
725
+ //#region ../../src/api/payments/index.ts
726
+ const AlephaApiPayments = $module({
727
+ name: "alepha.api.payments",
694
728
  services: [
695
- AdminBillingController,
696
- BillingController,
697
- BillingProvider,
698
- MemoryBillingProvider,
699
- BillingService,
729
+ AdminPaymentController,
730
+ PaymentController,
731
+ PaymentProvider,
732
+ MemoryPaymentProvider,
733
+ PaymentService,
700
734
  PaymentMethodService
701
735
  ],
702
736
  register: (alepha) => {
703
737
  alepha.with({
704
738
  optional: true,
705
- provide: BillingProvider,
706
- use: MemoryBillingProvider
739
+ provide: PaymentProvider,
740
+ use: MemoryPaymentProvider
707
741
  });
708
742
  }
709
743
  });
710
744
  //#endregion
711
- export { AdminBillingController, AlephaBilling, BillingController, BillingError, BillingProvider, BillingService, MemoryBillingProvider, PaymentMethodService, addPaymentMethodSchema, captureIntentSchema, checkoutResponseSchema, createCheckoutSchema, createIntentSchema, intentQuerySchema, intentResourceSchema, paymentIntents, paymentMethodResourceSchema, paymentMethods, recordCashSchema, refundIntentSchema, refundResourceSchema, refunds };
745
+ export { AdminPaymentController, AlephaApiPayments, MemoryPaymentProvider, PaymentController, PaymentError, PaymentMethodService, PaymentProvider, PaymentService, addPaymentMethodSchema, captureIntentSchema, checkoutResponseSchema, createCheckoutSchema, createIntentSchema, intentQuerySchema, intentResourceSchema, paymentIntents, paymentMethodResourceSchema, paymentMethods, recordCashSchema, refundIntentSchema, refundResourceSchema, refunds };
712
746
 
713
747
  //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/api/payments/entities/paymentIntents.ts","../../../src/api/payments/schemas/intentSchemas.ts","../../../src/api/payments/entities/refunds.ts","../../../src/api/payments/schemas/refundSchemas.ts","../../../src/api/payments/errors/PaymentError.ts","../../../src/api/payments/providers/PaymentProvider.ts","../../../src/api/payments/services/PaymentService.ts","../../../src/api/payments/controllers/AdminPaymentController.ts","../../../src/api/payments/entities/paymentMethods.ts","../../../src/api/payments/schemas/paymentMethodSchemas.ts","../../../src/api/payments/services/PaymentMethodService.ts","../../../src/api/payments/controllers/PaymentController.ts","../../../src/api/payments/providers/MemoryPaymentProvider.ts","../../../src/api/payments/index.ts"],"sourcesContent":["import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\n\nexport const paymentIntents = $entity({\n name: \"payment_intents\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n organizationId: db.organization(),\n amount: t.integer(),\n currency: t.text({ size: \"short\" }),\n status: t.enum([\n \"created\",\n \"processing\",\n \"authorized\",\n \"captured\",\n \"partially_refunded\",\n \"voided\",\n \"failed\",\n \"cancelled\",\n \"refunded\",\n \"expired\",\n ]),\n providerRef: t.optional(t.text()),\n providerRaw: t.optional(t.json()),\n metadata: t.optional(t.json()),\n paymentMethodId: t.optional(t.uuid()),\n userId: t.optional(t.uuid()),\n }),\n indexes: [\"status\", \"organizationId\", \"userId\", \"createdAt\"],\n});\n\nexport type PaymentIntentEntity = Static<typeof paymentIntents.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\nimport { paymentIntents } from \"../entities/paymentIntents.ts\";\n\nexport const createIntentSchema = t.object({\n amount: t.integer({ minimum: 1 }),\n currency: t.text({ size: \"short\" }),\n metadata: t.optional(t.json()),\n paymentMethodId: t.optional(t.uuid()),\n});\n\nexport type CreateIntent = Static<typeof createIntentSchema>;\n\nexport const createCheckoutSchema = t.object({\n intentId: t.uuid(),\n returnUrl: t.text(),\n authorize: t.optional(t.boolean()),\n});\n\nexport type CreateCheckout = Static<typeof createCheckoutSchema>;\n\nexport const checkoutResponseSchema = t.object({\n url: t.text(),\n intentId: t.text(),\n});\n\nexport type CheckoutResponse = Static<typeof checkoutResponseSchema>;\n\nexport const captureIntentSchema = t.object({\n amount: t.optional(t.integer({ minimum: 1 })),\n});\n\nexport type CaptureIntent = Static<typeof captureIntentSchema>;\n\nexport const refundIntentSchema = t.object({\n amount: t.integer({ minimum: 1 }),\n reason: t.optional(t.text()),\n});\n\nexport type RefundIntent = Static<typeof refundIntentSchema>;\n\nexport const recordCashSchema = t.object({\n amount: t.integer({ minimum: 1 }),\n currency: t.text({ size: \"short\" }),\n metadata: t.optional(t.json()),\n});\n\nexport type RecordCash = Static<typeof recordCashSchema>;\n\nexport const intentQuerySchema = t.extend(pageQuerySchema, {\n status: t.optional(t.text({ description: \"Filter by status\" })),\n userId: t.optional(t.uuid({ description: \"Filter by user ID\" })),\n});\n\nexport type IntentQuery = Static<typeof intentQuerySchema>;\n\nexport const intentResourceSchema = paymentIntents.schema;\n\nexport type IntentResource = Static<typeof intentResourceSchema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\n\nexport const refunds = $entity({\n name: \"refunds\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n organizationId: db.organization(),\n intentId: t.uuid(),\n amount: t.integer(),\n currency: t.text({ size: \"short\" }),\n status: t.enum([\"pending\", \"processing\", \"completed\", \"failed\"]),\n reason: t.optional(t.text()),\n providerRef: t.optional(t.text()),\n }),\n indexes: [\"intentId\", \"organizationId\", \"status\"],\n});\n\nexport type RefundEntity = Static<typeof refunds.schema>;\n","import type { Static } from \"alepha\";\nimport { refunds } from \"../entities/refunds.ts\";\n\nexport const refundResourceSchema = refunds.schema;\n\nexport type RefundResource = Static<typeof refundResourceSchema>;\n","import { AlephaError } from \"alepha\";\n\nexport class PaymentError extends AlephaError {\n public readonly status = 400;\n}\n","import type { PaymentIntentEntity } from \"../entities/paymentIntents.ts\";\n\nexport interface CreateSessionResult {\n url: string;\n providerRef: string;\n}\n\nexport interface RefundResult {\n providerRef: string;\n}\n\nexport interface WebhookEvent {\n providerRef: string;\n status: string;\n raw: unknown;\n}\n\nexport interface CreatePaymentMethodResult {\n providerRef: string;\n type: string;\n brand?: string;\n last4?: string;\n expMonth?: number;\n expYear?: number;\n}\n\nexport abstract class PaymentProvider {\n /**\n * Create a checkout session with the PSP.\n * Returns a URL to redirect the user to, and the PSP's reference ID.\n */\n abstract createSession(\n intent: PaymentIntentEntity,\n options: { returnUrl: string; authorize?: boolean },\n ): Promise<CreateSessionResult>;\n\n /**\n * Capture a previously authorized payment.\n * Amount can differ from the original authorization (partial capture).\n */\n abstract capturePayment(providerRef: string, amount: number): Promise<void>;\n\n /**\n * Void/cancel a previously authorized payment before capture.\n */\n abstract voidPayment(providerRef: string): Promise<void>;\n\n /**\n * Refund a captured payment (partial or full).\n */\n abstract refundPayment(\n providerRef: string,\n amount: number,\n ): Promise<RefundResult>;\n\n /**\n * Parse and verify an incoming PSP webhook request.\n *\n * Implementations MUST verify the webhook signature before returning.\n * Throw an error if the signature is invalid or missing — this is the\n * only authentication on the webhook endpoint (no $secure middleware).\n *\n * Failure to verify signatures allows attackers to forge payment\n * confirmations by POSTing fake webhook events.\n */\n abstract parseWebhook(request: Request): Promise<WebhookEvent>;\n\n /**\n * Store a payment method token with the PSP.\n */\n abstract createPaymentMethod(\n userId: string,\n token: string,\n ): Promise<CreatePaymentMethodResult>;\n\n /**\n * Delete a stored payment method from the PSP.\n */\n abstract deletePaymentMethod(providerRef: string): Promise<void>;\n\n /**\n * Expire/cancel a checkout session on the PSP side.\n * Called during stale session cleanup.\n */\n abstract expireSession(providerRef: string): Promise<void>;\n}\n","import { $inject, Alepha } from \"alepha\";\nimport { $job } from \"alepha/api/jobs\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport {\n type PaymentIntentEntity,\n paymentIntents,\n} from \"../entities/paymentIntents.ts\";\nimport { type RefundEntity, refunds } from \"../entities/refunds.ts\";\nimport { PaymentError } from \"../errors/PaymentError.ts\";\nimport { PaymentProvider } from \"../providers/PaymentProvider.ts\";\n\nexport class PaymentService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly dateTime = $inject(DateTimeProvider);\n protected readonly provider = $inject(PaymentProvider);\n protected readonly intentRepo = $repository(paymentIntents);\n protected readonly refundRepo = $repository(refunds);\n\n /**\n * Expires stale payment intents that have been in \"processing\" status\n * for more than 30 minutes. Runs every 15 minutes.\n */\n protected readonly expireStaleIntents = $job({\n cron: \"*/15 * * * *\",\n handler: async () => {\n const cutoff = this.dateTime.now().subtract(30, \"minutes\").toISOString();\n\n const stale = await this.intentRepo.findMany({\n where: { status: { eq: \"processing\" }, createdAt: { lt: cutoff } },\n });\n\n for (const intent of stale) {\n if (intent.providerRef) {\n try {\n await this.provider.expireSession(intent.providerRef);\n } catch (error) {\n this.log.warn(\n `Failed to expire session for intent ${intent.id}`,\n error,\n );\n }\n }\n await this.intentRepo.updateById(intent.id, { status: \"expired\" });\n this.log.info(`Expired stale intent ${intent.id}`);\n }\n },\n });\n\n /**\n * Create a new payment intent in \"created\" status.\n */\n public async createIntent(\n amount: number,\n currency: string,\n metadata?: unknown,\n options?: { paymentMethodId?: string; userId?: string },\n ): Promise<PaymentIntentEntity> {\n return await this.intentRepo.create({\n amount,\n currency: currency.toLowerCase(),\n status: \"created\",\n metadata: metadata as any,\n paymentMethodId: options?.paymentMethodId,\n userId: options?.userId,\n });\n }\n\n /**\n * Create a checkout session with the payment provider and\n * transition the intent to \"processing\".\n */\n public async createSession(\n intentId: string,\n returnUrl: string,\n authorize?: boolean,\n userId?: string,\n ): Promise<{ url: string; intentId: string }> {\n const intent = await this.getIntent(intentId);\n this.assertStatus(intent, \"created\", \"createSession\");\n\n // Verify intent ownership if userId is provided\n if (userId && intent.userId && intent.userId !== userId) {\n throw new PaymentError(\"Payment intent does not belong to this user\");\n }\n\n // Associate intent with user if not already set\n if (userId && !intent.userId) {\n await this.intentRepo.updateById(intent.id, { userId });\n }\n\n const result = await this.provider.createSession(intent, {\n returnUrl,\n authorize,\n });\n\n await this.intentRepo.updateById(intent.id, {\n status: \"processing\",\n providerRef: result.providerRef,\n });\n\n return { url: result.url, intentId: intent.id };\n }\n\n /**\n * Handle an incoming webhook from the payment provider.\n */\n public async handleWebhook(request: Request): Promise<void> {\n const event = await this.provider.parseWebhook(request);\n const intents = await this.intentRepo.findMany({\n where: { providerRef: { eq: event.providerRef } },\n limit: 1,\n });\n\n if (intents.length === 0) {\n this.log.warn(`Webhook for unknown providerRef: ${event.providerRef}`);\n return;\n }\n\n const intent = intents[0];\n await this.handleWebhookEvent(intent.id, event.status, event.raw);\n }\n\n /**\n * Process a webhook event by updating the intent status and emitting\n * the corresponding payment event.\n */\n /**\n * Valid status transitions from webhook events.\n * Only these transitions are allowed — all others are silently ignored.\n */\n protected static readonly VALID_WEBHOOK_TRANSITIONS: Record<\n string,\n string[]\n > = {\n processing: [\"authorized\", \"captured\", \"failed\"],\n authorized: [\"captured\", \"failed\"],\n };\n\n public async handleWebhookEvent(\n intentId: string,\n status: string,\n raw?: unknown,\n ): Promise<void> {\n const intent = await this.getIntent(intentId);\n\n const eventMap = {\n authorized: \"payments:authorized\",\n captured: \"payments:captured\",\n failed: \"payments:failed\",\n } as const;\n\n type WebhookStatus = keyof typeof eventMap;\n if (!(status in eventMap)) {\n this.log.warn(`Unknown webhook status: ${status}`);\n return;\n }\n\n const webhookStatus = status as WebhookStatus;\n\n // Validate status transition\n const allowed = PaymentService.VALID_WEBHOOK_TRANSITIONS[intent.status];\n if (!allowed?.includes(webhookStatus)) {\n this.log.warn(\n `Ignoring webhook: cannot transition ${intent.status} → ${webhookStatus}`,\n { intentId: intent.id },\n );\n return;\n }\n\n await this.intentRepo.updateById(intent.id, {\n status: webhookStatus,\n providerRaw: raw as any,\n });\n\n await this.alepha.events.emit(eventMap[webhookStatus], {\n intentId: intent.id,\n amount: intent.amount,\n currency: intent.currency,\n metadata: intent.metadata,\n });\n }\n\n /**\n * Capture a previously authorized payment. Optionally specify a different\n * amount for partial capture.\n */\n public async capture(\n intentId: string,\n finalAmount?: number,\n ): Promise<PaymentIntentEntity> {\n const intent = await this.getIntent(intentId);\n this.assertStatus(intent, \"authorized\", \"capture\");\n\n const amount = finalAmount ?? intent.amount;\n if (amount > intent.amount) {\n throw new PaymentError(\n `Capture amount ${amount} exceeds authorized amount ${intent.amount}`,\n );\n }\n\n if (intent.providerRef) {\n await this.provider.capturePayment(intent.providerRef, amount);\n }\n\n const updated = await this.intentRepo.updateById(intent.id, {\n status: \"captured\",\n amount,\n });\n\n await this.alepha.events.emit(\"payments:captured\", {\n intentId: intent.id,\n amount,\n currency: intent.currency,\n metadata: intent.metadata,\n });\n\n return updated;\n }\n\n /**\n * Void a previously authorized payment before capture.\n */\n public async void(intentId: string): Promise<PaymentIntentEntity> {\n const intent = await this.getIntent(intentId);\n this.assertStatus(intent, \"authorized\", \"void\");\n\n if (intent.providerRef) {\n await this.provider.voidPayment(intent.providerRef);\n }\n\n const updated = await this.intentRepo.updateById(intent.id, {\n status: \"voided\",\n });\n\n await this.alepha.events.emit(\"payments:voided\", {\n intentId: intent.id,\n amount: intent.amount,\n currency: intent.currency,\n metadata: intent.metadata,\n });\n\n return updated;\n }\n\n /**\n * Refund a captured payment (partial or full).\n */\n public async refund(\n intentId: string,\n amount: number,\n reason?: string,\n ): Promise<RefundEntity> {\n const intent = await this.getIntent(intentId);\n\n // Allow refunds from both \"captured\" and \"partially_refunded\" states\n if (\n intent.status !== \"captured\" &&\n intent.status !== \"partially_refunded\"\n ) {\n throw new PaymentError(\n `Cannot refund: intent ${intent.id} is '${intent.status}', expected 'captured' or 'partially_refunded'`,\n );\n }\n\n // Validate refund amount against remaining refundable amount\n const existingRefunds = await this.refundRepo.findMany({\n where: { intentId: { eq: intent.id } },\n });\n const totalRefunded = existingRefunds.reduce((sum, r) => sum + r.amount, 0);\n const remaining = intent.amount - totalRefunded;\n\n if (amount > remaining) {\n throw new PaymentError(\n `Refund amount ${amount} exceeds remaining refundable amount ${remaining}`,\n );\n }\n\n let refundProviderRef: string | undefined;\n if (intent.providerRef) {\n const result = await this.provider.refundPayment(\n intent.providerRef,\n amount,\n );\n refundProviderRef = result.providerRef;\n }\n\n const refund = await this.refundRepo.create({\n intentId: intent.id,\n organizationId: intent.organizationId,\n amount,\n currency: intent.currency,\n status: \"completed\",\n reason,\n providerRef: refundProviderRef,\n });\n\n // Set status based on whether fully or partially refunded\n const newTotalRefunded = totalRefunded + amount;\n const newStatus =\n newTotalRefunded >= intent.amount ? \"refunded\" : \"partially_refunded\";\n await this.intentRepo.updateById(intent.id, {\n status: newStatus,\n });\n\n await this.alepha.events.emit(\"payments:refunded\", {\n intentId: intent.id,\n refundId: refund.id,\n amount,\n currency: intent.currency,\n metadata: intent.metadata,\n });\n\n return refund;\n }\n\n /**\n * Record a cash or offline payment directly as captured,\n * bypassing the checkout flow.\n */\n public async recordCashPayment(\n amount: number,\n currency: string,\n metadata?: unknown,\n ): Promise<PaymentIntentEntity> {\n const intent = await this.intentRepo.create({\n amount,\n currency: currency.toLowerCase(),\n status: \"captured\",\n metadata: metadata as any,\n });\n\n await this.alepha.events.emit(\"payments:captured\", {\n intentId: intent.id,\n amount,\n currency,\n metadata,\n });\n\n return intent;\n }\n\n /**\n * Cancel a payment intent that has not yet entered processing.\n */\n public async cancel(intentId: string): Promise<PaymentIntentEntity> {\n const intent = await this.getIntent(intentId);\n this.assertStatus(intent, \"created\", \"cancel\");\n\n const cancelled = await this.intentRepo.updateById(intent.id, {\n status: \"cancelled\",\n });\n\n await this.alepha.events.emit(\"payments:cancelled\", {\n intentId: intent.id,\n amount: intent.amount,\n currency: intent.currency,\n metadata: intent.metadata,\n });\n\n return cancelled;\n }\n\n /**\n * Get a payment intent by ID. Throws NotFoundError if not found.\n */\n public async getIntent(intentId: string): Promise<PaymentIntentEntity> {\n return await this.intentRepo.getById(intentId);\n }\n\n /**\n * Find payment intents with optional filters and pagination.\n */\n public async findIntents(query: {\n status?: string;\n userId?: string;\n sort?: string;\n size?: number;\n page?: number;\n }) {\n const where = this.intentRepo.createQueryWhere();\n if (query.status)\n where.status = { eq: query.status as PaymentIntentEntity[\"status\"] };\n if (query.userId) where.userId = { eq: query.userId };\n return await this.intentRepo.paginate(query, { where }, { count: true });\n }\n\n protected assertStatus(\n intent: PaymentIntentEntity,\n expected: PaymentIntentEntity[\"status\"],\n operation: string,\n ): void {\n if (intent.status !== expected) {\n throw new PaymentError(\n `Cannot ${operation}: intent ${intent.id} is '${intent.status}', expected '${expected}'`,\n );\n }\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $secure } from \"alepha/security\";\nimport { $action, okSchema } from \"alepha/server\";\nimport {\n captureIntentSchema,\n intentQuerySchema,\n intentResourceSchema,\n recordCashSchema,\n refundIntentSchema,\n} from \"../schemas/intentSchemas.ts\";\nimport { refundResourceSchema } from \"../schemas/refundSchemas.ts\";\nimport { PaymentService } from \"../services/PaymentService.ts\";\n\nexport class AdminPaymentController {\n protected readonly url = \"/admin/payments\";\n protected readonly group = \"admin:payments\";\n protected readonly payments = $inject(PaymentService);\n\n /**\n * List payment intents with pagination and filtering.\n */\n public readonly listIntents = $action({\n path: `${this.url}/intents`,\n group: this.group,\n use: [$secure({ permissions: [\"payments:read\"] })],\n description: \"List payment intents\",\n schema: {\n query: intentQuerySchema,\n response: t.page(intentResourceSchema),\n },\n handler: ({ query }) => this.payments.findIntents(query),\n });\n\n /**\n * Get a payment intent by ID.\n */\n public readonly getIntent = $action({\n path: `${this.url}/intents/:id`,\n group: this.group,\n use: [$secure({ permissions: [\"payments:read\"] })],\n description: \"Get payment intent details\",\n schema: {\n params: t.object({ id: t.uuid() }),\n response: intentResourceSchema,\n },\n handler: ({ params }) => this.payments.getIntent(params.id),\n });\n\n /**\n * Capture an authorized intent.\n */\n public readonly captureIntent = $action({\n method: \"POST\",\n path: `${this.url}/intents/:id/capture`,\n group: this.group,\n use: [$secure({ permissions: [\"payments:write\"] })],\n description: \"Capture an authorized payment intent\",\n schema: {\n params: t.object({ id: t.uuid() }),\n body: captureIntentSchema,\n response: intentResourceSchema,\n },\n handler: ({ params, body }) =>\n this.payments.capture(params.id, body.amount),\n });\n\n /**\n * Void an authorized intent.\n */\n public readonly voidIntent = $action({\n method: \"POST\",\n path: `${this.url}/intents/:id/void`,\n group: this.group,\n use: [$secure({ permissions: [\"payments:write\"] })],\n description: \"Void an authorized payment intent\",\n schema: {\n params: t.object({ id: t.uuid() }),\n response: intentResourceSchema,\n },\n handler: ({ params }) => this.payments.void(params.id),\n });\n\n /**\n * Refund a captured intent.\n */\n public readonly refundIntent = $action({\n method: \"POST\",\n path: `${this.url}/intents/:id/refund`,\n group: this.group,\n use: [$secure({ permissions: [\"payments:write\"] })],\n description: \"Issue partial or full refund\",\n schema: {\n params: t.object({ id: t.uuid() }),\n body: refundIntentSchema,\n response: refundResourceSchema,\n },\n handler: ({ params, body }) =>\n this.payments.refund(params.id, body.amount, body.reason),\n });\n\n /**\n * Cancel a created intent.\n */\n public readonly cancelIntent = $action({\n method: \"POST\",\n path: `${this.url}/intents/:id/cancel`,\n group: this.group,\n use: [$secure({ permissions: [\"payments:write\"] })],\n description: \"Cancel a created payment intent\",\n schema: {\n params: t.object({ id: t.uuid() }),\n response: intentResourceSchema,\n },\n handler: ({ params }) => this.payments.cancel(params.id),\n });\n\n /**\n * Record a cash payment.\n */\n public readonly recordCash = $action({\n method: \"POST\",\n path: `${this.url}/cash`,\n group: this.group,\n use: [$secure({ permissions: [\"payments:write\"] })],\n description: \"Record a cash payment\",\n schema: {\n body: recordCashSchema,\n response: intentResourceSchema,\n },\n handler: ({ body }) =>\n this.payments.recordCashPayment(\n body.amount,\n body.currency,\n body.metadata,\n ),\n });\n\n /**\n * PSP webhook endpoint (not under /admin, no auth — verified by provider).\n */\n public readonly webhook = $action({\n method: \"POST\",\n path: \"/payments/webhook\",\n group: this.group,\n description: \"PSP webhook endpoint\",\n schema: {\n response: okSchema,\n },\n handler: async (request) => {\n await this.payments.handleWebhook(request.raw.web!.req);\n return { ok: true };\n },\n });\n}\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\n\nexport const paymentMethods = $entity({\n name: \"payment_methods\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n organizationId: db.organization(),\n userId: t.uuid(),\n type: t.text({ size: \"short\" }),\n brand: t.optional(t.text({ size: \"short\" })),\n last4: t.optional(t.text({ size: \"short\" })),\n expMonth: t.optional(t.integer()),\n expYear: t.optional(t.integer()),\n isDefault: t.boolean(),\n providerRef: t.text(),\n }),\n indexes: [\"userId\", \"organizationId\"],\n});\n\nexport type PaymentMethodEntity = Static<typeof paymentMethods.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { paymentMethods } from \"../entities/paymentMethods.ts\";\n\nexport const addPaymentMethodSchema = t.object({\n token: t.text(),\n});\n\nexport type AddPaymentMethod = Static<typeof addPaymentMethodSchema>;\n\nexport const paymentMethodResourceSchema = paymentMethods.schema;\n\nexport type PaymentMethodResource = Static<typeof paymentMethodResourceSchema>;\n","import { $inject } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport {\n type PaymentMethodEntity,\n paymentMethods,\n} from \"../entities/paymentMethods.ts\";\nimport { PaymentError } from \"../errors/PaymentError.ts\";\nimport { PaymentProvider } from \"../providers/PaymentProvider.ts\";\n\nexport class PaymentMethodService {\n protected readonly log = $logger();\n protected readonly provider = $inject(PaymentProvider);\n protected readonly methodRepo = $repository(paymentMethods);\n\n public async addPaymentMethod(\n userId: string,\n organizationId: string,\n token: string,\n ): Promise<PaymentMethodEntity> {\n const result = await this.provider.createPaymentMethod(userId, token);\n\n const existing = await this.methodRepo.findMany({\n where: { userId: { eq: userId } },\n });\n\n return await this.methodRepo.create({\n userId,\n organizationId,\n type: result.type,\n brand: result.brand,\n last4: result.last4,\n expMonth: result.expMonth,\n expYear: result.expYear,\n isDefault: existing.length === 0,\n providerRef: result.providerRef,\n });\n }\n\n public async listPaymentMethods(\n userId: string,\n ): Promise<PaymentMethodEntity[]> {\n return await this.methodRepo.findMany({\n where: { userId: { eq: userId } },\n });\n }\n\n public async removePaymentMethod(\n methodId: string,\n userId: string,\n ): Promise<void> {\n const method = await this.methodRepo.getById(methodId);\n if (method.userId !== userId) {\n throw new PaymentError(\"Cannot remove another user's payment method\");\n }\n\n await this.provider.deletePaymentMethod(method.providerRef);\n await this.methodRepo.deleteById(method.id);\n }\n\n public async setDefault(\n methodId: string,\n userId: string,\n ): Promise<PaymentMethodEntity> {\n const method = await this.methodRepo.getById(methodId);\n if (method.userId !== userId) {\n throw new PaymentError(\"Cannot modify another user's payment method\");\n }\n\n const userMethods = await this.methodRepo.findMany({\n where: { userId: { eq: userId } },\n });\n\n for (const m of userMethods) {\n if (m.isDefault) {\n await this.methodRepo.updateById(m.id, { isDefault: false });\n }\n }\n\n return await this.methodRepo.updateById(method.id, { isDefault: true });\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $secure } from \"alepha/security\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { PaymentError } from \"../errors/PaymentError.ts\";\nimport {\n checkoutResponseSchema,\n createCheckoutSchema,\n} from \"../schemas/intentSchemas.ts\";\nimport {\n addPaymentMethodSchema,\n paymentMethodResourceSchema,\n} from \"../schemas/paymentMethodSchemas.ts\";\nimport { PaymentMethodService } from \"../services/PaymentMethodService.ts\";\nimport { PaymentService } from \"../services/PaymentService.ts\";\n\nexport class PaymentController {\n protected readonly url = \"/payments\";\n protected readonly group = \"payments\";\n protected readonly payments = $inject(PaymentService);\n protected readonly paymentMethods = $inject(PaymentMethodService);\n\n /**\n * List the current user's saved payment methods.\n */\n public readonly listPaymentMethods = $action({\n path: `${this.url}/payment-methods`,\n group: this.group,\n use: [$secure()],\n description: \"List current user's saved payment methods\",\n schema: {\n response: t.array(paymentMethodResourceSchema),\n },\n handler: ({ user }) => this.paymentMethods.listPaymentMethods(user.id),\n });\n\n /**\n * Add a new payment method.\n */\n public readonly addPaymentMethod = $action({\n method: \"POST\",\n path: `${this.url}/payment-methods`,\n group: this.group,\n use: [$secure()],\n description: \"Tokenize and store a new payment method\",\n schema: {\n body: addPaymentMethodSchema,\n response: paymentMethodResourceSchema,\n },\n handler: ({ body, user }) => {\n if (!user.organization) {\n throw new PaymentError(\n \"Organization is required to add a payment method\",\n );\n }\n return this.paymentMethods.addPaymentMethod(\n user.id,\n user.organization,\n body.token,\n );\n },\n });\n\n /**\n * Remove a payment method.\n */\n public readonly removePaymentMethod = $action({\n method: \"DELETE\",\n path: `${this.url}/payment-methods/:id`,\n group: this.group,\n use: [$secure()],\n description: \"Remove own payment method\",\n schema: {\n params: t.object({ id: t.uuid() }),\n response: okSchema,\n },\n handler: async ({ params, user }) => {\n await this.paymentMethods.removePaymentMethod(params.id, user.id);\n return { ok: true, id: params.id };\n },\n });\n\n /**\n * Set a payment method as default.\n */\n public readonly setDefaultPaymentMethod = $action({\n method: \"PATCH\",\n path: `${this.url}/payment-methods/:id/default`,\n group: this.group,\n use: [$secure()],\n description: \"Set as default payment method\",\n schema: {\n params: t.object({ id: t.uuid() }),\n response: paymentMethodResourceSchema,\n },\n handler: ({ params, user }) =>\n this.paymentMethods.setDefault(params.id, user.id),\n });\n\n /**\n * Create a checkout session.\n */\n public readonly createCheckout = $action({\n method: \"POST\",\n path: `${this.url}/checkout`,\n group: this.group,\n use: [$secure()],\n description: \"Create checkout session and return URL\",\n schema: {\n body: createCheckoutSchema,\n response: checkoutResponseSchema,\n },\n handler: ({ body, user }) =>\n this.payments.createSession(\n body.intentId,\n body.returnUrl,\n body.authorize,\n user.id,\n ),\n });\n}\n","import { randomUUID } from \"node:crypto\";\nimport type { PaymentIntentEntity } from \"../entities/paymentIntents.ts\";\nimport type {\n CreatePaymentMethodResult,\n CreateSessionResult,\n PaymentProvider,\n RefundResult,\n WebhookEvent,\n} from \"./PaymentProvider.ts\";\n\ninterface MemoryCharge {\n providerRef: string;\n amount: number;\n status: string;\n}\n\ninterface MemoryRefund {\n providerRef: string;\n chargeRef: string;\n amount: number;\n}\n\nexport class MemoryPaymentProvider implements PaymentProvider {\n protected readonly charges: Map<string, MemoryCharge> = new Map();\n protected readonly refundRecords: Map<string, MemoryRefund> = new Map();\n protected readonly methods: Map<string, CreatePaymentMethodResult> =\n new Map();\n protected readonly expiredSessions: Set<string> = new Set();\n\n public async createSession(\n intent: PaymentIntentEntity,\n options: { returnUrl: string; authorize?: boolean },\n ): Promise<CreateSessionResult> {\n const providerRef = `mem_session_${randomUUID()}`;\n const status = options.authorize ? \"authorized\" : \"captured\";\n this.charges.set(providerRef, {\n providerRef,\n amount: intent.amount,\n status,\n });\n return {\n url: `/payments/mock-checkout/${intent.id}`,\n providerRef,\n };\n }\n\n public async capturePayment(\n providerRef: string,\n amount: number,\n ): Promise<void> {\n const charge = this.charges.get(providerRef);\n if (charge) {\n charge.status = \"captured\";\n charge.amount = amount;\n }\n }\n\n public async voidPayment(providerRef: string): Promise<void> {\n const charge = this.charges.get(providerRef);\n if (charge) {\n charge.status = \"voided\";\n }\n }\n\n public async refundPayment(\n providerRef: string,\n amount: number,\n ): Promise<RefundResult> {\n const refundRef = `mem_refund_${randomUUID()}`;\n this.refundRecords.set(refundRef, {\n providerRef: refundRef,\n chargeRef: providerRef,\n amount,\n });\n return { providerRef: refundRef };\n }\n\n public async parseWebhook(request: Request): Promise<WebhookEvent> {\n const body = (await request.json()) as {\n providerRef: string;\n status: string;\n };\n return {\n providerRef: body.providerRef,\n status: body.status,\n raw: body,\n };\n }\n\n public async createPaymentMethod(\n userId: string,\n token: string,\n ): Promise<CreatePaymentMethodResult> {\n const providerRef = `mem_pm_${randomUUID()}`;\n const result: CreatePaymentMethodResult = {\n providerRef,\n type: \"card\",\n brand: \"visa\",\n last4: \"4242\",\n expMonth: 12,\n expYear: 2030,\n };\n this.methods.set(providerRef, result);\n return result;\n }\n\n public async deletePaymentMethod(providerRef: string): Promise<void> {\n this.methods.delete(providerRef);\n }\n\n public async expireSession(providerRef: string): Promise<void> {\n this.expiredSessions.add(providerRef);\n }\n\n // --- Test assertion helpers ---\n\n public wasCharged(providerRef: string): boolean {\n const charge = this.charges.get(providerRef);\n return charge?.status === \"captured\";\n }\n\n public wasRefunded(providerRef: string): boolean {\n return Array.from(this.refundRecords.values()).some(\n (r) => r.chargeRef === providerRef,\n );\n }\n\n public wasExpired(providerRef: string): boolean {\n return this.expiredSessions.has(providerRef);\n }\n\n public getCharges(): MemoryCharge[] {\n return Array.from(this.charges.values());\n }\n\n public getRefunds(): MemoryRefund[] {\n return Array.from(this.refundRecords.values());\n }\n}\n","import { $module } from \"alepha\";\nimport { AdminPaymentController } from \"./controllers/AdminPaymentController.ts\";\nimport { PaymentController } from \"./controllers/PaymentController.ts\";\nimport { MemoryPaymentProvider } from \"./providers/MemoryPaymentProvider.ts\";\nimport { PaymentProvider } from \"./providers/PaymentProvider.ts\";\nimport { PaymentMethodService } from \"./services/PaymentMethodService.ts\";\nimport { PaymentService } from \"./services/PaymentService.ts\";\n\nexport * from \"./controllers/AdminPaymentController.ts\";\nexport * from \"./controllers/PaymentController.ts\";\nexport * from \"./entities/paymentIntents.ts\";\nexport * from \"./entities/paymentMethods.ts\";\nexport * from \"./entities/refunds.ts\";\nexport * from \"./errors/PaymentError.ts\";\nexport * from \"./providers/MemoryPaymentProvider.ts\";\nexport * from \"./providers/PaymentProvider.ts\";\nexport * from \"./schemas/intentSchemas.ts\";\nexport * from \"./schemas/paymentMethodSchemas.ts\";\nexport * from \"./schemas/refundSchemas.ts\";\nexport * from \"./services/PaymentMethodService.ts\";\nexport * from \"./services/PaymentService.ts\";\n\ndeclare module \"alepha\" {\n interface Hooks {\n \"payments:authorized\": {\n intentId: string;\n amount: number;\n currency: string;\n metadata?: unknown;\n };\n \"payments:captured\": {\n intentId: string;\n amount: number;\n currency: string;\n metadata?: unknown;\n };\n \"payments:failed\": {\n intentId: string;\n amount: number;\n currency: string;\n metadata?: unknown;\n };\n \"payments:voided\": {\n intentId: string;\n amount: number;\n currency: string;\n metadata?: unknown;\n };\n \"payments:refunded\": {\n intentId: string;\n refundId: string;\n amount: number;\n currency: string;\n metadata?: unknown;\n };\n \"payments:cancelled\": {\n intentId: string;\n amount: number;\n currency: string;\n metadata?: unknown;\n };\n }\n}\n\nexport const AlephaApiPayments = $module({\n name: \"alepha.api.payments\",\n services: [\n AdminPaymentController,\n PaymentController,\n PaymentProvider,\n MemoryPaymentProvider,\n PaymentService,\n PaymentMethodService,\n ],\n register: (alepha) => {\n alepha.with({\n optional: true,\n provide: PaymentProvider,\n use: MemoryPaymentProvider,\n });\n },\n});\n"],"mappings":"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"}