@strimz/shared-types 0.1.1 → 0.3.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.
- package/dist/api-keys.cjs +6 -1
- package/dist/api-keys.cjs.map +1 -1
- package/dist/api-keys.d.cts +12 -12
- package/dist/api-keys.d.ts +12 -12
- package/dist/api-keys.js +6 -1
- package/dist/api-keys.js.map +1 -1
- package/dist/events.cjs +25 -3
- package/dist/events.cjs.map +1 -1
- package/dist/events.d.cts +12 -12
- package/dist/events.d.ts +12 -12
- package/dist/events.js +25 -3
- package/dist/events.js.map +1 -1
- package/dist/index.cjs +186 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +296 -11
- package/dist/index.d.ts +296 -11
- package/dist/index.js +164 -5
- package/dist/index.js.map +1 -1
- package/dist/merchants.cjs +59 -1
- package/dist/merchants.cjs.map +1 -1
- package/dist/merchants.d.cts +171 -1
- package/dist/merchants.d.ts +171 -1
- package/dist/merchants.js +55 -2
- package/dist/merchants.js.map +1 -1
- package/dist/payment-sessions.cjs +6 -2
- package/dist/payment-sessions.cjs.map +1 -1
- package/dist/payment-sessions.d.cts +19 -3
- package/dist/payment-sessions.d.ts +19 -3
- package/dist/payment-sessions.js +6 -3
- package/dist/payment-sessions.js.map +1 -1
- package/dist/refunds.cjs +12 -0
- package/dist/refunds.cjs.map +1 -1
- package/dist/refunds.d.cts +134 -1
- package/dist/refunds.d.ts +134 -1
- package/dist/refunds.js +11 -1
- package/dist/refunds.js.map +1 -1
- package/dist/storefronts.cjs +18 -0
- package/dist/storefronts.cjs.map +1 -1
- package/dist/storefronts.d.cts +37 -1
- package/dist/storefronts.d.ts +37 -1
- package/dist/storefronts.js +17 -1
- package/dist/storefronts.js.map +1 -1
- package/dist/subscriptions.cjs +5 -0
- package/dist/subscriptions.cjs.map +1 -1
- package/dist/subscriptions.d.cts +13 -1
- package/dist/subscriptions.d.ts +13 -1
- package/dist/subscriptions.js +5 -1
- package/dist/subscriptions.js.map +1 -1
- package/dist/webhooks.cjs +6 -0
- package/dist/webhooks.cjs.map +1 -1
- package/dist/webhooks.d.cts +60 -1
- package/dist/webhooks.d.ts +60 -1
- package/dist/webhooks.js +6 -1
- package/dist/webhooks.js.map +1 -1
- package/package.json +1 -1
- package/src/api-keys.ts +5 -0
- package/src/contact.ts +26 -0
- package/src/index.ts +2 -0
- package/src/merchants.ts +84 -0
- package/src/notifications.ts +88 -0
- package/src/payment-sessions.ts +24 -2
- package/src/refunds.ts +19 -0
- package/src/storefronts.ts +24 -0
- package/src/subscriptions.ts +7 -0
- package/src/webhooks.ts +12 -0
package/src/index.ts
CHANGED
package/src/merchants.ts
CHANGED
|
@@ -30,6 +30,8 @@ export const merchantSchema = z.object({
|
|
|
30
30
|
tier: merchantTierSchema,
|
|
31
31
|
status: merchantStatusSchema,
|
|
32
32
|
payoutAddress: evmAddressSchema.nullable(),
|
|
33
|
+
walletAddress: evmAddressSchema.nullable(),
|
|
34
|
+
onboardingCompleted: z.boolean(),
|
|
33
35
|
defaultCurrency: z.enum(['USDC', 'EURC']),
|
|
34
36
|
countryCode: z.string().length(2).nullable(),
|
|
35
37
|
websiteUrl: z.string().url().nullable(),
|
|
@@ -52,6 +54,23 @@ export const createMerchantInputSchema = z.object({
|
|
|
52
54
|
})
|
|
53
55
|
export type CreateMerchantInput = z.infer<typeof createMerchantInputSchema>
|
|
54
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Merchant email opt-outs, persisted at `Merchant.metadata.emailPrefs`.
|
|
59
|
+
* Both default to true. Welcome, refunds, and admin broadcasts always send.
|
|
60
|
+
*/
|
|
61
|
+
export const merchantEmailPrefsSchema = z.object({
|
|
62
|
+
/** "You received X USDC" one-off payment notifications. */
|
|
63
|
+
paymentReceived: z.boolean(),
|
|
64
|
+
/** "You were paid X for subscription Y" per-charge notifications. */
|
|
65
|
+
subscriptionCharged: z.boolean(),
|
|
66
|
+
})
|
|
67
|
+
export type MerchantEmailPrefs = z.infer<typeof merchantEmailPrefsSchema>
|
|
68
|
+
|
|
69
|
+
export const DEFAULT_MERCHANT_EMAIL_PREFS: MerchantEmailPrefs = {
|
|
70
|
+
paymentReceived: true,
|
|
71
|
+
subscriptionCharged: true,
|
|
72
|
+
}
|
|
73
|
+
|
|
55
74
|
export const updateMerchantInputSchema = z
|
|
56
75
|
.object({
|
|
57
76
|
businessName: z.string().min(2).max(120),
|
|
@@ -61,6 +80,7 @@ export const updateMerchantInputSchema = z
|
|
|
61
80
|
logoUrl: z.string().url().nullable(),
|
|
62
81
|
countryCode: z.string().length(2).nullable(),
|
|
63
82
|
metadata: metadataSchema,
|
|
83
|
+
emailPrefs: merchantEmailPrefsSchema.partial(),
|
|
64
84
|
})
|
|
65
85
|
.partial()
|
|
66
86
|
export type UpdateMerchantInput = z.infer<typeof updateMerchantInputSchema>
|
|
@@ -70,6 +90,70 @@ export const changeTierInputSchema = z.object({
|
|
|
70
90
|
})
|
|
71
91
|
export type ChangeTierInput = z.infer<typeof changeTierInputSchema>
|
|
72
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Self-attested onboarding form the dashboard collects after a
|
|
95
|
+
* merchant first logs in via Privy. On success the API stamps
|
|
96
|
+
* `onboardingCompleted: true` on the row, and the dashboard exits
|
|
97
|
+
* the wizard. Shared here so the API's DTO and the web client's
|
|
98
|
+
* hook can enforce the same shape.
|
|
99
|
+
*/
|
|
100
|
+
export const onboardMerchantInputSchema = z.object({
|
|
101
|
+
businessName: z.string().min(2).max(120),
|
|
102
|
+
businessSector: z.string().min(2).max(80),
|
|
103
|
+
countryCode: z
|
|
104
|
+
.string()
|
|
105
|
+
.length(2)
|
|
106
|
+
.regex(/^[A-Z]{2}$/, 'must be a 2-letter ISO country code'),
|
|
107
|
+
websiteUrl: z.string().url().optional(),
|
|
108
|
+
logoUrl: z.string().url().optional(),
|
|
109
|
+
phone: z.string().min(6).max(20).optional(),
|
|
110
|
+
payoutAddress: evmAddressSchema,
|
|
111
|
+
defaultCurrency: z.enum(['USDC', 'EURC']).optional(),
|
|
112
|
+
})
|
|
113
|
+
export type OnboardMerchantInput = z.infer<typeof onboardMerchantInputSchema>
|
|
114
|
+
|
|
115
|
+
export const merchantPublicBrandSchema = z.object({
|
|
116
|
+
id: idSchema,
|
|
117
|
+
businessName: z.string().min(2).max(120),
|
|
118
|
+
logoUrl: z.string().url().nullable(),
|
|
119
|
+
walletAddress: evmAddressSchema.nullable(),
|
|
120
|
+
})
|
|
121
|
+
export type MerchantPublicBrand = z.infer<typeof merchantPublicBrandSchema>
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* On-chain balance view for the withdraw page. Strimz is
|
|
125
|
+
* non-custodial: payments settle directly into the merchant's
|
|
126
|
+
* `payoutAddress`, and "withdrawal" means the merchant transferring
|
|
127
|
+
* out with their own wallet. This shape reports current on-chain
|
|
128
|
+
* state so the dashboard renders live balances + decides whether to
|
|
129
|
+
* offer the signing UI.
|
|
130
|
+
*/
|
|
131
|
+
export const merchantBalanceSchema = z.object({
|
|
132
|
+
/** Payout wallet, checksummed; null when the merchant hasn't set one. */
|
|
133
|
+
payoutAddress: evmAddressSchema.nullable(),
|
|
134
|
+
/** Privy embedded wallet, checksummed if resolvable. */
|
|
135
|
+
walletAddress: evmAddressSchema.nullable(),
|
|
136
|
+
/**
|
|
137
|
+
* True when `payoutAddress === walletAddress`, i.e. the merchant
|
|
138
|
+
* can sign a transfer from the dashboard via Privy. False for
|
|
139
|
+
* merchants using an external wallet as payout — the UI falls back
|
|
140
|
+
* to "copy the address, transfer with your wallet."
|
|
141
|
+
*/
|
|
142
|
+
canSignFromDashboard: z.boolean(),
|
|
143
|
+
balances: z.array(
|
|
144
|
+
z.object({
|
|
145
|
+
currency: z.enum(['USDC', 'EURC']),
|
|
146
|
+
contractAddress: evmAddressSchema,
|
|
147
|
+
/** Base-6 integer as string. */
|
|
148
|
+
raw: z.string(),
|
|
149
|
+
/** Human-formatted, e.g. `1234.56`. */
|
|
150
|
+
formatted: z.string(),
|
|
151
|
+
decimals: z.number().int().min(0).max(30),
|
|
152
|
+
}),
|
|
153
|
+
),
|
|
154
|
+
})
|
|
155
|
+
export type MerchantBalanceView = z.infer<typeof merchantBalanceSchema>
|
|
156
|
+
|
|
73
157
|
// ---------- Team members ----------
|
|
74
158
|
|
|
75
159
|
export const merchantMemberSchema = z.object({
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard notifications. Derived server-side from recent activity
|
|
3
|
+
* (confirmed payment sessions, new subscribers, processed refunds).
|
|
4
|
+
* Read state is tracked on the merchant record via
|
|
5
|
+
* `notificationsLastReadAt` — `event.createdAt <= that timestamp` is
|
|
6
|
+
* "read", anything newer is "unread". No dedicated notifications
|
|
7
|
+
* table because the source rows already exist; a separate table would
|
|
8
|
+
* just be a denormalised copy.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { z } from 'zod'
|
|
12
|
+
import { idSchema, isoTimestampSchema } from './common.js'
|
|
13
|
+
|
|
14
|
+
export const notificationKindSchema = z.enum([
|
|
15
|
+
'payment_received',
|
|
16
|
+
'subscription_started',
|
|
17
|
+
'refund_processed',
|
|
18
|
+
/** Message an admin broadcast to this merchant (or all merchants). */
|
|
19
|
+
'admin_broadcast',
|
|
20
|
+
])
|
|
21
|
+
export type NotificationKind = z.infer<typeof notificationKindSchema>
|
|
22
|
+
|
|
23
|
+
// ---------- Admin broadcasts ----------
|
|
24
|
+
|
|
25
|
+
export const broadcastAudienceSchema = z.enum(['all', 'merchant'])
|
|
26
|
+
export type BroadcastAudience = z.infer<typeof broadcastAudienceSchema>
|
|
27
|
+
|
|
28
|
+
export const createBroadcastInputSchema = z
|
|
29
|
+
.object({
|
|
30
|
+
title: z.string().min(4).max(160),
|
|
31
|
+
body: z.string().min(4).max(4000),
|
|
32
|
+
audience: broadcastAudienceSchema.default('all'),
|
|
33
|
+
/** Required when `audience === 'merchant'`; ignored otherwise. */
|
|
34
|
+
merchantId: z.string().optional(),
|
|
35
|
+
})
|
|
36
|
+
.refine((v) => v.audience !== 'merchant' || !!v.merchantId, {
|
|
37
|
+
message: 'merchantId is required when audience is merchant',
|
|
38
|
+
path: ['merchantId'],
|
|
39
|
+
})
|
|
40
|
+
export type CreateBroadcastInput = z.infer<typeof createBroadcastInputSchema>
|
|
41
|
+
|
|
42
|
+
export const broadcastSchema = z.object({
|
|
43
|
+
id: idSchema,
|
|
44
|
+
title: z.string(),
|
|
45
|
+
body: z.string(),
|
|
46
|
+
audience: broadcastAudienceSchema,
|
|
47
|
+
merchantId: idSchema.nullable(),
|
|
48
|
+
merchantEmail: z.string().nullable(),
|
|
49
|
+
senderId: idSchema,
|
|
50
|
+
senderEmail: z.string(),
|
|
51
|
+
emailedAt: isoTimestampSchema.nullable(),
|
|
52
|
+
createdAt: isoTimestampSchema,
|
|
53
|
+
})
|
|
54
|
+
export type Broadcast = z.infer<typeof broadcastSchema>
|
|
55
|
+
|
|
56
|
+
export const broadcastListResponseSchema = z.object({
|
|
57
|
+
data: z.array(broadcastSchema),
|
|
58
|
+
})
|
|
59
|
+
export type BroadcastListResponse = z.infer<typeof broadcastListResponseSchema>
|
|
60
|
+
|
|
61
|
+
export const notificationSchema = z.object({
|
|
62
|
+
/** Deterministic client id — `{kind}:{sourceId}`. Safe for React keys. */
|
|
63
|
+
id: z.string().min(1),
|
|
64
|
+
kind: notificationKindSchema,
|
|
65
|
+
title: z.string().min(1),
|
|
66
|
+
detail: z.string(),
|
|
67
|
+
/** Dashboard route the tray links to. */
|
|
68
|
+
href: z.string().min(1),
|
|
69
|
+
/** ISO timestamp of the source event; drives sort + read comparison. */
|
|
70
|
+
createdAt: isoTimestampSchema,
|
|
71
|
+
/** Server-derived from the merchant's `notificationsLastReadAt`. */
|
|
72
|
+
read: z.boolean(),
|
|
73
|
+
/** Optional foreign key back to the source row (for deep-linking). */
|
|
74
|
+
sourceId: idSchema.optional(),
|
|
75
|
+
})
|
|
76
|
+
export type NotificationView = z.infer<typeof notificationSchema>
|
|
77
|
+
|
|
78
|
+
export const notificationListResponseSchema = z.object({
|
|
79
|
+
data: z.array(notificationSchema),
|
|
80
|
+
unreadCount: z.number().int().min(0),
|
|
81
|
+
lastReadAt: isoTimestampSchema.nullable(),
|
|
82
|
+
})
|
|
83
|
+
export type NotificationListResponse = z.infer<typeof notificationListResponseSchema>
|
|
84
|
+
|
|
85
|
+
export const markNotificationsReadResponseSchema = z.object({
|
|
86
|
+
lastReadAt: isoTimestampSchema,
|
|
87
|
+
})
|
|
88
|
+
export type MarkNotificationsReadResponse = z.infer<typeof markNotificationsReadResponseSchema>
|
package/src/payment-sessions.ts
CHANGED
|
@@ -31,6 +31,28 @@ export const paymentSessionStatusSchema = z.enum([
|
|
|
31
31
|
])
|
|
32
32
|
export type PaymentSessionStatus = z.infer<typeof paymentSessionStatusSchema>
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Merchant-supplied callback URL. The hosted checkout dispatches with
|
|
36
|
+
* `window.location.href = redirectUrl` on completion / cancellation,
|
|
37
|
+
* so we MUST reject anything other than `http://` and `https://`.
|
|
38
|
+
*
|
|
39
|
+
* Why this is a real risk: Zod's `.url()` validates RFC syntax, which
|
|
40
|
+
* accepts `javascript:`, `data:`, `vbscript:`, `file:`, blob:`, etc.
|
|
41
|
+
* A compromised merchant integration or an XSS upstream could ship a
|
|
42
|
+
* session whose redirect URL executes script (or exfiltrates session
|
|
43
|
+
* tokens) on every payer's browser. The damage is per-payer, but the
|
|
44
|
+
* blast radius is "every checkout the merchant runs."
|
|
45
|
+
*
|
|
46
|
+
* Mailto / tel and other handler protocols are also rejected — there
|
|
47
|
+
* is no legitimate flow on a payment checkout that would benefit.
|
|
48
|
+
*/
|
|
49
|
+
export const callbackUrlSchema = z
|
|
50
|
+
.string()
|
|
51
|
+
.url()
|
|
52
|
+
.refine((v) => v.startsWith('https://') || v.startsWith('http://'), {
|
|
53
|
+
message: 'must be an http:// or https:// URL',
|
|
54
|
+
})
|
|
55
|
+
|
|
34
56
|
export const paymentSessionSchema = z.object({
|
|
35
57
|
id: idSchema,
|
|
36
58
|
merchantId: idSchema,
|
|
@@ -63,8 +85,8 @@ export const paymentSessionSchema = z.object({
|
|
|
63
85
|
description: z.string().max(500).nullable(),
|
|
64
86
|
payerWalletAddress: evmAddressSchema.nullable(),
|
|
65
87
|
payerEmail: z.string().email().nullable(),
|
|
66
|
-
successUrl:
|
|
67
|
-
cancelUrl:
|
|
88
|
+
successUrl: callbackUrlSchema.nullable(),
|
|
89
|
+
cancelUrl: callbackUrlSchema.nullable(),
|
|
68
90
|
sourceChain: z
|
|
69
91
|
.enum(['arc', 'ethereum', 'base', 'polygon', 'arbitrum', 'optimism', 'avalanche', 'solana'])
|
|
70
92
|
.nullable(),
|
package/src/refunds.ts
CHANGED
|
@@ -66,3 +66,22 @@ export const submitRefundSignatureInputSchema = z.object({
|
|
|
66
66
|
refundTxHash: evmTxHashSchema,
|
|
67
67
|
})
|
|
68
68
|
export type SubmitRefundSignatureInput = z.infer<typeof submitRefundSignatureInputSchema>
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Response envelope for `POST /v1/refunds`. The API returns the newly
|
|
72
|
+
* created refund plus the concrete ERC-20 transfer the merchant now
|
|
73
|
+
* has to sign from their payout wallet.
|
|
74
|
+
*/
|
|
75
|
+
export const refundSigningInstructionsSchema = z.object({
|
|
76
|
+
token: evmAddressSchema,
|
|
77
|
+
to: evmAddressSchema,
|
|
78
|
+
amount: tokenAmountSchema,
|
|
79
|
+
note: z.string(),
|
|
80
|
+
})
|
|
81
|
+
export type RefundSigningInstructions = z.infer<typeof refundSigningInstructionsSchema>
|
|
82
|
+
|
|
83
|
+
export const refundCreateOutputSchema = z.object({
|
|
84
|
+
refund: refundSchema,
|
|
85
|
+
signingInstructions: refundSigningInstructionsSchema,
|
|
86
|
+
})
|
|
87
|
+
export type RefundCreateOutput = z.infer<typeof refundCreateOutputSchema>
|
package/src/storefronts.ts
CHANGED
|
@@ -87,3 +87,27 @@ export const createStorefrontProductInputSchema = storefrontProductSchema
|
|
|
87
87
|
imageUrl: httpsUrlSchema.optional(),
|
|
88
88
|
})
|
|
89
89
|
export type CreateStorefrontProductInput = z.infer<typeof createStorefrontProductInputSchema>
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Payload for the public `/store/:slug/products/:id/checkout` endpoint.
|
|
93
|
+
* Optional customer email flows through into the payment session so
|
|
94
|
+
* the receipt lands in the buyer's inbox after the tx confirms.
|
|
95
|
+
* `returnPath` is used to synthesise the success + cancel URLs when
|
|
96
|
+
* the merchant hasn't wired their own.
|
|
97
|
+
*/
|
|
98
|
+
export const storefrontCheckoutInputSchema = z.object({
|
|
99
|
+
customerEmail: z.string().email().optional(),
|
|
100
|
+
returnPath: z.string().max(200).optional(),
|
|
101
|
+
})
|
|
102
|
+
export type StorefrontCheckoutInput = z.infer<typeof storefrontCheckoutInputSchema>
|
|
103
|
+
|
|
104
|
+
/** Response shape from the public product-checkout endpoint. */
|
|
105
|
+
export const storefrontCheckoutResponseSchema = z.object({
|
|
106
|
+
/** Fully-qualified checkout URL — payer redirects here to sign. */
|
|
107
|
+
checkoutUrl: z.string().url(),
|
|
108
|
+
/** Payment-session id (one-time) or plan id (subscription). */
|
|
109
|
+
ref: idSchema,
|
|
110
|
+
/** Which downstream checkout the URL points at. */
|
|
111
|
+
kind: z.enum(['payment_session', 'subscription_plan']),
|
|
112
|
+
})
|
|
113
|
+
export type StorefrontCheckoutResponse = z.infer<typeof storefrontCheckoutResponseSchema>
|
package/src/subscriptions.ts
CHANGED
|
@@ -119,6 +119,13 @@ export const subscriptionSchema = z.object({
|
|
|
119
119
|
})
|
|
120
120
|
export type Subscription = z.infer<typeof subscriptionSchema>
|
|
121
121
|
|
|
122
|
+
/** Whether a wallet already subscribes to a given plan, and which one. */
|
|
123
|
+
export const subscriptionStatusResultSchema = z.object({
|
|
124
|
+
active: z.boolean(),
|
|
125
|
+
subscriptionId: idSchema.nullable(),
|
|
126
|
+
})
|
|
127
|
+
export type SubscriptionStatusResult = z.infer<typeof subscriptionStatusResultSchema>
|
|
128
|
+
|
|
122
129
|
export const createSubscriptionInputSchema = z.object({
|
|
123
130
|
planId: idSchema,
|
|
124
131
|
customer: z.object({
|
package/src/webhooks.ts
CHANGED
|
@@ -103,6 +103,18 @@ export const webhookDeliverySchema = z.object({
|
|
|
103
103
|
})
|
|
104
104
|
export type WebhookDelivery = z.infer<typeof webhookDeliverySchema>
|
|
105
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Detail projection returned by GET /v1/webhook-deliveries/:id.
|
|
108
|
+
* Carries the payload envelope, response body, and the deliveryId
|
|
109
|
+
* header the receiver saw. Kept separate so list responses stay slim.
|
|
110
|
+
*/
|
|
111
|
+
export const webhookDeliveryDetailSchema = webhookDeliverySchema.extend({
|
|
112
|
+
deliveryId: z.string(),
|
|
113
|
+
responseBody: z.string().nullable(),
|
|
114
|
+
requestPayload: z.record(z.string(), z.unknown()),
|
|
115
|
+
})
|
|
116
|
+
export type WebhookDeliveryDetail = z.infer<typeof webhookDeliveryDetailSchema>
|
|
117
|
+
|
|
106
118
|
export const replayDeliveryInputSchema = z.object({
|
|
107
119
|
id: idSchema,
|
|
108
120
|
})
|