@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,163 @@
1
+ /**
2
+ * Map `Stripe.PaymentIntent.NextAction` onto the framework's
3
+ * `PaymentNextAction` union.
4
+ *
5
+ * Stripe's discriminator is `next_action.type`; each variant
6
+ * carries its own field cluster (`promptpay_display_qr_code`,
7
+ * `wechat_pay_display_qr_code`, `redirect_to_url`,
8
+ * `konbini_display_details`, …). We collapse them onto the four
9
+ * framework kinds: `display_qr`, `redirect`, `authorize`,
10
+ * `voucher` (plus `wait` for variants without user-facing
11
+ * detail).
12
+ *
13
+ * Why this lives in `mappers/` (not the driver): the same
14
+ * mapping is useful from app code that pokes the raw intent
15
+ * (`driver.client.paymentIntents.retrieve(id)`) and wants the
16
+ * framework-shaped DTO without re-running `charges.create`.
17
+ */
18
+
19
+ import type Stripe from 'stripe'
20
+ import type { PaymentNextAction } from '../../../dto/index.ts'
21
+
22
+ function maybeDate(unix: number | null | undefined): Date | undefined {
23
+ if (unix === null || unix === undefined) return undefined
24
+ return new Date(unix * 1000)
25
+ }
26
+
27
+ /**
28
+ * Returns `null` when there's no actionable step (intent is
29
+ * already settled / failed / cancelled, or carries a variant we
30
+ * don't expose). Apps fall back to `intent.next_action` on `raw`
31
+ * for variants we haven't surfaced.
32
+ */
33
+ export function stripeNextAction(
34
+ na: Stripe.PaymentIntent.NextAction | null | undefined,
35
+ ): PaymentNextAction | null {
36
+ if (!na) return null
37
+ switch (na.type) {
38
+ // ─── QR-based ────────────────────────────────────────────────────────
39
+ case 'promptpay_display_qr_code': {
40
+ const d = na.promptpay_display_qr_code
41
+ if (!d) return { kind: 'wait' }
42
+ const action: PaymentNextAction = {
43
+ kind: 'display_qr',
44
+ qrData: d.data ?? '',
45
+ }
46
+ if (d.image_url_png) action.qrImageUrl = d.image_url_png
47
+ return action
48
+ }
49
+ case 'paynow_display_qr_code': {
50
+ const d = na.paynow_display_qr_code
51
+ if (!d) return { kind: 'wait' }
52
+ const action: PaymentNextAction = {
53
+ kind: 'display_qr',
54
+ qrData: d.data ?? '',
55
+ }
56
+ if (d.image_url_png) action.qrImageUrl = d.image_url_png
57
+ return action
58
+ }
59
+ case 'wechat_pay_display_qr_code': {
60
+ const d = na.wechat_pay_display_qr_code
61
+ if (!d) return { kind: 'wait' }
62
+ const action: PaymentNextAction = {
63
+ kind: 'display_qr',
64
+ qrData: d.data ?? '',
65
+ }
66
+ if (d.image_url_png) action.qrImageUrl = d.image_url_png
67
+ return action
68
+ }
69
+ case 'cashapp_handle_redirect_or_display_qr_code':
70
+ case 'swish_handle_redirect_or_display_qr_code': {
71
+ // Hybrid variant — Stripe gives both a redirect URL and a
72
+ // QR; we surface the QR (more universal for desktop checkout).
73
+ const d = (na as unknown as {
74
+ cashapp_handle_redirect_or_display_qr_code?: { qr_code?: { data?: string; image_url_png?: string }; hosted_instructions_url?: string }
75
+ swish_handle_redirect_or_display_qr_code?: { qr_code?: { data?: string; image_url_png?: string }; hosted_instructions_url?: string }
76
+ })[na.type]
77
+ const qr = d?.qr_code
78
+ if (qr?.data) {
79
+ const action: PaymentNextAction = { kind: 'display_qr', qrData: qr.data }
80
+ if (qr.image_url_png) action.qrImageUrl = qr.image_url_png
81
+ return action
82
+ }
83
+ if (d?.hosted_instructions_url) {
84
+ return { kind: 'redirect', url: d.hosted_instructions_url }
85
+ }
86
+ return { kind: 'wait' }
87
+ }
88
+ // ─── Redirect-based ─────────────────────────────────────────────────
89
+ case 'alipay_handle_redirect': {
90
+ const d = na.alipay_handle_redirect
91
+ if (!d?.url) return { kind: 'wait' }
92
+ return { kind: 'redirect', url: d.url }
93
+ }
94
+ case 'wechat_pay_redirect_to_android_app':
95
+ case 'wechat_pay_redirect_to_ios_app': {
96
+ const url = (na as unknown as {
97
+ wechat_pay_redirect_to_android_app?: { data?: string }
98
+ wechat_pay_redirect_to_ios_app?: { native_url?: string }
99
+ })[na.type]
100
+ const target =
101
+ (url as { native_url?: string })?.native_url ??
102
+ (url as { data?: string })?.data
103
+ if (!target) return { kind: 'wait' }
104
+ return { kind: 'redirect', url: target }
105
+ }
106
+ case 'redirect_to_url': {
107
+ const d = na.redirect_to_url
108
+ if (!d?.url) return { kind: 'wait' }
109
+ // 3DS card challenges + most non-card wallet redirects flow
110
+ // through this variant. Stripe doesn't tag which one — we
111
+ // pick `redirect`; apps that need to distinguish read
112
+ // `raw.next_action` (the intent's payment_method type tells
113
+ // the truth).
114
+ return { kind: 'redirect', url: d.url }
115
+ }
116
+ case 'use_stripe_sdk': {
117
+ // Card 3DS challenge — Stripe.js handles the UI, but apps
118
+ // calling server-side need to know an authorize step is
119
+ // pending. Stripe doesn't surface a server-side URL here;
120
+ // apps drive Stripe.js from the publishable key.
121
+ return { kind: 'authorize', url: '' }
122
+ }
123
+ // ─── Voucher / convenience-store ────────────────────────────────────
124
+ case 'konbini_display_details': {
125
+ const d = na.konbini_display_details
126
+ const ref =
127
+ d?.stores?.familymart?.confirmation_number ??
128
+ d?.stores?.lawson?.confirmation_number ??
129
+ d?.stores?.ministop?.confirmation_number ??
130
+ d?.stores?.seicomart?.confirmation_number ??
131
+ ''
132
+ const action: PaymentNextAction = { kind: 'voucher', reference: ref }
133
+ const expires = maybeDate(d?.expires_at)
134
+ if (expires) action.expiresAt = expires
135
+ if (d?.hosted_voucher_url) {
136
+ // Stripe doesn't expose a barcode image directly; the
137
+ // hosted voucher URL is the canonical display.
138
+ action.barcodeImageUrl = d.hosted_voucher_url
139
+ }
140
+ return action
141
+ }
142
+ case 'boleto_display_details':
143
+ case 'oxxo_display_details':
144
+ case 'display_oxxo_details':
145
+ case 'multibanco_display_details': {
146
+ const d = (na as unknown as Record<string, { number?: string; hosted_voucher_url?: string; expires_at?: number }>)[na.type]
147
+ const action: PaymentNextAction = {
148
+ kind: 'voucher',
149
+ reference: d?.number ?? '',
150
+ }
151
+ const expires = maybeDate(d?.expires_at)
152
+ if (expires) action.expiresAt = expires
153
+ if (d?.hosted_voucher_url) action.barcodeImageUrl = d.hosted_voucher_url
154
+ return action
155
+ }
156
+ // ─── No user-facing action ──────────────────────────────────────────
157
+ case 'verify_with_microdeposits':
158
+ case 'card_await_notification':
159
+ return { kind: 'wait' }
160
+ default:
161
+ return { kind: 'wait' }
162
+ }
163
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Stripe-specific provider config. Apps put one of these inside
3
+ * `config.payment.providers[name]` with `driver: 'stripe'`.
4
+ */
5
+
6
+ import type { ProviderConfig } from '../../types.ts'
7
+
8
+ export interface StripeProviderConfig extends ProviderConfig {
9
+ driver: 'stripe'
10
+ /** `sk_test_...` / `sk_live_...`. Required. */
11
+ secret: string
12
+ /** `whsec_...` from the Stripe Dashboard. Required for webhook routes. */
13
+ webhookSecret?: string
14
+ /** Pin the SDK to a specific API version. Defaults to SDK-bundled. */
15
+ apiVersion?: string
16
+ /** Optional: pass a pre-built `Stripe` instance (tests). */
17
+ client?: unknown
18
+ }