@wefunder/sdk 0.1.0-beta.8 → 0.1.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -25,24 +25,33 @@ __export(index_exports, {
25
25
  DEFAULT_AUTHORIZE_BASE_URL: () => DEFAULT_AUTHORIZE_BASE_URL,
26
26
  DEFAULT_OAUTH_BASE_URL: () => DEFAULT_OAUTH_BASE_URL,
27
27
  DEFAULT_TOKEN_BASE_URL: () => DEFAULT_TOKEN_BASE_URL,
28
- DELIVERY_ID_HEADER: () => DELIVERY_ID_HEADER,
29
- EVENT_HEADER: () => EVENT_HEADER,
28
+ DEFAULT_TOLERANCE_SECONDS: () => DEFAULT_TOLERANCE_SECONDS,
29
+ LEGACY_DELIVERY_ID_HEADER: () => LEGACY_DELIVERY_ID_HEADER,
30
+ LEGACY_EVENT_HEADER: () => LEGACY_EVENT_HEADER,
31
+ LEGACY_SIGNATURE_HEADER: () => LEGACY_SIGNATURE_HEADER,
32
+ LEGACY_TIMESTAMP_HEADER: () => LEGACY_TIMESTAMP_HEADER,
30
33
  REQUEST_ID_HEADER: () => REQUEST_ID_HEADER,
31
34
  SANDBOX_AUTHORIZE_BASE_URL: () => SANDBOX_AUTHORIZE_BASE_URL,
32
35
  SIGNATURE_HEADER: () => SIGNATURE_HEADER,
33
- TIMESTAMP_HEADER: () => TIMESTAMP_HEADER,
36
+ WebhookSignatureError: () => WebhookSignatureError,
34
37
  Wefunder: () => Wefunder,
35
38
  WefunderAuthError: () => WefunderAuthError,
36
39
  WefunderError: () => WefunderError,
40
+ checkWebhookSignature: () => checkWebhookSignature,
37
41
  clientCredentialsGrant: () => clientCredentialsGrant,
38
42
  collect: () => collect,
43
+ computeWebhookSignature: () => computeWebhookSignature,
39
44
  constructEvent: () => constructEvent,
45
+ constructEventFromRequest: () => constructEventFromRequest,
40
46
  createAuthorizationUrl: () => createAuthorizationUrl,
47
+ dispatchWebhook: () => dispatchWebhook,
41
48
  exchangeCode: () => exchangeCode,
42
49
  generatePkce: () => generatePkce,
43
50
  modeForToken: () => modeForToken,
44
51
  paginate: () => paginate,
52
+ parseSignatureHeader: () => parseSignatureHeader,
45
53
  refreshToken: () => refreshToken,
54
+ signWebhook: () => signWebhook,
46
55
  verifyWebhook: () => verifyWebhook
47
56
  });
48
57
  module.exports = __toCommonJS(index_exports);
