@strav/payment 1.0.0-alpha.24

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 (55) hide show
  1. package/package.json +34 -0
  2. package/src/drivers/index.ts +6 -0
  3. package/src/drivers/mock_driver.ts +534 -0
  4. package/src/drivers/omise/index.ts +56 -0
  5. package/src/drivers/omise/omise_config.ts +19 -0
  6. package/src/drivers/omise/omise_driver.ts +576 -0
  7. package/src/drivers/omise/omise_mappers.ts +180 -0
  8. package/src/drivers/omise/omise_method_spec.ts +88 -0
  9. package/src/drivers/omise/omise_next_action_mapper.ts +89 -0
  10. package/src/drivers/omise/omise_price_spec.ts +85 -0
  11. package/src/drivers/omise/omise_provider.ts +33 -0
  12. package/src/drivers/omise/omise_schedule_mapper.ts +156 -0
  13. package/src/drivers/omise/omise_webhook.ts +162 -0
  14. package/src/drivers/payment_method_helpers.ts +35 -0
  15. package/src/drivers/stripe/index.ts +40 -0
  16. package/src/drivers/stripe/mappers/stripe_mappers.ts +312 -0
  17. package/src/drivers/stripe/mappers/stripe_method_spec.ts +77 -0
  18. package/src/drivers/stripe/mappers/stripe_next_action_mapper.ts +163 -0
  19. package/src/drivers/stripe/stripe_config.ts +18 -0
  20. package/src/drivers/stripe/stripe_driver.ts +650 -0
  21. package/src/drivers/stripe/stripe_provider.ts +38 -0
  22. package/src/drivers/stripe/webhook/stripe_normalize.ts +139 -0
  23. package/src/drivers/unsupported.ts +20 -0
  24. package/src/dto/index.ts +72 -0
  25. package/src/dto/payment_charge.ts +158 -0
  26. package/src/dto/payment_checkout.ts +46 -0
  27. package/src/dto/payment_customer.ts +52 -0
  28. package/src/dto/payment_event.ts +83 -0
  29. package/src/dto/payment_invoice.ts +39 -0
  30. package/src/dto/payment_link.ts +81 -0
  31. package/src/dto/payment_method.ts +43 -0
  32. package/src/dto/payment_price.ts +47 -0
  33. package/src/dto/payment_product.ts +40 -0
  34. package/src/dto/payment_subscription.ts +71 -0
  35. package/src/index.ts +78 -0
  36. package/src/ledger/apply_payment_ledger_migration.ts +106 -0
  37. package/src/ledger/index.ts +13 -0
  38. package/src/ledger/payment_ledger.ts +260 -0
  39. package/src/ledger/payment_ledger_models.ts +66 -0
  40. package/src/ledger/schemas/payment_customer_schema.ts +34 -0
  41. package/src/ledger/schemas/payment_invoice_schema.ts +39 -0
  42. package/src/ledger/schemas/payment_subscription_schema.ts +34 -0
  43. package/src/payment_capabilities.ts +91 -0
  44. package/src/payment_driver.ts +167 -0
  45. package/src/payment_error.ts +159 -0
  46. package/src/payment_manager.ts +174 -0
  47. package/src/payment_provider.ts +93 -0
  48. package/src/tenant_metadata.ts +60 -0
  49. package/src/types.ts +49 -0
  50. package/src/webhook/index.ts +8 -0
  51. package/src/webhook/payment_webhook.ts +190 -0
  52. package/src/webhook/payment_webhook_event.ts +22 -0
  53. package/src/webhook/payment_webhook_event_repository.ts +65 -0
  54. package/src/webhook/payment_webhook_event_schema.ts +40 -0
  55. package/src/webhook/payment_webhook_registry.ts +65 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `paymentWebhookEventSchema` — system-wide dedup ledger for
