payweave 0.0.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/LICENSE +21 -0
- package/README.md +272 -0
- package/dist/_tsup-dts-rollup.d.ts +19470 -0
- package/dist/chunk-4R5OGOCC.js +94 -0
- package/dist/chunk-4R5OGOCC.js.map +1 -0
- package/dist/chunk-535Q3GUN.js +175 -0
- package/dist/chunk-535Q3GUN.js.map +1 -0
- package/dist/chunk-67NRYGRC.js +192 -0
- package/dist/chunk-67NRYGRC.js.map +1 -0
- package/dist/chunk-CGQ73I4I.js +568 -0
- package/dist/chunk-CGQ73I4I.js.map +1 -0
- package/dist/chunk-CJEYDHY6.js +18 -0
- package/dist/chunk-CJEYDHY6.js.map +1 -0
- package/dist/chunk-CR63JAAS.js +3 -0
- package/dist/chunk-CR63JAAS.js.map +1 -0
- package/dist/chunk-E633MKVX.js +1507 -0
- package/dist/chunk-E633MKVX.js.map +1 -0
- package/dist/chunk-FBPOCXRZ.js +1135 -0
- package/dist/chunk-FBPOCXRZ.js.map +1 -0
- package/dist/chunk-HTRGMZFH.js +267 -0
- package/dist/chunk-HTRGMZFH.js.map +1 -0
- package/dist/chunk-IUVGM3K7.js +765 -0
- package/dist/chunk-IUVGM3K7.js.map +1 -0
- package/dist/chunk-JD4OZHRI.js +326 -0
- package/dist/chunk-JD4OZHRI.js.map +1 -0
- package/dist/chunk-NV3MZF7L.js +139 -0
- package/dist/chunk-NV3MZF7L.js.map +1 -0
- package/dist/chunk-NXQBP35U.js +13 -0
- package/dist/chunk-NXQBP35U.js.map +1 -0
- package/dist/chunk-QJHXDZYC.js +412 -0
- package/dist/chunk-QJHXDZYC.js.map +1 -0
- package/dist/chunk-TWZK62TZ.js +83 -0
- package/dist/chunk-TWZK62TZ.js.map +1 -0
- package/dist/cli/index.js +35381 -0
- package/dist/core/index.d.ts +85 -0
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -0
- package/dist/db/drizzle/index.d.ts +6 -0
- package/dist/db/drizzle/index.js +1532 -0
- package/dist/db/drizzle/index.js.map +1 -0
- package/dist/db/index.d.ts +43 -0
- package/dist/db/index.js +3 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/mongodb/index.d.ts +3 -0
- package/dist/db/mongodb/index.js +934 -0
- package/dist/db/mongodb/index.js.map +1 -0
- package/dist/db/mysql/index.d.ts +1 -0
- package/dist/db/mysql/index.js +12 -0
- package/dist/db/mysql/index.js.map +1 -0
- package/dist/db/postgres/index.d.ts +5 -0
- package/dist/db/postgres/index.js +791 -0
- package/dist/db/postgres/index.js.map +1 -0
- package/dist/db/prisma/index.d.ts +1 -0
- package/dist/db/prisma/index.js +12 -0
- package/dist/db/prisma/index.js.map +1 -0
- package/dist/db/sqlite/index.d.ts +5 -0
- package/dist/db/sqlite/index.js +750 -0
- package/dist/db/sqlite/index.js.map +1 -0
- package/dist/express/index.d.ts +1 -0
- package/dist/express/index.js +3 -0
- package/dist/express/index.js.map +1 -0
- package/dist/fastify/index.d.ts +1 -0
- package/dist/fastify/index.js +3 -0
- package/dist/fastify/index.js.map +1 -0
- package/dist/flutterwave/index.d.ts +27 -0
- package/dist/flutterwave/index.js +9 -0
- package/dist/flutterwave/index.js.map +1 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +3677 -0
- package/dist/index.js.map +1 -0
- package/dist/next/index.d.ts +1 -0
- package/dist/next/index.js +3 -0
- package/dist/next/index.js.map +1 -0
- package/dist/paystack/index.d.ts +33 -0
- package/dist/paystack/index.js +9 -0
- package/dist/paystack/index.js.map +1 -0
- package/dist/products/index.d.ts +22 -0
- package/dist/products/index.js +7 -0
- package/dist/products/index.js.map +1 -0
- package/dist/testing/index.d.ts +11 -0
- package/dist/testing/index.js +69 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/unified/index.d.ts +41 -0
- package/dist/unified/index.js +6 -0
- package/dist/unified/index.js.map +1 -0
- package/dist/webhooks/index.d.ts +8 -0
- package/dist/webhooks/index.js +8 -0
- package/dist/webhooks/index.js.map +1 -0
- package/package.json +164 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3677 @@
|
|
|
1
|
+
import './chunk-CR63JAAS.js';
|
|
2
|
+
import { PaystackClient } from './chunk-E633MKVX.js';
|
|
3
|
+
export { PaystackClient } from './chunk-E633MKVX.js';
|
|
4
|
+
import { FlutterwaveClient } from './chunk-FBPOCXRZ.js';
|
|
5
|
+
export { FlutterwaveClient } from './chunk-FBPOCXRZ.js';
|
|
6
|
+
import { defineProvider, STRIPE_BASE_URL, stripeProviderConfigSchema, readHeader, inferEnvironment, HttpClient, STRIPE_API_VERSION, resolvePayweaveConfig, SDK_VERSION, oauthClientCredentials, bearer } from './chunk-IUVGM3K7.js';
|
|
7
|
+
export { SDK_VERSION as VERSION } from './chunk-IUVGM3K7.js';
|
|
8
|
+
export { feature, plan } from './chunk-535Q3GUN.js';
|
|
9
|
+
import { createPaystackUnified, createFlutterwaveUnified } from './chunk-JD4OZHRI.js';
|
|
10
|
+
import './chunk-TWZK62TZ.js';
|
|
11
|
+
import { verifyStripe, subscribe, constructEvent, resolvePlanGroup, BILLING_CAPABLE_PROVIDERS } from './chunk-CGQ73I4I.js';
|
|
12
|
+
import './chunk-4R5OGOCC.js';
|
|
13
|
+
import { verifyPaystack } from './chunk-NXQBP35U.js';
|
|
14
|
+
import { verifyFlutterwaveV4, verifyFlutterwaveV3 } from './chunk-CJEYDHY6.js';
|
|
15
|
+
import { toUnifiedEventType, UNIFIED_CAPABILITY_MATRIX, assertUnifiedCapability } from './chunk-67NRYGRC.js';
|
|
16
|
+
import { PayweaveConfigError, PayweaveValidationError, PayweaveWebhookVerificationError, PayweaveProviderError, PayweaveError } from './chunk-HTRGMZFH.js';
|
|
17
|
+
export { PayweaveAuthError, PayweaveConfigError, PayweaveError, PayweaveNetworkError, PayweaveNotFoundError, PayweaveProviderError, PayweaveRateLimitError, PayweaveValidationError, PayweaveWebhookVerificationError } from './chunk-HTRGMZFH.js';
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import { createHash } from 'crypto';
|
|
20
|
+
|
|
21
|
+
// src/webhooks/dispatch.ts
|
|
22
|
+
var SIGNATURE_HEADER_TABLE = [
|
|
23
|
+
{ headerName: "x-paystack-signature", provider: "paystack" },
|
|
24
|
+
{ headerName: "verif-hash", provider: "flutterwave", flutterwaveVersion: "v3" },
|
|
25
|
+
{ headerName: "flutterwave-signature", provider: "flutterwave", flutterwaveVersion: "v4" },
|
|
26
|
+
{ headerName: "stripe-signature", provider: "stripe" }
|
|
27
|
+
];
|
|
28
|
+
var KNOWN_HEADER_NAMES = SIGNATURE_HEADER_TABLE.map((spec) => spec.headerName).join(", ");
|
|
29
|
+
function detectSignatureHeader(headers) {
|
|
30
|
+
const found = [];
|
|
31
|
+
for (const spec of SIGNATURE_HEADER_TABLE) {
|
|
32
|
+
const value = readHeader(headers, spec.headerName);
|
|
33
|
+
if (value !== void 0) found.push({ spec, value });
|
|
34
|
+
}
|
|
35
|
+
const [only, ...rest] = found;
|
|
36
|
+
if (only === void 0) {
|
|
37
|
+
throw new PayweaveWebhookVerificationError(
|
|
38
|
+
`No known webhook signature header on the request \u2014 expected exactly one of: ${KNOWN_HEADER_NAMES}.`,
|
|
39
|
+
{ provider: "unknown", isRetryable: false }
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (rest.length > 0) {
|
|
43
|
+
const names = found.map((f) => f.spec.headerName).join(", ");
|
|
44
|
+
throw new PayweaveWebhookVerificationError(
|
|
45
|
+
`Multiple known webhook signature headers on one request (${names}) \u2014 ambiguous and likely forged; rejecting.`,
|
|
46
|
+
{ provider: "unknown", isRetryable: false }
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return only;
|
|
50
|
+
}
|
|
51
|
+
function requireConfigured(resolved, spec) {
|
|
52
|
+
const entry = resolved.providerConfigs[spec.provider];
|
|
53
|
+
if (entry === void 0) {
|
|
54
|
+
throw new PayweaveWebhookVerificationError(
|
|
55
|
+
`Received a "${spec.headerName}" header but ${spec.provider} is not configured on this client (configured: ${resolved.providers.join(", ")}) \u2014 failing closed.`,
|
|
56
|
+
{ provider: spec.provider, isRetryable: false }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
if (spec.provider === "flutterwave") {
|
|
60
|
+
const configuredVersion = entry.version === "v4" ? "v4" : "v3";
|
|
61
|
+
if (spec.flutterwaveVersion !== configuredVersion) {
|
|
62
|
+
throw new PayweaveWebhookVerificationError(
|
|
63
|
+
`Received the Flutterwave ${spec.flutterwaveVersion} signature header ("${spec.headerName}") but this client is configured for Flutterwave ${configuredVersion} \u2014 a client verifies webhooks only with its own version's scheme; failing closed.`,
|
|
64
|
+
{ provider: "flutterwave", isRetryable: false }
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return entry;
|
|
69
|
+
}
|
|
70
|
+
function verifyDetected(entry, spec, input) {
|
|
71
|
+
if (spec.provider === "paystack") {
|
|
72
|
+
const secret = entry.webhookSecret ?? entry.secretKey;
|
|
73
|
+
if (!secret) {
|
|
74
|
+
throw new PayweaveConfigError("Missing Paystack secret key for webhook verification.", {
|
|
75
|
+
provider: "paystack"
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return verifyPaystack(input.rawBody, readHeader(input.headers, spec.headerName), secret);
|
|
79
|
+
}
|
|
80
|
+
if (spec.provider === "flutterwave") {
|
|
81
|
+
if (!entry.webhookSecret) {
|
|
82
|
+
throw new PayweaveConfigError(
|
|
83
|
+
"Flutterwave webhook verification requires `webhookSecret` (the dashboard secret hash).",
|
|
84
|
+
{ provider: "flutterwave" }
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (spec.flutterwaveVersion === "v4") {
|
|
88
|
+
return verifyFlutterwaveV4(
|
|
89
|
+
input.rawBody,
|
|
90
|
+
readHeader(input.headers, spec.headerName),
|
|
91
|
+
entry.webhookSecret
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return verifyFlutterwaveV3(readHeader(input.headers, spec.headerName), entry.webhookSecret);
|
|
95
|
+
}
|
|
96
|
+
if (!entry.webhookSecret) {
|
|
97
|
+
throw new PayweaveConfigError(
|
|
98
|
+
"Stripe webhook verification requires `webhookSecret` (the endpoint signing secret, `whsec_*`).",
|
|
99
|
+
{ provider: "stripe" }
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
return verifyStripe(input.rawBody, readHeader(input.headers, spec.headerName), entry.webhookSecret);
|
|
103
|
+
}
|
|
104
|
+
function createWebhookDispatch(resolved, billing) {
|
|
105
|
+
function dispatch(headers) {
|
|
106
|
+
const { spec } = detectSignatureHeader(headers);
|
|
107
|
+
return { spec, entry: requireConfigured(resolved, spec) };
|
|
108
|
+
}
|
|
109
|
+
function verify(input) {
|
|
110
|
+
const { spec, entry } = dispatch(input.headers);
|
|
111
|
+
return verifyDetected(entry, spec, input);
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
verify,
|
|
115
|
+
verifyOrThrow(input) {
|
|
116
|
+
const { spec, entry } = dispatch(input.headers);
|
|
117
|
+
if (!verifyDetected(entry, spec, input)) {
|
|
118
|
+
throw new PayweaveWebhookVerificationError("Webhook signature verification failed.", {
|
|
119
|
+
provider: spec.provider,
|
|
120
|
+
isRetryable: false
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
constructEvent(input) {
|
|
125
|
+
const { spec, entry } = dispatch(input.headers);
|
|
126
|
+
const version = spec.provider === "flutterwave" ? entry.version === "v4" ? "v4" : "v3" : void 0;
|
|
127
|
+
return constructEvent({
|
|
128
|
+
rawBody: input.rawBody,
|
|
129
|
+
headers: input.headers,
|
|
130
|
+
provider: spec.provider,
|
|
131
|
+
version,
|
|
132
|
+
verify: (i) => verifyDetected(entry, spec, i),
|
|
133
|
+
logger: entry.logger,
|
|
134
|
+
billing
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/stripe/form-encoding.ts
|
|
141
|
+
var STRIPE_FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
|
|
142
|
+
function isPlainObject(value) {
|
|
143
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
144
|
+
const proto = Object.getPrototypeOf(value);
|
|
145
|
+
return proto === Object.prototype || proto === null;
|
|
146
|
+
}
|
|
147
|
+
function describe(value) {
|
|
148
|
+
if (typeof value === "number") return `non-finite number ${String(value)}`;
|
|
149
|
+
if (value instanceof Date) return "Date (pass a Unix timestamp in seconds instead)";
|
|
150
|
+
return `unsupported value of type ${typeof value}`;
|
|
151
|
+
}
|
|
152
|
+
function unsupported(path, value) {
|
|
153
|
+
return new PayweaveValidationError(
|
|
154
|
+
`Cannot form-encode "${path}" for Stripe: ${describe(value)}.`,
|
|
155
|
+
{ provider: "stripe" }
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
function appendPairs(pairs, key, value) {
|
|
159
|
+
if (value === void 0 || value === null) return;
|
|
160
|
+
switch (typeof value) {
|
|
161
|
+
case "string":
|
|
162
|
+
pairs.push(`${key}=${encodeURIComponent(value)}`);
|
|
163
|
+
return;
|
|
164
|
+
case "boolean":
|
|
165
|
+
pairs.push(`${key}=${value ? "true" : "false"}`);
|
|
166
|
+
return;
|
|
167
|
+
case "number":
|
|
168
|
+
if (!Number.isFinite(value)) throw unsupported(key, value);
|
|
169
|
+
pairs.push(`${key}=${encodeURIComponent(String(value))}`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (Array.isArray(value)) {
|
|
173
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
174
|
+
appendPairs(pairs, `${key}[${i}]`, value[i]);
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (isPlainObject(value)) {
|
|
179
|
+
for (const [childKey, childValue] of Object.entries(value)) {
|
|
180
|
+
appendPairs(pairs, `${key}[${encodeURIComponent(childKey)}]`, childValue);
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
throw unsupported(key, value);
|
|
185
|
+
}
|
|
186
|
+
function encodeStripeForm(body) {
|
|
187
|
+
if (!isPlainObject(body)) {
|
|
188
|
+
throw new PayweaveValidationError(
|
|
189
|
+
"Stripe request bodies must be plain objects (validated by the request schema before encoding).",
|
|
190
|
+
{ provider: "stripe" }
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
const pairs = [];
|
|
194
|
+
for (const [key, value] of Object.entries(body)) {
|
|
195
|
+
appendPairs(pairs, encodeURIComponent(key), value);
|
|
196
|
+
}
|
|
197
|
+
return { contentType: STRIPE_FORM_CONTENT_TYPE, body: pairs.join("&") };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/stripe/http-options.ts
|
|
201
|
+
function stripeAuth(opts) {
|
|
202
|
+
const version = opts.apiVersion ?? STRIPE_API_VERSION;
|
|
203
|
+
return {
|
|
204
|
+
async applyAuth(init) {
|
|
205
|
+
init.headers.set("Authorization", `Bearer ${opts.secretKey}`);
|
|
206
|
+
init.headers.set("Stripe-Version", version);
|
|
207
|
+
if (opts.accountId !== void 0) {
|
|
208
|
+
init.headers.set("Stripe-Account", opts.accountId);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function stripeHttpOptions(resolved) {
|
|
214
|
+
if (resolved.provider !== "stripe") {
|
|
215
|
+
throw new PayweaveConfigError(
|
|
216
|
+
`stripeHttpOptions received a "${resolved.provider}" config \u2014 expected provider "stripe".`,
|
|
217
|
+
{ provider: "stripe" }
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
if (resolved.secretKey === void 0) {
|
|
221
|
+
throw new PayweaveConfigError("Stripe config resolved without a secretKey.", {
|
|
222
|
+
provider: "stripe"
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
baseUrl: resolved.baseUrl,
|
|
227
|
+
provider: "stripe",
|
|
228
|
+
auth: stripeAuth({
|
|
229
|
+
secretKey: resolved.secretKey,
|
|
230
|
+
apiVersion: resolved.apiVersion,
|
|
231
|
+
accountId: resolved.accountId
|
|
232
|
+
}),
|
|
233
|
+
bodyEncoder: encodeStripeForm,
|
|
234
|
+
timeoutMs: resolved.timeoutMs,
|
|
235
|
+
maxRetries: resolved.maxRetries,
|
|
236
|
+
fetch: resolved.fetch,
|
|
237
|
+
logger: resolved.logger
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function parseRequest(schema, input) {
|
|
241
|
+
try {
|
|
242
|
+
return schema.parse(input);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
if (err instanceof z.ZodError) {
|
|
245
|
+
const detail = err.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
246
|
+
throw new PayweaveValidationError(`stripe request validation failed \u2014 ${detail}`, {
|
|
247
|
+
provider: "stripe",
|
|
248
|
+
cause: err
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
throw err;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
var metadataSchema = z.record(
|
|
255
|
+
z.string(),
|
|
256
|
+
z.union([z.string(), z.number(), z.boolean()])
|
|
257
|
+
);
|
|
258
|
+
var createdRange = z.object({
|
|
259
|
+
gt: z.number().int().optional(),
|
|
260
|
+
gte: z.number().int().optional(),
|
|
261
|
+
lt: z.number().int().optional(),
|
|
262
|
+
lte: z.number().int().optional()
|
|
263
|
+
});
|
|
264
|
+
var listCursorFields = {
|
|
265
|
+
/** Page size, 1–100 (Stripe default 10). */
|
|
266
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
267
|
+
/** Cursor: object id — return items AFTER this id (next page). */
|
|
268
|
+
starting_after: z.string().optional(),
|
|
269
|
+
/** Cursor: object id — return items BEFORE this id (previous page). */
|
|
270
|
+
ending_before: z.string().optional()
|
|
271
|
+
};
|
|
272
|
+
var stripeList = (item) => z.looseObject({
|
|
273
|
+
object: z.string(),
|
|
274
|
+
data: z.array(item),
|
|
275
|
+
has_more: z.boolean(),
|
|
276
|
+
url: z.string().optional()
|
|
277
|
+
});
|
|
278
|
+
async function* iterateStripeList(fetchPage) {
|
|
279
|
+
let startingAfter;
|
|
280
|
+
for (; ; ) {
|
|
281
|
+
const page = await fetchPage(startingAfter);
|
|
282
|
+
for (const item of page.data) yield item;
|
|
283
|
+
const last = page.data[page.data.length - 1];
|
|
284
|
+
if (!page.has_more || last === void 0) return;
|
|
285
|
+
startingAfter = last.id;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function flattenQuery(query) {
|
|
289
|
+
const out = {};
|
|
290
|
+
for (const [key, value] of Object.entries(query)) {
|
|
291
|
+
if (value === void 0 || value === null) continue;
|
|
292
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
293
|
+
for (const [childKey, childValue] of Object.entries(value)) {
|
|
294
|
+
if (childValue === void 0 || childValue === null) continue;
|
|
295
|
+
out[`${key}[${childKey}]`] = childValue;
|
|
296
|
+
}
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
out[key] = value;
|
|
300
|
+
}
|
|
301
|
+
return out;
|
|
302
|
+
}
|
|
303
|
+
function requireId(value, what) {
|
|
304
|
+
return parseRequest(z.string().min(1, `${what} id must be a non-empty string`), value);
|
|
305
|
+
}
|
|
306
|
+
var stripeSearchResult = (item) => z.looseObject({
|
|
307
|
+
object: z.string(),
|
|
308
|
+
data: z.array(item),
|
|
309
|
+
has_more: z.boolean(),
|
|
310
|
+
next_page: z.string().nullable().optional(),
|
|
311
|
+
url: z.string().optional(),
|
|
312
|
+
total_count: z.number().optional()
|
|
313
|
+
});
|
|
314
|
+
async function* iterateStripeSearch(fetchPage) {
|
|
315
|
+
let page;
|
|
316
|
+
for (; ; ) {
|
|
317
|
+
const result = await fetchPage(page);
|
|
318
|
+
for (const item of result.data) yield item;
|
|
319
|
+
if (!result.has_more || result.next_page == null) return;
|
|
320
|
+
page = result.next_page;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
var productData = z.object({
|
|
324
|
+
/** Product name — required by Stripe when creating a product inline. */
|
|
325
|
+
name: z.string(),
|
|
326
|
+
description: z.string().optional(),
|
|
327
|
+
images: z.array(z.string()).optional(),
|
|
328
|
+
metadata: metadataSchema.optional(),
|
|
329
|
+
tax_code: z.string().optional()
|
|
330
|
+
});
|
|
331
|
+
var recurring = z.object({
|
|
332
|
+
interval: z.enum(["day", "week", "month", "year"]),
|
|
333
|
+
interval_count: z.number().int().optional()
|
|
334
|
+
});
|
|
335
|
+
var priceData = z.object({
|
|
336
|
+
/** Three-letter ISO currency code, lowercase (e.g. `usd`). */
|
|
337
|
+
currency: z.string(),
|
|
338
|
+
/** Amount in the smallest currency unit (minor units) — integer, unchanged. */
|
|
339
|
+
unit_amount: z.number().int().nonnegative().optional(),
|
|
340
|
+
/** Decimal string alternative to `unit_amount` (e.g. fractional cents). */
|
|
341
|
+
unit_amount_decimal: z.string().optional(),
|
|
342
|
+
/** Existing Product id (`prod_*`) the price belongs to. */
|
|
343
|
+
product: z.string().optional(),
|
|
344
|
+
/** Inline Product creation (alternative to `product`). */
|
|
345
|
+
product_data: productData.optional(),
|
|
346
|
+
/** Present only for `subscription` mode line items. */
|
|
347
|
+
recurring: recurring.optional(),
|
|
348
|
+
tax_behavior: z.enum(["inclusive", "exclusive", "unspecified"]).optional()
|
|
349
|
+
});
|
|
350
|
+
var lineItemInput = z.object({
|
|
351
|
+
/** Saved Price (or Plan) id (`price_*`). Alternative to `price_data`. */
|
|
352
|
+
price: z.string().optional(),
|
|
353
|
+
/** Inline price definition. Alternative to `price`. */
|
|
354
|
+
price_data: priceData.optional(),
|
|
355
|
+
quantity: z.number().int().optional(),
|
|
356
|
+
/** Let the customer adjust the quantity in Checkout. */
|
|
357
|
+
adjustable_quantity: z.object({
|
|
358
|
+
enabled: z.boolean(),
|
|
359
|
+
minimum: z.number().int().optional(),
|
|
360
|
+
maximum: z.number().int().optional()
|
|
361
|
+
}).optional(),
|
|
362
|
+
/** Tax Rate ids applied to this line item. */
|
|
363
|
+
tax_rates: z.array(z.string()).optional(),
|
|
364
|
+
metadata: metadataSchema.optional()
|
|
365
|
+
});
|
|
366
|
+
var paymentIntentData = z.object({
|
|
367
|
+
capture_method: z.enum(["automatic", "automatic_async", "manual"]).optional(),
|
|
368
|
+
setup_future_usage: z.enum(["on_session", "off_session"]).optional(),
|
|
369
|
+
statement_descriptor: z.string().optional(),
|
|
370
|
+
statement_descriptor_suffix: z.string().optional(),
|
|
371
|
+
receipt_email: z.string().optional(),
|
|
372
|
+
metadata: metadataSchema.optional()
|
|
373
|
+
});
|
|
374
|
+
var sessionCreateReq = z.object({
|
|
375
|
+
/** Session mode — determines what the session sets up. Required. */
|
|
376
|
+
mode: z.enum(["payment", "setup", "subscription"]),
|
|
377
|
+
/** Items being purchased. Required for `payment`/`subscription` modes. */
|
|
378
|
+
line_items: z.array(lineItemInput).optional(),
|
|
379
|
+
/** Redirect target after a successful session (hosted page). */
|
|
380
|
+
success_url: z.string().optional(),
|
|
381
|
+
/** Redirect target when the customer cancels (hosted page). */
|
|
382
|
+
cancel_url: z.string().optional(),
|
|
383
|
+
/** Return target for embedded UI modes. */
|
|
384
|
+
return_url: z.string().optional(),
|
|
385
|
+
/** Checkout UI surface (see the JSDoc above for version-pinned values). */
|
|
386
|
+
ui_mode: z.enum(["hosted_page", "embedded_page", "elements"]).optional(),
|
|
387
|
+
/** Your internal reference for this session (≤200 chars). */
|
|
388
|
+
client_reference_id: z.string().max(200).optional(),
|
|
389
|
+
/** Existing Customer id (`cus_*`) to prefill. */
|
|
390
|
+
customer: z.string().optional(),
|
|
391
|
+
/** Email to prefill when no `customer` is given (≤800 chars). */
|
|
392
|
+
customer_email: z.string().max(800).optional(),
|
|
393
|
+
customer_creation: z.enum(["always", "if_required"]).optional(),
|
|
394
|
+
/** Three-letter ISO currency code — required in some `setup`-mode cases. */
|
|
395
|
+
currency: z.string().optional(),
|
|
396
|
+
/** Payment method types to accept (e.g. `["card"]`). Provider-validated. */
|
|
397
|
+
payment_method_types: z.array(z.string()).optional(),
|
|
398
|
+
metadata: metadataSchema.optional(),
|
|
399
|
+
/** Unix timestamp — 30 minutes to 24 hours after creation. */
|
|
400
|
+
expires_at: z.number().int().optional(),
|
|
401
|
+
payment_intent_data: paymentIntentData.optional(),
|
|
402
|
+
billing_address_collection: z.enum(["auto", "required"]).optional(),
|
|
403
|
+
allow_promotion_codes: z.boolean().optional()
|
|
404
|
+
});
|
|
405
|
+
var checkoutSession = z.looseObject({
|
|
406
|
+
id: z.string(),
|
|
407
|
+
object: z.string().optional(),
|
|
408
|
+
/** `open` | `complete` | `expired` (normalization is the unified layer's job). */
|
|
409
|
+
status: z.string().nullable().optional(),
|
|
410
|
+
/** `paid` | `unpaid` | `no_payment_required`. */
|
|
411
|
+
payment_status: z.string().optional(),
|
|
412
|
+
mode: z.string().optional(),
|
|
413
|
+
/** Hosted checkout URL — null once the session is no longer open. */
|
|
414
|
+
url: z.string().nullable().optional(),
|
|
415
|
+
client_reference_id: z.string().nullable().optional(),
|
|
416
|
+
/** Customer id string, or an expanded object — kept unknown. */
|
|
417
|
+
customer: z.unknown().optional(),
|
|
418
|
+
customer_email: z.string().nullable().optional(),
|
|
419
|
+
/** PaymentIntent id string, or an expanded object — kept unknown. */
|
|
420
|
+
payment_intent: z.unknown().optional(),
|
|
421
|
+
/** Integer minor units. */
|
|
422
|
+
amount_subtotal: z.number().nullable().optional(),
|
|
423
|
+
/** Integer minor units. */
|
|
424
|
+
amount_total: z.number().nullable().optional(),
|
|
425
|
+
currency: z.string().nullable().optional(),
|
|
426
|
+
metadata: z.record(z.string(), z.string()).nullable().optional(),
|
|
427
|
+
success_url: z.string().nullable().optional(),
|
|
428
|
+
cancel_url: z.string().nullable().optional(),
|
|
429
|
+
created: z.number().optional(),
|
|
430
|
+
expires_at: z.number().optional(),
|
|
431
|
+
livemode: z.boolean().optional()
|
|
432
|
+
});
|
|
433
|
+
var sessionListQuery = z.object({
|
|
434
|
+
...listCursorFields,
|
|
435
|
+
/** Filter by Customer id. */
|
|
436
|
+
customer: z.string().optional(),
|
|
437
|
+
/** Filter by customer email (`customer_details[email]`, ≤800 chars). */
|
|
438
|
+
customer_details: z.object({ email: z.string().max(800) }).optional(),
|
|
439
|
+
/** Filter by PaymentIntent id. */
|
|
440
|
+
payment_intent: z.string().optional(),
|
|
441
|
+
/** Filter by Payment Link id. */
|
|
442
|
+
payment_link: z.string().optional(),
|
|
443
|
+
/** Filter by Subscription id. */
|
|
444
|
+
subscription: z.string().optional(),
|
|
445
|
+
status: z.enum(["open", "complete", "expired"]).optional(),
|
|
446
|
+
/** Creation-date window (Unix timestamps). */
|
|
447
|
+
created: createdRange.optional()
|
|
448
|
+
});
|
|
449
|
+
var sessionLineItem = z.looseObject({
|
|
450
|
+
id: z.string(),
|
|
451
|
+
object: z.string().optional(),
|
|
452
|
+
amount_discount: z.number().optional(),
|
|
453
|
+
amount_subtotal: z.number().optional(),
|
|
454
|
+
amount_tax: z.number().optional(),
|
|
455
|
+
amount_total: z.number().optional(),
|
|
456
|
+
currency: z.string().optional(),
|
|
457
|
+
description: z.string().nullable().optional(),
|
|
458
|
+
/** The Price object backing this item — kept loose. */
|
|
459
|
+
price: z.looseObject({}).nullable().optional(),
|
|
460
|
+
quantity: z.number().nullable().optional()
|
|
461
|
+
});
|
|
462
|
+
var lineItemsQuery = z.object({ ...listCursorFields });
|
|
463
|
+
|
|
464
|
+
// src/stripe/resources/checkout-sessions.ts
|
|
465
|
+
var sessionListRes = stripeList(checkoutSession);
|
|
466
|
+
var lineItemListRes = stripeList(sessionLineItem);
|
|
467
|
+
var CheckoutSessions = class {
|
|
468
|
+
constructor(http) {
|
|
469
|
+
this.http = http;
|
|
470
|
+
}
|
|
471
|
+
http;
|
|
472
|
+
/**
|
|
473
|
+
* Create a Checkout Session. `session.url` is the hosted checkout page to
|
|
474
|
+
* redirect the customer to. Amounts (`price_data.unit_amount`) are integer
|
|
475
|
+
* minor units, sent to Stripe unchanged. Pass `idempotencyKey` to make the
|
|
476
|
+
* POST safely replayable (and retry-eligible).
|
|
477
|
+
*
|
|
478
|
+
* Docs: https://docs.stripe.com/api/checkout/sessions/create
|
|
479
|
+
*
|
|
480
|
+
* @example
|
|
481
|
+
* const session = await payweave.stripe.checkout.sessions.create({
|
|
482
|
+
* mode: "payment",
|
|
483
|
+
* line_items: [{ price: "price_123", quantity: 2 }],
|
|
484
|
+
* success_url: "https://example.com/success",
|
|
485
|
+
* cancel_url: "https://example.com/cancel",
|
|
486
|
+
* }, { idempotencyKey: "order-8123-checkout" });
|
|
487
|
+
* console.log(session.url);
|
|
488
|
+
*/
|
|
489
|
+
async create(input, opts = {}) {
|
|
490
|
+
const body = parseRequest(sessionCreateReq, input);
|
|
491
|
+
return this.http.request({
|
|
492
|
+
method: "POST",
|
|
493
|
+
path: "/v1/checkout/sessions",
|
|
494
|
+
body,
|
|
495
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
496
|
+
schema: checkoutSession
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Retrieve a Checkout Session by id (`cs_*`). A 404 for an unknown id
|
|
501
|
+
* surfaces as {@link PayweaveNotFoundError}. Note `session.url` is null once
|
|
502
|
+
* the session is no longer open.
|
|
503
|
+
*
|
|
504
|
+
* Docs: https://docs.stripe.com/api/checkout/sessions/retrieve
|
|
505
|
+
*
|
|
506
|
+
* @example
|
|
507
|
+
* const session = await payweave.stripe.checkout.sessions.retrieve("cs_test_123");
|
|
508
|
+
* if (session.payment_status === "paid") { }
|
|
509
|
+
*/
|
|
510
|
+
async retrieve(id) {
|
|
511
|
+
const sessionId = requireId(id, "checkout session");
|
|
512
|
+
return this.http.request({
|
|
513
|
+
method: "GET",
|
|
514
|
+
path: `/v1/checkout/sessions/${encodeURIComponent(sessionId)}`,
|
|
515
|
+
schema: checkoutSession
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* List Checkout Sessions (newest first). Cursor pagination: `limit` +
|
|
520
|
+
* `starting_after`/`ending_before`; nested filters (`created`,
|
|
521
|
+
* `customer_details`) become bracket query keys.
|
|
522
|
+
*
|
|
523
|
+
* Docs: https://docs.stripe.com/api/checkout/sessions/list
|
|
524
|
+
*
|
|
525
|
+
* @example
|
|
526
|
+
* const page = await payweave.stripe.checkout.sessions.list({ status: "complete", limit: 50 });
|
|
527
|
+
* console.log(page.data.length, page.has_more);
|
|
528
|
+
*/
|
|
529
|
+
async list(query = {}) {
|
|
530
|
+
const q = parseRequest(sessionListQuery, query);
|
|
531
|
+
return this.http.request({
|
|
532
|
+
method: "GET",
|
|
533
|
+
path: "/v1/checkout/sessions",
|
|
534
|
+
query: flattenQuery(q),
|
|
535
|
+
schema: sessionListRes
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Async iterator over ALL sessions matching `query`, transparently following
|
|
540
|
+
* `has_more` with `starting_after = <last id>`.
|
|
541
|
+
*
|
|
542
|
+
* Docs: https://docs.stripe.com/api/checkout/sessions/list
|
|
543
|
+
*
|
|
544
|
+
* @example
|
|
545
|
+
* for await (const session of payweave.stripe.checkout.sessions.iterate({ status: "open" })) {
|
|
546
|
+
* console.log(session.id);
|
|
547
|
+
* }
|
|
548
|
+
*/
|
|
549
|
+
async *iterate(query = {}) {
|
|
550
|
+
const base = parseRequest(sessionListQuery, query);
|
|
551
|
+
yield* iterateStripeList(
|
|
552
|
+
(startingAfter) => this.http.request({
|
|
553
|
+
method: "GET",
|
|
554
|
+
path: "/v1/checkout/sessions",
|
|
555
|
+
query: flattenQuery({ ...base, starting_after: startingAfter }),
|
|
556
|
+
schema: sessionListRes
|
|
557
|
+
})
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Expire an OPEN Checkout Session so it can no longer be paid — Stripe
|
|
562
|
+
* errors when the session is not `open` (surfaced as
|
|
563
|
+
* {@link PayweaveValidationError} on the 400). Returns the session with
|
|
564
|
+
* `status: "expired"`.
|
|
565
|
+
*
|
|
566
|
+
* Docs: https://docs.stripe.com/api/checkout/sessions/expire
|
|
567
|
+
*
|
|
568
|
+
* @example
|
|
569
|
+
* const expired = await payweave.stripe.checkout.sessions.expire("cs_test_123");
|
|
570
|
+
* console.log(expired.status); // "expired"
|
|
571
|
+
*/
|
|
572
|
+
async expire(id) {
|
|
573
|
+
const sessionId = requireId(id, "checkout session");
|
|
574
|
+
return this.http.request({
|
|
575
|
+
method: "POST",
|
|
576
|
+
path: `/v1/checkout/sessions/${encodeURIComponent(sessionId)}/expire`,
|
|
577
|
+
schema: checkoutSession
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* List a session's line items (cursor-paginated; `amount_*` are integer
|
|
582
|
+
* minor units).
|
|
583
|
+
*
|
|
584
|
+
* Docs: https://docs.stripe.com/api/checkout/sessions/line_items
|
|
585
|
+
*
|
|
586
|
+
* @example
|
|
587
|
+
* const items = await payweave.stripe.checkout.sessions.lineItems("cs_test_123", { limit: 20 });
|
|
588
|
+
* console.log(items.data[0]?.amount_total);
|
|
589
|
+
*/
|
|
590
|
+
async lineItems(id, query = {}) {
|
|
591
|
+
const sessionId = requireId(id, "checkout session");
|
|
592
|
+
const q = parseRequest(lineItemsQuery, query);
|
|
593
|
+
return this.http.request({
|
|
594
|
+
method: "GET",
|
|
595
|
+
path: `/v1/checkout/sessions/${encodeURIComponent(sessionId)}/line_items`,
|
|
596
|
+
query: flattenQuery(q),
|
|
597
|
+
schema: lineItemListRes
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Async iterator over ALL line items of a session, following `has_more`
|
|
602
|
+
* with `starting_after = <last id>` — line items are themselves a paginated
|
|
603
|
+
* list (PW-602 work order).
|
|
604
|
+
*
|
|
605
|
+
* Docs: https://docs.stripe.com/api/checkout/sessions/line_items
|
|
606
|
+
*
|
|
607
|
+
* @example
|
|
608
|
+
* for await (const item of payweave.stripe.checkout.sessions.iterateLineItems("cs_test_123")) {
|
|
609
|
+
* console.log(item.description, item.quantity);
|
|
610
|
+
* }
|
|
611
|
+
*/
|
|
612
|
+
async *iterateLineItems(id, query = {}) {
|
|
613
|
+
const sessionId = requireId(id, "checkout session");
|
|
614
|
+
const base = parseRequest(lineItemsQuery, query);
|
|
615
|
+
yield* iterateStripeList(
|
|
616
|
+
(startingAfter) => this.http.request({
|
|
617
|
+
method: "GET",
|
|
618
|
+
path: `/v1/checkout/sessions/${encodeURIComponent(sessionId)}/line_items`,
|
|
619
|
+
query: flattenQuery({ ...base, starting_after: startingAfter }),
|
|
620
|
+
schema: lineItemListRes
|
|
621
|
+
})
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
var addressInput = z.object({
|
|
626
|
+
city: z.string().optional(),
|
|
627
|
+
/** Two-letter country code (ISO 3166-1 alpha-2). */
|
|
628
|
+
country: z.string().optional(),
|
|
629
|
+
line1: z.string().optional(),
|
|
630
|
+
line2: z.string().optional(),
|
|
631
|
+
postal_code: z.string().optional(),
|
|
632
|
+
/** State/county/province/region (ISO 3166-2 recommended). */
|
|
633
|
+
state: z.string().optional()
|
|
634
|
+
});
|
|
635
|
+
var cashBalanceInput = z.object({
|
|
636
|
+
settings: z.object({
|
|
637
|
+
reconciliation_mode: z.enum(["automatic", "manual", "merchant_default"]).optional()
|
|
638
|
+
}).optional()
|
|
639
|
+
});
|
|
640
|
+
var invoiceSettingsInput = z.object({
|
|
641
|
+
/** Up to 4 custom fields shown on invoices. */
|
|
642
|
+
custom_fields: z.array(
|
|
643
|
+
z.object({
|
|
644
|
+
/** ≤40 chars. */
|
|
645
|
+
name: z.string().max(40),
|
|
646
|
+
/** ≤140 chars. */
|
|
647
|
+
value: z.string().max(140)
|
|
648
|
+
})
|
|
649
|
+
).optional(),
|
|
650
|
+
default_payment_method: z.string().optional(),
|
|
651
|
+
footer: z.string().optional(),
|
|
652
|
+
rendering_options: z.object({
|
|
653
|
+
amount_tax_display: z.enum(["exclude_tax", "include_inclusive_tax"]).optional(),
|
|
654
|
+
template: z.string().optional()
|
|
655
|
+
}).optional()
|
|
656
|
+
});
|
|
657
|
+
var shippingInput = z.object({
|
|
658
|
+
address: addressInput,
|
|
659
|
+
name: z.string(),
|
|
660
|
+
phone: z.string().optional()
|
|
661
|
+
});
|
|
662
|
+
var customerSharedFields = {
|
|
663
|
+
/** Required when calculating taxes. */
|
|
664
|
+
address: addressInput.optional(),
|
|
665
|
+
/** Integer MINOR units — a credit/debit applied to future invoices. */
|
|
666
|
+
balance: z.number().int().optional(),
|
|
667
|
+
/** ≤150 chars. */
|
|
668
|
+
business_name: z.string().max(150).optional(),
|
|
669
|
+
cash_balance: cashBalanceInput.optional(),
|
|
670
|
+
description: z.string().optional(),
|
|
671
|
+
/** ≤512 chars. */
|
|
672
|
+
email: z.string().max(512).optional(),
|
|
673
|
+
/** ≤150 chars. */
|
|
674
|
+
individual_name: z.string().max(150).optional(),
|
|
675
|
+
/** 3–12 uppercase letters or numbers (provider-validated). */
|
|
676
|
+
invoice_prefix: z.string().optional(),
|
|
677
|
+
invoice_settings: invoiceSettingsInput.optional(),
|
|
678
|
+
metadata: metadataSchema.optional(),
|
|
679
|
+
/** ≤256 chars. */
|
|
680
|
+
name: z.string().max(256).optional(),
|
|
681
|
+
next_invoice_sequence: z.number().int().optional(),
|
|
682
|
+
/** ≤20 chars. */
|
|
683
|
+
phone: z.string().max(20).optional(),
|
|
684
|
+
preferred_locales: z.array(z.string()).optional(),
|
|
685
|
+
shipping: shippingInput.optional(),
|
|
686
|
+
/** Payment source id; on update it becomes the new default source. */
|
|
687
|
+
source: z.string().optional(),
|
|
688
|
+
tax_exempt: z.enum(["none", "exempt", "reverse"]).optional()
|
|
689
|
+
};
|
|
690
|
+
var customerCreateReq = z.object({
|
|
691
|
+
...customerSharedFields,
|
|
692
|
+
/** PaymentMethod id (`pm_*`) to attach to the new customer. */
|
|
693
|
+
payment_method: z.string().optional(),
|
|
694
|
+
/** Tax details — recommended when calculating taxes. */
|
|
695
|
+
tax: z.object({
|
|
696
|
+
ip_address: z.string().optional(),
|
|
697
|
+
/** Create documents `deferred` (default) | `immediately`. */
|
|
698
|
+
validate_location: z.enum(["deferred", "immediately"]).optional()
|
|
699
|
+
}).optional(),
|
|
700
|
+
/**
|
|
701
|
+
* The customer's tax IDs. `type` is a country-specific code (e.g. `eu_vat`,
|
|
702
|
+
* `us_ein`) — 100+ documented values, provider-validated (kept `string`).
|
|
703
|
+
*/
|
|
704
|
+
tax_id_data: z.array(z.object({ type: z.string(), value: z.string() })).optional(),
|
|
705
|
+
/** Test clock id to attach (test mode). */
|
|
706
|
+
test_clock: z.string().optional()
|
|
707
|
+
});
|
|
708
|
+
var customerUpdateReq = z.object({
|
|
709
|
+
...customerSharedFields,
|
|
710
|
+
/** Payment source id to make the customer's new default (≤500 chars). */
|
|
711
|
+
default_source: z.string().max(500).optional(),
|
|
712
|
+
/** Tax details — recommended when calculating taxes. */
|
|
713
|
+
tax: z.object({
|
|
714
|
+
ip_address: z.string().optional(),
|
|
715
|
+
/** Update documents `auto` (default) | `deferred` | `immediately`. */
|
|
716
|
+
validate_location: z.enum(["auto", "deferred", "immediately"]).optional()
|
|
717
|
+
}).optional()
|
|
718
|
+
});
|
|
719
|
+
var customerListQuery = z.object({
|
|
720
|
+
...listCursorFields,
|
|
721
|
+
/** Creation-date window (Unix timestamps). */
|
|
722
|
+
created: createdRange.optional(),
|
|
723
|
+
/** Case-sensitive exact-match filter on email (≤512 chars). */
|
|
724
|
+
email: z.string().max(512).optional(),
|
|
725
|
+
/** Filter by test clock id. */
|
|
726
|
+
test_clock: z.string().optional()
|
|
727
|
+
});
|
|
728
|
+
var searchReqFields = {
|
|
729
|
+
/** Search query string, e.g. `name:'Jane' AND metadata['foo']:'bar'`. */
|
|
730
|
+
query: z.string().min(1, "search query must be a non-empty string"),
|
|
731
|
+
/** Page size, 1–100 (Stripe default 10). */
|
|
732
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
733
|
+
/** Pagination token from the previous page's `next_page` — omit initially. */
|
|
734
|
+
page: z.string().optional()
|
|
735
|
+
};
|
|
736
|
+
var customerSearchReq = z.object({ ...searchReqFields });
|
|
737
|
+
var customer = z.looseObject({
|
|
738
|
+
id: z.string(),
|
|
739
|
+
object: z.string().optional(),
|
|
740
|
+
address: z.looseObject({}).nullable().optional(),
|
|
741
|
+
/** Integer minor units. */
|
|
742
|
+
balance: z.number().optional(),
|
|
743
|
+
created: z.number().optional(),
|
|
744
|
+
currency: z.string().nullable().optional(),
|
|
745
|
+
/** Source id string, or an expanded object — kept unknown. */
|
|
746
|
+
default_source: z.unknown().optional(),
|
|
747
|
+
delinquent: z.boolean().nullable().optional(),
|
|
748
|
+
description: z.string().nullable().optional(),
|
|
749
|
+
email: z.string().nullable().optional(),
|
|
750
|
+
invoice_prefix: z.string().nullable().optional(),
|
|
751
|
+
invoice_settings: z.looseObject({}).optional(),
|
|
752
|
+
livemode: z.boolean().optional(),
|
|
753
|
+
metadata: z.record(z.string(), z.string()).nullable().optional(),
|
|
754
|
+
name: z.string().nullable().optional(),
|
|
755
|
+
next_invoice_sequence: z.number().optional(),
|
|
756
|
+
phone: z.string().nullable().optional(),
|
|
757
|
+
preferred_locales: z.array(z.string()).nullable().optional(),
|
|
758
|
+
shipping: z.looseObject({}).nullable().optional(),
|
|
759
|
+
/** `none` | `exempt` | `reverse`. */
|
|
760
|
+
tax_exempt: z.string().nullable().optional(),
|
|
761
|
+
/** Test clock id string, or an expanded object — kept unknown. */
|
|
762
|
+
test_clock: z.unknown().optional(),
|
|
763
|
+
/**
|
|
764
|
+
* `true` only on the reduced stub `retrieve` returns for a DELETED customer
|
|
765
|
+
* (https://docs.stripe.com/api/customers/retrieve — verified 2026-07-12:
|
|
766
|
+
* deleted customers stay retrievable "to be able to track their history").
|
|
767
|
+
*/
|
|
768
|
+
deleted: z.boolean().optional()
|
|
769
|
+
});
|
|
770
|
+
var deletedCustomer = z.looseObject({
|
|
771
|
+
id: z.string(),
|
|
772
|
+
object: z.string().optional(),
|
|
773
|
+
deleted: z.boolean()
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
// src/stripe/resources/customers.ts
|
|
777
|
+
var customerListRes = stripeList(customer);
|
|
778
|
+
var customerSearchRes = stripeSearchResult(customer);
|
|
779
|
+
function bodyIfNonEmpty(body) {
|
|
780
|
+
return Object.keys(body).length > 0 ? { body } : {};
|
|
781
|
+
}
|
|
782
|
+
var Customers = class {
|
|
783
|
+
constructor(http) {
|
|
784
|
+
this.http = http;
|
|
785
|
+
}
|
|
786
|
+
http;
|
|
787
|
+
/**
|
|
788
|
+
* Create a Customer. Every field is optional — Stripe allows an empty
|
|
789
|
+
* customer. `metadata` maps go to the wire as bracket pairs
|
|
790
|
+
* (`metadata[pwv_reference]=...`). Pass `idempotencyKey` to make the POST
|
|
791
|
+
* safely replayable (and retry-eligible).
|
|
792
|
+
*
|
|
793
|
+
* Docs: https://docs.stripe.com/api/customers/create
|
|
794
|
+
*
|
|
795
|
+
* @example
|
|
796
|
+
* const cus = await payweave.stripe.customers.create({
|
|
797
|
+
* email: "jenny.rosen@example.com",
|
|
798
|
+
* name: "Jenny Rosen",
|
|
799
|
+
* metadata: { pwv_reference: "pwv_ref_0001" },
|
|
800
|
+
* }, { idempotencyKey: "signup-8123-customer" });
|
|
801
|
+
* console.log(cus.id);
|
|
802
|
+
*/
|
|
803
|
+
async create(input = {}, opts = {}) {
|
|
804
|
+
const body = parseRequest(customerCreateReq, input);
|
|
805
|
+
return this.http.request({
|
|
806
|
+
method: "POST",
|
|
807
|
+
path: "/v1/customers",
|
|
808
|
+
...bodyIfNonEmpty(body),
|
|
809
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
810
|
+
schema: customer
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Retrieve a Customer by id (`cus_*`). A 404 for an unknown id surfaces as
|
|
815
|
+
* {@link PayweaveNotFoundError}. DELETED customers stay retrievable (to
|
|
816
|
+
* track history) and come back as a reduced stub with `deleted: true` —
|
|
817
|
+
* check that flag before using other fields.
|
|
818
|
+
*
|
|
819
|
+
* Docs: https://docs.stripe.com/api/customers/retrieve
|
|
820
|
+
*
|
|
821
|
+
* @example
|
|
822
|
+
* const cus = await payweave.stripe.customers.retrieve("cus_123");
|
|
823
|
+
* if (!cus.deleted) console.log(cus.email);
|
|
824
|
+
*/
|
|
825
|
+
async retrieve(id) {
|
|
826
|
+
const customerId = requireId(id, "customer");
|
|
827
|
+
return this.http.request({
|
|
828
|
+
method: "GET",
|
|
829
|
+
path: `/v1/customers/${encodeURIComponent(customerId)}`,
|
|
830
|
+
schema: customer
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Update a Customer. Fields not provided remain unchanged; unset an
|
|
835
|
+
* individual `metadata` key by posting an empty string value for it. Pass
|
|
836
|
+
* `idempotencyKey` to make the POST safely replayable (and retry-eligible).
|
|
837
|
+
*
|
|
838
|
+
* Docs: https://docs.stripe.com/api/customers/update
|
|
839
|
+
*
|
|
840
|
+
* @example
|
|
841
|
+
* const cus = await payweave.stripe.customers.update("cus_123", {
|
|
842
|
+
* metadata: { pwv_reference: "pwv_ref_0001", legacy_id: "" }, // "" unsets
|
|
843
|
+
* });
|
|
844
|
+
*/
|
|
845
|
+
async update(id, input = {}, opts = {}) {
|
|
846
|
+
const customerId = requireId(id, "customer");
|
|
847
|
+
const body = parseRequest(customerUpdateReq, input);
|
|
848
|
+
return this.http.request({
|
|
849
|
+
method: "POST",
|
|
850
|
+
path: `/v1/customers/${encodeURIComponent(customerId)}`,
|
|
851
|
+
...bodyIfNonEmpty(body),
|
|
852
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
853
|
+
schema: customer
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* PERMANENTLY delete a Customer — cannot be undone. Immediately cancels
|
|
858
|
+
* any active subscriptions, removes all stored payment details, and blocks
|
|
859
|
+
* further operations on the customer. Returns the deletion stub
|
|
860
|
+
* `{ id, object: "customer", deleted: true }`; the customer remains
|
|
861
|
+
* retrievable afterwards (as a deleted stub) for history.
|
|
862
|
+
*
|
|
863
|
+
* Docs: https://docs.stripe.com/api/customers/delete
|
|
864
|
+
*
|
|
865
|
+
* @example
|
|
866
|
+
* const gone = await payweave.stripe.customers.delete("cus_123");
|
|
867
|
+
* console.log(gone.deleted); // true
|
|
868
|
+
*/
|
|
869
|
+
async delete(id) {
|
|
870
|
+
const customerId = requireId(id, "customer");
|
|
871
|
+
return this.http.request({
|
|
872
|
+
method: "DELETE",
|
|
873
|
+
path: `/v1/customers/${encodeURIComponent(customerId)}`,
|
|
874
|
+
schema: deletedCustomer
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* List Customers (newest first). Cursor pagination: `limit` +
|
|
879
|
+
* `starting_after`/`ending_before`; the `created` window becomes bracket
|
|
880
|
+
* query keys (`created[gte]`).
|
|
881
|
+
*
|
|
882
|
+
* Docs: https://docs.stripe.com/api/customers/list
|
|
883
|
+
*
|
|
884
|
+
* @example
|
|
885
|
+
* const page = await payweave.stripe.customers.list({ email: "jenny.rosen@example.com" });
|
|
886
|
+
* console.log(page.data.length, page.has_more);
|
|
887
|
+
*/
|
|
888
|
+
async list(query = {}) {
|
|
889
|
+
const q = parseRequest(customerListQuery, query);
|
|
890
|
+
return this.http.request({
|
|
891
|
+
method: "GET",
|
|
892
|
+
path: "/v1/customers",
|
|
893
|
+
query: flattenQuery(q),
|
|
894
|
+
schema: customerListRes
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Async iterator over ALL Customers matching `query`, transparently
|
|
899
|
+
* following `has_more` with `starting_after = <last id>`.
|
|
900
|
+
*
|
|
901
|
+
* Docs: https://docs.stripe.com/api/customers/list
|
|
902
|
+
*
|
|
903
|
+
* @example
|
|
904
|
+
* for await (const cus of payweave.stripe.customers.iterate({ email: "a@example.com" })) {
|
|
905
|
+
* console.log(cus.id);
|
|
906
|
+
* }
|
|
907
|
+
*/
|
|
908
|
+
async *iterate(query = {}) {
|
|
909
|
+
const base = parseRequest(customerListQuery, query);
|
|
910
|
+
yield* iterateStripeList(
|
|
911
|
+
(startingAfter) => this.http.request({
|
|
912
|
+
method: "GET",
|
|
913
|
+
path: "/v1/customers",
|
|
914
|
+
query: flattenQuery({ ...base, starting_after: startingAfter }),
|
|
915
|
+
schema: customerListRes
|
|
916
|
+
})
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Search Customers with Stripe's search query language (e.g.
|
|
921
|
+
* `name:'Jane Doe' AND metadata['foo']:'bar'`). Pagination is TOKEN-based:
|
|
922
|
+
* pass the previous page's `next_page` as `page` (never `starting_after`).
|
|
923
|
+
* Search is eventually consistent (typically <1 minute; up to an hour
|
|
924
|
+
* during outages) — don't use it in read-after-write flows.
|
|
925
|
+
*
|
|
926
|
+
* Docs: https://docs.stripe.com/api/customers/search
|
|
927
|
+
*
|
|
928
|
+
* @example
|
|
929
|
+
* const found = await payweave.stripe.customers.search({
|
|
930
|
+
* query: "metadata['pwv_reference']:'pwv_ref_0001'",
|
|
931
|
+
* });
|
|
932
|
+
* console.log(found.data[0]?.id, found.next_page);
|
|
933
|
+
*/
|
|
934
|
+
async search(input) {
|
|
935
|
+
const q = parseRequest(customerSearchReq, input);
|
|
936
|
+
return this.http.request({
|
|
937
|
+
method: "GET",
|
|
938
|
+
path: "/v1/customers/search",
|
|
939
|
+
query: flattenQuery(q),
|
|
940
|
+
schema: customerSearchRes
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Async iterator over ALL Customers matching a search query, transparently
|
|
945
|
+
* following `has_more` with `page = <next_page token>` — search pagination
|
|
946
|
+
* differs from list pagination (https://docs.stripe.com/api/pagination/search).
|
|
947
|
+
*
|
|
948
|
+
* Docs: https://docs.stripe.com/api/customers/search
|
|
949
|
+
*
|
|
950
|
+
* @example
|
|
951
|
+
* for await (const cus of payweave.stripe.customers.iterateSearch({
|
|
952
|
+
* query: "email:'jenny.rosen@example.com'",
|
|
953
|
+
* })) {
|
|
954
|
+
* console.log(cus.id);
|
|
955
|
+
* }
|
|
956
|
+
*/
|
|
957
|
+
async *iterateSearch(input) {
|
|
958
|
+
const base = parseRequest(customerSearchReq, input);
|
|
959
|
+
yield* iterateStripeSearch(
|
|
960
|
+
(page) => this.http.request({
|
|
961
|
+
method: "GET",
|
|
962
|
+
path: "/v1/customers/search",
|
|
963
|
+
query: flattenQuery({ ...base, page }),
|
|
964
|
+
schema: customerSearchRes
|
|
965
|
+
})
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
var automaticPaymentMethods = z.object({
|
|
970
|
+
enabled: z.boolean(),
|
|
971
|
+
allow_redirects: z.enum(["always", "never"]).optional()
|
|
972
|
+
});
|
|
973
|
+
var paymentIntentCreateReq = z.object({
|
|
974
|
+
/** Positive integer in the smallest currency unit (minor units) — unchanged. */
|
|
975
|
+
amount: z.number().int().positive(),
|
|
976
|
+
/** Three-letter ISO currency code, lowercase. Required. */
|
|
977
|
+
currency: z.string(),
|
|
978
|
+
automatic_payment_methods: automaticPaymentMethods.optional(),
|
|
979
|
+
/** `true` = create + confirm in one call. Defaults to false. */
|
|
980
|
+
confirm: z.boolean().optional(),
|
|
981
|
+
/** Customer id (`cus_*`) this intent belongs to. */
|
|
982
|
+
customer: z.string().optional(),
|
|
983
|
+
description: z.string().optional(),
|
|
984
|
+
metadata: metadataSchema.optional(),
|
|
985
|
+
/** PaymentMethod id (`pm_*`) to attach. */
|
|
986
|
+
payment_method: z.string().optional(),
|
|
987
|
+
/** Payment method types to accept (e.g. `["card", "link"]`). */
|
|
988
|
+
payment_method_types: z.array(z.string()).optional(),
|
|
989
|
+
receipt_email: z.string().optional(),
|
|
990
|
+
/** `automatic` | `automatic_async` (default) | `manual`. */
|
|
991
|
+
capture_method: z.enum(["automatic", "automatic_async", "manual"]).optional(),
|
|
992
|
+
confirmation_method: z.enum(["automatic", "manual"]).optional(),
|
|
993
|
+
setup_future_usage: z.enum(["on_session", "off_session"]).optional(),
|
|
994
|
+
/** ≤22 chars — statement text for non-card charges. */
|
|
995
|
+
statement_descriptor: z.string().max(22).optional(),
|
|
996
|
+
/** ≤22 chars — suffix appended to the account descriptor for card charges. */
|
|
997
|
+
statement_descriptor_suffix: z.string().max(22).optional(),
|
|
998
|
+
/** Redirect URL after off-page authentication (use with `confirm: true`). */
|
|
999
|
+
return_url: z.string().optional(),
|
|
1000
|
+
/** Customer is not in the checkout flow (docs type: boolean or string). */
|
|
1001
|
+
off_session: z.union([z.boolean(), z.string()]).optional(),
|
|
1002
|
+
/** Transaction-group identifier for Connect. */
|
|
1003
|
+
transfer_group: z.string().optional(),
|
|
1004
|
+
/** Connect application fee, integer minor units. */
|
|
1005
|
+
application_fee_amount: z.number().int().optional()
|
|
1006
|
+
});
|
|
1007
|
+
var paymentIntentConfirmReq = z.object({
|
|
1008
|
+
/** PaymentMethod id (`pm_*`) to use for this confirmation attempt. */
|
|
1009
|
+
payment_method: z.string().optional(),
|
|
1010
|
+
/** Redirect URL after authentication for redirect-based methods. */
|
|
1011
|
+
return_url: z.string().optional(),
|
|
1012
|
+
receipt_email: z.string().optional(),
|
|
1013
|
+
setup_future_usage: z.enum(["on_session", "off_session"]).optional(),
|
|
1014
|
+
capture_method: z.enum(["automatic", "automatic_async", "manual"]).optional(),
|
|
1015
|
+
/** Customer is not in the checkout flow (docs type: boolean or string). */
|
|
1016
|
+
off_session: z.union([z.boolean(), z.string()]).optional(),
|
|
1017
|
+
/** ConfirmationToken id capturing client-collected payment details. */
|
|
1018
|
+
confirmation_token: z.string().optional(),
|
|
1019
|
+
/** Fail (instead of returning `requires_action`) when an action is needed. */
|
|
1020
|
+
error_on_requires_action: z.boolean().optional(),
|
|
1021
|
+
/** Mandate id to use for this confirmation. */
|
|
1022
|
+
mandate: z.string().optional()
|
|
1023
|
+
});
|
|
1024
|
+
var paymentIntentCaptureReq = z.object({
|
|
1025
|
+
/** Integer minor units to capture; defaults to full `amount_capturable`. */
|
|
1026
|
+
amount_to_capture: z.number().int().positive().optional(),
|
|
1027
|
+
/** Connect application fee, integer minor units. */
|
|
1028
|
+
application_fee_amount: z.number().int().optional(),
|
|
1029
|
+
/** Default true. `false` retains uncaptured funds (multicapture accounts). */
|
|
1030
|
+
final_capture: z.boolean().optional(),
|
|
1031
|
+
metadata: metadataSchema.optional(),
|
|
1032
|
+
/** ≤22 chars — statement text for non-card charges. */
|
|
1033
|
+
statement_descriptor: z.string().max(22).optional(),
|
|
1034
|
+
/** ≤22 chars — suffix appended to the account descriptor for card charges. */
|
|
1035
|
+
statement_descriptor_suffix: z.string().max(22).optional()
|
|
1036
|
+
});
|
|
1037
|
+
var paymentIntentCancelReq = z.object({
|
|
1038
|
+
cancellation_reason: z.enum(["duplicate", "fraudulent", "requested_by_customer", "abandoned"]).optional()
|
|
1039
|
+
});
|
|
1040
|
+
var paymentIntentListQuery = z.object({
|
|
1041
|
+
...listCursorFields,
|
|
1042
|
+
/** Filter by Customer id. */
|
|
1043
|
+
customer: z.string().optional(),
|
|
1044
|
+
/** Creation-date window (Unix timestamps). */
|
|
1045
|
+
created: createdRange.optional()
|
|
1046
|
+
});
|
|
1047
|
+
var paymentIntent = z.looseObject({
|
|
1048
|
+
id: z.string(),
|
|
1049
|
+
object: z.string().optional(),
|
|
1050
|
+
amount: z.number().optional(),
|
|
1051
|
+
amount_capturable: z.number().optional(),
|
|
1052
|
+
amount_received: z.number().optional(),
|
|
1053
|
+
currency: z.string().optional(),
|
|
1054
|
+
/**
|
|
1055
|
+
* `requires_payment_method` | `requires_confirmation` | `requires_action` |
|
|
1056
|
+
* `processing` | `requires_capture` | `canceled` | `succeeded` —
|
|
1057
|
+
* normalization is the unified layer's job.
|
|
1058
|
+
*/
|
|
1059
|
+
status: z.string().optional(),
|
|
1060
|
+
client_secret: z.string().nullable().optional(),
|
|
1061
|
+
capture_method: z.string().optional(),
|
|
1062
|
+
confirmation_method: z.string().optional(),
|
|
1063
|
+
/** Customer id string, or an expanded object — kept unknown. */
|
|
1064
|
+
customer: z.unknown().optional(),
|
|
1065
|
+
description: z.string().nullable().optional(),
|
|
1066
|
+
/** Latest Charge id string, or an expanded object — kept unknown. */
|
|
1067
|
+
latest_charge: z.unknown().optional(),
|
|
1068
|
+
metadata: z.record(z.string(), z.string()).nullable().optional(),
|
|
1069
|
+
/** PaymentMethod id string, or an expanded object — kept unknown. */
|
|
1070
|
+
payment_method: z.unknown().optional(),
|
|
1071
|
+
receipt_email: z.string().nullable().optional(),
|
|
1072
|
+
/** Client-side action required to continue — kept loose. */
|
|
1073
|
+
next_action: z.looseObject({}).nullable().optional(),
|
|
1074
|
+
cancellation_reason: z.string().nullable().optional(),
|
|
1075
|
+
canceled_at: z.number().nullable().optional(),
|
|
1076
|
+
created: z.number().optional(),
|
|
1077
|
+
livemode: z.boolean().optional()
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
// src/stripe/resources/payment-intents.ts
|
|
1081
|
+
var paymentIntentListRes = stripeList(paymentIntent);
|
|
1082
|
+
function bodyIfNonEmpty2(body) {
|
|
1083
|
+
return Object.keys(body).length > 0 ? { body } : {};
|
|
1084
|
+
}
|
|
1085
|
+
var PaymentIntents = class {
|
|
1086
|
+
constructor(http) {
|
|
1087
|
+
this.http = http;
|
|
1088
|
+
}
|
|
1089
|
+
http;
|
|
1090
|
+
/**
|
|
1091
|
+
* Create a PaymentIntent. `amount` is a positive integer in MINOR units
|
|
1092
|
+
* (e.g. 2000 = $20.00) and is sent to Stripe unchanged. Pass
|
|
1093
|
+
* `idempotencyKey` to make the POST safely replayable (and retry-eligible).
|
|
1094
|
+
*
|
|
1095
|
+
* Docs: https://docs.stripe.com/api/payment_intents/create
|
|
1096
|
+
*
|
|
1097
|
+
* @example
|
|
1098
|
+
* const pi = await payweave.stripe.paymentIntents.create({
|
|
1099
|
+
* amount: 2000,
|
|
1100
|
+
* currency: "usd",
|
|
1101
|
+
* automatic_payment_methods: { enabled: true },
|
|
1102
|
+
* }, { idempotencyKey: "order-8123-intent" });
|
|
1103
|
+
* console.log(pi.client_secret);
|
|
1104
|
+
*/
|
|
1105
|
+
async create(input, opts = {}) {
|
|
1106
|
+
const body = parseRequest(paymentIntentCreateReq, input);
|
|
1107
|
+
return this.http.request({
|
|
1108
|
+
method: "POST",
|
|
1109
|
+
path: "/v1/payment_intents",
|
|
1110
|
+
body,
|
|
1111
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
1112
|
+
schema: paymentIntent
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Retrieve a PaymentIntent by id (`pi_*`). A 404 for an unknown id surfaces
|
|
1117
|
+
* as {@link PayweaveNotFoundError}.
|
|
1118
|
+
*
|
|
1119
|
+
* Docs: https://docs.stripe.com/api/payment_intents/retrieve
|
|
1120
|
+
*
|
|
1121
|
+
* @example
|
|
1122
|
+
* const pi = await payweave.stripe.paymentIntents.retrieve("pi_123");
|
|
1123
|
+
* if (pi.status === "succeeded") { }
|
|
1124
|
+
*/
|
|
1125
|
+
async retrieve(id) {
|
|
1126
|
+
const intentId = requireId(id, "payment intent");
|
|
1127
|
+
return this.http.request({
|
|
1128
|
+
method: "GET",
|
|
1129
|
+
path: `/v1/payment_intents/${encodeURIComponent(intentId)}`,
|
|
1130
|
+
schema: paymentIntent
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Confirm a PaymentIntent (attempt payment with the attached/provided
|
|
1135
|
+
* payment method). On success the status moves to `succeeded` — or
|
|
1136
|
+
* `requires_capture` when `capture_method` is `manual`, or
|
|
1137
|
+
* `requires_action` when authentication is needed. Pass `idempotencyKey`
|
|
1138
|
+
* to make the POST safely replayable (and retry-eligible).
|
|
1139
|
+
*
|
|
1140
|
+
* Docs: https://docs.stripe.com/api/payment_intents/confirm
|
|
1141
|
+
*
|
|
1142
|
+
* @example
|
|
1143
|
+
* const pi = await payweave.stripe.paymentIntents.confirm("pi_123", {
|
|
1144
|
+
* payment_method: "pm_card_visa",
|
|
1145
|
+
* return_url: "https://example.com/return",
|
|
1146
|
+
* }, { idempotencyKey: "order-8123-confirm" });
|
|
1147
|
+
*/
|
|
1148
|
+
async confirm(id, input = {}, opts = {}) {
|
|
1149
|
+
const intentId = requireId(id, "payment intent");
|
|
1150
|
+
const body = parseRequest(paymentIntentConfirmReq, input);
|
|
1151
|
+
return this.http.request({
|
|
1152
|
+
method: "POST",
|
|
1153
|
+
path: `/v1/payment_intents/${encodeURIComponent(intentId)}/confirm`,
|
|
1154
|
+
...bodyIfNonEmpty2(body),
|
|
1155
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
1156
|
+
schema: paymentIntent
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Capture the funds of a `requires_capture` PaymentIntent (manual capture
|
|
1161
|
+
* flow — uncaptured intents cancel after 7 days by default).
|
|
1162
|
+
* `amount_to_capture` is integer minor units and defaults to the full
|
|
1163
|
+
* `amount_capturable`. Pass `idempotencyKey` to make the POST safely
|
|
1164
|
+
* replayable (and retry-eligible).
|
|
1165
|
+
*
|
|
1166
|
+
* Docs: https://docs.stripe.com/api/payment_intents/capture
|
|
1167
|
+
*
|
|
1168
|
+
* @example
|
|
1169
|
+
* const pi = await payweave.stripe.paymentIntents.capture("pi_123", {
|
|
1170
|
+
* amount_to_capture: 1500,
|
|
1171
|
+
* }, { idempotencyKey: "order-8123-capture" });
|
|
1172
|
+
* console.log(pi.status); // "succeeded"
|
|
1173
|
+
*/
|
|
1174
|
+
async capture(id, input = {}, opts = {}) {
|
|
1175
|
+
const intentId = requireId(id, "payment intent");
|
|
1176
|
+
const body = parseRequest(paymentIntentCaptureReq, input);
|
|
1177
|
+
return this.http.request({
|
|
1178
|
+
method: "POST",
|
|
1179
|
+
path: `/v1/payment_intents/${encodeURIComponent(intentId)}/capture`,
|
|
1180
|
+
...bodyIfNonEmpty2(body),
|
|
1181
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
1182
|
+
schema: paymentIntent
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* Cancel a PaymentIntent. Valid only for statuses
|
|
1187
|
+
* `requires_payment_method`, `requires_capture`, `requires_confirmation`,
|
|
1188
|
+
* `requires_action` and (rarely) `processing` — Stripe errors otherwise
|
|
1189
|
+
* (surfaced as {@link PayweaveValidationError} on the 400). For
|
|
1190
|
+
* `requires_capture` intents the remaining `amount_capturable` is
|
|
1191
|
+
* automatically refunded.
|
|
1192
|
+
*
|
|
1193
|
+
* Docs: https://docs.stripe.com/api/payment_intents/cancel
|
|
1194
|
+
*
|
|
1195
|
+
* @example
|
|
1196
|
+
* const pi = await payweave.stripe.paymentIntents.cancel("pi_123", {
|
|
1197
|
+
* cancellation_reason: "requested_by_customer",
|
|
1198
|
+
* });
|
|
1199
|
+
* console.log(pi.status); // "canceled"
|
|
1200
|
+
*/
|
|
1201
|
+
async cancel(id, input = {}) {
|
|
1202
|
+
const intentId = requireId(id, "payment intent");
|
|
1203
|
+
const body = parseRequest(paymentIntentCancelReq, input);
|
|
1204
|
+
return this.http.request({
|
|
1205
|
+
method: "POST",
|
|
1206
|
+
path: `/v1/payment_intents/${encodeURIComponent(intentId)}/cancel`,
|
|
1207
|
+
...bodyIfNonEmpty2(body),
|
|
1208
|
+
schema: paymentIntent
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
/**
|
|
1212
|
+
* List PaymentIntents (newest first). Cursor pagination: `limit` +
|
|
1213
|
+
* `starting_after`/`ending_before`; the `created` window becomes bracket
|
|
1214
|
+
* query keys (`created[gte]`).
|
|
1215
|
+
*
|
|
1216
|
+
* Docs: https://docs.stripe.com/api/payment_intents/list
|
|
1217
|
+
*
|
|
1218
|
+
* @example
|
|
1219
|
+
* const page = await payweave.stripe.paymentIntents.list({ customer: "cus_123", limit: 50 });
|
|
1220
|
+
* console.log(page.data.length, page.has_more);
|
|
1221
|
+
*/
|
|
1222
|
+
async list(query = {}) {
|
|
1223
|
+
const q = parseRequest(paymentIntentListQuery, query);
|
|
1224
|
+
return this.http.request({
|
|
1225
|
+
method: "GET",
|
|
1226
|
+
path: "/v1/payment_intents",
|
|
1227
|
+
query: flattenQuery(q),
|
|
1228
|
+
schema: paymentIntentListRes
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Async iterator over ALL PaymentIntents matching `query`, transparently
|
|
1233
|
+
* following `has_more` with `starting_after = <last id>`.
|
|
1234
|
+
*
|
|
1235
|
+
* Docs: https://docs.stripe.com/api/payment_intents/list
|
|
1236
|
+
*
|
|
1237
|
+
* @example
|
|
1238
|
+
* for await (const pi of payweave.stripe.paymentIntents.iterate({ customer: "cus_123" })) {
|
|
1239
|
+
* console.log(pi.id, pi.status);
|
|
1240
|
+
* }
|
|
1241
|
+
*/
|
|
1242
|
+
async *iterate(query = {}) {
|
|
1243
|
+
const base = parseRequest(paymentIntentListQuery, query);
|
|
1244
|
+
yield* iterateStripeList(
|
|
1245
|
+
(startingAfter) => this.http.request({
|
|
1246
|
+
method: "GET",
|
|
1247
|
+
path: "/v1/payment_intents",
|
|
1248
|
+
query: flattenQuery({ ...base, starting_after: startingAfter }),
|
|
1249
|
+
schema: paymentIntentListRes
|
|
1250
|
+
})
|
|
1251
|
+
);
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
1254
|
+
var customUnitAmountInput = z.object({
|
|
1255
|
+
/** Pass `true` to enable — otherwise omit the whole object. */
|
|
1256
|
+
enabled: z.boolean(),
|
|
1257
|
+
maximum: z.number().int().optional(),
|
|
1258
|
+
minimum: z.number().int().optional(),
|
|
1259
|
+
preset: z.number().int().optional()
|
|
1260
|
+
});
|
|
1261
|
+
var recurringInput = z.object({
|
|
1262
|
+
interval: z.enum(["day", "week", "month", "year"]),
|
|
1263
|
+
/** Intervals between billings — max 3 years (36 months, 156 weeks). */
|
|
1264
|
+
interval_count: z.number().int().optional(),
|
|
1265
|
+
/** Defaults to `licensed`. */
|
|
1266
|
+
usage_type: z.enum(["licensed", "metered"]).optional(),
|
|
1267
|
+
/** Billing Meter id tracking usage for a metered price. */
|
|
1268
|
+
meter: z.string().optional()
|
|
1269
|
+
});
|
|
1270
|
+
var tierInput = z.object({
|
|
1271
|
+
/** Upper bound of this tier — `"inf"` for the fallback tier. Required. */
|
|
1272
|
+
up_to: z.union([z.literal("inf"), z.number().int()]),
|
|
1273
|
+
/** Flat amount for the whole tier, integer minor units. */
|
|
1274
|
+
flat_amount: z.number().int().optional(),
|
|
1275
|
+
/** Decimal-string alternative to `flat_amount` (only one of the two). */
|
|
1276
|
+
flat_amount_decimal: z.string().optional(),
|
|
1277
|
+
/** Per-unit amount, integer minor units. */
|
|
1278
|
+
unit_amount: z.number().int().optional(),
|
|
1279
|
+
/** Decimal-string alternative to `unit_amount` (only one of the two). */
|
|
1280
|
+
unit_amount_decimal: z.string().optional()
|
|
1281
|
+
});
|
|
1282
|
+
var productDataInput = z.object({
|
|
1283
|
+
/** Displayable product name. Required. */
|
|
1284
|
+
name: z.string(),
|
|
1285
|
+
/** Defaults to true. */
|
|
1286
|
+
active: z.boolean().optional(),
|
|
1287
|
+
metadata: metadataSchema.optional(),
|
|
1288
|
+
/** ≤22 chars; may not include `<`, `>`, `\`, `"`, `'`. */
|
|
1289
|
+
statement_descriptor: z.string().max(22).optional(),
|
|
1290
|
+
/** Tax code id — recommended when calculating taxes. */
|
|
1291
|
+
tax_code: z.string().optional(),
|
|
1292
|
+
tax_details: z.object({
|
|
1293
|
+
tax_code: z.string().optional(),
|
|
1294
|
+
/** Tax location id — required/optional/unsupported per tax code. */
|
|
1295
|
+
performance_location: z.string().optional()
|
|
1296
|
+
}).optional(),
|
|
1297
|
+
/** ≤12 chars. */
|
|
1298
|
+
unit_label: z.string().max(12).optional()
|
|
1299
|
+
});
|
|
1300
|
+
var priceCreateReq = z.object({
|
|
1301
|
+
/** Three-letter ISO currency code, lowercase. Required. */
|
|
1302
|
+
currency: z.string(),
|
|
1303
|
+
/** Integer minor units (or 0 for a free price). */
|
|
1304
|
+
unit_amount: z.number().int().nonnegative().optional(),
|
|
1305
|
+
/** Decimal string alternative to `unit_amount` (≤12 decimal places). */
|
|
1306
|
+
unit_amount_decimal: z.string().optional(),
|
|
1307
|
+
custom_unit_amount: customUnitAmountInput.optional(),
|
|
1308
|
+
/** Usable for new purchases. Defaults to true; `false` = archived. */
|
|
1309
|
+
active: z.boolean().optional(),
|
|
1310
|
+
/** Defaults to `per_unit`. */
|
|
1311
|
+
billing_scheme: z.enum(["per_unit", "tiered"]).optional(),
|
|
1312
|
+
/** Stable retrieval key (≤200 chars) — PW-803's price identifier. */
|
|
1313
|
+
lookup_key: z.string().max(200).optional(),
|
|
1314
|
+
/** Atomically move `lookup_key` off the price currently holding it. */
|
|
1315
|
+
transfer_lookup_key: z.boolean().optional(),
|
|
1316
|
+
metadata: metadataSchema.optional(),
|
|
1317
|
+
/** Internal description, hidden from customers. */
|
|
1318
|
+
nickname: z.string().optional(),
|
|
1319
|
+
/** Existing Product id (`prod_*`). Alternative to `product_data`. */
|
|
1320
|
+
product: z.string().optional(),
|
|
1321
|
+
/** Inline Product creation. Alternative to `product`. */
|
|
1322
|
+
product_data: productDataInput.optional(),
|
|
1323
|
+
recurring: recurringInput.optional(),
|
|
1324
|
+
/** Cannot be changed after being set to `inclusive`/`exclusive`. */
|
|
1325
|
+
tax_behavior: z.enum(["inclusive", "exclusive", "unspecified"]).optional(),
|
|
1326
|
+
/** Required when `billing_scheme=tiered`. */
|
|
1327
|
+
tiers: z.array(tierInput).optional(),
|
|
1328
|
+
/** Required when `billing_scheme=tiered`. */
|
|
1329
|
+
tiers_mode: z.enum(["graduated", "volume"]).optional(),
|
|
1330
|
+
/** Transform reported quantity before billing — cannot combine with tiers. */
|
|
1331
|
+
transform_quantity: z.object({
|
|
1332
|
+
divide_by: z.number().int(),
|
|
1333
|
+
round: z.enum(["up", "down"])
|
|
1334
|
+
}).optional()
|
|
1335
|
+
});
|
|
1336
|
+
var priceUpdateReq = z.object({
|
|
1337
|
+
/** `false` archives the price for new purchases (PW-803 price rotation). */
|
|
1338
|
+
active: z.boolean().optional(),
|
|
1339
|
+
/** Stable retrieval key (≤200 chars). */
|
|
1340
|
+
lookup_key: z.string().max(200).optional(),
|
|
1341
|
+
/** Atomically move `lookup_key` off the price currently holding it. */
|
|
1342
|
+
transfer_lookup_key: z.boolean().optional(),
|
|
1343
|
+
metadata: metadataSchema.optional(),
|
|
1344
|
+
/** Internal description, hidden from customers. */
|
|
1345
|
+
nickname: z.string().optional(),
|
|
1346
|
+
/** Cannot be changed after being set to `inclusive`/`exclusive`. */
|
|
1347
|
+
tax_behavior: z.enum(["inclusive", "exclusive", "unspecified"]).optional()
|
|
1348
|
+
});
|
|
1349
|
+
var priceListQuery = z.object({
|
|
1350
|
+
...listCursorFields,
|
|
1351
|
+
/** Only active (or only archived) prices. */
|
|
1352
|
+
active: z.boolean().optional(),
|
|
1353
|
+
/** Creation-date window (Unix timestamps). */
|
|
1354
|
+
created: createdRange.optional(),
|
|
1355
|
+
/** Filter by currency code. */
|
|
1356
|
+
currency: z.string().optional(),
|
|
1357
|
+
/** Only prices with these lookup_keys (up to 10). */
|
|
1358
|
+
lookup_keys: z.array(z.string()).optional(),
|
|
1359
|
+
/** Filter by Product id. */
|
|
1360
|
+
product: z.string().optional(),
|
|
1361
|
+
/** Filter by recurring components. */
|
|
1362
|
+
recurring: z.object({
|
|
1363
|
+
interval: z.enum(["day", "week", "month", "year"]).optional(),
|
|
1364
|
+
/** Filter by the price's Billing Meter id. */
|
|
1365
|
+
meter: z.string().optional(),
|
|
1366
|
+
usage_type: z.enum(["licensed", "metered"]).optional()
|
|
1367
|
+
}).optional(),
|
|
1368
|
+
type: z.enum(["one_time", "recurring"]).optional()
|
|
1369
|
+
});
|
|
1370
|
+
var priceSearchReq = z.object({ ...searchReqFields });
|
|
1371
|
+
var price = z.looseObject({
|
|
1372
|
+
id: z.string(),
|
|
1373
|
+
object: z.string().optional(),
|
|
1374
|
+
active: z.boolean().optional(),
|
|
1375
|
+
/** `per_unit` | `tiered`. */
|
|
1376
|
+
billing_scheme: z.string().optional(),
|
|
1377
|
+
created: z.number().optional(),
|
|
1378
|
+
currency: z.string().optional(),
|
|
1379
|
+
custom_unit_amount: z.looseObject({}).nullable().optional(),
|
|
1380
|
+
livemode: z.boolean().optional(),
|
|
1381
|
+
lookup_key: z.string().nullable().optional(),
|
|
1382
|
+
metadata: z.record(z.string(), z.string()).nullable().optional(),
|
|
1383
|
+
nickname: z.string().nullable().optional(),
|
|
1384
|
+
/** Product id string, or an expanded object — kept unknown. */
|
|
1385
|
+
product: z.unknown().optional(),
|
|
1386
|
+
recurring: z.looseObject({
|
|
1387
|
+
interval: z.string().optional(),
|
|
1388
|
+
interval_count: z.number().nullable().optional(),
|
|
1389
|
+
trial_period_days: z.number().nullable().optional(),
|
|
1390
|
+
usage_type: z.string().optional(),
|
|
1391
|
+
meter: z.string().nullable().optional()
|
|
1392
|
+
}).nullable().optional(),
|
|
1393
|
+
tax_behavior: z.string().nullable().optional(),
|
|
1394
|
+
/** `graduated` | `volume` — null unless tiered. */
|
|
1395
|
+
tiers_mode: z.string().nullable().optional(),
|
|
1396
|
+
transform_quantity: z.looseObject({}).nullable().optional(),
|
|
1397
|
+
/** `one_time` | `recurring`. */
|
|
1398
|
+
type: z.string().optional(),
|
|
1399
|
+
/** Integer minor units — null for tiered/custom-amount prices. */
|
|
1400
|
+
unit_amount: z.number().nullable().optional(),
|
|
1401
|
+
unit_amount_decimal: z.string().nullable().optional()
|
|
1402
|
+
});
|
|
1403
|
+
|
|
1404
|
+
// src/stripe/resources/prices.ts
|
|
1405
|
+
var priceListRes = stripeList(price);
|
|
1406
|
+
var priceSearchRes = stripeSearchResult(price);
|
|
1407
|
+
function bodyIfNonEmpty3(body) {
|
|
1408
|
+
return Object.keys(body).length > 0 ? { body } : {};
|
|
1409
|
+
}
|
|
1410
|
+
var Prices = class {
|
|
1411
|
+
constructor(http) {
|
|
1412
|
+
this.http = http;
|
|
1413
|
+
}
|
|
1414
|
+
http;
|
|
1415
|
+
/**
|
|
1416
|
+
* Create a Price. `unit_amount` is integer MINOR units (e.g. 1500 = $15.00)
|
|
1417
|
+
* and is sent to Stripe unchanged; `recurring` makes it a subscription
|
|
1418
|
+
* price; `lookup_key` (+ `transfer_lookup_key`) gives it a stable
|
|
1419
|
+
* identifier that survives price rotation. Amount, currency, product and
|
|
1420
|
+
* recurring are IMMUTABLE afterwards. Pass `idempotencyKey` to make the
|
|
1421
|
+
* POST safely replayable (and retry-eligible).
|
|
1422
|
+
*
|
|
1423
|
+
* Docs: https://docs.stripe.com/api/prices/create
|
|
1424
|
+
*
|
|
1425
|
+
* @example
|
|
1426
|
+
* const pr = await payweave.stripe.prices.create({
|
|
1427
|
+
* currency: "usd",
|
|
1428
|
+
* unit_amount: 1500,
|
|
1429
|
+
* product: "prod_123",
|
|
1430
|
+
* recurring: { interval: "month" },
|
|
1431
|
+
* lookup_key: "pro-monthly",
|
|
1432
|
+
* }, { idempotencyKey: "push-pro-monthly-v2" });
|
|
1433
|
+
* console.log(pr.id);
|
|
1434
|
+
*/
|
|
1435
|
+
async create(input, opts = {}) {
|
|
1436
|
+
const body = parseRequest(priceCreateReq, input);
|
|
1437
|
+
return this.http.request({
|
|
1438
|
+
method: "POST",
|
|
1439
|
+
path: "/v1/prices",
|
|
1440
|
+
body,
|
|
1441
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
1442
|
+
schema: price
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Retrieve a Price by id (`price_*`). A 404 for an unknown id surfaces as
|
|
1447
|
+
* {@link PayweaveNotFoundError}.
|
|
1448
|
+
*
|
|
1449
|
+
* Docs: https://docs.stripe.com/api/prices/retrieve
|
|
1450
|
+
*
|
|
1451
|
+
* @example
|
|
1452
|
+
* const pr = await payweave.stripe.prices.retrieve("price_123");
|
|
1453
|
+
* console.log(pr.unit_amount, pr.currency);
|
|
1454
|
+
*/
|
|
1455
|
+
async retrieve(id) {
|
|
1456
|
+
const priceId = requireId(id, "price");
|
|
1457
|
+
return this.http.request({
|
|
1458
|
+
method: "GET",
|
|
1459
|
+
path: `/v1/prices/${encodeURIComponent(priceId)}`,
|
|
1460
|
+
schema: price
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
/**
|
|
1464
|
+
* Update a Price. ONLY `active`, `lookup_key`/`transfer_lookup_key`,
|
|
1465
|
+
* `metadata`, `nickname` and `tax_behavior` (until set) are updatable —
|
|
1466
|
+
* amount/currency/product/recurring are immutable, and prices cannot be
|
|
1467
|
+
* deleted: rotate by creating a new Price and archiving this one with
|
|
1468
|
+
* `active: false` (PW-803's price-change flow). Pass `idempotencyKey` to
|
|
1469
|
+
* make the POST safely replayable (and retry-eligible).
|
|
1470
|
+
*
|
|
1471
|
+
* Docs: https://docs.stripe.com/api/prices/update
|
|
1472
|
+
*
|
|
1473
|
+
* @example
|
|
1474
|
+
* const archived = await payweave.stripe.prices.update("price_123", {
|
|
1475
|
+
* active: false,
|
|
1476
|
+
* });
|
|
1477
|
+
* console.log(archived.active); // false
|
|
1478
|
+
*/
|
|
1479
|
+
async update(id, input = {}, opts = {}) {
|
|
1480
|
+
const priceId = requireId(id, "price");
|
|
1481
|
+
const body = parseRequest(priceUpdateReq, input);
|
|
1482
|
+
return this.http.request({
|
|
1483
|
+
method: "POST",
|
|
1484
|
+
path: `/v1/prices/${encodeURIComponent(priceId)}`,
|
|
1485
|
+
...bodyIfNonEmpty3(body),
|
|
1486
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
1487
|
+
schema: price
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* List Prices (newest first). Cursor pagination: `limit` +
|
|
1492
|
+
* `starting_after`/`ending_before`; `created`/`recurring` become bracket
|
|
1493
|
+
* query keys (`recurring[interval]`) and `lookup_keys` explicit indices
|
|
1494
|
+
* (`lookup_keys[0]=...`).
|
|
1495
|
+
*
|
|
1496
|
+
* Docs: https://docs.stripe.com/api/prices/list
|
|
1497
|
+
*
|
|
1498
|
+
* @example
|
|
1499
|
+
* const page = await payweave.stripe.prices.list({ product: "prod_123", active: true });
|
|
1500
|
+
* console.log(page.data.length, page.has_more);
|
|
1501
|
+
*/
|
|
1502
|
+
async list(query = {}) {
|
|
1503
|
+
const { lookup_keys, ...rest } = parseRequest(priceListQuery, query);
|
|
1504
|
+
const q = flattenQuery(rest);
|
|
1505
|
+
lookup_keys?.forEach((key, i) => {
|
|
1506
|
+
q[`lookup_keys[${i}]`] = key;
|
|
1507
|
+
});
|
|
1508
|
+
return this.http.request({
|
|
1509
|
+
method: "GET",
|
|
1510
|
+
path: "/v1/prices",
|
|
1511
|
+
query: q,
|
|
1512
|
+
schema: priceListRes
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Async iterator over ALL Prices matching `query`, transparently following
|
|
1517
|
+
* `has_more` with `starting_after = <last id>`.
|
|
1518
|
+
*
|
|
1519
|
+
* Docs: https://docs.stripe.com/api/prices/list
|
|
1520
|
+
*
|
|
1521
|
+
* @example
|
|
1522
|
+
* for await (const pr of payweave.stripe.prices.iterate({ product: "prod_123" })) {
|
|
1523
|
+
* console.log(pr.id, pr.unit_amount);
|
|
1524
|
+
* }
|
|
1525
|
+
*/
|
|
1526
|
+
async *iterate(query = {}) {
|
|
1527
|
+
const { lookup_keys, ...base } = parseRequest(priceListQuery, query);
|
|
1528
|
+
const lookupPairs = {};
|
|
1529
|
+
lookup_keys?.forEach((key, i) => {
|
|
1530
|
+
lookupPairs[`lookup_keys[${i}]`] = key;
|
|
1531
|
+
});
|
|
1532
|
+
yield* iterateStripeList(
|
|
1533
|
+
(startingAfter) => this.http.request({
|
|
1534
|
+
method: "GET",
|
|
1535
|
+
path: "/v1/prices",
|
|
1536
|
+
query: {
|
|
1537
|
+
...flattenQuery({ ...base, starting_after: startingAfter }),
|
|
1538
|
+
...lookupPairs
|
|
1539
|
+
},
|
|
1540
|
+
schema: priceListRes
|
|
1541
|
+
})
|
|
1542
|
+
);
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Search Prices with Stripe's search query language (e.g.
|
|
1546
|
+
* `active:'true' AND lookup_key:'pro-monthly'`). Pagination is TOKEN-based:
|
|
1547
|
+
* pass the previous page's `next_page` as `page` (never `starting_after`).
|
|
1548
|
+
* Search is eventually consistent — don't use it in read-after-write flows.
|
|
1549
|
+
*
|
|
1550
|
+
* Docs: https://docs.stripe.com/api/prices/search
|
|
1551
|
+
*
|
|
1552
|
+
* @example
|
|
1553
|
+
* const found = await payweave.stripe.prices.search({
|
|
1554
|
+
* query: "metadata['pwv_reference']:'pwv_plan_pro'",
|
|
1555
|
+
* });
|
|
1556
|
+
* console.log(found.data[0]?.id, found.next_page);
|
|
1557
|
+
*/
|
|
1558
|
+
async search(input) {
|
|
1559
|
+
const q = parseRequest(priceSearchReq, input);
|
|
1560
|
+
return this.http.request({
|
|
1561
|
+
method: "GET",
|
|
1562
|
+
path: "/v1/prices/search",
|
|
1563
|
+
query: flattenQuery(q),
|
|
1564
|
+
schema: priceSearchRes
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1568
|
+
* Async iterator over ALL Prices matching a search query, transparently
|
|
1569
|
+
* following `has_more` with `page = <next_page token>` — search pagination
|
|
1570
|
+
* differs from list pagination (https://docs.stripe.com/api/pagination/search).
|
|
1571
|
+
*
|
|
1572
|
+
* Docs: https://docs.stripe.com/api/prices/search
|
|
1573
|
+
*
|
|
1574
|
+
* @example
|
|
1575
|
+
* for await (const pr of payweave.stripe.prices.iterateSearch({
|
|
1576
|
+
* query: "type:'recurring'",
|
|
1577
|
+
* })) {
|
|
1578
|
+
* console.log(pr.id);
|
|
1579
|
+
* }
|
|
1580
|
+
*/
|
|
1581
|
+
async *iterateSearch(input) {
|
|
1582
|
+
const base = parseRequest(priceSearchReq, input);
|
|
1583
|
+
yield* iterateStripeSearch(
|
|
1584
|
+
(page) => this.http.request({
|
|
1585
|
+
method: "GET",
|
|
1586
|
+
path: "/v1/prices/search",
|
|
1587
|
+
query: flattenQuery({ ...base, page }),
|
|
1588
|
+
schema: priceSearchRes
|
|
1589
|
+
})
|
|
1590
|
+
);
|
|
1591
|
+
}
|
|
1592
|
+
};
|
|
1593
|
+
var marketingFeatureInput = z.object({
|
|
1594
|
+
/** ≤80 chars. */
|
|
1595
|
+
name: z.string().max(80)
|
|
1596
|
+
});
|
|
1597
|
+
var packageDimensionsInput = z.object({
|
|
1598
|
+
/** Inches, ≤2 decimal places. */
|
|
1599
|
+
height: z.number(),
|
|
1600
|
+
/** Inches, ≤2 decimal places. */
|
|
1601
|
+
length: z.number(),
|
|
1602
|
+
/** Ounces, ≤2 decimal places. */
|
|
1603
|
+
weight: z.number(),
|
|
1604
|
+
/** Inches, ≤2 decimal places. */
|
|
1605
|
+
width: z.number()
|
|
1606
|
+
});
|
|
1607
|
+
var taxDetailsInput = z.object({
|
|
1608
|
+
tax_code: z.string().optional(),
|
|
1609
|
+
/** Tax location id — required/optional/unsupported depending on tax code. */
|
|
1610
|
+
performance_location: z.string().optional()
|
|
1611
|
+
});
|
|
1612
|
+
var customUnitAmountInput2 = z.object({
|
|
1613
|
+
/** Pass `true` to enable — otherwise omit the whole object. */
|
|
1614
|
+
enabled: z.boolean(),
|
|
1615
|
+
maximum: z.number().int().optional(),
|
|
1616
|
+
minimum: z.number().int().optional(),
|
|
1617
|
+
preset: z.number().int().optional()
|
|
1618
|
+
});
|
|
1619
|
+
var defaultPriceDataInput = z.object({
|
|
1620
|
+
/** Three-letter ISO currency code, lowercase. Required. */
|
|
1621
|
+
currency: z.string(),
|
|
1622
|
+
/** Integer minor units (or 0 for a free price). */
|
|
1623
|
+
unit_amount: z.number().int().nonnegative().optional(),
|
|
1624
|
+
/** Decimal string alternative to `unit_amount` (≤12 decimal places). */
|
|
1625
|
+
unit_amount_decimal: z.string().optional(),
|
|
1626
|
+
custom_unit_amount: customUnitAmountInput2.optional(),
|
|
1627
|
+
metadata: metadataSchema.optional(),
|
|
1628
|
+
recurring: z.object({
|
|
1629
|
+
interval: z.enum(["day", "week", "month", "year"]),
|
|
1630
|
+
/** Intervals between billings — max 3 years (36 months, 156 weeks). */
|
|
1631
|
+
interval_count: z.number().int().optional()
|
|
1632
|
+
}).optional(),
|
|
1633
|
+
/** Cannot be changed after being set to `inclusive`/`exclusive`. */
|
|
1634
|
+
tax_behavior: z.enum(["inclusive", "exclusive", "unspecified"]).optional()
|
|
1635
|
+
});
|
|
1636
|
+
var productSharedFields = {
|
|
1637
|
+
/** Available for purchase. Defaults to true; `false` = archived. */
|
|
1638
|
+
active: z.boolean().optional(),
|
|
1639
|
+
description: z.string().optional(),
|
|
1640
|
+
/** Up to 8 image URLs. */
|
|
1641
|
+
images: z.array(z.string()).optional(),
|
|
1642
|
+
/** Up to 15 marketing features (pricing tables). */
|
|
1643
|
+
marketing_features: z.array(marketingFeatureInput).optional(),
|
|
1644
|
+
metadata: metadataSchema.optional(),
|
|
1645
|
+
package_dimensions: packageDimensionsInput.optional(),
|
|
1646
|
+
/** Whether the product is shipped (physical goods). */
|
|
1647
|
+
shippable: z.boolean().optional(),
|
|
1648
|
+
/** ≤22 chars, `type=service` subscriptions only. */
|
|
1649
|
+
statement_descriptor: z.string().max(22).optional(),
|
|
1650
|
+
/** Tax code id — recommended when calculating taxes. */
|
|
1651
|
+
tax_code: z.string().optional(),
|
|
1652
|
+
tax_details: taxDetailsInput.optional(),
|
|
1653
|
+
/** ≤12 chars, `type=service` only. */
|
|
1654
|
+
unit_label: z.string().max(12).optional(),
|
|
1655
|
+
/** Publicly-accessible webpage for the product. */
|
|
1656
|
+
url: z.string().optional()
|
|
1657
|
+
};
|
|
1658
|
+
var productCreateReq = z.object({
|
|
1659
|
+
/** Displayable product name. Required. */
|
|
1660
|
+
name: z.string(),
|
|
1661
|
+
...productSharedFields,
|
|
1662
|
+
/** Custom unique id (`prod_*` generated when omitted). */
|
|
1663
|
+
id: z.string().optional(),
|
|
1664
|
+
/** Create the product's default Price in the same call. */
|
|
1665
|
+
default_price_data: defaultPriceDataInput.optional()
|
|
1666
|
+
});
|
|
1667
|
+
var productUpdateReq = z.object({
|
|
1668
|
+
...productSharedFields,
|
|
1669
|
+
/** Existing Price id (`price_*`) to make the default for this product. */
|
|
1670
|
+
default_price: z.string().optional(),
|
|
1671
|
+
name: z.string().optional()
|
|
1672
|
+
});
|
|
1673
|
+
var productListQuery = z.object({
|
|
1674
|
+
...listCursorFields,
|
|
1675
|
+
/** Only active (or only archived) products. */
|
|
1676
|
+
active: z.boolean().optional(),
|
|
1677
|
+
/** Creation-date window (Unix timestamps). */
|
|
1678
|
+
created: createdRange.optional(),
|
|
1679
|
+
/** Only these product ids — cannot combine with the cursor params. */
|
|
1680
|
+
ids: z.array(z.string()).optional(),
|
|
1681
|
+
/** Only shippable (physical) products. */
|
|
1682
|
+
shippable: z.boolean().optional(),
|
|
1683
|
+
/** Only products with this url. */
|
|
1684
|
+
url: z.string().optional()
|
|
1685
|
+
});
|
|
1686
|
+
var productSearchReq = z.object({ ...searchReqFields });
|
|
1687
|
+
var product = z.looseObject({
|
|
1688
|
+
id: z.string(),
|
|
1689
|
+
object: z.string().optional(),
|
|
1690
|
+
active: z.boolean().optional(),
|
|
1691
|
+
created: z.number().optional(),
|
|
1692
|
+
/** Price id string, or an expanded object — kept unknown. */
|
|
1693
|
+
default_price: z.unknown().optional(),
|
|
1694
|
+
description: z.string().nullable().optional(),
|
|
1695
|
+
images: z.array(z.string()).optional(),
|
|
1696
|
+
marketing_features: z.array(z.looseObject({})).optional(),
|
|
1697
|
+
livemode: z.boolean().optional(),
|
|
1698
|
+
metadata: z.record(z.string(), z.string()).nullable().optional(),
|
|
1699
|
+
name: z.string().optional(),
|
|
1700
|
+
package_dimensions: z.looseObject({}).nullable().optional(),
|
|
1701
|
+
shippable: z.boolean().nullable().optional(),
|
|
1702
|
+
statement_descriptor: z.string().nullable().optional(),
|
|
1703
|
+
/** Tax code id string, or an expanded object — kept unknown. */
|
|
1704
|
+
tax_code: z.unknown().optional(),
|
|
1705
|
+
unit_label: z.string().nullable().optional(),
|
|
1706
|
+
updated: z.number().optional(),
|
|
1707
|
+
url: z.string().nullable().optional()
|
|
1708
|
+
});
|
|
1709
|
+
var deletedProduct = z.looseObject({
|
|
1710
|
+
id: z.string(),
|
|
1711
|
+
object: z.string().optional(),
|
|
1712
|
+
deleted: z.boolean()
|
|
1713
|
+
});
|
|
1714
|
+
|
|
1715
|
+
// src/stripe/resources/products.ts
|
|
1716
|
+
var productListRes = stripeList(product);
|
|
1717
|
+
var productSearchRes = stripeSearchResult(product);
|
|
1718
|
+
function bodyIfNonEmpty4(body) {
|
|
1719
|
+
return Object.keys(body).length > 0 ? { body } : {};
|
|
1720
|
+
}
|
|
1721
|
+
var Products = class {
|
|
1722
|
+
constructor(http) {
|
|
1723
|
+
this.http = http;
|
|
1724
|
+
}
|
|
1725
|
+
http;
|
|
1726
|
+
/**
|
|
1727
|
+
* Create a Product. `default_price_data` creates the product's default
|
|
1728
|
+
* Price in the same call — its `unit_amount` is integer MINOR units, sent
|
|
1729
|
+
* to Stripe unchanged. Pass `idempotencyKey` to make the POST safely
|
|
1730
|
+
* replayable (and retry-eligible).
|
|
1731
|
+
*
|
|
1732
|
+
* Docs: https://docs.stripe.com/api/products/create
|
|
1733
|
+
*
|
|
1734
|
+
* @example
|
|
1735
|
+
* const prod = await payweave.stripe.products.create({
|
|
1736
|
+
* name: "Pro Plan",
|
|
1737
|
+
* metadata: { pwv_reference: "pwv_plan_pro" },
|
|
1738
|
+
* default_price_data: {
|
|
1739
|
+
* currency: "usd",
|
|
1740
|
+
* unit_amount: 1500, // $15.00 in minor units
|
|
1741
|
+
* recurring: { interval: "month" },
|
|
1742
|
+
* },
|
|
1743
|
+
* }, { idempotencyKey: "push-pro-plan-v1" });
|
|
1744
|
+
* console.log(prod.id, prod.default_price);
|
|
1745
|
+
*/
|
|
1746
|
+
async create(input, opts = {}) {
|
|
1747
|
+
const body = parseRequest(productCreateReq, input);
|
|
1748
|
+
return this.http.request({
|
|
1749
|
+
method: "POST",
|
|
1750
|
+
path: "/v1/products",
|
|
1751
|
+
body,
|
|
1752
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
1753
|
+
schema: product
|
|
1754
|
+
});
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Retrieve a Product by id (`prod_*`). A 404 for an unknown id surfaces as
|
|
1758
|
+
* {@link PayweaveNotFoundError}.
|
|
1759
|
+
*
|
|
1760
|
+
* Docs: https://docs.stripe.com/api/products/retrieve
|
|
1761
|
+
*
|
|
1762
|
+
* @example
|
|
1763
|
+
* const prod = await payweave.stripe.products.retrieve("prod_123");
|
|
1764
|
+
* console.log(prod.name, prod.active);
|
|
1765
|
+
*/
|
|
1766
|
+
async retrieve(id) {
|
|
1767
|
+
const productId = requireId(id, "product");
|
|
1768
|
+
return this.http.request({
|
|
1769
|
+
method: "GET",
|
|
1770
|
+
path: `/v1/products/${encodeURIComponent(productId)}`,
|
|
1771
|
+
schema: product
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* Update a Product. Fields not provided remain unchanged. `active: false`
|
|
1776
|
+
* ARCHIVES the product (no new purchases) — the sync-flow alternative to
|
|
1777
|
+
* deletion, since products with prices cannot be deleted (PW-803 relies on
|
|
1778
|
+
* this). Pass `idempotencyKey` to make the POST safely replayable (and
|
|
1779
|
+
* retry-eligible).
|
|
1780
|
+
*
|
|
1781
|
+
* Docs: https://docs.stripe.com/api/products/update
|
|
1782
|
+
*
|
|
1783
|
+
* @example
|
|
1784
|
+
* const archived = await payweave.stripe.products.update("prod_123", { active: false });
|
|
1785
|
+
* console.log(archived.active); // false
|
|
1786
|
+
*/
|
|
1787
|
+
async update(id, input = {}, opts = {}) {
|
|
1788
|
+
const productId = requireId(id, "product");
|
|
1789
|
+
const body = parseRequest(productUpdateReq, input);
|
|
1790
|
+
return this.http.request({
|
|
1791
|
+
method: "POST",
|
|
1792
|
+
path: `/v1/products/${encodeURIComponent(productId)}`,
|
|
1793
|
+
...bodyIfNonEmpty4(body),
|
|
1794
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
1795
|
+
schema: product
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
/**
|
|
1799
|
+
* Delete a Product. Only succeeds when the product has NO prices attached
|
|
1800
|
+
* (and, for `type=good`, no SKUs) — otherwise Stripe rejects with a 400
|
|
1801
|
+
* (surfaced as {@link PayweaveValidationError}). Sync flows should archive
|
|
1802
|
+
* with `update(id, { active: false })` instead. Returns the deletion stub
|
|
1803
|
+
* `{ id, object: "product", deleted: true }`.
|
|
1804
|
+
*
|
|
1805
|
+
* Docs: https://docs.stripe.com/api/products/delete
|
|
1806
|
+
*
|
|
1807
|
+
* @example
|
|
1808
|
+
* const gone = await payweave.stripe.products.delete("prod_123");
|
|
1809
|
+
* console.log(gone.deleted); // true
|
|
1810
|
+
*/
|
|
1811
|
+
async delete(id) {
|
|
1812
|
+
const productId = requireId(id, "product");
|
|
1813
|
+
return this.http.request({
|
|
1814
|
+
method: "DELETE",
|
|
1815
|
+
path: `/v1/products/${encodeURIComponent(productId)}`,
|
|
1816
|
+
schema: deletedProduct
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
/**
|
|
1820
|
+
* List Products (newest first). Cursor pagination: `limit` +
|
|
1821
|
+
* `starting_after`/`ending_before`; `created` becomes bracket query keys
|
|
1822
|
+
* and `ids` explicit indices (`ids[0]=...` — cannot combine `ids` with the
|
|
1823
|
+
* cursor params).
|
|
1824
|
+
*
|
|
1825
|
+
* Docs: https://docs.stripe.com/api/products/list
|
|
1826
|
+
*
|
|
1827
|
+
* @example
|
|
1828
|
+
* const page = await payweave.stripe.products.list({ active: true, limit: 50 });
|
|
1829
|
+
* console.log(page.data.length, page.has_more);
|
|
1830
|
+
*/
|
|
1831
|
+
async list(query = {}) {
|
|
1832
|
+
const { ids, ...rest } = parseRequest(productListQuery, query);
|
|
1833
|
+
const q = flattenQuery(rest);
|
|
1834
|
+
ids?.forEach((id, i) => {
|
|
1835
|
+
q[`ids[${i}]`] = id;
|
|
1836
|
+
});
|
|
1837
|
+
return this.http.request({
|
|
1838
|
+
method: "GET",
|
|
1839
|
+
path: "/v1/products",
|
|
1840
|
+
query: q,
|
|
1841
|
+
schema: productListRes
|
|
1842
|
+
});
|
|
1843
|
+
}
|
|
1844
|
+
/**
|
|
1845
|
+
* Async iterator over ALL Products matching `query`, transparently
|
|
1846
|
+
* following `has_more` with `starting_after = <last id>` (providers.md
|
|
1847
|
+
* §3.1). `ids` is not supported here — Stripe forbids combining it with
|
|
1848
|
+
* cursors; use {@link Products.list}.
|
|
1849
|
+
*
|
|
1850
|
+
* Docs: https://docs.stripe.com/api/products/list
|
|
1851
|
+
*
|
|
1852
|
+
* @example
|
|
1853
|
+
* for await (const prod of payweave.stripe.products.iterate({ active: true })) {
|
|
1854
|
+
* console.log(prod.id, prod.name);
|
|
1855
|
+
* }
|
|
1856
|
+
*/
|
|
1857
|
+
async *iterate(query = {}) {
|
|
1858
|
+
const base = parseRequest(productListQuery, query);
|
|
1859
|
+
yield* iterateStripeList(
|
|
1860
|
+
(startingAfter) => this.http.request({
|
|
1861
|
+
method: "GET",
|
|
1862
|
+
path: "/v1/products",
|
|
1863
|
+
query: flattenQuery({ ...base, starting_after: startingAfter }),
|
|
1864
|
+
schema: productListRes
|
|
1865
|
+
})
|
|
1866
|
+
);
|
|
1867
|
+
}
|
|
1868
|
+
/**
|
|
1869
|
+
* Search Products with Stripe's search query language (e.g.
|
|
1870
|
+
* `active:'true' AND metadata['order_id']:'6735'`). Pagination is
|
|
1871
|
+
* TOKEN-based: pass the previous page's `next_page` as `page` (never
|
|
1872
|
+
* `starting_after`). Search is eventually consistent — don't use it in
|
|
1873
|
+
* read-after-write flows.
|
|
1874
|
+
*
|
|
1875
|
+
* Docs: https://docs.stripe.com/api/products/search
|
|
1876
|
+
*
|
|
1877
|
+
* @example
|
|
1878
|
+
* const found = await payweave.stripe.products.search({
|
|
1879
|
+
* query: "metadata['pwv_reference']:'pwv_plan_pro'",
|
|
1880
|
+
* });
|
|
1881
|
+
* console.log(found.data[0]?.id, found.has_more);
|
|
1882
|
+
*/
|
|
1883
|
+
async search(input) {
|
|
1884
|
+
const q = parseRequest(productSearchReq, input);
|
|
1885
|
+
return this.http.request({
|
|
1886
|
+
method: "GET",
|
|
1887
|
+
path: "/v1/products/search",
|
|
1888
|
+
query: flattenQuery(q),
|
|
1889
|
+
schema: productSearchRes
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
/**
|
|
1893
|
+
* Async iterator over ALL Products matching a search query, transparently
|
|
1894
|
+
* following `has_more` with `page = <next_page token>` — search pagination
|
|
1895
|
+
* differs from list pagination (https://docs.stripe.com/api/pagination/search).
|
|
1896
|
+
*
|
|
1897
|
+
* Docs: https://docs.stripe.com/api/products/search
|
|
1898
|
+
*
|
|
1899
|
+
* @example
|
|
1900
|
+
* for await (const prod of payweave.stripe.products.iterateSearch({
|
|
1901
|
+
* query: "active:'true'",
|
|
1902
|
+
* })) {
|
|
1903
|
+
* console.log(prod.id);
|
|
1904
|
+
* }
|
|
1905
|
+
*/
|
|
1906
|
+
async *iterateSearch(input) {
|
|
1907
|
+
const base = parseRequest(productSearchReq, input);
|
|
1908
|
+
yield* iterateStripeSearch(
|
|
1909
|
+
(page) => this.http.request({
|
|
1910
|
+
method: "GET",
|
|
1911
|
+
path: "/v1/products/search",
|
|
1912
|
+
query: flattenQuery({ ...base, page }),
|
|
1913
|
+
schema: productSearchRes
|
|
1914
|
+
})
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
};
|
|
1918
|
+
var refundCreateReq = z.object({
|
|
1919
|
+
/**
|
|
1920
|
+
* Positive integer in the smallest currency unit (minor units) — "can
|
|
1921
|
+
* refund only up to the remaining, unrefunded amount of the charge".
|
|
1922
|
+
* Omit to refund in full.
|
|
1923
|
+
*/
|
|
1924
|
+
amount: z.number().int().positive().optional(),
|
|
1925
|
+
/** The identifier of the charge to refund (`ch_*`). */
|
|
1926
|
+
charge: z.string().optional(),
|
|
1927
|
+
/**
|
|
1928
|
+
* The identifier of the PaymentIntent to refund (`pi_*`) — the path the
|
|
1929
|
+
* unified layer uses (providers.md §3.3: refunds go against the payment
|
|
1930
|
+
* intent).
|
|
1931
|
+
*/
|
|
1932
|
+
payment_intent: z.string().optional(),
|
|
1933
|
+
/**
|
|
1934
|
+
* Reason for the refund. If set: `duplicate` | `fraudulent` |
|
|
1935
|
+
* `requested_by_customer`.
|
|
1936
|
+
*/
|
|
1937
|
+
reason: z.enum(["duplicate", "fraudulent", "requested_by_customer"]).optional(),
|
|
1938
|
+
/**
|
|
1939
|
+
* For payment methods without native refund support (e.g. Konbini,
|
|
1940
|
+
* PromptPay) — customer email to receive refund instructions.
|
|
1941
|
+
*/
|
|
1942
|
+
instructions_email: z.string().optional(),
|
|
1943
|
+
metadata: metadataSchema.optional(),
|
|
1944
|
+
/**
|
|
1945
|
+
* Origin of the refund. `customer_balance` refunds from a Customer
|
|
1946
|
+
* Balance instead of a Charge/PaymentIntent — when provided, a
|
|
1947
|
+
* Charge/PaymentIntent identifier is not required.
|
|
1948
|
+
*/
|
|
1949
|
+
origin: z.enum(["customer_balance"]).optional(),
|
|
1950
|
+
/**
|
|
1951
|
+
* Whether the application fee should be refunded too (refundable only by
|
|
1952
|
+
* the application that created the charge — Connect).
|
|
1953
|
+
*/
|
|
1954
|
+
refund_application_fee: z.boolean().optional(),
|
|
1955
|
+
/**
|
|
1956
|
+
* Whether the transfer should be reversed when refunding (reversible only
|
|
1957
|
+
* by the application that created the charge — Connect).
|
|
1958
|
+
*/
|
|
1959
|
+
reverse_transfer: z.boolean().optional()
|
|
1960
|
+
}).refine(
|
|
1961
|
+
(v) => v.charge !== void 0 || v.payment_intent !== void 0 || v.origin !== void 0,
|
|
1962
|
+
{
|
|
1963
|
+
message: "one of charge, payment_intent, or origin is required (https://docs.stripe.com/api/refunds/create)"
|
|
1964
|
+
}
|
|
1965
|
+
);
|
|
1966
|
+
var refundUpdateReq = z.object({
|
|
1967
|
+
metadata: metadataSchema.optional()
|
|
1968
|
+
});
|
|
1969
|
+
var refundListQuery = z.object({
|
|
1970
|
+
...listCursorFields,
|
|
1971
|
+
/** Only return refunds for this Charge id. */
|
|
1972
|
+
charge: z.string().optional(),
|
|
1973
|
+
/** Only return refunds for this PaymentIntent id. */
|
|
1974
|
+
payment_intent: z.string().optional(),
|
|
1975
|
+
/** Creation-date window (Unix timestamps). */
|
|
1976
|
+
created: createdRange.optional()
|
|
1977
|
+
});
|
|
1978
|
+
var refund = z.looseObject({
|
|
1979
|
+
id: z.string(),
|
|
1980
|
+
object: z.string().optional(),
|
|
1981
|
+
amount: z.number().optional(),
|
|
1982
|
+
/** Balance transaction id string, or an expanded object — kept unknown. */
|
|
1983
|
+
balance_transaction: z.unknown().optional(),
|
|
1984
|
+
/** Charge id string, or an expanded object — kept unknown. */
|
|
1985
|
+
charge: z.unknown().optional(),
|
|
1986
|
+
created: z.number().optional(),
|
|
1987
|
+
currency: z.string().optional(),
|
|
1988
|
+
/** Non-card refunds only. */
|
|
1989
|
+
description: z.string().nullable().optional(),
|
|
1990
|
+
/** Transaction-specific details — child hashes deliberately untyped. */
|
|
1991
|
+
destination_details: z.looseObject({}).nullable().optional(),
|
|
1992
|
+
/** Balance transaction reversing the initial one after a failed refund. */
|
|
1993
|
+
failure_balance_transaction: z.unknown().optional(),
|
|
1994
|
+
/**
|
|
1995
|
+
* `lost_or_stolen_card` | `expired_or_canceled_card` |
|
|
1996
|
+
* `charge_for_pending_refund_disputed` | `insufficient_funds` | `declined` |
|
|
1997
|
+
* `merchant_request` | `unknown` — kept loose (drift logs, never throws).
|
|
1998
|
+
*/
|
|
1999
|
+
failure_reason: z.string().nullable().optional(),
|
|
2000
|
+
instructions_email: z.string().nullable().optional(),
|
|
2001
|
+
metadata: z.record(z.string(), z.string()).nullable().optional(),
|
|
2002
|
+
/** Present when status is `requires_action` — child shape kept loose. */
|
|
2003
|
+
next_action: z.looseObject({}).nullable().optional(),
|
|
2004
|
+
/** PaymentIntent id string, or an expanded object — kept unknown. */
|
|
2005
|
+
payment_intent: z.unknown().optional(),
|
|
2006
|
+
/** `processing` | `insufficient_funds` | `charge_pending` — kept loose. */
|
|
2007
|
+
pending_reason: z.string().nullable().optional(),
|
|
2008
|
+
/**
|
|
2009
|
+
* `duplicate` | `fraudulent` | `requested_by_customer` or Stripe-generated
|
|
2010
|
+
* `expired_uncaptured_charge` — kept loose.
|
|
2011
|
+
*/
|
|
2012
|
+
reason: z.string().nullable().optional(),
|
|
2013
|
+
receipt_number: z.string().nullable().optional(),
|
|
2014
|
+
/** Transfer reversal id string, or an expanded object — kept unknown. */
|
|
2015
|
+
source_transfer_reversal: z.unknown().optional(),
|
|
2016
|
+
/**
|
|
2017
|
+
* `pending` | `requires_action` | `succeeded` | `failed` | `canceled` —
|
|
2018
|
+
* normalization is the unified layer's job.
|
|
2019
|
+
*/
|
|
2020
|
+
status: z.string().nullable().optional(),
|
|
2021
|
+
/** Transfer reversal id string, or an expanded object — kept unknown. */
|
|
2022
|
+
transfer_reversal: z.unknown().optional()
|
|
2023
|
+
});
|
|
2024
|
+
|
|
2025
|
+
// src/stripe/resources/refunds.ts
|
|
2026
|
+
var refundListRes = stripeList(refund);
|
|
2027
|
+
function bodyIfNonEmpty5(body) {
|
|
2028
|
+
return Object.keys(body).length > 0 ? { body } : {};
|
|
2029
|
+
}
|
|
2030
|
+
var Refunds = class {
|
|
2031
|
+
constructor(http) {
|
|
2032
|
+
this.http = http;
|
|
2033
|
+
}
|
|
2034
|
+
http;
|
|
2035
|
+
/**
|
|
2036
|
+
* Create a refund. Target either a `charge` or a `payment_intent` (the
|
|
2037
|
+
* unified layer refunds against the payment intent — providers.md §3.3);
|
|
2038
|
+
* one of the two is required unless `origin: "customer_balance"`. `amount`
|
|
2039
|
+
* is integer MINOR units and defaults to the full remaining unrefunded
|
|
2040
|
+
* amount. Stripe errors if the target is already fully refunded (surfaced
|
|
2041
|
+
* as {@link PayweaveValidationError} with providerCode
|
|
2042
|
+
* `charge_already_refunded` on the 400).
|
|
2043
|
+
*
|
|
2044
|
+
* Money-moving POST: never auto-retried bare — pass `idempotencyKey` to
|
|
2045
|
+
* make it safely replayable (and retry-eligible).
|
|
2046
|
+
*
|
|
2047
|
+
* Docs: https://docs.stripe.com/api/refunds/create
|
|
2048
|
+
*
|
|
2049
|
+
* @example
|
|
2050
|
+
* const re = await payweave.stripe.refunds.create({
|
|
2051
|
+
* payment_intent: "pi_123",
|
|
2052
|
+
* amount: 500, // partial refund, minor units
|
|
2053
|
+
* reason: "requested_by_customer",
|
|
2054
|
+
* }, { idempotencyKey: "order-8123-refund" });
|
|
2055
|
+
* console.log(re.status); // "succeeded" | "pending" | ...
|
|
2056
|
+
*/
|
|
2057
|
+
async create(input, opts = {}) {
|
|
2058
|
+
const body = parseRequest(refundCreateReq, input);
|
|
2059
|
+
return this.http.request({
|
|
2060
|
+
method: "POST",
|
|
2061
|
+
path: "/v1/refunds",
|
|
2062
|
+
body,
|
|
2063
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
2064
|
+
schema: refund
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Retrieve a refund by id (`re_*`). A 404 for an unknown id surfaces as
|
|
2069
|
+
* {@link PayweaveNotFoundError}.
|
|
2070
|
+
*
|
|
2071
|
+
* Docs: https://docs.stripe.com/api/refunds/retrieve
|
|
2072
|
+
*
|
|
2073
|
+
* @example
|
|
2074
|
+
* const re = await payweave.stripe.refunds.retrieve("re_123");
|
|
2075
|
+
* if (re.status === "succeeded") { }
|
|
2076
|
+
*/
|
|
2077
|
+
async retrieve(id) {
|
|
2078
|
+
const refundId = requireId(id, "refund");
|
|
2079
|
+
return this.http.request({
|
|
2080
|
+
method: "GET",
|
|
2081
|
+
path: `/v1/refunds/${encodeURIComponent(refundId)}`,
|
|
2082
|
+
schema: refund
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
/**
|
|
2086
|
+
* Update a refund. `metadata` is the ONLY updatable field; unset individual
|
|
2087
|
+
* keys by posting an empty string value.
|
|
2088
|
+
*
|
|
2089
|
+
* Docs: https://docs.stripe.com/api/refunds/update
|
|
2090
|
+
*
|
|
2091
|
+
* @example
|
|
2092
|
+
* const re = await payweave.stripe.refunds.update("re_123", {
|
|
2093
|
+
* metadata: { order_id: "6735" },
|
|
2094
|
+
* });
|
|
2095
|
+
*/
|
|
2096
|
+
async update(id, input = {}) {
|
|
2097
|
+
const refundId = requireId(id, "refund");
|
|
2098
|
+
const body = parseRequest(refundUpdateReq, input);
|
|
2099
|
+
return this.http.request({
|
|
2100
|
+
method: "POST",
|
|
2101
|
+
path: `/v1/refunds/${encodeURIComponent(refundId)}`,
|
|
2102
|
+
...bodyIfNonEmpty5(body),
|
|
2103
|
+
schema: refund
|
|
2104
|
+
});
|
|
2105
|
+
}
|
|
2106
|
+
/**
|
|
2107
|
+
* Cancel a refund. ONLY valid while the refund's status is
|
|
2108
|
+
* `requires_action` — "you can't cancel refunds in other states" (only
|
|
2109
|
+
* refunds for payment methods that require customer action ever enter that
|
|
2110
|
+
* state); Stripe raises an error otherwise (surfaced as
|
|
2111
|
+
* {@link PayweaveValidationError} on the 400). Takes no body parameters.
|
|
2112
|
+
*
|
|
2113
|
+
* Docs: https://docs.stripe.com/api/refunds/cancel
|
|
2114
|
+
*
|
|
2115
|
+
* @example
|
|
2116
|
+
* const re = await payweave.stripe.refunds.cancel("re_123");
|
|
2117
|
+
* console.log(re.status); // "canceled"
|
|
2118
|
+
*/
|
|
2119
|
+
async cancel(id) {
|
|
2120
|
+
const refundId = requireId(id, "refund");
|
|
2121
|
+
return this.http.request({
|
|
2122
|
+
method: "POST",
|
|
2123
|
+
path: `/v1/refunds/${encodeURIComponent(refundId)}/cancel`,
|
|
2124
|
+
schema: refund
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
/**
|
|
2128
|
+
* List refunds (newest first). Filter by `charge` or `payment_intent`;
|
|
2129
|
+
* cursor pagination via `limit` + `starting_after`/`ending_before`; the
|
|
2130
|
+
* `created` window becomes bracket query keys (`created[gte]`).
|
|
2131
|
+
*
|
|
2132
|
+
* Docs: https://docs.stripe.com/api/refunds/list
|
|
2133
|
+
*
|
|
2134
|
+
* @example
|
|
2135
|
+
* const page = await payweave.stripe.refunds.list({ payment_intent: "pi_123", limit: 50 });
|
|
2136
|
+
* console.log(page.data.length, page.has_more);
|
|
2137
|
+
*/
|
|
2138
|
+
async list(query = {}) {
|
|
2139
|
+
const q = parseRequest(refundListQuery, query);
|
|
2140
|
+
return this.http.request({
|
|
2141
|
+
method: "GET",
|
|
2142
|
+
path: "/v1/refunds",
|
|
2143
|
+
query: flattenQuery(q),
|
|
2144
|
+
schema: refundListRes
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2147
|
+
/**
|
|
2148
|
+
* Async iterator over ALL refunds matching `query`, transparently following
|
|
2149
|
+
* `has_more` with `starting_after = <last id>`.
|
|
2150
|
+
*
|
|
2151
|
+
* Docs: https://docs.stripe.com/api/refunds/list
|
|
2152
|
+
*
|
|
2153
|
+
* @example
|
|
2154
|
+
* for await (const re of payweave.stripe.refunds.iterate({ charge: "ch_123" })) {
|
|
2155
|
+
* console.log(re.id, re.status);
|
|
2156
|
+
* }
|
|
2157
|
+
*/
|
|
2158
|
+
async *iterate(query = {}) {
|
|
2159
|
+
const base = parseRequest(refundListQuery, query);
|
|
2160
|
+
yield* iterateStripeList(
|
|
2161
|
+
(startingAfter) => this.http.request({
|
|
2162
|
+
method: "GET",
|
|
2163
|
+
path: "/v1/refunds",
|
|
2164
|
+
query: flattenQuery({ ...base, starting_after: startingAfter }),
|
|
2165
|
+
schema: refundListRes
|
|
2166
|
+
})
|
|
2167
|
+
);
|
|
2168
|
+
}
|
|
2169
|
+
};
|
|
2170
|
+
var recurring2 = z.object({
|
|
2171
|
+
interval: z.enum(["day", "week", "month", "year"]),
|
|
2172
|
+
interval_count: z.number().int().optional()
|
|
2173
|
+
});
|
|
2174
|
+
var subscriptionItemPriceData = z.object({
|
|
2175
|
+
/** Three-letter ISO currency code, lowercase (e.g. `usd`). Required. */
|
|
2176
|
+
currency: z.string(),
|
|
2177
|
+
/** Existing Product id (`prod_*`) this price belongs to. Required. */
|
|
2178
|
+
product: z.string(),
|
|
2179
|
+
/** Billing frequency. Required. */
|
|
2180
|
+
recurring: recurring2,
|
|
2181
|
+
tax_behavior: z.enum(["exclusive", "inclusive", "unspecified"]).optional(),
|
|
2182
|
+
/** Amount in the smallest currency unit (minor units) — integer, unchanged. */
|
|
2183
|
+
unit_amount: z.number().int().nonnegative().optional(),
|
|
2184
|
+
/** Decimal string alternative to `unit_amount` (≤12 decimal places). */
|
|
2185
|
+
unit_amount_decimal: z.string().optional()
|
|
2186
|
+
});
|
|
2187
|
+
var discountParam = z.object({
|
|
2188
|
+
/** Coupon id to create a new discount from. */
|
|
2189
|
+
coupon: z.string().optional(),
|
|
2190
|
+
/** Existing Discount id to reuse. */
|
|
2191
|
+
discount: z.string().optional(),
|
|
2192
|
+
/** Promotion code id to create a new discount from. */
|
|
2193
|
+
promotion_code: z.string().optional()
|
|
2194
|
+
});
|
|
2195
|
+
var itemBillingThresholds = z.object({
|
|
2196
|
+
usage_gte: z.number().int()
|
|
2197
|
+
});
|
|
2198
|
+
var paymentBehavior = z.enum([
|
|
2199
|
+
"allow_incomplete",
|
|
2200
|
+
"default_incomplete",
|
|
2201
|
+
"error_if_incomplete",
|
|
2202
|
+
"pending_if_incomplete"
|
|
2203
|
+
]);
|
|
2204
|
+
var prorationBehavior = z.enum(["always_invoice", "create_prorations", "none"]);
|
|
2205
|
+
var subscriptionItemCreateReq = z.object({
|
|
2206
|
+
/** Subscription id (`sub_*`) to add the item to. Required. */
|
|
2207
|
+
subscription: z.string().min(1),
|
|
2208
|
+
/** Saved Price id (`price_*`). Alternative to `price_data`. */
|
|
2209
|
+
price: z.string().optional(),
|
|
2210
|
+
/** Inline price definition. Alternative to `price`. */
|
|
2211
|
+
price_data: subscriptionItemPriceData.optional(),
|
|
2212
|
+
quantity: z.number().int().nonnegative().optional(),
|
|
2213
|
+
metadata: metadataSchema.optional(),
|
|
2214
|
+
billing_thresholds: itemBillingThresholds.optional(),
|
|
2215
|
+
/** Coupons/promotion codes to redeem into discounts for this item. */
|
|
2216
|
+
discounts: z.array(discountParam).optional(),
|
|
2217
|
+
payment_behavior: paymentBehavior.optional(),
|
|
2218
|
+
proration_behavior: prorationBehavior.optional(),
|
|
2219
|
+
/** Unix timestamp — prorate as though the update happened at this time. */
|
|
2220
|
+
proration_date: z.number().int().optional(),
|
|
2221
|
+
/** Tax Rate ids overriding the subscription's `default_tax_rates`. */
|
|
2222
|
+
tax_rates: z.array(z.string()).optional()
|
|
2223
|
+
});
|
|
2224
|
+
var subscriptionItemUpdateReq = z.object({
|
|
2225
|
+
/** Saved Price id (`price_*`). Alternative to `price_data`. */
|
|
2226
|
+
price: z.string().optional(),
|
|
2227
|
+
/** Inline price definition. Alternative to `price`. */
|
|
2228
|
+
price_data: subscriptionItemPriceData.optional(),
|
|
2229
|
+
quantity: z.number().int().nonnegative().optional(),
|
|
2230
|
+
metadata: metadataSchema.optional(),
|
|
2231
|
+
billing_thresholds: itemBillingThresholds.optional(),
|
|
2232
|
+
/** Coupons/promotion codes to redeem into discounts for this item. */
|
|
2233
|
+
discounts: z.array(discountParam).optional(),
|
|
2234
|
+
/** Customer is not in the flow — attempt payment immediately off-session. */
|
|
2235
|
+
off_session: z.boolean().optional(),
|
|
2236
|
+
payment_behavior: paymentBehavior.optional(),
|
|
2237
|
+
proration_behavior: prorationBehavior.optional(),
|
|
2238
|
+
/** Unix timestamp — prorate as though the update happened at this time. */
|
|
2239
|
+
proration_date: z.number().int().optional(),
|
|
2240
|
+
/** Tax Rate ids overriding the subscription's `default_tax_rates`. */
|
|
2241
|
+
tax_rates: z.array(z.string()).optional()
|
|
2242
|
+
});
|
|
2243
|
+
var subscriptionItemDeleteReq = z.object({
|
|
2244
|
+
/** Delete all usage for the item — only when the price's `usage_type` is `metered`. */
|
|
2245
|
+
clear_usage: z.boolean().optional(),
|
|
2246
|
+
payment_behavior: paymentBehavior.optional(),
|
|
2247
|
+
proration_behavior: prorationBehavior.optional(),
|
|
2248
|
+
/** Unix timestamp — prorate as though the update happened at this time. */
|
|
2249
|
+
proration_date: z.number().int().optional()
|
|
2250
|
+
});
|
|
2251
|
+
var subscriptionItemListQuery = z.object({
|
|
2252
|
+
...listCursorFields,
|
|
2253
|
+
/** Subscription id (`sub_*`) whose items to list. Required. */
|
|
2254
|
+
subscription: z.string().min(1)
|
|
2255
|
+
});
|
|
2256
|
+
var subscriptionItem = z.looseObject({
|
|
2257
|
+
id: z.string(),
|
|
2258
|
+
object: z.string().optional(),
|
|
2259
|
+
/** Parent subscription id (`sub_*`). */
|
|
2260
|
+
subscription: z.string().optional(),
|
|
2261
|
+
/** The Price the customer is subscribed to — kept loose beyond its id. */
|
|
2262
|
+
price: z.looseObject({ id: z.string().optional() }).nullable().optional(),
|
|
2263
|
+
quantity: z.number().nullable().optional(),
|
|
2264
|
+
metadata: z.record(z.string(), z.string()).nullable().optional(),
|
|
2265
|
+
created: z.number().optional(),
|
|
2266
|
+
/** Start of the item's current billing period (Unix timestamp). */
|
|
2267
|
+
current_period_start: z.number().optional(),
|
|
2268
|
+
/** End of the item's current billing period (Unix timestamp). */
|
|
2269
|
+
current_period_end: z.number().optional(),
|
|
2270
|
+
/** Time period the item has been billed for — nullable, expandable. */
|
|
2271
|
+
billed_until: z.unknown().optional(),
|
|
2272
|
+
billing_thresholds: z.looseObject({}).nullable().optional(),
|
|
2273
|
+
/** Discount ids (or expanded objects) — kept unknown. */
|
|
2274
|
+
discounts: z.unknown().optional(),
|
|
2275
|
+
/** Tax Rate objects applied to this item — kept unknown. */
|
|
2276
|
+
tax_rates: z.unknown().optional()
|
|
2277
|
+
});
|
|
2278
|
+
var subscriptionItemDeleted = z.looseObject({
|
|
2279
|
+
id: z.string(),
|
|
2280
|
+
object: z.string().optional(),
|
|
2281
|
+
deleted: z.boolean().optional()
|
|
2282
|
+
});
|
|
2283
|
+
|
|
2284
|
+
// src/stripe/resources/subscription-items.ts
|
|
2285
|
+
var subscriptionItemListRes = stripeList(subscriptionItem);
|
|
2286
|
+
function bodyIfNonEmpty6(body) {
|
|
2287
|
+
return Object.keys(body).length > 0 ? { body } : {};
|
|
2288
|
+
}
|
|
2289
|
+
var SubscriptionItems = class {
|
|
2290
|
+
constructor(http) {
|
|
2291
|
+
this.http = http;
|
|
2292
|
+
}
|
|
2293
|
+
http;
|
|
2294
|
+
/**
|
|
2295
|
+
* Add a new item (price) to an existing subscription. Prorations follow
|
|
2296
|
+
* `proration_behavior` (default `create_prorations`). Pass `idempotencyKey`
|
|
2297
|
+
* to make the POST safely replayable (and retry-eligible).
|
|
2298
|
+
*
|
|
2299
|
+
* Docs: https://docs.stripe.com/api/subscription_items/create
|
|
2300
|
+
*
|
|
2301
|
+
* @example
|
|
2302
|
+
* const item = await payweave.stripe.subscriptionItems.create({
|
|
2303
|
+
* subscription: "sub_123",
|
|
2304
|
+
* price: "price_123",
|
|
2305
|
+
* quantity: 2,
|
|
2306
|
+
* }, { idempotencyKey: "sub_123-addon-seat" });
|
|
2307
|
+
* console.log(item.id, item.current_period_end);
|
|
2308
|
+
*/
|
|
2309
|
+
async create(input, opts = {}) {
|
|
2310
|
+
const body = parseRequest(subscriptionItemCreateReq, input);
|
|
2311
|
+
return this.http.request({
|
|
2312
|
+
method: "POST",
|
|
2313
|
+
path: "/v1/subscription_items",
|
|
2314
|
+
body,
|
|
2315
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
2316
|
+
schema: subscriptionItem
|
|
2317
|
+
});
|
|
2318
|
+
}
|
|
2319
|
+
/**
|
|
2320
|
+
* Retrieve a subscription item by id (`si_*`). A 404 for an unknown id
|
|
2321
|
+
* surfaces as {@link PayweaveNotFoundError}.
|
|
2322
|
+
*
|
|
2323
|
+
* Docs: https://docs.stripe.com/api/subscription_items/retrieve
|
|
2324
|
+
*
|
|
2325
|
+
* @example
|
|
2326
|
+
* const item = await payweave.stripe.subscriptionItems.retrieve("si_123");
|
|
2327
|
+
* console.log(item.price?.id, item.quantity);
|
|
2328
|
+
*/
|
|
2329
|
+
async retrieve(id) {
|
|
2330
|
+
const itemId = requireId(id, "subscription item");
|
|
2331
|
+
return this.http.request({
|
|
2332
|
+
method: "GET",
|
|
2333
|
+
path: `/v1/subscription_items/${encodeURIComponent(itemId)}`,
|
|
2334
|
+
schema: subscriptionItem
|
|
2335
|
+
});
|
|
2336
|
+
}
|
|
2337
|
+
/**
|
|
2338
|
+
* Update a subscription item — switch its `price`, change `quantity`, etc.
|
|
2339
|
+
* Proration parameters (`proration_behavior`, `proration_date`) are passed
|
|
2340
|
+
* through exactly as documented. Pass `idempotencyKey` to make the POST
|
|
2341
|
+
* safely replayable (and retry-eligible).
|
|
2342
|
+
*
|
|
2343
|
+
* Docs: https://docs.stripe.com/api/subscription_items/update
|
|
2344
|
+
*
|
|
2345
|
+
* @example
|
|
2346
|
+
* const item = await payweave.stripe.subscriptionItems.update("si_123", {
|
|
2347
|
+
* quantity: 5,
|
|
2348
|
+
* proration_behavior: "create_prorations",
|
|
2349
|
+
* });
|
|
2350
|
+
*/
|
|
2351
|
+
async update(id, input = {}, opts = {}) {
|
|
2352
|
+
const itemId = requireId(id, "subscription item");
|
|
2353
|
+
const body = parseRequest(subscriptionItemUpdateReq, input);
|
|
2354
|
+
return this.http.request({
|
|
2355
|
+
method: "POST",
|
|
2356
|
+
path: `/v1/subscription_items/${encodeURIComponent(itemId)}`,
|
|
2357
|
+
...bodyIfNonEmpty6(body),
|
|
2358
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
2359
|
+
schema: subscriptionItem
|
|
2360
|
+
});
|
|
2361
|
+
}
|
|
2362
|
+
/**
|
|
2363
|
+
* Delete an item from its subscription — removing a price the customer was
|
|
2364
|
+
* subscribed to. Optional form params ride the DELETE (`clear_usage` only
|
|
2365
|
+
* for metered prices; prorations per `proration_behavior`). Returns the
|
|
2366
|
+
* `{ id, object, deleted: true }` acknowledgement. A subscription cannot
|
|
2367
|
+
* lose its last item this way — Stripe rejects that with a 400
|
|
2368
|
+
* (surfaced as {@link PayweaveValidationError}); cancel the subscription
|
|
2369
|
+
* instead.
|
|
2370
|
+
*
|
|
2371
|
+
* Docs: https://docs.stripe.com/api/subscription_items/delete
|
|
2372
|
+
*
|
|
2373
|
+
* @example
|
|
2374
|
+
* const gone = await payweave.stripe.subscriptionItems.delete("si_123", {
|
|
2375
|
+
* proration_behavior: "none",
|
|
2376
|
+
* });
|
|
2377
|
+
* console.log(gone.deleted); // true
|
|
2378
|
+
*/
|
|
2379
|
+
async delete(id, input = {}) {
|
|
2380
|
+
const itemId = requireId(id, "subscription item");
|
|
2381
|
+
const body = parseRequest(subscriptionItemDeleteReq, input);
|
|
2382
|
+
return this.http.request({
|
|
2383
|
+
method: "DELETE",
|
|
2384
|
+
path: `/v1/subscription_items/${encodeURIComponent(itemId)}`,
|
|
2385
|
+
...bodyIfNonEmpty6(body),
|
|
2386
|
+
schema: subscriptionItemDeleted
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
2389
|
+
/**
|
|
2390
|
+
* List the items of ONE subscription — `subscription` is required by the
|
|
2391
|
+
* API. Cursor pagination: `limit` + `starting_after`/`ending_before`.
|
|
2392
|
+
*
|
|
2393
|
+
* Docs: https://docs.stripe.com/api/subscription_items/list
|
|
2394
|
+
*
|
|
2395
|
+
* @example
|
|
2396
|
+
* const page = await payweave.stripe.subscriptionItems.list({
|
|
2397
|
+
* subscription: "sub_123",
|
|
2398
|
+
* limit: 10,
|
|
2399
|
+
* });
|
|
2400
|
+
* console.log(page.data.length, page.has_more);
|
|
2401
|
+
*/
|
|
2402
|
+
async list(query) {
|
|
2403
|
+
const q = parseRequest(subscriptionItemListQuery, query);
|
|
2404
|
+
return this.http.request({
|
|
2405
|
+
method: "GET",
|
|
2406
|
+
path: "/v1/subscription_items",
|
|
2407
|
+
query: flattenQuery(q),
|
|
2408
|
+
schema: subscriptionItemListRes
|
|
2409
|
+
});
|
|
2410
|
+
}
|
|
2411
|
+
/**
|
|
2412
|
+
* Async iterator over ALL items of a subscription, transparently following
|
|
2413
|
+
* `has_more` with `starting_after = <last id>`.
|
|
2414
|
+
*
|
|
2415
|
+
* Docs: https://docs.stripe.com/api/subscription_items/list
|
|
2416
|
+
*
|
|
2417
|
+
* @example
|
|
2418
|
+
* for await (const item of payweave.stripe.subscriptionItems.iterate({ subscription: "sub_123" })) {
|
|
2419
|
+
* console.log(item.id, item.price?.id);
|
|
2420
|
+
* }
|
|
2421
|
+
*/
|
|
2422
|
+
async *iterate(query) {
|
|
2423
|
+
const base = parseRequest(subscriptionItemListQuery, query);
|
|
2424
|
+
yield* iterateStripeList(
|
|
2425
|
+
(startingAfter) => this.http.request({
|
|
2426
|
+
method: "GET",
|
|
2427
|
+
path: "/v1/subscription_items",
|
|
2428
|
+
query: flattenQuery({ ...base, starting_after: startingAfter }),
|
|
2429
|
+
schema: subscriptionItemListRes
|
|
2430
|
+
})
|
|
2431
|
+
);
|
|
2432
|
+
}
|
|
2433
|
+
};
|
|
2434
|
+
var createItemParam = z.object({
|
|
2435
|
+
/** Saved Price id (`price_*`). Alternative to `price_data`. */
|
|
2436
|
+
price: z.string().optional(),
|
|
2437
|
+
/** Inline price definition (recurring required). Alternative to `price`. */
|
|
2438
|
+
price_data: subscriptionItemPriceData.optional(),
|
|
2439
|
+
quantity: z.number().int().nonnegative().optional(),
|
|
2440
|
+
metadata: metadataSchema.optional(),
|
|
2441
|
+
billing_thresholds: itemBillingThresholds.optional(),
|
|
2442
|
+
/** Coupons/promotion codes to redeem into discounts for this item. */
|
|
2443
|
+
discounts: z.array(discountParam).optional(),
|
|
2444
|
+
/** Tax Rate ids overriding the subscription's `default_tax_rates`. */
|
|
2445
|
+
tax_rates: z.array(z.string()).optional()
|
|
2446
|
+
});
|
|
2447
|
+
var updateItemParam = z.object({
|
|
2448
|
+
/** Existing SubscriptionItem id (`si_*`) to update. */
|
|
2449
|
+
id: z.string().optional(),
|
|
2450
|
+
/** Saved Price id (`price_*`). Alternative to `price_data`. */
|
|
2451
|
+
price: z.string().optional(),
|
|
2452
|
+
/** Inline price definition (recurring required). Alternative to `price`. */
|
|
2453
|
+
price_data: subscriptionItemPriceData.optional(),
|
|
2454
|
+
quantity: z.number().int().nonnegative().optional(),
|
|
2455
|
+
metadata: metadataSchema.optional(),
|
|
2456
|
+
/** Set to `true` to delete the specified item. */
|
|
2457
|
+
deleted: z.boolean().optional(),
|
|
2458
|
+
/** Delete all usage for the item (metered prices). */
|
|
2459
|
+
clear_usage: z.boolean().optional(),
|
|
2460
|
+
billing_thresholds: itemBillingThresholds.optional(),
|
|
2461
|
+
/** Coupons/promotion codes to redeem into discounts for this item. */
|
|
2462
|
+
discounts: z.array(discountParam).optional(),
|
|
2463
|
+
/** Tax Rate ids overriding the subscription's `default_tax_rates`. */
|
|
2464
|
+
tax_rates: z.array(z.string()).optional()
|
|
2465
|
+
});
|
|
2466
|
+
var cancelAt = z.union([
|
|
2467
|
+
z.number().int(),
|
|
2468
|
+
z.enum(["max_billed_until", "max_period_end", "min_period_end"])
|
|
2469
|
+
]);
|
|
2470
|
+
var cancellationDetails = z.object({
|
|
2471
|
+
/** Additional comments about why the user canceled. */
|
|
2472
|
+
comment: z.string().optional(),
|
|
2473
|
+
/** The customer-submitted reason for cancellation. */
|
|
2474
|
+
feedback: z.enum([
|
|
2475
|
+
"customer_service",
|
|
2476
|
+
"low_quality",
|
|
2477
|
+
"missing_features",
|
|
2478
|
+
"other",
|
|
2479
|
+
"switched_service",
|
|
2480
|
+
"too_complex",
|
|
2481
|
+
"too_expensive",
|
|
2482
|
+
"unused"
|
|
2483
|
+
]).optional()
|
|
2484
|
+
});
|
|
2485
|
+
var trialEnd = z.union([z.literal("now"), z.number().int()]);
|
|
2486
|
+
var subscriptionCreateReq = z.object({
|
|
2487
|
+
/**
|
|
2488
|
+
* Customer id (`cus_*`) to subscribe. Stripe requires a customer (the
|
|
2489
|
+
* unverified `customer_account` alternative is untyped in this P0 subset).
|
|
2490
|
+
*/
|
|
2491
|
+
customer: z.string().optional(),
|
|
2492
|
+
/** Items being subscribed to — up to 20, each with an attached price. Required. */
|
|
2493
|
+
items: z.array(createItemParam).max(20),
|
|
2494
|
+
/** Past Unix timestamp to backdate the subscription's start date to. */
|
|
2495
|
+
backdate_start_date: z.number().int().optional(),
|
|
2496
|
+
/** Future Unix timestamp anchoring the billing cycle (first full invoice). */
|
|
2497
|
+
billing_cycle_anchor: z.number().int().optional(),
|
|
2498
|
+
/** Unix timestamp or symbolic value at which to auto-cancel. */
|
|
2499
|
+
cancel_at: cancelAt.optional(),
|
|
2500
|
+
/** Cancel at the end of the current period instead of immediately. Default false. */
|
|
2501
|
+
cancel_at_period_end: z.boolean().optional(),
|
|
2502
|
+
collection_method: z.enum(["charge_automatically", "send_invoice"]).optional(),
|
|
2503
|
+
/** Three-letter ISO currency code, lowercase. */
|
|
2504
|
+
currency: z.string().optional(),
|
|
2505
|
+
/** Days until invoices are due — `send_invoice` collection only. */
|
|
2506
|
+
days_until_due: z.number().int().optional(),
|
|
2507
|
+
/** PaymentMethod id (`pm_*`) used as this subscription's default. */
|
|
2508
|
+
default_payment_method: z.string().optional(),
|
|
2509
|
+
/** Source id used as this subscription's default payment source. */
|
|
2510
|
+
default_source: z.string().optional(),
|
|
2511
|
+
/** Tax Rate ids applied by default to the subscription's items. */
|
|
2512
|
+
default_tax_rates: z.array(z.string()).optional(),
|
|
2513
|
+
/** ≤500 chars, displayable to customers. */
|
|
2514
|
+
description: z.string().max(500).optional(),
|
|
2515
|
+
/** Coupons/promotion codes to redeem into discounts for the subscription. */
|
|
2516
|
+
discounts: z.array(discountParam).optional(),
|
|
2517
|
+
metadata: metadataSchema.optional(),
|
|
2518
|
+
/** Customer is not in the checkout flow. Default false. */
|
|
2519
|
+
off_session: z.boolean().optional(),
|
|
2520
|
+
payment_behavior: z.enum(["allow_incomplete", "default_incomplete", "error_if_incomplete"]).optional(),
|
|
2521
|
+
/** Invoice-pending-items schedule between billing cycles. */
|
|
2522
|
+
pending_invoice_item_interval: z.object({
|
|
2523
|
+
interval: z.enum(["day", "month", "week", "year"]),
|
|
2524
|
+
interval_count: z.number().int().optional()
|
|
2525
|
+
}).optional(),
|
|
2526
|
+
proration_behavior: z.enum(["create_prorations", "none"]).optional(),
|
|
2527
|
+
/** Unix timestamp or `"now"` — when the trial ends. */
|
|
2528
|
+
trial_end: trialEnd.optional(),
|
|
2529
|
+
/** Use the price's/plan's trial period. Default false. */
|
|
2530
|
+
trial_from_plan: z.boolean().optional(),
|
|
2531
|
+
/** Trial length in days (alternative to `trial_end`). */
|
|
2532
|
+
trial_period_days: z.number().int().optional(),
|
|
2533
|
+
/** What happens at trial end when no payment method is present. */
|
|
2534
|
+
trial_settings: z.object({
|
|
2535
|
+
end_behavior: z.object({
|
|
2536
|
+
missing_payment_method: z.enum(["cancel", "create_invoice", "pause"])
|
|
2537
|
+
})
|
|
2538
|
+
}).optional()
|
|
2539
|
+
});
|
|
2540
|
+
var pauseCollection = z.union([
|
|
2541
|
+
z.object({
|
|
2542
|
+
behavior: z.enum(["keep_as_draft", "mark_uncollectible", "void"]),
|
|
2543
|
+
/** Unix timestamp at which collection automatically resumes. */
|
|
2544
|
+
resumes_at: z.number().int().optional()
|
|
2545
|
+
}),
|
|
2546
|
+
z.literal("")
|
|
2547
|
+
]);
|
|
2548
|
+
var subscriptionUpdateReq = z.object({
|
|
2549
|
+
/** Item changes: update by `id`, add by `price`, remove with `deleted: true`. */
|
|
2550
|
+
items: z.array(updateItemParam).optional(),
|
|
2551
|
+
/** Unix timestamp or symbolic value at which to auto-cancel. */
|
|
2552
|
+
cancel_at: cancelAt.optional(),
|
|
2553
|
+
/** `true` = cancel when the current period ends; `false` = clear a pending one. */
|
|
2554
|
+
cancel_at_period_end: z.boolean().optional(),
|
|
2555
|
+
cancellation_details: cancellationDetails.optional(),
|
|
2556
|
+
/** `now` resets the billing cycle anchor; `unchanged` keeps it. */
|
|
2557
|
+
billing_cycle_anchor: z.enum(["now", "unchanged"]).optional(),
|
|
2558
|
+
collection_method: z.enum(["charge_automatically", "send_invoice"]).optional(),
|
|
2559
|
+
/** Days until invoices are due — `send_invoice` collection only. */
|
|
2560
|
+
days_until_due: z.number().int().optional(),
|
|
2561
|
+
/** PaymentMethod id (`pm_*`) used as this subscription's default. */
|
|
2562
|
+
default_payment_method: z.string().optional(),
|
|
2563
|
+
/** ≤500 chars, displayable to customers. */
|
|
2564
|
+
description: z.string().max(500).optional(),
|
|
2565
|
+
/** Coupons/promotion codes to redeem into discounts for the subscription. */
|
|
2566
|
+
discounts: z.array(discountParam).optional(),
|
|
2567
|
+
metadata: metadataSchema.optional(),
|
|
2568
|
+
/** Customer is not in the checkout flow. Default false. */
|
|
2569
|
+
off_session: z.boolean().optional(),
|
|
2570
|
+
/** Pause payment collection (subscription stays `active`); `""` unsets. */
|
|
2571
|
+
pause_collection: pauseCollection.optional(),
|
|
2572
|
+
payment_behavior: z.enum([
|
|
2573
|
+
"allow_incomplete",
|
|
2574
|
+
"default_incomplete",
|
|
2575
|
+
"error_if_incomplete",
|
|
2576
|
+
"pending_if_incomplete"
|
|
2577
|
+
]).optional(),
|
|
2578
|
+
proration_behavior: z.enum(["always_invoice", "create_prorations", "none"]).optional(),
|
|
2579
|
+
/** Unix timestamp — prorate as though the update happened at this time. */
|
|
2580
|
+
proration_date: z.number().int().optional(),
|
|
2581
|
+
/** Unix timestamp or `"now"` — when the trial ends. */
|
|
2582
|
+
trial_end: trialEnd.optional()
|
|
2583
|
+
});
|
|
2584
|
+
var subscriptionCancelReq = z.object({
|
|
2585
|
+
cancellation_details: cancellationDetails.optional(),
|
|
2586
|
+
/** Generate a final invoice for un-invoiced metered usage and pending prorations. Default false. */
|
|
2587
|
+
invoice_now: z.boolean().optional(),
|
|
2588
|
+
/** Credit remaining unused time until the period end via a proration item. Default false. */
|
|
2589
|
+
prorate: z.boolean().optional()
|
|
2590
|
+
});
|
|
2591
|
+
var subscriptionResumeReq = z.object({
|
|
2592
|
+
/** `now` resets the anchor and starts a new period; `unchanged` keeps it. Default `now`. */
|
|
2593
|
+
billing_cycle_anchor: z.enum(["now", "unchanged"]).optional(),
|
|
2594
|
+
proration_behavior: z.enum(["always_invoice", "create_prorations", "none"]).optional(),
|
|
2595
|
+
/** Unix timestamp — prorate as though resumed at this time. */
|
|
2596
|
+
proration_date: z.number().int().optional()
|
|
2597
|
+
});
|
|
2598
|
+
var subscriptionListQuery = z.object({
|
|
2599
|
+
...listCursorFields,
|
|
2600
|
+
/** Filter by Customer id. */
|
|
2601
|
+
customer: z.string().optional(),
|
|
2602
|
+
/** Filter by recurring Price id. */
|
|
2603
|
+
price: z.string().optional(),
|
|
2604
|
+
/**
|
|
2605
|
+
* Status filter. Documented special values (verified 2026-07-12):
|
|
2606
|
+
* `canceled` (includes deleted customers' subscriptions), `ended` (canceled
|
|
2607
|
+
* + expired due to incomplete payment), `all`; omitted = all non-canceled
|
|
2608
|
+
* subscriptions. Kept as an open string — the docs page does not enumerate
|
|
2609
|
+
* a closed value set for this filter (conservative); Stripe
|
|
2610
|
+
* validates server-side.
|
|
2611
|
+
*/
|
|
2612
|
+
status: z.string().optional(),
|
|
2613
|
+
collection_method: z.enum(["charge_automatically", "send_invoice"]).optional(),
|
|
2614
|
+
/** Creation-date window (Unix timestamps). */
|
|
2615
|
+
created: createdRange.optional(),
|
|
2616
|
+
/** Current-period-start window (Unix timestamps). */
|
|
2617
|
+
current_period_start: createdRange.optional(),
|
|
2618
|
+
/** Current-period-end window (Unix timestamps). */
|
|
2619
|
+
current_period_end: createdRange.optional()
|
|
2620
|
+
});
|
|
2621
|
+
var subscription = z.looseObject({
|
|
2622
|
+
id: z.string(),
|
|
2623
|
+
object: z.string().optional(),
|
|
2624
|
+
/**
|
|
2625
|
+
* `incomplete` | `incomplete_expired` | `trialing` | `active` | `past_due`
|
|
2626
|
+
* | `canceled` | `unpaid` | `paused` — normalization is the unified layer's
|
|
2627
|
+
* job.
|
|
2628
|
+
*/
|
|
2629
|
+
status: z.string().optional(),
|
|
2630
|
+
/** Customer id string, or an expanded object — kept unknown. */
|
|
2631
|
+
customer: z.unknown().optional(),
|
|
2632
|
+
/** Embedded (paginated) list of the subscription's items. */
|
|
2633
|
+
items: stripeList(subscriptionItem).optional(),
|
|
2634
|
+
currency: z.string().optional(),
|
|
2635
|
+
collection_method: z.string().optional(),
|
|
2636
|
+
/** Unix timestamp at which the subscription will auto-cancel. */
|
|
2637
|
+
cancel_at: z.number().nullable().optional(),
|
|
2638
|
+
/** Whether the subscription cancels when the current period ends. */
|
|
2639
|
+
cancel_at_period_end: z.boolean().optional(),
|
|
2640
|
+
/** When the cancellation was requested (Unix timestamp). */
|
|
2641
|
+
canceled_at: z.number().nullable().optional(),
|
|
2642
|
+
/** `{ reason, feedback, comment }` — kept loose. */
|
|
2643
|
+
cancellation_details: z.looseObject({}).nullable().optional(),
|
|
2644
|
+
created: z.number().optional(),
|
|
2645
|
+
/** Start of the subscription (may differ from `created` when backdated). */
|
|
2646
|
+
start_date: z.number().optional(),
|
|
2647
|
+
/** Anchor aligning future billing cycle dates (Unix timestamp). */
|
|
2648
|
+
billing_cycle_anchor: z.number().optional(),
|
|
2649
|
+
trial_start: z.number().nullable().optional(),
|
|
2650
|
+
trial_end: z.number().nullable().optional(),
|
|
2651
|
+
/** When the subscription ended (Unix timestamp). */
|
|
2652
|
+
ended_at: z.number().nullable().optional(),
|
|
2653
|
+
/** Latest Invoice id string, or an expanded object — kept unknown. */
|
|
2654
|
+
latest_invoice: z.unknown().optional(),
|
|
2655
|
+
/** Default PaymentMethod id string, or an expanded object — kept unknown. */
|
|
2656
|
+
default_payment_method: z.unknown().optional(),
|
|
2657
|
+
days_until_due: z.number().nullable().optional(),
|
|
2658
|
+
/** Payment-collection pause state — `{ behavior, resumes_at }`, kept loose. */
|
|
2659
|
+
pause_collection: z.looseObject({}).nullable().optional(),
|
|
2660
|
+
metadata: z.record(z.string(), z.string()).nullable().optional(),
|
|
2661
|
+
description: z.string().nullable().optional(),
|
|
2662
|
+
livemode: z.boolean().optional()
|
|
2663
|
+
});
|
|
2664
|
+
|
|
2665
|
+
// src/stripe/resources/subscriptions.ts
|
|
2666
|
+
var subscriptionListRes = stripeList(subscription);
|
|
2667
|
+
function bodyIfNonEmpty7(body) {
|
|
2668
|
+
return Object.keys(body).length > 0 ? { body } : {};
|
|
2669
|
+
}
|
|
2670
|
+
var Subscriptions = class {
|
|
2671
|
+
constructor(http) {
|
|
2672
|
+
this.http = http;
|
|
2673
|
+
}
|
|
2674
|
+
http;
|
|
2675
|
+
/**
|
|
2676
|
+
* Create a subscription for an existing customer — the first invoice is
|
|
2677
|
+
* attempted immediately unless a trial or `billing_cycle_anchor` defers it.
|
|
2678
|
+
* `items` (up to 20, each with a `price` or inline `price_data`) is
|
|
2679
|
+
* required. Pass `idempotencyKey` to make the POST safely replayable (and
|
|
2680
|
+
* retry-eligible).
|
|
2681
|
+
*
|
|
2682
|
+
* Docs: https://docs.stripe.com/api/subscriptions/create
|
|
2683
|
+
*
|
|
2684
|
+
* @example
|
|
2685
|
+
* const sub = await payweave.stripe.subscriptions.create({
|
|
2686
|
+
* customer: "cus_123",
|
|
2687
|
+
* items: [{ price: "price_123", quantity: 1 }],
|
|
2688
|
+
* metadata: { pwv_plan: "pro" },
|
|
2689
|
+
* }, { idempotencyKey: "sub-cus_123-pro" });
|
|
2690
|
+
* console.log(sub.status, sub.items?.data[0]?.current_period_end);
|
|
2691
|
+
*/
|
|
2692
|
+
async create(input, opts = {}) {
|
|
2693
|
+
const body = parseRequest(subscriptionCreateReq, input);
|
|
2694
|
+
return this.http.request({
|
|
2695
|
+
method: "POST",
|
|
2696
|
+
path: "/v1/subscriptions",
|
|
2697
|
+
body,
|
|
2698
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
2699
|
+
schema: subscription
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
/**
|
|
2703
|
+
* Retrieve a subscription by id (`sub_*`). A 404 for an unknown id surfaces
|
|
2704
|
+
* as {@link PayweaveNotFoundError}. Period boundaries live on the embedded
|
|
2705
|
+
* items (`sub.items.data[].current_period_start/end`) on the SDK's pinned
|
|
2706
|
+
* API version.
|
|
2707
|
+
*
|
|
2708
|
+
* Docs: https://docs.stripe.com/api/subscriptions/retrieve
|
|
2709
|
+
*
|
|
2710
|
+
* @example
|
|
2711
|
+
* const sub = await payweave.stripe.subscriptions.retrieve("sub_123");
|
|
2712
|
+
* if (sub.status === "active") { }
|
|
2713
|
+
*/
|
|
2714
|
+
async retrieve(id) {
|
|
2715
|
+
const subscriptionId = requireId(id, "subscription");
|
|
2716
|
+
return this.http.request({
|
|
2717
|
+
method: "GET",
|
|
2718
|
+
path: `/v1/subscriptions/${encodeURIComponent(subscriptionId)}`,
|
|
2719
|
+
schema: subscription
|
|
2720
|
+
});
|
|
2721
|
+
}
|
|
2722
|
+
/**
|
|
2723
|
+
* Update a subscription: change items/quantities (proration per
|
|
2724
|
+
* `proration_behavior`), schedule an END-OF-PERIOD cancellation with
|
|
2725
|
+
* `cancel_at_period_end: true` (or clear one with `false`), pause payment
|
|
2726
|
+
* collection with `pause_collection` (unset with `""` to resume
|
|
2727
|
+
* collection), etc. For IMMEDIATE cancellation use {@link cancel} instead.
|
|
2728
|
+
* Pass `idempotencyKey` to make the POST safely replayable (and
|
|
2729
|
+
* retry-eligible).
|
|
2730
|
+
*
|
|
2731
|
+
* Docs: https://docs.stripe.com/api/subscriptions/update
|
|
2732
|
+
*
|
|
2733
|
+
* @example
|
|
2734
|
+
* // Cancel when the current billing period ends — NOT immediate:
|
|
2735
|
+
* const sub = await payweave.stripe.subscriptions.update("sub_123", {
|
|
2736
|
+
* cancel_at_period_end: true,
|
|
2737
|
+
* });
|
|
2738
|
+
* console.log(sub.status, sub.cancel_at_period_end); // "active", true
|
|
2739
|
+
*/
|
|
2740
|
+
async update(id, input = {}, opts = {}) {
|
|
2741
|
+
const subscriptionId = requireId(id, "subscription");
|
|
2742
|
+
const body = parseRequest(subscriptionUpdateReq, input);
|
|
2743
|
+
return this.http.request({
|
|
2744
|
+
method: "POST",
|
|
2745
|
+
path: `/v1/subscriptions/${encodeURIComponent(subscriptionId)}`,
|
|
2746
|
+
...bodyIfNonEmpty7(body),
|
|
2747
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
2748
|
+
schema: subscription
|
|
2749
|
+
});
|
|
2750
|
+
}
|
|
2751
|
+
/**
|
|
2752
|
+
* Cancel a subscription IMMEDIATELY (`DELETE /v1/subscriptions/{id}`) —
|
|
2753
|
+
* the customer is not charged again, and pending prorations are dropped
|
|
2754
|
+
* unless `invoice_now`/`prorate` say otherwise. To cancel at the END of the
|
|
2755
|
+
* current period instead, use
|
|
2756
|
+
* `update(id, { cancel_at_period_end: true })` — the two shapes are
|
|
2757
|
+
* distinct on Stripe and PW-805's state machine relies on that.
|
|
2758
|
+
*
|
|
2759
|
+
* Docs: https://docs.stripe.com/api/subscriptions/cancel
|
|
2760
|
+
*
|
|
2761
|
+
* @example
|
|
2762
|
+
* const sub = await payweave.stripe.subscriptions.cancel("sub_123", {
|
|
2763
|
+
* invoice_now: true,
|
|
2764
|
+
* prorate: true,
|
|
2765
|
+
* cancellation_details: { feedback: "too_expensive" },
|
|
2766
|
+
* });
|
|
2767
|
+
* console.log(sub.status); // "canceled"
|
|
2768
|
+
*/
|
|
2769
|
+
async cancel(id, input = {}) {
|
|
2770
|
+
const subscriptionId = requireId(id, "subscription");
|
|
2771
|
+
const body = parseRequest(subscriptionCancelReq, input);
|
|
2772
|
+
return this.http.request({
|
|
2773
|
+
method: "DELETE",
|
|
2774
|
+
path: `/v1/subscriptions/${encodeURIComponent(subscriptionId)}`,
|
|
2775
|
+
...bodyIfNonEmpty7(body),
|
|
2776
|
+
schema: subscription
|
|
2777
|
+
});
|
|
2778
|
+
}
|
|
2779
|
+
/**
|
|
2780
|
+
* Resume a subscription whose STATUS is `paused` (e.g. a trial that ended
|
|
2781
|
+
* with `trial_settings.end_behavior.missing_payment_method: "pause"`) —
|
|
2782
|
+
* only available under `charge_automatically` collection. Not for paused
|
|
2783
|
+
* payment COLLECTION: unset that via
|
|
2784
|
+
* `update(id, { pause_collection: "" })`. `billing_cycle_anchor` defaults
|
|
2785
|
+
* to `now` (resets the cycle; no prorations); `unchanged` keeps the anchor
|
|
2786
|
+
* and prorates per `proration_behavior`. Pass `idempotencyKey` to make the
|
|
2787
|
+
* POST safely replayable (and retry-eligible).
|
|
2788
|
+
*
|
|
2789
|
+
* Docs: https://docs.stripe.com/api/subscriptions/resume
|
|
2790
|
+
*
|
|
2791
|
+
* @example
|
|
2792
|
+
* const sub = await payweave.stripe.subscriptions.resume("sub_123", {
|
|
2793
|
+
* billing_cycle_anchor: "now",
|
|
2794
|
+
* });
|
|
2795
|
+
* console.log(sub.status); // "active"
|
|
2796
|
+
*/
|
|
2797
|
+
async resume(id, input = {}, opts = {}) {
|
|
2798
|
+
const subscriptionId = requireId(id, "subscription");
|
|
2799
|
+
const body = parseRequest(subscriptionResumeReq, input);
|
|
2800
|
+
return this.http.request({
|
|
2801
|
+
method: "POST",
|
|
2802
|
+
path: `/v1/subscriptions/${encodeURIComponent(subscriptionId)}/resume`,
|
|
2803
|
+
...bodyIfNonEmpty7(body),
|
|
2804
|
+
...opts.idempotencyKey !== void 0 ? { idempotencyKey: opts.idempotencyKey } : {},
|
|
2805
|
+
schema: subscription
|
|
2806
|
+
});
|
|
2807
|
+
}
|
|
2808
|
+
/**
|
|
2809
|
+
* List subscriptions. By default only non-canceled subscriptions are
|
|
2810
|
+
* returned — pass `status: "canceled"`, `"ended"`, or `"all"` to widen.
|
|
2811
|
+
* Filters are query params; range filters become bracket keys
|
|
2812
|
+
* (`current_period_end[gte]`).
|
|
2813
|
+
*
|
|
2814
|
+
* Docs: https://docs.stripe.com/api/subscriptions/list
|
|
2815
|
+
*
|
|
2816
|
+
* @example
|
|
2817
|
+
* const page = await payweave.stripe.subscriptions.list({
|
|
2818
|
+
* customer: "cus_123",
|
|
2819
|
+
* status: "all",
|
|
2820
|
+
* limit: 50,
|
|
2821
|
+
* });
|
|
2822
|
+
* console.log(page.data.length, page.has_more);
|
|
2823
|
+
*/
|
|
2824
|
+
async list(query = {}) {
|
|
2825
|
+
const q = parseRequest(subscriptionListQuery, query);
|
|
2826
|
+
return this.http.request({
|
|
2827
|
+
method: "GET",
|
|
2828
|
+
path: "/v1/subscriptions",
|
|
2829
|
+
query: flattenQuery(q),
|
|
2830
|
+
schema: subscriptionListRes
|
|
2831
|
+
});
|
|
2832
|
+
}
|
|
2833
|
+
/**
|
|
2834
|
+
* Async iterator over ALL subscriptions matching `query`, transparently
|
|
2835
|
+
* following `has_more` with `starting_after = <last id>`.
|
|
2836
|
+
*
|
|
2837
|
+
* Docs: https://docs.stripe.com/api/subscriptions/list
|
|
2838
|
+
*
|
|
2839
|
+
* @example
|
|
2840
|
+
* for await (const sub of payweave.stripe.subscriptions.iterate({ customer: "cus_123" })) {
|
|
2841
|
+
* console.log(sub.id, sub.status);
|
|
2842
|
+
* }
|
|
2843
|
+
*/
|
|
2844
|
+
async *iterate(query = {}) {
|
|
2845
|
+
const base = parseRequest(subscriptionListQuery, query);
|
|
2846
|
+
yield* iterateStripeList(
|
|
2847
|
+
(startingAfter) => this.http.request({
|
|
2848
|
+
method: "GET",
|
|
2849
|
+
path: "/v1/subscriptions",
|
|
2850
|
+
query: flattenQuery({ ...base, starting_after: startingAfter }),
|
|
2851
|
+
schema: subscriptionListRes
|
|
2852
|
+
})
|
|
2853
|
+
);
|
|
2854
|
+
}
|
|
2855
|
+
};
|
|
2856
|
+
var enabledEvents = z.array(z.string()).min(1);
|
|
2857
|
+
var webhookEndpointCreateReq = z.object({
|
|
2858
|
+
/** The URL of the webhook endpoint. Required. */
|
|
2859
|
+
url: z.string(),
|
|
2860
|
+
/**
|
|
2861
|
+
* The list of events to enable for this endpoint; `["*"]` enables all
|
|
2862
|
+
* events except those that require explicit selection. Required. Encodes as
|
|
2863
|
+
* `enabled_events[0]=...`.
|
|
2864
|
+
*/
|
|
2865
|
+
enabled_events: enabledEvents,
|
|
2866
|
+
/**
|
|
2867
|
+
* Events sent to this endpoint are rendered with this Stripe version
|
|
2868
|
+
* instead of the account default.
|
|
2869
|
+
*/
|
|
2870
|
+
api_version: z.string().optional(),
|
|
2871
|
+
/**
|
|
2872
|
+
* `true` = receive events from connected accounts; `false` (default) = from
|
|
2873
|
+
* your own account.
|
|
2874
|
+
*/
|
|
2875
|
+
connect: z.boolean().optional(),
|
|
2876
|
+
/** Optional description of what the webhook is used for. */
|
|
2877
|
+
description: z.string().optional(),
|
|
2878
|
+
metadata: metadataSchema.optional()
|
|
2879
|
+
});
|
|
2880
|
+
var webhookEndpointUpdateReq = z.object({
|
|
2881
|
+
/** Optional description of what the webhook is used for. */
|
|
2882
|
+
description: z.string().optional(),
|
|
2883
|
+
/** Disable the webhook endpoint if set to true. */
|
|
2884
|
+
disabled: z.boolean().optional(),
|
|
2885
|
+
/** Replacement list of enabled events (`["*"]` = all). */
|
|
2886
|
+
enabled_events: enabledEvents.optional(),
|
|
2887
|
+
metadata: metadataSchema.optional(),
|
|
2888
|
+
/** The URL of the webhook endpoint. */
|
|
2889
|
+
url: z.string().optional()
|
|
2890
|
+
});
|
|
2891
|
+
var webhookEndpointListQuery = z.object({
|
|
2892
|
+
...listCursorFields
|
|
2893
|
+
});
|
|
2894
|
+
var webhookEndpoint = z.looseObject({
|
|
2895
|
+
id: z.string(),
|
|
2896
|
+
object: z.string().optional(),
|
|
2897
|
+
/** API version events are rendered as for this endpoint. */
|
|
2898
|
+
api_version: z.string().nullable().optional(),
|
|
2899
|
+
/** ID of the associated Connect application. */
|
|
2900
|
+
application: z.string().nullable().optional(),
|
|
2901
|
+
created: z.number().optional(),
|
|
2902
|
+
description: z.string().nullable().optional(),
|
|
2903
|
+
/** `["*"]` indicates all events (except explicit-selection ones). */
|
|
2904
|
+
enabled_events: z.array(z.string()).optional(),
|
|
2905
|
+
livemode: z.boolean().optional(),
|
|
2906
|
+
metadata: z.record(z.string(), z.string()).nullable().optional(),
|
|
2907
|
+
/**
|
|
2908
|
+
* The endpoint's signing secret (`whsec_*`), used to verify webhook
|
|
2909
|
+
* signatures. ⚠️ "Only returned at creation"
|
|
2910
|
+
* (https://docs.stripe.com/api/webhook_endpoints/object — verified
|
|
2911
|
+
* 2026-07-12) — capture it from the `create` response or it is gone;
|
|
2912
|
+
* retrieve/update/list never include it. Store it as the provider config's
|
|
2913
|
+
* `webhookSecret`; core `redact` masks it from all logs.
|
|
2914
|
+
*/
|
|
2915
|
+
secret: z.string().optional(),
|
|
2916
|
+
/** `enabled` | `disabled` — kept loose (drift logs, never throws). */
|
|
2917
|
+
status: z.string().optional(),
|
|
2918
|
+
url: z.string().optional()
|
|
2919
|
+
});
|
|
2920
|
+
var webhookEndpointDeleted = z.looseObject({
|
|
2921
|
+
id: z.string(),
|
|
2922
|
+
object: z.string().optional(),
|
|
2923
|
+
deleted: z.boolean().optional()
|
|
2924
|
+
});
|
|
2925
|
+
|
|
2926
|
+
// src/stripe/resources/webhook-endpoints.ts
|
|
2927
|
+
var webhookEndpointListRes = stripeList(webhookEndpoint);
|
|
2928
|
+
function bodyIfNonEmpty8(body) {
|
|
2929
|
+
return Object.keys(body).length > 0 ? { body } : {};
|
|
2930
|
+
}
|
|
2931
|
+
var WebhookEndpoints = class {
|
|
2932
|
+
constructor(http) {
|
|
2933
|
+
this.http = http;
|
|
2934
|
+
}
|
|
2935
|
+
http;
|
|
2936
|
+
/**
|
|
2937
|
+
* Create a webhook endpoint. `url` and `enabled_events` are required;
|
|
2938
|
+
* `enabled_events` bracket-encodes as `enabled_events[0]=...` and `["*"]`
|
|
2939
|
+
* enables all events (except those requiring explicit selection).
|
|
2940
|
+
*
|
|
2941
|
+
* ⚠️ The response is the ONLY place the endpoint's `whsec_*` signing
|
|
2942
|
+
* `secret` is ever returned ("Only returned at creation") — persist it
|
|
2943
|
+
* immediately (e.g. as the `stripe.webhookSecret` config) or it cannot be
|
|
2944
|
+
* recovered; retrieve/update/list responses never include it.
|
|
2945
|
+
*
|
|
2946
|
+
* Docs: https://docs.stripe.com/api/webhook_endpoints/create
|
|
2947
|
+
*
|
|
2948
|
+
* @example
|
|
2949
|
+
* const we = await payweave.stripe.webhookEndpoints.create({
|
|
2950
|
+
* url: "https://example.com/payweave/webhooks",
|
|
2951
|
+
* enabled_events: ["checkout.session.completed", "payment_intent.succeeded"],
|
|
2952
|
+
* });
|
|
2953
|
+
* const signingSecret = we.secret; // whsec_* — shown ONCE, store it now
|
|
2954
|
+
*/
|
|
2955
|
+
async create(input) {
|
|
2956
|
+
const body = parseRequest(webhookEndpointCreateReq, input);
|
|
2957
|
+
return this.http.request({
|
|
2958
|
+
method: "POST",
|
|
2959
|
+
path: "/v1/webhook_endpoints",
|
|
2960
|
+
body,
|
|
2961
|
+
schema: webhookEndpoint
|
|
2962
|
+
});
|
|
2963
|
+
}
|
|
2964
|
+
/**
|
|
2965
|
+
* Retrieve a webhook endpoint by id (`we_*`). The response does NOT include
|
|
2966
|
+
* the signing `secret` (create-only). A 404 for an unknown id surfaces as
|
|
2967
|
+
* {@link PayweaveNotFoundError}.
|
|
2968
|
+
*
|
|
2969
|
+
* Docs: https://docs.stripe.com/api/webhook_endpoints/retrieve
|
|
2970
|
+
*
|
|
2971
|
+
* @example
|
|
2972
|
+
* const we = await payweave.stripe.webhookEndpoints.retrieve("we_123");
|
|
2973
|
+
* console.log(we.status, we.enabled_events);
|
|
2974
|
+
*/
|
|
2975
|
+
async retrieve(id) {
|
|
2976
|
+
const endpointId = requireId(id, "webhook endpoint");
|
|
2977
|
+
return this.http.request({
|
|
2978
|
+
method: "GET",
|
|
2979
|
+
path: `/v1/webhook_endpoints/${encodeURIComponent(endpointId)}`,
|
|
2980
|
+
schema: webhookEndpoint
|
|
2981
|
+
});
|
|
2982
|
+
}
|
|
2983
|
+
/**
|
|
2984
|
+
* Update a webhook endpoint (`description`, `enabled_events`, `url`,
|
|
2985
|
+
* `metadata`, or `disabled: true` to disable it). The response does NOT
|
|
2986
|
+
* include the signing `secret` (create-only).
|
|
2987
|
+
*
|
|
2988
|
+
* Docs: https://docs.stripe.com/api/webhook_endpoints/update
|
|
2989
|
+
*
|
|
2990
|
+
* @example
|
|
2991
|
+
* const we = await payweave.stripe.webhookEndpoints.update("we_123", {
|
|
2992
|
+
* disabled: true,
|
|
2993
|
+
* });
|
|
2994
|
+
* console.log(we.status); // "disabled"
|
|
2995
|
+
*/
|
|
2996
|
+
async update(id, input = {}) {
|
|
2997
|
+
const endpointId = requireId(id, "webhook endpoint");
|
|
2998
|
+
const body = parseRequest(webhookEndpointUpdateReq, input);
|
|
2999
|
+
return this.http.request({
|
|
3000
|
+
method: "POST",
|
|
3001
|
+
path: `/v1/webhook_endpoints/${encodeURIComponent(endpointId)}`,
|
|
3002
|
+
...bodyIfNonEmpty8(body),
|
|
3003
|
+
schema: webhookEndpoint
|
|
3004
|
+
});
|
|
3005
|
+
}
|
|
3006
|
+
/**
|
|
3007
|
+
* Delete a webhook endpoint. Returns `{ id, object, deleted: true }`;
|
|
3008
|
+
* deleting an already-deleted endpoint raises a Stripe error (404 →
|
|
3009
|
+
* {@link PayweaveNotFoundError}). `payweave listen` calls this on
|
|
3010
|
+
* teardown.
|
|
3011
|
+
*
|
|
3012
|
+
* Docs: https://docs.stripe.com/api/webhook_endpoints/delete
|
|
3013
|
+
*
|
|
3014
|
+
* @example
|
|
3015
|
+
* const gone = await payweave.stripe.webhookEndpoints.delete("we_123");
|
|
3016
|
+
* console.log(gone.deleted); // true
|
|
3017
|
+
*/
|
|
3018
|
+
async delete(id) {
|
|
3019
|
+
const endpointId = requireId(id, "webhook endpoint");
|
|
3020
|
+
return this.http.request({
|
|
3021
|
+
method: "DELETE",
|
|
3022
|
+
path: `/v1/webhook_endpoints/${encodeURIComponent(endpointId)}`,
|
|
3023
|
+
schema: webhookEndpointDeleted
|
|
3024
|
+
});
|
|
3025
|
+
}
|
|
3026
|
+
/**
|
|
3027
|
+
* List webhook endpoints. Cursor pagination only (`limit` +
|
|
3028
|
+
* `starting_after`/`ending_before`) — no other documented filters.
|
|
3029
|
+
* Responses never include signing secrets.
|
|
3030
|
+
*
|
|
3031
|
+
* Docs: https://docs.stripe.com/api/webhook_endpoints/list
|
|
3032
|
+
*
|
|
3033
|
+
* @example
|
|
3034
|
+
* const page = await payweave.stripe.webhookEndpoints.list({ limit: 20 });
|
|
3035
|
+
* console.log(page.data.map((we) => we.url));
|
|
3036
|
+
*/
|
|
3037
|
+
async list(query = {}) {
|
|
3038
|
+
const q = parseRequest(webhookEndpointListQuery, query);
|
|
3039
|
+
return this.http.request({
|
|
3040
|
+
method: "GET",
|
|
3041
|
+
path: "/v1/webhook_endpoints",
|
|
3042
|
+
query: flattenQuery(q),
|
|
3043
|
+
schema: webhookEndpointListRes
|
|
3044
|
+
});
|
|
3045
|
+
}
|
|
3046
|
+
/**
|
|
3047
|
+
* Async iterator over ALL webhook endpoints, transparently following
|
|
3048
|
+
* `has_more` with `starting_after = <last id>`.
|
|
3049
|
+
*
|
|
3050
|
+
* Docs: https://docs.stripe.com/api/webhook_endpoints/list
|
|
3051
|
+
*
|
|
3052
|
+
* @example
|
|
3053
|
+
* for await (const we of payweave.stripe.webhookEndpoints.iterate()) {
|
|
3054
|
+
* console.log(we.id, we.url, we.status);
|
|
3055
|
+
* }
|
|
3056
|
+
*/
|
|
3057
|
+
async *iterate(query = {}) {
|
|
3058
|
+
const base = parseRequest(webhookEndpointListQuery, query);
|
|
3059
|
+
yield* iterateStripeList(
|
|
3060
|
+
(startingAfter) => this.http.request({
|
|
3061
|
+
method: "GET",
|
|
3062
|
+
path: "/v1/webhook_endpoints",
|
|
3063
|
+
query: flattenQuery({ ...base, starting_after: startingAfter }),
|
|
3064
|
+
schema: webhookEndpointListRes
|
|
3065
|
+
})
|
|
3066
|
+
);
|
|
3067
|
+
}
|
|
3068
|
+
};
|
|
3069
|
+
|
|
3070
|
+
// src/stripe/client.ts
|
|
3071
|
+
var StripeClient = class {
|
|
3072
|
+
/** Shared HTTP client every Stripe resource is constructed with. */
|
|
3073
|
+
http;
|
|
3074
|
+
/** Checkout namespace (`payweave.stripe.checkout.sessions.create(...)`). */
|
|
3075
|
+
checkout;
|
|
3076
|
+
/** PaymentIntents: create, retrieve, confirm, capture, cancel, list/iterate. */
|
|
3077
|
+
paymentIntents;
|
|
3078
|
+
/** Customers: create, retrieve, update, delete, list/iterate, search/iterateSearch. */
|
|
3079
|
+
customers;
|
|
3080
|
+
/** Products: create, retrieve, update, delete, list/iterate, search/iterateSearch. */
|
|
3081
|
+
products;
|
|
3082
|
+
/** Prices: create, retrieve, update, list/iterate, search/iterateSearch (no delete — prices are archived). */
|
|
3083
|
+
prices;
|
|
3084
|
+
/** Subscriptions: create, retrieve, update, cancel, resume, list/iterate. */
|
|
3085
|
+
subscriptions;
|
|
3086
|
+
/** SubscriptionItems: create, retrieve, update, delete, list/iterate. */
|
|
3087
|
+
subscriptionItems;
|
|
3088
|
+
/** Refunds: create (idempotencyKey), retrieve, update, cancel, list/iterate. */
|
|
3089
|
+
refunds;
|
|
3090
|
+
/** WebhookEndpoints: create (secret shown ONCE), retrieve, update, delete, list/iterate. */
|
|
3091
|
+
webhookEndpoints;
|
|
3092
|
+
constructor(http) {
|
|
3093
|
+
this.http = http;
|
|
3094
|
+
this.checkout = { sessions: new CheckoutSessions(this.http) };
|
|
3095
|
+
this.paymentIntents = new PaymentIntents(this.http);
|
|
3096
|
+
this.customers = new Customers(this.http);
|
|
3097
|
+
this.products = new Products(this.http);
|
|
3098
|
+
this.prices = new Prices(this.http);
|
|
3099
|
+
this.subscriptions = new Subscriptions(this.http);
|
|
3100
|
+
this.subscriptionItems = new SubscriptionItems(this.http);
|
|
3101
|
+
this.refunds = new Refunds(this.http);
|
|
3102
|
+
this.webhookEndpoints = new WebhookEndpoints(this.http);
|
|
3103
|
+
}
|
|
3104
|
+
};
|
|
3105
|
+
function stripeHttp(cfg) {
|
|
3106
|
+
return new HttpClient(stripeHttpOptions(cfg));
|
|
3107
|
+
}
|
|
3108
|
+
defineProvider({
|
|
3109
|
+
id: "stripe",
|
|
3110
|
+
configKey: "stripe",
|
|
3111
|
+
configSchema: stripeProviderConfigSchema,
|
|
3112
|
+
environments: {
|
|
3113
|
+
// One host for both — Stripe's environment is key-derived, not host-derived.
|
|
3114
|
+
test: { baseUrl: STRIPE_BASE_URL },
|
|
3115
|
+
live: { baseUrl: STRIPE_BASE_URL }
|
|
3116
|
+
},
|
|
3117
|
+
inferEnvironment: (secretKey) => {
|
|
3118
|
+
try {
|
|
3119
|
+
return inferEnvironment("stripe", secretKey);
|
|
3120
|
+
} catch {
|
|
3121
|
+
return null;
|
|
3122
|
+
}
|
|
3123
|
+
},
|
|
3124
|
+
createHttp: stripeHttp,
|
|
3125
|
+
webhooks: {
|
|
3126
|
+
signatureHeader: "stripe-signature",
|
|
3127
|
+
verify: ({ rawBody, headers, secret }) => verifyStripe(rawBody, readHeader(headers, "stripe-signature"), secret),
|
|
3128
|
+
parse: (rawBody) => {
|
|
3129
|
+
const root = JSON.parse(rawBody);
|
|
3130
|
+
return {
|
|
3131
|
+
type: typeof root.type === "string" ? root.type : "unknown",
|
|
3132
|
+
data: root.data,
|
|
3133
|
+
raw: root,
|
|
3134
|
+
...typeof root.id === "string" ? { id: root.id } : {}
|
|
3135
|
+
};
|
|
3136
|
+
},
|
|
3137
|
+
toUnified: (e) => ({
|
|
3138
|
+
provider: "stripe",
|
|
3139
|
+
type: e.type,
|
|
3140
|
+
unifiedType: toUnifiedEventType("stripe", void 0, e.type, e.data),
|
|
3141
|
+
data: e.data,
|
|
3142
|
+
raw: e.raw
|
|
3143
|
+
})
|
|
3144
|
+
}
|
|
3145
|
+
});
|
|
3146
|
+
function isBillingCapableProvider(provider) {
|
|
3147
|
+
return BILLING_CAPABLE_PROVIDERS.includes(provider);
|
|
3148
|
+
}
|
|
3149
|
+
function stableStringify(value) {
|
|
3150
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
3151
|
+
if (value !== null && typeof value === "object") {
|
|
3152
|
+
const entries = Object.entries(value).sort(
|
|
3153
|
+
([a], [b]) => a < b ? -1 : a > b ? 1 : 0
|
|
3154
|
+
);
|
|
3155
|
+
return `{${entries.map(([key, val]) => `${JSON.stringify(key)}:${stableStringify(val)}`).join(",")}}`;
|
|
3156
|
+
}
|
|
3157
|
+
return JSON.stringify(value);
|
|
3158
|
+
}
|
|
3159
|
+
function planPriceHash(plan2) {
|
|
3160
|
+
const price2 = plan2.price;
|
|
3161
|
+
const payload = price2 ? { amount: price2.amount, currency: price2.currency, interval: price2.interval } : null;
|
|
3162
|
+
return createHash("sha256").update(stableStringify(payload)).digest("hex");
|
|
3163
|
+
}
|
|
3164
|
+
function planPaystackHash(plan2) {
|
|
3165
|
+
const price2 = plan2.price;
|
|
3166
|
+
const payload = {
|
|
3167
|
+
name: plan2.name ?? null,
|
|
3168
|
+
price: price2 ? { amount: price2.amount, currency: price2.currency, interval: price2.interval } : null
|
|
3169
|
+
};
|
|
3170
|
+
return createHash("sha256").update(stableStringify(payload)).digest("hex");
|
|
3171
|
+
}
|
|
3172
|
+
function buildFeatures(plan2) {
|
|
3173
|
+
const out = {};
|
|
3174
|
+
for (const inclusion of plan2.includes) {
|
|
3175
|
+
out[inclusion.featureId] = inclusion.type === "boolean" ? { type: "boolean" } : { type: "metered", limit: inclusion.limit, reset: inclusion.reset };
|
|
3176
|
+
}
|
|
3177
|
+
return out;
|
|
3178
|
+
}
|
|
3179
|
+
async function findStripeProduct(stripe, planId) {
|
|
3180
|
+
const found = await stripe.products.search({ query: `metadata['pwv_plan']:'${planId}'` });
|
|
3181
|
+
return found.data[0];
|
|
3182
|
+
}
|
|
3183
|
+
async function findStripePrice(stripe, planId, hash) {
|
|
3184
|
+
const found = await stripe.prices.search({
|
|
3185
|
+
query: `metadata['pwv_plan']:'${planId}' AND metadata['pwv_hash']:'${hash}'`
|
|
3186
|
+
});
|
|
3187
|
+
return found.data[0];
|
|
3188
|
+
}
|
|
3189
|
+
async function syncStripe(stripe, plan2, existingVersion) {
|
|
3190
|
+
const price2 = plan2.price;
|
|
3191
|
+
if (!price2) throw new PayweaveConfigError(`plan "${plan2.id}" has no price to sync to stripe.`);
|
|
3192
|
+
const priceHash = planPriceHash(plan2);
|
|
3193
|
+
const prevRefs = existingVersion?.providerRefs.stripe;
|
|
3194
|
+
const prevProductId = prevRefs?.productId;
|
|
3195
|
+
const prevPriceId = prevRefs?.priceId;
|
|
3196
|
+
const nameChanged = (existingVersion?.name ?? null) !== (plan2.name ?? null);
|
|
3197
|
+
const priceChanged = prevRefs?.hash !== priceHash;
|
|
3198
|
+
if (prevProductId !== void 0 && prevPriceId !== void 0 && !nameChanged && !priceChanged) {
|
|
3199
|
+
return { refs: { productId: prevProductId, priceId: prevPriceId, hash: priceHash }, action: "unchanged" };
|
|
3200
|
+
}
|
|
3201
|
+
let created = false;
|
|
3202
|
+
let adopted = false;
|
|
3203
|
+
let productId = prevProductId;
|
|
3204
|
+
if (productId === void 0) {
|
|
3205
|
+
const found = await findStripeProduct(stripe, plan2.id);
|
|
3206
|
+
if (found) {
|
|
3207
|
+
productId = found.id;
|
|
3208
|
+
adopted = true;
|
|
3209
|
+
} else {
|
|
3210
|
+
const createdProduct = await stripe.products.create(
|
|
3211
|
+
{ name: plan2.name ?? plan2.id, metadata: { pwv_plan: plan2.id } },
|
|
3212
|
+
{ idempotencyKey: `pwv-push-product-${plan2.id}` }
|
|
3213
|
+
);
|
|
3214
|
+
productId = createdProduct.id;
|
|
3215
|
+
created = true;
|
|
3216
|
+
}
|
|
3217
|
+
} else if (nameChanged) {
|
|
3218
|
+
await stripe.products.update(productId, {
|
|
3219
|
+
name: plan2.name ?? plan2.id,
|
|
3220
|
+
metadata: { pwv_plan: plan2.id }
|
|
3221
|
+
});
|
|
3222
|
+
}
|
|
3223
|
+
let priceId = prevPriceId;
|
|
3224
|
+
if (priceId === void 0 || priceChanged) {
|
|
3225
|
+
const foundPrice = await findStripePrice(stripe, plan2.id, priceHash);
|
|
3226
|
+
if (foundPrice) {
|
|
3227
|
+
priceId = foundPrice.id;
|
|
3228
|
+
adopted = true;
|
|
3229
|
+
} else {
|
|
3230
|
+
const createdPrice = await stripe.prices.create(
|
|
3231
|
+
{
|
|
3232
|
+
currency: price2.currency.toLowerCase(),
|
|
3233
|
+
unit_amount: price2.amount,
|
|
3234
|
+
recurring: { interval: price2.interval },
|
|
3235
|
+
product: productId,
|
|
3236
|
+
metadata: { pwv_plan: plan2.id, pwv_hash: priceHash }
|
|
3237
|
+
},
|
|
3238
|
+
{ idempotencyKey: `pwv-push-price-${plan2.id}-${priceHash}` }
|
|
3239
|
+
);
|
|
3240
|
+
priceId = createdPrice.id;
|
|
3241
|
+
created = true;
|
|
3242
|
+
}
|
|
3243
|
+
if (prevPriceId !== void 0 && prevPriceId !== priceId) {
|
|
3244
|
+
await stripe.prices.update(prevPriceId, { active: false });
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
return {
|
|
3248
|
+
refs: { productId, priceId, hash: priceHash },
|
|
3249
|
+
action: adopted ? "adopted" : created ? "created" : "unchanged"
|
|
3250
|
+
};
|
|
3251
|
+
}
|
|
3252
|
+
var PAYSTACK_INTERVAL = {
|
|
3253
|
+
month: "monthly",
|
|
3254
|
+
year: "annually"
|
|
3255
|
+
};
|
|
3256
|
+
function encodePaystackTag(tag) {
|
|
3257
|
+
return JSON.stringify(tag);
|
|
3258
|
+
}
|
|
3259
|
+
function decodePaystackTag(description) {
|
|
3260
|
+
if (description === null || description === void 0) return void 0;
|
|
3261
|
+
try {
|
|
3262
|
+
const parsed = JSON.parse(description);
|
|
3263
|
+
if (parsed !== null && typeof parsed === "object" && typeof parsed.pwv_plan === "string" && typeof parsed.pwv_hash === "string") {
|
|
3264
|
+
return parsed;
|
|
3265
|
+
}
|
|
3266
|
+
} catch {
|
|
3267
|
+
}
|
|
3268
|
+
return void 0;
|
|
3269
|
+
}
|
|
3270
|
+
async function findAdoptablePaystackPlan(paystack, planId, hash) {
|
|
3271
|
+
for await (const candidate of paystack.plans.iterate()) {
|
|
3272
|
+
const tag = decodePaystackTag(candidate.description ?? void 0);
|
|
3273
|
+
if (tag?.pwv_plan === planId && tag.pwv_hash === hash) {
|
|
3274
|
+
const code = candidate.plan_code;
|
|
3275
|
+
if (code !== void 0) return code;
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
return void 0;
|
|
3279
|
+
}
|
|
3280
|
+
async function syncPaystack(paystack, plan2, existingVersion) {
|
|
3281
|
+
const price2 = plan2.price;
|
|
3282
|
+
if (!price2) throw new PayweaveConfigError(`plan "${plan2.id}" has no price to sync to paystack.`);
|
|
3283
|
+
const hash = planPaystackHash(plan2);
|
|
3284
|
+
const prevRefs = existingVersion?.providerRefs.paystack;
|
|
3285
|
+
const prevPlanCode = prevRefs?.planCode;
|
|
3286
|
+
if (prevPlanCode !== void 0 && prevRefs?.hash === hash) {
|
|
3287
|
+
return { refs: { planCode: prevPlanCode, hash }, action: "unchanged" };
|
|
3288
|
+
}
|
|
3289
|
+
const adoptedCode = await findAdoptablePaystackPlan(paystack, plan2.id, hash);
|
|
3290
|
+
if (adoptedCode !== void 0) {
|
|
3291
|
+
return { refs: { planCode: adoptedCode, hash }, action: "adopted" };
|
|
3292
|
+
}
|
|
3293
|
+
const created = await paystack.plans.create({
|
|
3294
|
+
name: plan2.name ?? plan2.id,
|
|
3295
|
+
amount: price2.amount,
|
|
3296
|
+
interval: PAYSTACK_INTERVAL[price2.interval],
|
|
3297
|
+
currency: price2.currency,
|
|
3298
|
+
description: encodePaystackTag({ pwv_plan: plan2.id, pwv_hash: hash })
|
|
3299
|
+
});
|
|
3300
|
+
const planCode = created.data.plan_code;
|
|
3301
|
+
if (planCode === void 0) {
|
|
3302
|
+
throw new PayweaveProviderError("paystack created a plan without a plan_code", {
|
|
3303
|
+
provider: "paystack"
|
|
3304
|
+
});
|
|
3305
|
+
}
|
|
3306
|
+
return { refs: { planCode, hash }, action: "created" };
|
|
3307
|
+
}
|
|
3308
|
+
async function pushPlanToProvider(ctx, provider, plan2, existingVersion) {
|
|
3309
|
+
if (provider === "stripe") {
|
|
3310
|
+
if (!ctx.stripe) throw new PayweaveConfigError("stripe is not configured on this client.");
|
|
3311
|
+
return syncStripe(ctx.stripe, plan2, existingVersion);
|
|
3312
|
+
}
|
|
3313
|
+
if (provider === "paystack") {
|
|
3314
|
+
if (!ctx.paystack) throw new PayweaveConfigError("paystack is not configured on this client.");
|
|
3315
|
+
return syncPaystack(ctx.paystack, plan2, existingVersion);
|
|
3316
|
+
}
|
|
3317
|
+
throw new PayweaveConfigError(
|
|
3318
|
+
`payweave.sync() does not support pushing plans to "${provider}" yet \u2014 its payment-plan mapping is deferred pending further API verification; billing-capable providers: ${BILLING_CAPABLE_PROVIDERS.join(", ")}.`
|
|
3319
|
+
);
|
|
3320
|
+
}
|
|
3321
|
+
function planVersionInput(plan2, providerRefs) {
|
|
3322
|
+
return {
|
|
3323
|
+
planId: plan2.id,
|
|
3324
|
+
group: resolvePlanGroup(plan2),
|
|
3325
|
+
isDefault: plan2.default,
|
|
3326
|
+
name: plan2.name ?? null,
|
|
3327
|
+
priceMinor: plan2.price?.amount ?? null,
|
|
3328
|
+
priceCurrency: plan2.price?.currency ?? null,
|
|
3329
|
+
priceInterval: plan2.price?.interval ?? null,
|
|
3330
|
+
features: buildFeatures(plan2),
|
|
3331
|
+
providerRefs
|
|
3332
|
+
};
|
|
3333
|
+
}
|
|
3334
|
+
async function sync(ctx) {
|
|
3335
|
+
const database = ctx.database;
|
|
3336
|
+
if (!database) {
|
|
3337
|
+
throw new PayweaveConfigError(
|
|
3338
|
+
"payweave.sync() needs a database \u2014 pass a payweave/db/* adapter to createPayweave()."
|
|
3339
|
+
);
|
|
3340
|
+
}
|
|
3341
|
+
const products = ctx.products;
|
|
3342
|
+
if (!products) {
|
|
3343
|
+
throw new PayweaveConfigError(
|
|
3344
|
+
"payweave.sync() needs products \u2014 pass a `products` array to createPayweave()."
|
|
3345
|
+
);
|
|
3346
|
+
}
|
|
3347
|
+
const billingProviders = ctx.providers.filter(isBillingCapableProvider);
|
|
3348
|
+
const skippedProviders = ctx.providers.filter((provider) => !isBillingCapableProvider(provider));
|
|
3349
|
+
const plans = [];
|
|
3350
|
+
for (const plan2 of products) {
|
|
3351
|
+
const existingVersion = await database.plans.getActiveVersion(plan2.id);
|
|
3352
|
+
const providerRefs = {};
|
|
3353
|
+
const providerActions = {};
|
|
3354
|
+
if (plan2.price !== void 0) {
|
|
3355
|
+
for (const provider of billingProviders) {
|
|
3356
|
+
const result = await pushPlanToProvider(ctx, provider, plan2, existingVersion);
|
|
3357
|
+
providerRefs[provider] = result.refs;
|
|
3358
|
+
providerActions[provider] = result.action;
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
const pushed = await database.plans.pushVersion(planVersionInput(plan2, providerRefs));
|
|
3362
|
+
plans.push({
|
|
3363
|
+
planId: plan2.id,
|
|
3364
|
+
version: pushed.version,
|
|
3365
|
+
versionChanged: existingVersion === null || existingVersion.version !== pushed.version,
|
|
3366
|
+
providers: providerActions
|
|
3367
|
+
});
|
|
3368
|
+
}
|
|
3369
|
+
return { plans, skippedProviders };
|
|
3370
|
+
}
|
|
3371
|
+
|
|
3372
|
+
// src/products/usage.ts
|
|
3373
|
+
function requireDatabase(ctx, method) {
|
|
3374
|
+
if (!ctx.database) {
|
|
3375
|
+
throw new PayweaveConfigError(
|
|
3376
|
+
`payweave.${method}() needs a database \u2014 pass a payweave/db/* adapter to createPayweave().`
|
|
3377
|
+
);
|
|
3378
|
+
}
|
|
3379
|
+
return ctx.database;
|
|
3380
|
+
}
|
|
3381
|
+
function assertPositiveSafeInteger(value, name) {
|
|
3382
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
3383
|
+
throw new PayweaveValidationError(`${name} must be a positive safe integer \u2014 got ${value}`);
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
async function resolveFeatureContext(ctx, database, customerId, featureId) {
|
|
3387
|
+
const products = ctx.products ?? [];
|
|
3388
|
+
let type;
|
|
3389
|
+
let group;
|
|
3390
|
+
for (const p of products) {
|
|
3391
|
+
const inc = p.includes.find((i) => i.featureId === featureId);
|
|
3392
|
+
if (inc) {
|
|
3393
|
+
type = inc.type;
|
|
3394
|
+
group = resolvePlanGroup(p);
|
|
3395
|
+
break;
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
if (type === void 0 || group === void 0) {
|
|
3399
|
+
throw new PayweaveValidationError(
|
|
3400
|
+
`unknown feature "${featureId}" \u2014 configure it in \`products\`.`
|
|
3401
|
+
);
|
|
3402
|
+
}
|
|
3403
|
+
let customerRow = await database.customers.getByExternalId(customerId);
|
|
3404
|
+
if (customerRow === null) {
|
|
3405
|
+
customerRow = await database.customers.upsert({ externalId: customerId });
|
|
3406
|
+
}
|
|
3407
|
+
const subscription2 = await database.subscriptions.getActive(customerRow.id, group);
|
|
3408
|
+
if (subscription2) {
|
|
3409
|
+
const plan2 = products.find((p) => p.id === subscription2.planId);
|
|
3410
|
+
if (!plan2) {
|
|
3411
|
+
throw new PayweaveValidationError(
|
|
3412
|
+
`customer's active subscription references plan "${subscription2.planId}", which is no longer in \`products\` \u2014 was it renamed or removed?`
|
|
3413
|
+
);
|
|
3414
|
+
}
|
|
3415
|
+
const inclusion2 = plan2.includes.find((i) => i.featureId === featureId);
|
|
3416
|
+
return { group, type, plan: plan2, inclusion: inclusion2, subscription: subscription2, customerRow };
|
|
3417
|
+
}
|
|
3418
|
+
const defaultPlan = products.find((p) => p.default && resolvePlanGroup(p) === group);
|
|
3419
|
+
if (!defaultPlan) {
|
|
3420
|
+
throw new PayweaveConfigError(
|
|
3421
|
+
`group "${group}" has no default plan \u2014 check()/report() need one to answer for a customer with no active subscription in that group.`
|
|
3422
|
+
);
|
|
3423
|
+
}
|
|
3424
|
+
const inclusion = defaultPlan.includes.find((i) => i.featureId === featureId);
|
|
3425
|
+
return { group, type, plan: defaultPlan, inclusion, subscription: null, customerRow };
|
|
3426
|
+
}
|
|
3427
|
+
async function buildMeteredInit(database, resolved, inclusion, now) {
|
|
3428
|
+
const isPaid = resolved.plan.price !== void 0;
|
|
3429
|
+
const anchor = isPaid && resolved.subscription ? resolved.subscription.currentPeriodStart : now;
|
|
3430
|
+
const planVersion = resolved.subscription ? resolved.subscription.planVersion : (await database.plans.getActiveVersion(resolved.plan.id))?.version ?? 1;
|
|
3431
|
+
return {
|
|
3432
|
+
limit: inclusion.limit,
|
|
3433
|
+
resetInterval: inclusion.reset,
|
|
3434
|
+
anchor,
|
|
3435
|
+
planId: resolved.plan.id,
|
|
3436
|
+
planVersion
|
|
3437
|
+
};
|
|
3438
|
+
}
|
|
3439
|
+
async function check(ctx, input) {
|
|
3440
|
+
const database = requireDatabase(ctx, "check");
|
|
3441
|
+
const resolved = await resolveFeatureContext(ctx, database, input.customerId, input.featureId);
|
|
3442
|
+
if (resolved.type === "boolean") {
|
|
3443
|
+
return {
|
|
3444
|
+
allowed: resolved.inclusion !== void 0,
|
|
3445
|
+
balance: null,
|
|
3446
|
+
limit: null,
|
|
3447
|
+
resetsAt: null,
|
|
3448
|
+
planId: resolved.plan.id
|
|
3449
|
+
};
|
|
3450
|
+
}
|
|
3451
|
+
if (!resolved.inclusion) {
|
|
3452
|
+
return { allowed: false, balance: null, limit: null, resetsAt: null, planId: resolved.plan.id };
|
|
3453
|
+
}
|
|
3454
|
+
const inclusion = resolved.inclusion;
|
|
3455
|
+
const now = /* @__PURE__ */ new Date();
|
|
3456
|
+
const init = await buildMeteredInit(database, resolved, inclusion, now);
|
|
3457
|
+
const conditional = input.consume === true;
|
|
3458
|
+
const amount = conditional ? 1 : 0;
|
|
3459
|
+
const result = await database.balances.consume({
|
|
3460
|
+
customerId: resolved.customerRow.id,
|
|
3461
|
+
featureId: input.featureId,
|
|
3462
|
+
group: resolved.group,
|
|
3463
|
+
amount,
|
|
3464
|
+
conditional,
|
|
3465
|
+
init,
|
|
3466
|
+
now
|
|
3467
|
+
});
|
|
3468
|
+
const balance = result.limit - result.used;
|
|
3469
|
+
return {
|
|
3470
|
+
allowed: conditional ? result.applied : balance > 0,
|
|
3471
|
+
balance,
|
|
3472
|
+
limit: result.limit,
|
|
3473
|
+
resetsAt: result.periodEnd,
|
|
3474
|
+
planId: resolved.plan.id
|
|
3475
|
+
};
|
|
3476
|
+
}
|
|
3477
|
+
async function report(ctx, input) {
|
|
3478
|
+
const database = requireDatabase(ctx, "report");
|
|
3479
|
+
const amount = input.amount ?? 1;
|
|
3480
|
+
assertPositiveSafeInteger(amount, "amount");
|
|
3481
|
+
const resolved = await resolveFeatureContext(ctx, database, input.customerId, input.featureId);
|
|
3482
|
+
if (resolved.type === "boolean") {
|
|
3483
|
+
throw new PayweaveValidationError(
|
|
3484
|
+
`report() only accepts metered features \u2014 "${input.featureId}" is a boolean feature.`
|
|
3485
|
+
);
|
|
3486
|
+
}
|
|
3487
|
+
if (!resolved.inclusion) {
|
|
3488
|
+
throw new PayweaveValidationError(
|
|
3489
|
+
`report(): feature "${input.featureId}" is not included in customer "${input.customerId}"'s current plan "${resolved.plan.id}" \u2014 call check() first, which answers { allowed: false } for a plan lacking a feature.`
|
|
3490
|
+
);
|
|
3491
|
+
}
|
|
3492
|
+
const inclusion = resolved.inclusion;
|
|
3493
|
+
const now = /* @__PURE__ */ new Date();
|
|
3494
|
+
const init = await buildMeteredInit(database, resolved, inclusion, now);
|
|
3495
|
+
const result = await database.balances.consume({
|
|
3496
|
+
customerId: resolved.customerRow.id,
|
|
3497
|
+
featureId: input.featureId,
|
|
3498
|
+
group: resolved.group,
|
|
3499
|
+
amount,
|
|
3500
|
+
conditional: false,
|
|
3501
|
+
init,
|
|
3502
|
+
now
|
|
3503
|
+
});
|
|
3504
|
+
return { balance: result.limit - result.used, resetsAt: result.periodEnd };
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
// src/index.ts
|
|
3508
|
+
function buildAuth(resolved) {
|
|
3509
|
+
if (resolved.provider === "flutterwave" && resolved.version === "v4") {
|
|
3510
|
+
if (!resolved.clientId || !resolved.clientSecret || !resolved.tokenUrl) {
|
|
3511
|
+
throw new PayweaveConfigError("Flutterwave v4 requires clientId, clientSecret, and a token URL.", {
|
|
3512
|
+
provider: "flutterwave"
|
|
3513
|
+
});
|
|
3514
|
+
}
|
|
3515
|
+
return oauthClientCredentials({
|
|
3516
|
+
clientId: resolved.clientId,
|
|
3517
|
+
clientSecret: resolved.clientSecret,
|
|
3518
|
+
tokenUrl: resolved.tokenUrl,
|
|
3519
|
+
...resolved.fetch ? { fetch: resolved.fetch } : {},
|
|
3520
|
+
...resolved.logger ? { logger: resolved.logger } : {}
|
|
3521
|
+
});
|
|
3522
|
+
}
|
|
3523
|
+
if (!resolved.secretKey) {
|
|
3524
|
+
throw new PayweaveConfigError(`Missing secret key for ${resolved.provider}.`, {
|
|
3525
|
+
provider: resolved.provider
|
|
3526
|
+
});
|
|
3527
|
+
}
|
|
3528
|
+
return bearer(resolved.secretKey);
|
|
3529
|
+
}
|
|
3530
|
+
function buildHttpClient(resolved) {
|
|
3531
|
+
return new HttpClient({
|
|
3532
|
+
baseUrl: resolved.baseUrl,
|
|
3533
|
+
auth: buildAuth(resolved),
|
|
3534
|
+
provider: resolved.provider,
|
|
3535
|
+
version: resolved.version,
|
|
3536
|
+
timeoutMs: resolved.timeoutMs,
|
|
3537
|
+
maxRetries: resolved.maxRetries,
|
|
3538
|
+
fetch: resolved.fetch,
|
|
3539
|
+
logger: resolved.logger,
|
|
3540
|
+
userAgent: `payweave/${SDK_VERSION} (${resolved.provider})`
|
|
3541
|
+
});
|
|
3542
|
+
}
|
|
3543
|
+
function stripeUnifiedNamespace() {
|
|
3544
|
+
const notImplemented = (op) => Promise.reject(
|
|
3545
|
+
new PayweaveError(
|
|
3546
|
+
`stripe unified "${op}" is supported in principle but not implemented yet \u2014 the resource endpoints landed, but the unified mapping (\`src/unified/stripe.ts\`) has not. Use the per-call \`provider\` override to route to another configured provider, or call the Surface A \`payweave.stripe.*\` resources directly.`,
|
|
3547
|
+
{ provider: "stripe", isRetryable: false }
|
|
3548
|
+
)
|
|
3549
|
+
);
|
|
3550
|
+
const capabilityGap = (op) => {
|
|
3551
|
+
try {
|
|
3552
|
+
assertUnifiedCapability("stripe", op);
|
|
3553
|
+
} catch (err) {
|
|
3554
|
+
return Promise.reject(err);
|
|
3555
|
+
}
|
|
3556
|
+
return Promise.reject(new PayweaveError(`${op} is not supported on stripe.`, { provider: "stripe" }));
|
|
3557
|
+
};
|
|
3558
|
+
return {
|
|
3559
|
+
checkout: { create: () => notImplemented("checkout.create") },
|
|
3560
|
+
verify: () => notImplemented("verify"),
|
|
3561
|
+
refunds: { create: () => notImplemented("refunds.create") },
|
|
3562
|
+
transfers: { create: () => capabilityGap("transfers.create") },
|
|
3563
|
+
banks: {
|
|
3564
|
+
list: () => capabilityGap("banks.list"),
|
|
3565
|
+
resolveAccount: () => capabilityGap("banks.resolveAccount")
|
|
3566
|
+
}
|
|
3567
|
+
};
|
|
3568
|
+
}
|
|
3569
|
+
function createPayweave(config) {
|
|
3570
|
+
const resolved = resolvePayweaveConfig(config);
|
|
3571
|
+
const surfaces = {};
|
|
3572
|
+
const unifiedByProvider = {};
|
|
3573
|
+
for (const key of resolved.providers) {
|
|
3574
|
+
const entry = resolved.providerConfigs[key];
|
|
3575
|
+
if (entry === void 0) continue;
|
|
3576
|
+
if (key === "paystack") {
|
|
3577
|
+
const http = buildHttpClient(entry);
|
|
3578
|
+
surfaces.paystack = new PaystackClient(http);
|
|
3579
|
+
unifiedByProvider.paystack = createPaystackUnified(http);
|
|
3580
|
+
} else if (key === "flutterwave") {
|
|
3581
|
+
const http = buildHttpClient(entry);
|
|
3582
|
+
const version = entry.version === "v4" ? "v4" : "v3";
|
|
3583
|
+
surfaces.flutterwave = new FlutterwaveClient(http, version, entry.encryptionKey);
|
|
3584
|
+
unifiedByProvider.flutterwave = createFlutterwaveUnified(http, version);
|
|
3585
|
+
} else {
|
|
3586
|
+
surfaces.stripe = new StripeClient(new HttpClient(stripeHttpOptions(entry)));
|
|
3587
|
+
unifiedByProvider.stripe = stripeUnifiedNamespace();
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
const routeTo = (override) => {
|
|
3591
|
+
const key = override ?? resolved.defaultProvider;
|
|
3592
|
+
const ns = unifiedByProvider[key];
|
|
3593
|
+
if (ns === void 0) {
|
|
3594
|
+
throw new PayweaveConfigError(
|
|
3595
|
+
`provider "${key}" is not configured on this client \u2014 configured: ${resolved.providers.join(", ")}.`,
|
|
3596
|
+
{ provider: key }
|
|
3597
|
+
);
|
|
3598
|
+
}
|
|
3599
|
+
return ns;
|
|
3600
|
+
};
|
|
3601
|
+
const guardedRoute = (op, override) => {
|
|
3602
|
+
const ns = routeTo(override);
|
|
3603
|
+
assertUnifiedCapability(override ?? resolved.defaultProvider, op);
|
|
3604
|
+
return ns;
|
|
3605
|
+
};
|
|
3606
|
+
const unified = {
|
|
3607
|
+
checkout: {
|
|
3608
|
+
create: ({ provider, ...input }) => guardedRoute("checkout.create", provider).checkout.create(input)
|
|
3609
|
+
},
|
|
3610
|
+
verify: ({ provider, ...input }) => guardedRoute("verify", provider).verify(input),
|
|
3611
|
+
refunds: {
|
|
3612
|
+
create: ({ provider, ...input }) => guardedRoute("refunds.create", provider).refunds.create(input)
|
|
3613
|
+
},
|
|
3614
|
+
transfers: {
|
|
3615
|
+
create: ({ provider, ...input }) => guardedRoute("transfers.create", provider).transfers.create(input)
|
|
3616
|
+
},
|
|
3617
|
+
banks: {
|
|
3618
|
+
list: ({ provider, ...input }) => guardedRoute("banks.list", provider).banks.list(input),
|
|
3619
|
+
resolveAccount: ({ provider, ...input }) => guardedRoute("banks.resolveAccount", provider).banks.resolveAccount(input)
|
|
3620
|
+
}
|
|
3621
|
+
};
|
|
3622
|
+
function capabilities(provider) {
|
|
3623
|
+
if (provider !== void 0) {
|
|
3624
|
+
if (!resolved.providers.includes(provider)) {
|
|
3625
|
+
throw new PayweaveConfigError(
|
|
3626
|
+
`provider "${provider}" is not configured on this client \u2014 configured: ${resolved.providers.join(", ")}.`,
|
|
3627
|
+
{ provider }
|
|
3628
|
+
);
|
|
3629
|
+
}
|
|
3630
|
+
return UNIFIED_CAPABILITY_MATRIX[provider];
|
|
3631
|
+
}
|
|
3632
|
+
const out = {};
|
|
3633
|
+
for (const key of resolved.providers) out[key] = UNIFIED_CAPABILITY_MATRIX[key];
|
|
3634
|
+
return out;
|
|
3635
|
+
}
|
|
3636
|
+
const billingContext = {
|
|
3637
|
+
database: resolved.database,
|
|
3638
|
+
products: resolved.products,
|
|
3639
|
+
providers: resolved.providers,
|
|
3640
|
+
defaultProvider: resolved.defaultProvider,
|
|
3641
|
+
stripe: surfaces.stripe,
|
|
3642
|
+
paystack: surfaces.paystack
|
|
3643
|
+
};
|
|
3644
|
+
const client = {
|
|
3645
|
+
providers: resolved.providers,
|
|
3646
|
+
defaultProvider: resolved.defaultProvider,
|
|
3647
|
+
environment: resolved.environment,
|
|
3648
|
+
// Multi-provider header dispatch: the
|
|
3649
|
+
// provider is detected from the signature header NAME, then verified with
|
|
3650
|
+
// that provider's existing timing-safe verifier — fail closed on
|
|
3651
|
+
// unconfigured/ambiguous/unknown headers. `billingContext` (below) is
|
|
3652
|
+
// threaded through so every constructed event's `.apply()` can
|
|
3653
|
+
// reach the database — the same object the billing surface below uses.
|
|
3654
|
+
webhooks: createWebhookDispatch(resolved, billingContext),
|
|
3655
|
+
checkout: unified.checkout,
|
|
3656
|
+
verify: unified.verify,
|
|
3657
|
+
refunds: unified.refunds,
|
|
3658
|
+
transfers: unified.transfers,
|
|
3659
|
+
banks: unified.banks,
|
|
3660
|
+
capabilities,
|
|
3661
|
+
// See `PayweaveClientBase.database`'s doc comment.
|
|
3662
|
+
database: resolved.database,
|
|
3663
|
+
products: resolved.products,
|
|
3664
|
+
subscribe: (input) => subscribe(billingContext, input),
|
|
3665
|
+
// BillingSync push.
|
|
3666
|
+
sync: () => sync(billingContext),
|
|
3667
|
+
// metered usage (real implementations, not stubs).
|
|
3668
|
+
check: (input) => check(billingContext, input),
|
|
3669
|
+
report: (input) => report(billingContext, input),
|
|
3670
|
+
...surfaces
|
|
3671
|
+
};
|
|
3672
|
+
return Object.freeze(client);
|
|
3673
|
+
}
|
|
3674
|
+
|
|
3675
|
+
export { StripeClient, createPayweave };
|
|
3676
|
+
//# sourceMappingURL=index.js.map
|
|
3677
|
+
//# sourceMappingURL=index.js.map
|