@@ -1179,23 +1188,36 @@ var sdk_gen_exports = {};
1179
1188
  __export(sdk_gen_exports, {
1180
1189
  approveSyndicateMember: () => approveSyndicateMember,
1181
1190
  closeSyndicateDeal: () => closeSyndicateDeal,
1191
+ createInstallation: () => createInstallation,
1192
+ createInstallationToken: () => createInstallationToken,
1182
1193
  createIntent: () => createIntent,
1183
1194
  createPartnerInvite: () => createPartnerInvite,
1195
+ createWebhookEndpoint: () => createWebhookEndpoint,
1184
1196
  createWebhookSubscription: () => createWebhookSubscription,
1197
+ deleteWebhookEndpoint: () => deleteWebhookEndpoint,
1185
1198
  deleteWebhookSubscription: () => deleteWebhookSubscription,
1186
1199
  demoteSyndicateMember: () => demoteSyndicateMember,
1187
1200
  exportSyndicateMembersCsv: () => exportSyndicateMembersCsv,
1188
1201
  finalizeSyndicateDeal: () => finalizeSyndicateDeal,
1202
+ followCompany: () => followCompany,
1189
1203
  getAttributionMe: () => getAttributionMe,
1190
1204
  getAttributionStats: () => getAttributionStats,
1205
+ getCompany: () => getCompany,
1206
+ getCompanyDisclosures: () => getCompanyDisclosures,
1207
+ getCompanyPitch: () => getCompanyPitch,
1208
+ getCompanyUpdate: () => getCompanyUpdate,
1191
1209
  getCurrentUser: () => getCurrentUser,
1210
+ getInstallation: () => getInstallation,
1192
1211
  getIntent: () => getIntent,
1212
+ getInvestment: () => getInvestment,
1193
1213
  getOffering: () => getOffering,
1214
+ getOfferingStats: () => getOfferingStats,
1194
1215
  getPortfolio: () => getPortfolio,
1195
1216
  getSyndicate: () => getSyndicate,
1196
1217
  getSyndicateDeal: () => getSyndicateDeal,
1197
1218
  getSyndicatePortfolio: () => getSyndicatePortfolio,
1198
1219
  getSyndicateStatistics: () => getSyndicateStatistics,
1220
+ getWebhookEndpoint: () => getWebhookEndpoint,
1199
1221
  getWebhookSubscription: () => getWebhookSubscription,
1200
1222
  hideSyndicateMember: () => hideSyndicateMember,
1201
1223
  inviteSyndicateMember: () => inviteSyndicateMember,
@@ -1203,9 +1225,15 @@ __export(sdk_gen_exports, {
1203
1225
  listAttributedInvestments: () => listAttributedInvestments,
1204
1226
  listAttributionCampaigns: () => listAttributionCampaigns,
1205
1227
  listCampaigns: () => listCampaigns,
1228
+ listCompanyQuestions: () => listCompanyQuestions,
1229
+ listCompanyUpdates: () => listCompanyUpdates,
1206
1230
  listConnectedApps: () => listConnectedApps,
1231
+ listEligibleInstallTargets: () => listEligibleInstallTargets,
1232
+ listFollowedCompanies: () => listFollowedCompanies,
1233
+ listInstallations: () => listInstallations,
1207
1234
  listIntents: () => listIntents,
1208
1235
  listInvestments: () => listInvestments,
1236
+ listMyCompanies: () => listMyCompanies,
1209
1237
  listOfferings: () => listOfferings,
1210
1238
  listPartnerInvites: () => listPartnerInvites,
1211
1239
  listPortfolioPositions: () => listPortfolioPositions,
@@ -1215,9 +1243,12 @@ __export(sdk_gen_exports, {
1215
1243
  listSyndicateMembers: () => listSyndicateMembers,
1216
1244
  listSyndicatePortfolioPositions: () => listSyndicatePortfolioPositions,
1217
1245
  listSyndicates: () => listSyndicates,
1246
+ listWebhookEndpoints: () => listWebhookEndpoints,
1218
1247
  listWebhookSubscriptions: () => listWebhookSubscriptions,
1248
+ previewIntent: () => previewIntent,
1219
1249
  promoteSyndicateMember: () => promoteSyndicateMember,
1220
1250
  reactivateWebhookSubscription: () => reactivateWebhookSubscription,
1251
+ reenableWebhookEndpoint: () => reenableWebhookEndpoint,
1221
1252
  regenerateWebhookSecret: () => regenerateWebhookSecret,
1222
1253
  registerAsPartner: () => registerAsPartner,
1223
1254
  removeSyndicateMember: () => removeSyndicateMember,
@@ -1225,10 +1256,16 @@ __export(sdk_gen_exports, {
1225
1256
  resendSyndicateInvite: () => resendSyndicateInvite,
1226
1257
  restoreSyndicateMember: () => restoreSyndicateMember,
1227
1258
  revokeConnectedApp: () => revokeConnectedApp,
1259
+ revokeInstallation: () => revokeInstallation,
1228
1260
  revokePartnerInvite: () => revokePartnerInvite,
1261
+ rotateWebhookEndpointSecret: () => rotateWebhookEndpointSecret,
1262
+ searchCompanies: () => searchCompanies,
1263
+ testWebhookEndpoint: () => testWebhookEndpoint,
1229
1264
  testWebhookSubscription: () => testWebhookSubscription,
1265
+ unfollowCompany: () => unfollowCompany,
1230
1266
  updateSyndicate: () => updateSyndicate,
1231
- updateSyndicateMember: () => updateSyndicateMember
1267
+ updateSyndicateMember: () => updateSyndicateMember,
1268
+ updateWebhookEndpoint: () => updateWebhookEndpoint
1232
1269
  });
1233
1270
 
1234
1271
  // src/generated/client.gen.ts
@@ -1236,6 +1273,12 @@ var client = createClient(createConfig());
1236
1273
 
1237
1274
  // src/generated/sdk.gen.ts
1238
1275
  var listOfferings = (options) => (options?.client ?? client).get({
1276
+ querySerializer: {
1277
+ parameters: {
1278
+ industry: { array: { explode: false } },
1279
+ business_model: { array: { explode: false } }
1280
+ }
1281
+ },
1239
1282
  security: [{ scheme: "bearer", type: "http" }],
1240
1283
  url: "/explore",
1241
1284
  ...options
@@ -1245,16 +1288,87 @@ var getOffering = (options) => (options.client ?? client).get({
1245
1288
  url: "/offerings/{external_id}",
1246
1289
  ...options
1247
1290
  });
1291
+ var getCompanyDisclosures = (options) => (options.client ?? client).get({
1292
+ querySerializer: {
1293
+ parameters: { sections: { array: { explode: false } } }
1294
+ },
1295
+ security: [{ scheme: "bearer", type: "http" }],
1296
+ url: "/companies/{id}/disclosures",
1297
+ ...options
1298
+ });
1299
+ var getCompanyPitch = (options) => (options.client ?? client).get({
1300
+ querySerializer: {
1301
+ parameters: { sections: { array: { explode: false } } }
1302
+ },
1303
+ security: [{ scheme: "bearer", type: "http" }],
1304
+ url: "/companies/{id}/pitch",
1305
+ ...options
1306
+ });
1307
+ var listCompanyUpdates = (options) => (options.client ?? client).get({
1308
+ security: [{ scheme: "bearer", type: "http" }],
1309
+ url: "/companies/{id}/updates",
1310
+ ...options
1311
+ });
1312
+ var getCompanyUpdate = (options) => (options.client ?? client).get({
1313
+ security: [{ scheme: "bearer", type: "http" }],
1314
+ url: "/companies/{id}/updates/{update_id}",
1315
+ ...options
1316
+ });
1317
+ var listCompanyQuestions = (options) => (options.client ?? client).get({
1318
+ security: [{ scheme: "bearer", type: "http" }],
1319
+ url: "/companies/{id}/questions",
1320
+ ...options
1321
+ });
1322
+ var searchCompanies = (options) => (options.client ?? client).get({
1323
+ security: [{ scheme: "bearer", type: "http" }],
1324
+ url: "/companies/search",
1325
+ ...options
1326
+ });
1327
+ var getCompany = (options) => (options.client ?? client).get({
1328
+ security: [{ scheme: "bearer", type: "http" }],
1329
+ url: "/companies/{id}",
1330
+ ...options
1331
+ });
1248
1332
  var getCurrentUser = (options) => (options?.client ?? client).get({
1249
1333
  security: [{ scheme: "bearer", type: "http" }],
1250
1334
  url: "/users/me",
1251
1335
  ...options
1252
1336
  });
1337
+ var listMyCompanies = (options) => (options?.client ?? client).get({
1338
+ security: [{ scheme: "bearer", type: "http" }],
1339
+ url: "/users/me/companies",
1340
+ ...options
1341
+ });
1342
+ var listFollowedCompanies = (options) => (options?.client ?? client).get({
1343
+ security: [{ scheme: "bearer", type: "http" }],
1344
+ url: "/users/me/follows",
1345
+ ...options
1346
+ });
1347
+ var unfollowCompany = (options) => (options.client ?? client).delete({
1348
+ security: [{ scheme: "bearer", type: "http" }],
1349
+ url: "/users/me/follows/{company_id}",
1350
+ ...options
1351
+ });
1352
+ var followCompany = (options) => (options.client ?? client).put({
1353
+ security: [{ scheme: "bearer", type: "http" }],
1354
+ url: "/users/me/follows/{company_id}",
1355
+ ...options
1356
+ });
1253
1357
  var listInvestments = (options) => (options?.client ?? client).get({
1254
1358
  security: [{ scheme: "bearer", type: "http" }],
1255
1359
  url: "/investments",
1256
1360
  ...options
1257
1361
  });
1362
+ var getInvestment = (options) => (options.client ?? client).get({
1363
+ security: [{ scheme: "bearer", type: "http" }],
1364
+ url: "/investments/{id}",
1365
+ ...options
1366
+ });
1367
+ var getOfferingStats = (options) => (options.client ?? client).get({
1368
+ security: [{ scheme: "bearer", type: "http" }],
1369
+ url: "/offerings/{offering_id}/stats",
1370
+ ...options
1371
+ });
1258
1372
  var getPortfolio = (options) => (options?.client ?? client).get({
1259
1373
  security: [{ scheme: "bearer", type: "http" }],
1260
1374
  url: "/portfolio",
@@ -1406,6 +1520,15 @@ var finalizeSyndicateDeal = (options) => (options.client ?? client).post({
1406
1520
  url: "/syndicates/{syndicate_id}/deals/{fundraise_id}/finalize",
1407
1521
  ...options
1408
1522
  });
1523
+ var previewIntent = (options) => (options.client ?? client).post({
1524
+ security: [{ scheme: "bearer", type: "http" }],
1525
+ url: "/intents/preview",
1526
+ ...options,
1527
+ headers: {
1528
+ "Content-Type": "application/json",
1529
+ ...options.headers
1530
+ }
1531
+ });
1409
1532
  var listIntents = (options) => (options?.client ?? client).get({
1410
1533
  security: [{ scheme: "bearer", type: "http" }],
1411
1534
  url: "/intents",
@@ -1450,6 +1573,96 @@ var listAttributedInvestments = (options) => (options.client ?? client).get({
1450
1573
  url: "/campaigns/{campaign_id}/attribution/investments",
1451
1574
  ...options
1452
1575
  });
1576
+ var listInstallations = (options) => (options?.client ?? client).get({
1577
+ security: [{ scheme: "bearer", type: "http" }],
1578
+ url: "/installations",
1579
+ ...options
1580
+ });
1581
+ var createInstallation = (options) => (options.client ?? client).post({
1582
+ security: [{ scheme: "bearer", type: "http" }],
1583
+ url: "/installations",
1584
+ ...options,
1585
+ headers: {
1586
+ "Content-Type": "application/json",
1587
+ ...options.headers
1588
+ }
1589
+ });
1590
+ var listEligibleInstallTargets = (options) => (options?.client ?? client).get({
1591
+ security: [{ scheme: "bearer", type: "http" }],
1592
+ url: "/installations/eligible",
1593
+ ...options
1594
+ });
1595
+ var revokeInstallation = (options) => (options.client ?? client).delete({
1596
+ security: [{ scheme: "bearer", type: "http" }],
1597
+ url: "/installations/{external_id}",
1598
+ ...options
1599
+ });
1600
+ var getInstallation = (options) => (options.client ?? client).get({
1601
+ security: [{ scheme: "bearer", type: "http" }],
1602
+ url: "/installations/{external_id}",
1603
+ ...options
1604
+ });
1605
+ var createInstallationToken = (options) => (options.client ?? client).post({
1606
+ security: [{ scheme: "bearer", type: "http" }],
1607
+ url: "/installations/{external_id}/tokens",
1608
+ ...options,
1609
+ headers: {
1610
+ "Content-Type": "application/json",
1611
+ ...options.headers
1612
+ }
1613
+ });
1614
+ var listWebhookEndpoints = (options) => (options?.client ?? client).get({
1615
+ security: [{ scheme: "bearer", type: "http" }],
1616
+ url: "/webhook_endpoints",
1617
+ ...options
1618
+ });
1619
+ var createWebhookEndpoint = (options) => (options.client ?? client).post({
1620
+ security: [{ scheme: "bearer", type: "http" }],
1621
+ url: "/webhook_endpoints",
1622
+ ...options,
1623
+ headers: {
1624
+ "Content-Type": "application/json",
1625
+ ...options.headers
1626
+ }
1627
+ });
1628
+ var deleteWebhookEndpoint = (options) => (options.client ?? client).delete({
1629
+ security: [{ scheme: "bearer", type: "http" }],
1630
+ url: "/webhook_endpoints/{external_id}",
1631
+ ...options
1632
+ });
1633
+ var getWebhookEndpoint = (options) => (options.client ?? client).get({
1634
+ security: [{ scheme: "bearer", type: "http" }],
1635
+ url: "/webhook_endpoints/{external_id}",
1636
+ ...options
1637
+ });
1638
+ var updateWebhookEndpoint = (options) => (options.client ?? client).patch({
1639
+ security: [{ scheme: "bearer", type: "http" }],
1640
+ url: "/webhook_endpoints/{external_id}",
1641
+ ...options,
1642
+ headers: {
1643
+ "Content-Type": "application/json",
1644
+ ...options.headers
1645
+ }
1646
+ });
1647
+ var rotateWebhookEndpointSecret = (options) => (options.client ?? client).post({
1648
+ security: [{ scheme: "bearer", type: "http" }],
1649
+ url: "/webhook_endpoints/{external_id}/rotate_secret",
1650
+ ...options
1651
+ });
1652
+ var reenableWebhookEndpoint = (options) => (options.client ?? client).post({
1653
+ security: [{ scheme: "bearer", type: "http" }],
1654
+ url: "/webhook_endpoints/{external_id}/reenable",
1655
+ ...options
1656
+ });
1657
+ var testWebhookEndpoint = (options) => (options.client ?? client).post({
1658
+ security: [{ scheme: "bearer", type: "http" }],
1659
+ url: "/webhook_endpoints/{external_id}/test",
1660
+ ...options,
1661
+ headers: {
1662
+ "Content-Type": "application/json",
1663
+ ...options.headers
1664
+ }
1665
+ });
1453
1666
  var listWebhookSubscriptions = (options) => (options.client ?? client).get({
1454
1667
  security: [{ scheme: "bearer", type: "http" }],
1455
1668
  url: "/campaigns/{campaign_id}/attribution/webhooks",
@@ -1667,10 +1880,17 @@ var Wefunder = class _Wefunder {
1667
1880
  getOffering({ client: this.#client, path: { external_id: externalId } })
1668
1881
  )
1669
1882
  };
1883
+ /**
1884
+ * Investments (`read:investments`). `list` is a bootstrap listing; pass `updated_since`
1885
+ * or a saved `cursor` for sync mode (`meta.mode === "delta"`). Persist `meta.next_cursor`
1886
+ * from your last page — it is always present — and resume from it next time.
1887
+ */
1670
1888
  investments = {
1671
1889
  list: this.#page(listInvestments),
1672
- all: () => paginate((cursor) => this.investments.list({ cursor })),
1673
- collect: () => collect((cursor) => this.investments.list({ cursor }))
1890
+ all: (query) => paginate((cursor) => this.investments.list({ ...query, cursor })),
1891
+ collect: (query) => collect((cursor) => this.investments.list({ ...query, cursor })),
1892
+ /** The current record (not the published projection) for one investment (`inv_…`). */
1893
+ get: (id) => this.#unwrapData(getInvestment({ client: this.#client, path: { id } }))
1674
1894
  };
1675
1895
  portfolio = {
1676
1896
  get: (query) => this.#unwrapData(getPortfolio({ client: this.#client, query })),
@@ -1700,17 +1920,76 @@ var Wefunder = class _Wefunder {
1700
1920
  attribution = {
1701
1921
  me: () => this.#unwrapData(getAttributionMe({ client: this.#client }))
1702
1922
  };
1923
+ /**
1924
+ * Webhook endpoints (`/webhook_endpoints`, scopes `read:webhooks` / `write:webhooks`).
1925
+ * Endpoints belong to your application and are managed through the LIVE API —
1926
+ * create sandbox receivers there with `mode: "test"`; they're mirrored into
1927
+ * sandbox. The signing `secret` is returned ONLY by `create` and `rotateSecret`.
1928
+ * Verify deliveries with `constructEvent` (see the webhooks module).
1929
+ */
1930
+ webhookEndpoints = {
1931
+ /** All of your app's endpoints (secret omitted). `meta.quota` is the per-app limit. */
1932
+ list: () => this.#unwrap(listWebhookEndpoints({ client: this.#client })),
1933
+ get: (id) => this.#unwrapData(
1934
+ getWebhookEndpoint({ client: this.#client, path: { external_id: id } })
1935
+ ),
1936
+ /** Store `attributes.secret` from the result — it is never shown again. */
1937
+ create: (input) => this.#unwrapData(createWebhookEndpoint({ client: this.#client, body: input })),
1938
+ /** `events` replaces the subscription list wholesale (no merge); omit to leave unchanged. */
1939
+ update: (id, input) => this.#unwrapData(
1940
+ updateWebhookEndpoint({ client: this.#client, path: { external_id: id }, body: input })
1941
+ ),
1942
+ /** Stops deliveries immediately. Removal is permanent — create a new endpoint to resume. */
1943
+ remove: (id) => this.#unwrapData(
1944
+ deleteWebhookEndpoint({ client: this.#client, path: { external_id: id } })
1945
+ ),
1946
+ /**
1947
+ * New secret (shown once). The old one keeps signing for a 24h overlap — deliveries
1948
+ * carry a `v1` for both, and `constructEvent` accepts either.
1949
+ */
1950
+ rotateSecret: (id) => this.#unwrapData(
1951
+ rotateWebhookEndpointSecret({ client: this.#client, path: { external_id: id } })
1952
+ ),
1953
+ /** Recover an auto-disabled endpoint after fixing your server. Idempotent. */
1954
+ reenable: (id) => this.#unwrapData(
1955
+ reenableWebhookEndpoint({ client: this.#client, path: { external_id: id } })
1956
+ ),
1957
+ /**
1958
+ * POST a real, signed example event at the endpoint (production envelope + signature)
1959
+ * and report the outcome inline. Defaults to the endpoint's first subscribed event.
1960
+ * Doesn't affect delivery health. Rate-limited to 10/min per endpoint.
1961
+ */
1962
+ test: (id, event) => this.#unwrapData(
1963
+ testWebhookEndpoint({
1964
+ client: this.#client,
1965
+ path: { external_id: id },
1966
+ body: event ? { event } : void 0
1967
+ })
1968
+ )
1969
+ };
1703
1970
  };
1704
1971
 
1705
1972
  // src/webhooks.ts
1706
1973
  var import_node_crypto2 = require("crypto");
1707
- var SIGNATURE_HEADER = "x-wefunder-signature";
1708
- var TIMESTAMP_HEADER = "x-wefunder-timestamp";
1709
- var EVENT_HEADER = "x-wefunder-event";
1710
- var DELIVERY_ID_HEADER = "x-wefunder-delivery-id";
1711
- function computeSignature(secret, timestamp, payload) {
1712
- const mac = (0, import_node_crypto2.createHmac)("sha256", secret).update(`${timestamp}.${payload}`).digest("hex");
1713
- return `sha256=${mac}`;
1974
+ var SIGNATURE_HEADER = "wefunder-signature";
1975
+ var DEFAULT_TOLERANCE_SECONDS = 300;
1976
+ var LEGACY_SIGNATURE_HEADER = "x-wefunder-signature";
1977
+ var LEGACY_TIMESTAMP_HEADER = "x-wefunder-timestamp";
1978
+ var LEGACY_EVENT_HEADER = "x-wefunder-event";
1979
+ var LEGACY_DELIVERY_ID_HEADER = "x-wefunder-delivery-id";
1980
+ var WebhookSignatureError = class extends Error {
1981
+ reason;
1982
+ constructor(reason, message) {
1983
+ super(message);
1984
+ this.name = "WebhookSignatureError";
1985
+ this.reason = reason;
1986
+ }
1987
+ };
1988
+ function bodyBuffer(payload) {
1989
+ return typeof payload === "string" ? Buffer.from(payload, "utf8") : Buffer.from(payload);
1990
+ }
1991
+ function computeWebhookSignature(secret, timestamp, payload) {
1992
+ return (0, import_node_crypto2.createHmac)("sha256", secret).update(`${timestamp}.`).update(bodyBuffer(payload)).digest("hex");
1714
1993
  }
1715
1994
  function constantTimeEqual(a, b) {
1716
1995
  const ab = Buffer.from(a);
@@ -1718,42 +1997,142 @@ function constantTimeEqual(a, b) {
1718
1997
  if (ab.length !== bb.length) return false;
1719
1998
  return (0, import_node_crypto2.timingSafeEqual)(ab, bb);
1720
1999
  }
2000
+ function parseSignatureHeader(header) {
2001
+ let timestamp;
2002
+ const signatures = [];
2003
+ for (const part of header.split(",")) {
2004
+ const eq = part.indexOf("=");
2005
+ if (eq < 0) continue;
2006
+ const key = part.slice(0, eq).trim();
2007
+ const value = part.slice(eq + 1).trim();
2008
+ if (key === "t") {
2009
+ if (!/^\d+$/.test(value)) return null;
2010
+ timestamp = Number(value);
2011
+ } else if (key === "v1" && value) {
2012
+ signatures.push(value);
2013
+ }
2014
+ }
2015
+ if (timestamp === void 0 || signatures.length === 0) return null;
2016
+ return { timestamp, signatures };
2017
+ }
2018
+ function signWebhook(opts) {
2019
+ const t = opts.timestamp ?? Math.floor(Date.now() / 1e3);
2020
+ const secrets = [opts.secret, ...opts.additionalSecrets ?? []];
2021
+ const entries = secrets.map((s) => `v1=${computeWebhookSignature(s, t, opts.payload)}`);
2022
+ return [`t=${t}`, ...entries].join(",");
2023
+ }
2024
+ function checkWebhookSignature(opts) {
2025
+ const parsed = parseSignatureHeader(opts.header);
2026
+ if (!parsed) return "malformed_header";
2027
+ const tolerance = opts.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
2028
+ if (tolerance > 0) {
2029
+ const nowSec = (opts.now ?? Date.now)() / 1e3;
2030
+ if (Math.abs(nowSec - parsed.timestamp) > tolerance) return "timestamp_out_of_tolerance";
2031
+ }
2032
+ const expected = computeWebhookSignature(opts.secret, parsed.timestamp, opts.payload);
2033
+ return parsed.signatures.some((sig) => constantTimeEqual(sig, expected)) ? null : "signature_mismatch";
2034
+ }
1721
2035
  function verifyWebhook(opts) {
1722
- const tolerance = opts.toleranceSeconds ?? 300;
1723
- const now = (opts.now ?? Date.now)();
2036
+ if ("header" in opts) return checkWebhookSignature(opts) === null;
2037
+ return verifyLegacyWebhook(opts);
2038
+ }
2039
+ function verifyLegacyWebhook(opts) {
2040
+ const tolerance = opts.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
1724
2041
  const ts = Number(opts.timestamp);
1725
2042
  if (!Number.isFinite(ts)) return false;
1726
- if (tolerance > 0 && Math.abs(now / 1e3 - ts) > tolerance) return false;
1727
- const expected = computeSignature(opts.secret, opts.timestamp, opts.payload);
2043
+ if (tolerance > 0 && Math.abs((opts.now ?? Date.now)() / 1e3 - ts) > tolerance) return false;
2044
+ const expected = `sha256=${computeWebhookSignature(opts.secret, opts.timestamp, opts.payload)}`;
1728
2045
  return constantTimeEqual(opts.signature, expected);
1729
2046
  }
1730
- function constructEvent(payload, headers, secret, opts) {
1731
- const get = (name) => {
1732
- if (headers instanceof Headers) return headers.get(name) ?? void 0;
1733
- const v = headers[name] ?? headers[name.toLowerCase()];
2047
+ function headerGetter(headers) {
2048
+ if (typeof headers === "string") {
2049
+ return (name) => name === SIGNATURE_HEADER ? headers : void 0;
2050
+ }
2051
+ if (headers instanceof Headers) return (name) => headers.get(name) ?? void 0;
2052
+ return (name) => {
2053
+ let v = headers[name];
2054
+ if (v === void 0) {
2055
+ const hit = Object.keys(headers).find((k) => k.toLowerCase() === name);
2056
+ if (hit) v = headers[hit];
2057
+ }
1734
2058
  return Array.isArray(v) ? v[0] : v;
1735
2059
  };
1736
- const signature = get(SIGNATURE_HEADER);
1737
- const timestamp = get(TIMESTAMP_HEADER);
1738
- if (!signature || !timestamp) {
1739
- throw new Error("Missing X-Wefunder-Signature or X-Wefunder-Timestamp header");
1740
- }
1741
- const ok = verifyWebhook({
1742
- payload,
1743
- signature,
1744
- timestamp,
1745
- secret,
1746
- toleranceSeconds: opts?.toleranceSeconds,
1747
- now: opts?.now
1748
- });
1749
- if (!ok) throw new Error("Webhook signature verification failed");
2060
+ }
2061
+ function constructEvent(payload, headers, secret, opts) {
2062
+ const get = headerGetter(headers);
2063
+ const header = get(SIGNATURE_HEADER);
2064
+ if (!header) {
2065
+ const legacySig = get(LEGACY_SIGNATURE_HEADER);
2066
+ const legacyTs = get(LEGACY_TIMESTAMP_HEADER);
2067
+ if (legacySig && legacyTs) return constructLegacyEvent(payload, get, legacySig, legacyTs, secret, opts);
2068
+ throw new WebhookSignatureError("missing_header", describeFailure("missing_header"));
2069
+ }
2070
+ const failure = checkWebhookSignature({ payload, header, secret, ...opts });
2071
+ if (failure) throw new WebhookSignatureError(failure, describeFailure(failure));
2072
+ const parsed = parseSignatureHeader(header);
2073
+ const env = parseEnvelope(payload);
1750
2074
  return {
1751
- event: get(EVENT_HEADER) ?? "",
1752
- deliveryId: get(DELIVERY_ID_HEADER) ?? "",
1753
- timestamp: Number(timestamp),
1754
- data: JSON.parse(payload)
2075
+ id: String(env.id ?? ""),
2076
+ event: String(env.event ?? ""),
2077
+ created_at: String(env.created_at ?? ""),
2078
+ mode: env.mode === "test" ? "test" : "live",
2079
+ data: env.data,
2080
+ timestamp: parsed.timestamp
1755
2081
  };
1756
2082
  }
2083
+ function constructLegacyEvent(payload, get, signature, timestamp, secret, opts) {
2084
+ if (!verifyLegacyWebhook({ payload, signature, timestamp, secret, ...opts })) {
2085
+ throw new WebhookSignatureError("signature_mismatch", describeFailure("signature_mismatch"));
2086
+ }
2087
+ const env = parseEnvelope(payload);
2088
+ return {
2089
+ id: String(env.id ?? get(LEGACY_DELIVERY_ID_HEADER) ?? ""),
2090
+ event: String(env.event ?? get(LEGACY_EVENT_HEADER) ?? ""),
2091
+ created_at: String(env.created_at ?? ""),
2092
+ mode: env.mode === "test" ? "test" : "live",
2093
+ data: env.data ?? env,
2094
+ timestamp: Number(timestamp)
2095
+ };
2096
+ }
2097
+ function parseEnvelope(payload) {
2098
+ try {
2099
+ const parsed = JSON.parse(bodyBuffer(payload).toString("utf8"));
2100
+ if (parsed === null || typeof parsed !== "object") throw new Error("not an object");
2101
+ return parsed;
2102
+ } catch {
2103
+ throw new WebhookSignatureError("invalid_payload", describeFailure("invalid_payload"));
2104
+ }
2105
+ }
2106
+ function describeFailure(reason) {
2107
+ switch (reason) {
2108
+ case "missing_header":
2109
+ return "Missing Wefunder-Signature header";
2110
+ case "malformed_header":
2111
+ return "Wefunder-Signature header is malformed (expected t=<unix>,v1=<hex>)";
2112
+ case "timestamp_out_of_tolerance":
2113
+ return "Webhook timestamp is outside the replay tolerance";
2114
+ case "signature_mismatch":
2115
+ return "Webhook signature verification failed";
2116
+ case "invalid_payload":
2117
+ return "Webhook body is not a JSON object";
2118
+ }
2119
+ }
2120
+ async function constructEventFromRequest(request, secret, opts) {
2121
+ const payload = await request.text();
2122
+ return constructEvent(payload, request.headers, secret, opts);
2123
+ }
2124
+ async function dispatchWebhook(event, handlers) {
2125
+ const specific = event.event === "default" ? void 0 : handlers[event.event];
2126
+ if (specific) {
2127
+ await specific(event);
2128
+ return true;
2129
+ }
2130
+ if (handlers.default) {
2131
+ await handlers.default(event);
2132
+ return true;
2133
+ }
2134
+ return false;
2135
+ }
1757
2136
  // Annotate the CommonJS export names for ESM import in node:
1758
2137
  0 && (module.exports = {
1759
2138
  DEFAULT_API_BASE_URL,
@@ -1761,24 +2140,33 @@ function constructEvent(payload, headers, secret, opts) {
1761
2140
  DEFAULT_AUTHORIZE_BASE_URL,
1762
2141
  DEFAULT_OAUTH_BASE_URL,
1763
2142
  DEFAULT_TOKEN_BASE_URL,
1764
- DELIVERY_ID_HEADER,
1765
- EVENT_HEADER,
2143
+ DEFAULT_TOLERANCE_SECONDS,
2144
+ LEGACY_DELIVERY_ID_HEADER,
2145
+ LEGACY_EVENT_HEADER,
2146
+ LEGACY_SIGNATURE_HEADER,
2147
+ LEGACY_TIMESTAMP_HEADER,
1766
2148
  REQUEST_ID_HEADER,
1767
2149
  SANDBOX_AUTHORIZE_BASE_URL,
1768
2150
  SIGNATURE_HEADER,
1769
- TIMESTAMP_HEADER,
2151
+ WebhookSignatureError,
1770
2152
  Wefunder,
1771
2153
  WefunderAuthError,
1772
2154
  WefunderError,
2155
+ checkWebhookSignature,
1773
2156
  clientCredentialsGrant,
1774
2157
  collect,
2158
+ computeWebhookSignature,
1775
2159
  constructEvent,
2160
+ constructEventFromRequest,
1776
2161
  createAuthorizationUrl,
2162
+ dispatchWebhook,
1777
2163
  exchangeCode,
1778
2164
  generatePkce,
1779
2165
  modeForToken,
1780
2166
  paginate,
2167
+ parseSignatureHeader,
1781
2168
  refreshToken,
2169
+ signWebhook,
1782
2170
  verifyWebhook
1783
2171
  });
1784
2172
  //# sourceMappingURL=index.cjs.map