3
+ * incoming webhooks from every configured provider.
4
+ *
5
+ * On every delivery (after signature verification), the framework
6
+ * does:
7
+ *
8
+ * INSERT INTO payment_webhook_event (...) ON CONFLICT DO NOTHING
9
+ *
10
+ * The first delivery wins the INSERT and fires user handlers;
11
+ * subsequent deliveries (provider retries, concurrent webhook
12
+ * workers) see the conflict and return 200 without re-firing.
13
+ *
14
+ * Why NOT tenanted: webhooks arrive without tenant context. The
15
+ * endpoint can't know which tenant a payload belongs to until
16
+ * after signature verification + payload inspection — too late
17
+ * for the framework-level dedup INSERT.
18
+ *
19
+ * Why a composite unique key `(provider, provider_event_id)`:
20
+ * different providers may emit colliding event id formats; the
21
+ * pair is the actual uniqueness contract.
22
+ */
23
+
24
+ import { Archetype, defineSchema } from '@strav/database'
25
+
26
+ export const paymentWebhookEventSchema = defineSchema(
27
+ 'payment_webhook_event',
28
+ Archetype.Event,
29
+ (t) => {
30
+ t.id()
31
+ t.string('provider').max(64).notNull()
32
+ t.string('provider_event_id').max(255).notNull()
33
+ t.string('event_type').max(128).notNull()
34
+ t.timestamp('received_at').notNull()
35
+ t.timestamp('processed_at').nullable()
36
+ // Composite (provider, provider_event_id) unique constraint
37
+ // is added by `applyPaymentLedgerMigration` — the schema
38
+ // builder only exposes per-column `.unique()`.
39
+ },
40
+ )
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Handler registry for normalized payment webhook events.
3
+ *
4
+ * Apps register handlers at boot:
5
+ *
6
+ * payment.onWebhookEvent('subscription.created', (ctx) => { ... })
7
+ * payment.onWebhookEvent('charge.succeeded', { provider: 'stripe' }, (ctx) => { ... })
8
+ *
9
+ * Handlers fire in registration order. A thrown handler aborts
10
+ * the rest and surfaces a 500 (the provider retries). Multiple
11
+ * handlers per `(eventType, provider?)` are fine.
12
+ *
13
+ * Filter semantics: when `filter.provider` is set, the handler
14
+ * only fires for that provider; when omitted, the handler fires
15
+ * for any provider that emits the type.
16
+ */
17
+
18
+ import type {
19
+ PaymentEventType,
20
+ WebhookHandler,
21
+ WebhookHandlerFilter,
22
+ } from '../dto/payment_event.ts'
23
+
24
+ interface RegisteredHandler {
25
+ filter: WebhookHandlerFilter
26
+ handler: WebhookHandler
27
+ }
28
+
29
+ export class PaymentWebhookRegistry {
30
+ private readonly handlers = new Map<PaymentEventType, RegisteredHandler[]>()
31
+
32
+ on(eventType: PaymentEventType, handler: WebhookHandler): void
33
+ on(
34
+ eventType: PaymentEventType,
35
+ filter: WebhookHandlerFilter,
36
+ handler: WebhookHandler,
37
+ ): void
38
+ on(
39
+ eventType: PaymentEventType,
40
+ filterOrHandler: WebhookHandlerFilter | WebhookHandler,
41
+ maybeHandler?: WebhookHandler,
42
+ ): void {
43
+ const { filter, handler } =
44
+ typeof filterOrHandler === 'function'
45
+ ? { filter: {}, handler: filterOrHandler }
46
+ : { filter: filterOrHandler, handler: maybeHandler! }
47
+ const existing = this.handlers.get(eventType) ?? []
48
+ existing.push({ filter, handler })
49
+ this.handlers.set(eventType, existing)
50
+ }
51
+
52
+ /** Remove every registered handler. Tests use this to keep cases isolated. */
53
+ clear(): void {
54
+ this.handlers.clear()
55
+ }
56
+
57
+ /** Resolve the handlers that match a given `(type, provider)` pair. */
58
+ resolve(eventType: PaymentEventType, provider: string): readonly WebhookHandler[] {
59
+ const matches = this.handlers.get(eventType)
60
+ if (!matches) return []
61
+ return matches
62
+ .filter((m) => !m.filter.provider || m.filter.provider === provider)
63
+ .map((m) => m.handler)
64
+ }
65
+ }