@edge-markets/connect-node 1.6.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,16 +1,23 @@
1
1
  import * as node_https from 'node:https';
2
- import { EdgeEnvironment, TransferCategory, User, VerifyIdentityOptions, VerifyIdentityResult, Balance, Transfer, ListTransfersParams, TransferList, CreateVerificationSessionRequest, VerificationSession, VerificationSessionStatusResponse, EdgeTokens } from '@edge-markets/connect';
3
- export { Balance, CreateVerificationSessionRequest, EdgeApiError, EdgeAuthenticationError, EdgeConsentRequiredError, EdgeEnvironment, EdgeError, EdgeIdentityVerificationError, EdgeInsufficientScopeError, EdgeNetworkError, EdgeNotFoundError, EdgeTokenExchangeError, EdgeTokens, ListTransfersParams, SdkGeolocation, TRANSFER_CATEGORIES, Transfer, TransferCategory, TransferList, TransferListItem, TransferStatus, TransferType, User, UserAddress, VerificationSession, VerificationSessionStatus, VerificationSessionStatusResponse, VerifyIdentityAddress, VerifyIdentityOptions, VerifyIdentityResult, VerifyIdentityScores, getEnvironmentConfig, isApiError, isAuthenticationError, isConsentRequiredError, isEdgeError, isIdentityVerificationError, isNetworkError, isProductionEnvironment } from '@edge-markets/connect';
2
+ import { EdgeEnvironment, TransferCategory, Transfer, VerificationSession, TransferType, User, VerifyIdentityOptions, VerifyIdentityResult, Balance, ListTransfersParams, TransferList, CreateVerificationSessionRequest, VerificationSessionStatusResponse, EdgeTokens, EdgeWebhookEvent, TransferStatus, EdgeWebhookEventType } from '@edge-markets/connect';
3
+ export { Balance, BaseWebhookEvent, ConsentRevokedEventData, CreateVerificationSessionRequest, EDGE_WEBHOOK_EVENT_TYPES, EdgeApiError, EdgeAuthenticationError, EdgeConsentRequiredError, EdgeEnvironment, EdgeError, EdgeIdentityVerificationError, EdgeInsufficientScopeError, EdgeNetworkError, EdgeNotFoundError, EdgeTokenExchangeError, EdgeTokens, EdgeValidationError, EdgeWebhookEvent, EdgeWebhookEventType, ListTransfersParams, SdkGeolocation, TRANSFER_CATEGORIES, Transfer, TransferCategory, TransferCompletedEventData, TransferExpiredEventData, TransferFailedEventData, TransferList, TransferListItem, TransferProcessingEventData, TransferStatus, TransferType, User, UserAddress, VerificationSession, VerificationSessionStatus, VerificationSessionStatusResponse, VerifyIdentityAddress, VerifyIdentityOptions, VerifyIdentityResult, VerifyIdentityScores, getEnvironmentConfig, isApiError, isAuthenticationError, isConsentRequiredError, isEdgeError, isIdentityVerificationError, isNetworkError, isProductionEnvironment } from '@edge-markets/connect';
4
4
 
5
+ type RequestEndpoint = 'connect_api' | 'oauth_token' | 'partner_webhook_sync';
5
6
  interface RequestInfo {
6
7
  method: string;
7
8
  url: string;
8
9
  body?: unknown;
10
+ endpoint?: RequestEndpoint;
11
+ attempt?: number;
9
12
  }
10
13
  interface ResponseInfo {
11
14
  status: number;
12
15
  body: unknown;
13
16
  durationMs: number;
17
+ method?: string;
18
+ url?: string;
19
+ endpoint?: RequestEndpoint;
20
+ attempt?: number;
14
21
  }
15
22
  interface RetryConfig {
16
23
  maxRetries?: number;
@@ -23,6 +30,17 @@ interface EdgeConnectServerConfig {
23
30
  clientSecret: string;
24
31
  environment: EdgeEnvironment;
25
32
  apiBaseUrl?: string;
33
+ /**
34
+ * Override the partner dashboard API base URL.
35
+ *
36
+ * Most integrations should omit this. The SDK derives the dashboard base
37
+ * from `apiBaseUrl` by replacing `/connect/v1` with `/v1`, matching the
38
+ * EDGE Connect gateway layout:
39
+ *
40
+ * - user-scoped API: `https://connect-staging.edgeboost.io/connect/v1`
41
+ * - partner dashboard API: `https://connect-staging.edgeboost.io/v1`
42
+ */
43
+ partnerApiBaseUrl?: string;
26
44
  oauthBaseUrl?: string;
27
45
  /**
28
46
  * @deprecated cognitoDomain is no longer used. Token exchange now goes through EdgeBoost API.
@@ -42,12 +60,40 @@ interface EdgeConnectServerConfig {
42
60
  strictResponseEncryption?: boolean;
43
61
  };
44
62
  mtls?: MtlsConfig;
63
+ /**
64
+ * Partner-level OAuth client ID for the `client_credentials` grant.
65
+ *
66
+ * Required ONLY when calling {@link EdgeConnectServer.syncWebhookEvents}.
67
+ * This is a different OAuth client than {@link clientId} — it authenticates
68
+ * your backend (not your users) for partner-scoped surfaces like the
69
+ * webhook reconciliation sync endpoint.
70
+ *
71
+ * If omitted, `syncWebhookEvents()` throws on first call but the rest of
72
+ * the SDK (token exchange, user-scoped API calls) continues to work.
73
+ */
74
+ partnerClientId?: string;
75
+ /**
76
+ * Partner-level OAuth client secret paired with {@link partnerClientId}.
77
+ *
78
+ * Required ONLY when calling {@link EdgeConnectServer.syncWebhookEvents}.
79
+ */
80
+ partnerClientSecret?: string;
45
81
  }
46
82
  interface MtlsConfig {
47
83
  enabled: boolean;
84
+ /** Client certificate PEM used to authenticate this partner to EDGE Connect. */
48
85
  cert: string;
86
+ /** Client private key PEM paired with `cert`. */
49
87
  key: string;
50
- ca?: string;
88
+ /**
89
+ * Optional additional server trust root(s), as PEM text.
90
+ *
91
+ * The SDK appends these certificates to Node's default public trust store
92
+ * instead of replacing it. Public EDGE Connect gateways use public ACM
93
+ * certificates, so most partners should omit this unless EDGE provides a
94
+ * private server CA for a non-public endpoint.
95
+ */
96
+ ca?: string | string[];
51
97
  }
52
98
  interface TransferOptions {
53
99
  type: 'debit' | 'credit';
@@ -57,6 +103,38 @@ interface TransferOptions {
57
103
  category?: TransferCategory;
58
104
  }
59
105
 
106
+ interface TransferAmountLimits {
107
+ min?: string | number;
108
+ max?: string | number;
109
+ }
110
+ interface CreateTransferIdempotencyKeyOptions {
111
+ partnerUserId: string;
112
+ type: TransferType;
113
+ amount: string | number;
114
+ category?: TransferCategory;
115
+ externalId?: string;
116
+ nonce?: string;
117
+ namespace?: string;
118
+ }
119
+ interface TransferIntent {
120
+ type: TransferType;
121
+ amount: string | number;
122
+ category?: TransferCategory | null;
123
+ }
124
+ interface StartTransferVerificationOptions {
125
+ transfer: TransferOptions;
126
+ origin: string;
127
+ }
128
+ interface StartTransferVerificationResult {
129
+ transfer: Transfer;
130
+ verificationSession: VerificationSession;
131
+ }
132
+ declare function normalizeMoneyAmount(value: string | number): string;
133
+ declare function validateTransferAmount(value: string | number, limits?: TransferAmountLimits): string;
134
+ declare function createTransferIdempotencyKey(options: CreateTransferIdempotencyKeyOptions): string;
135
+ declare function assertSameTransferIntent(existing: TransferIntent, requested: TransferIntent): void;
136
+ declare function startTransferVerification(client: Pick<EdgeUserClient, 'initiateTransfer' | 'createVerificationSession'>, options: StartTransferVerificationOptions): Promise<StartTransferVerificationResult>;
137
+
60
138
  /**
61
139
  * A lightweight, user-scoped API client created via {@link EdgeConnectServer.forUser}.
62
140
  *
@@ -71,6 +149,7 @@ declare class EdgeUserClient {
71
149
  verifyIdentity(options: VerifyIdentityOptions): Promise<VerifyIdentityResult>;
72
150
  getBalance(): Promise<Balance>;
73
151
  initiateTransfer(options: TransferOptions): Promise<Transfer>;
152
+ startTransferVerification(options: StartTransferVerificationOptions): Promise<StartTransferVerificationResult>;
74
153
  getTransfer(transferId: string): Promise<Transfer>;
75
154
  listTransfers(params?: ListTransfersParams): Promise<TransferList>;
76
155
  revokeConsent(): Promise<{
@@ -89,14 +168,134 @@ declare class EdgeUserClient {
89
168
  getVerificationSessionStatus(transferId: string, sessionId: string): Promise<VerificationSessionStatusResponse>;
90
169
  }
91
170
 
171
+ type EdgeTokenVaultKeyMaterial = string | Buffer | Uint8Array;
172
+ interface EdgeTokenVaultKey {
173
+ id: string;
174
+ key: EdgeTokenVaultKeyMaterial;
175
+ }
176
+ interface EdgeTokenVaultOptions {
177
+ currentKey: EdgeTokenVaultKey;
178
+ previousKeys?: EdgeTokenVaultKey[];
179
+ }
180
+ declare class EdgeTokenVault {
181
+ private readonly currentKey;
182
+ private readonly keysById;
183
+ constructor(options: EdgeTokenVaultOptions);
184
+ encryptTokens(tokens: EdgeTokens): string;
185
+ decryptTokens(envelope: string): EdgeTokens;
186
+ isEncrypted(value: unknown): value is string;
187
+ }
188
+ declare function createEdgeTokenVault(options: EdgeTokenVaultOptions): EdgeTokenVault;
189
+ declare function isEdgeTokenVaultEnvelope(value: unknown): value is string;
190
+
191
+ type EdgeTokenStoreValue = EdgeTokens | string;
192
+ interface EdgeTokenStoreSaveContext {
193
+ tokens: EdgeTokens;
194
+ refreshed: boolean;
195
+ }
196
+ interface EdgeTokenStore {
197
+ load: (subjectId: string) => Promise<EdgeTokenStoreValue | null | undefined> | EdgeTokenStoreValue | null | undefined;
198
+ save: (subjectId: string, value: EdgeTokenStoreValue, context: EdgeTokenStoreSaveContext) => Promise<void> | void;
199
+ }
200
+ interface EdgeUserSessionOptions {
201
+ edge: Pick<EdgeConnectServer, 'forUser' | 'refreshTokens'>;
202
+ subjectId: string;
203
+ tokenStore: EdgeTokenStore;
204
+ tokenVault?: Pick<EdgeTokenVault, 'encryptTokens' | 'decryptTokens' | 'isEncrypted'>;
205
+ refreshSkewMs?: number;
206
+ now?: () => number;
207
+ onRefresh?: (tokens: EdgeTokens) => Promise<void> | void;
208
+ }
209
+ interface GetValidTokensOptions {
210
+ forceRefresh?: boolean;
211
+ }
212
+ declare class EdgeUserSession {
213
+ private readonly edge;
214
+ private readonly subjectId;
215
+ private readonly tokenStore;
216
+ private readonly tokenVault?;
217
+ private readonly refreshSkewMs;
218
+ private readonly now;
219
+ private readonly onRefresh?;
220
+ constructor(options: EdgeUserSessionOptions);
221
+ getValidTokens(options?: GetValidTokensOptions): Promise<EdgeTokens>;
222
+ getClient(options?: GetValidTokensOptions): Promise<EdgeUserClient>;
223
+ withClient<T>(callback: (client: EdgeUserClient, tokens: EdgeTokens) => Promise<T> | T): Promise<T>;
224
+ saveTokens(tokens: EdgeTokens, refreshed?: boolean): Promise<void>;
225
+ private loadTokens;
226
+ }
227
+ declare function createEdgeUserSession(options: EdgeUserSessionOptions): EdgeUserSession;
228
+
229
+ interface WebhookReconcilerCursorStore {
230
+ load: () => Promise<string | null | undefined> | string | null | undefined;
231
+ save: (cursor: string) => Promise<void> | void;
232
+ }
233
+ interface WebhookReconcilerOptions {
234
+ edge: Pick<EdgeConnectServer, 'syncWebhookEvents'>;
235
+ cursorStore: WebhookReconcilerCursorStore;
236
+ processEvent: (event: EdgeWebhookEvent) => Promise<void> | void;
237
+ limit?: SyncWebhookEventsOptions['limit'];
238
+ maxPages?: number;
239
+ }
240
+ interface WebhookReconcilerRunResult {
241
+ processed: number;
242
+ pages: number;
243
+ cursor?: string;
244
+ hasMore: boolean;
245
+ skipped: boolean;
246
+ reason?: 'already_running' | 'empty_page_with_has_more' | 'max_pages_reached';
247
+ }
248
+ declare class EdgeWebhookReconciler {
249
+ private readonly options;
250
+ private running;
251
+ constructor(options: WebhookReconcilerOptions);
252
+ run(): Promise<WebhookReconcilerRunResult>;
253
+ private runInternal;
254
+ }
255
+ declare function createWebhookReconciler(options: WebhookReconcilerOptions): EdgeWebhookReconciler;
256
+
257
+ /**
258
+ * Options for {@link EdgeConnectServer.syncWebhookEvents}.
259
+ */
260
+ interface SyncWebhookEventsOptions {
261
+ /**
262
+ * Cursor token. Pass the `id` of the last event you successfully processed
263
+ * to receive only newer events. Omit on the first call to start from the
264
+ * beginning of the 30-day server-side lookback window.
265
+ */
266
+ afterEventId?: string;
267
+ /**
268
+ * Maximum events per page (1–100). Defaults to 20 server-side.
269
+ */
270
+ limit?: number;
271
+ }
272
+ /**
273
+ * Result of {@link EdgeConnectServer.syncWebhookEvents}. Field names are
274
+ * normalized to camelCase from the wire format (`has_more` → `hasMore`).
275
+ */
276
+ interface SyncWebhookEventsResult {
277
+ /**
278
+ * Events newer than the supplied cursor, in ascending order. Each event
279
+ * is the unwrapped {@link EdgeWebhookEvent} payload (the SDK strips the
280
+ * sync API's outer envelope).
281
+ */
282
+ events: EdgeWebhookEvent[];
283
+ /**
284
+ * `true` if more events are available beyond this page. Loop while this
285
+ * is truthy, advancing your cursor to the `id` of the last processed event.
286
+ */
287
+ hasMore: boolean;
288
+ }
92
289
  declare class EdgeConnectServer {
93
290
  private readonly config;
94
291
  private readonly apiBaseUrl;
292
+ private readonly partnerApiBaseUrl;
95
293
  private readonly oauthBaseUrl;
96
294
  private readonly timeout;
97
295
  private readonly retryConfig;
98
296
  private readonly httpsAgent;
99
297
  private readonly dispatcher;
298
+ private partnerTokenCache;
100
299
  static getInstance(config: EdgeConnectServerConfig): EdgeConnectServer;
101
300
  static clearInstances(): void;
102
301
  constructor(config: EdgeConnectServerConfig);
@@ -112,10 +311,84 @@ declare class EdgeConnectServer {
112
311
  * or per user session and discard it when the token changes.
113
312
  */
114
313
  forUser(accessToken: string): EdgeUserClient;
314
+ createUserSession(options: Omit<EdgeUserSessionOptions, 'edge'>): EdgeUserSession;
115
315
  exchangeCode(code: string, codeVerifier: string, redirectUri?: string): Promise<EdgeTokens>;
116
316
  refreshTokens(refreshToken: string): Promise<EdgeTokens>;
117
317
  /** @internal Called by {@link EdgeUserClient} — not part of the public API. */
118
318
  _apiRequest<T>(method: string, path: string, accessToken: string, body?: unknown): Promise<T>;
319
+ /**
320
+ * Reconcile webhook events from the partner sync endpoint.
321
+ *
322
+ * Use this from a scheduled cron (every ~5 minutes is typical) to catch
323
+ * events your primary webhook receiver missed — partner downtime, retry
324
+ * exhaustion, dead-letter queue. This is the third reliability layer
325
+ * ("Leg 3") on top of HTTP delivery + DLQ.
326
+ *
327
+ * **Authentication.** This method uses the OAuth `client_credentials`
328
+ * grant against {@link EdgeConnectServerConfig.partnerClientId} and
329
+ * {@link EdgeConnectServerConfig.partnerClientSecret}. The token is
330
+ * cached internally and refreshed automatically.
331
+ *
332
+ * **Pagination.** Pass `afterEventId` with the last `id` you processed.
333
+ * Loop while `hasMore` is true. The server enforces a 30-day lookback;
334
+ * cursors older than 30 days return events from 30 days ago, not an error.
335
+ *
336
+ * @example
337
+ * ```typescript
338
+ * let cursor: string | undefined = await loadCursorFromDb()
339
+ *
340
+ * while (true) {
341
+ * const { events, hasMore } = await edge.syncWebhookEvents({ afterEventId: cursor })
342
+ * for (const event of events) {
343
+ * await processEvent(event)
344
+ * cursor = event.id
345
+ * }
346
+ * if (events.length > 0) await saveCursorToDb(cursor!)
347
+ * if (!hasMore) break
348
+ * }
349
+ * ```
350
+ *
351
+ * @throws {EdgeAuthenticationError} If partner credentials are missing
352
+ * or the partner token cannot be obtained / refreshed
353
+ * @throws {EdgeNetworkError} On network failure
354
+ * @throws {EdgeApiError} For other non-2xx responses after retries
355
+ */
356
+ syncWebhookEvents(options?: SyncWebhookEventsOptions): Promise<SyncWebhookEventsResult>;
357
+ /**
358
+ * Creates a cursor-based webhook reconciler bound to this SDK instance.
359
+ *
360
+ * Use this from cron jobs to process events returned by
361
+ * {@link EdgeConnectServer.syncWebhookEvents} without hand-writing
362
+ * pagination, cursor advancement, or process-level concurrency guards.
363
+ */
364
+ createWebhookReconciler(options: Omit<WebhookReconcilerOptions, 'edge'>): EdgeWebhookReconciler;
365
+ /**
366
+ * Builds the sync URL with normalized query parameters.
367
+ */
368
+ private normalizeSyncWebhookEventsOptions;
369
+ private buildSyncUrl;
370
+ /**
371
+ * Issues a GET to the sync endpoint with the partner Bearer token. Kept
372
+ * separate from `_apiRequest` because partner tokens have a different
373
+ * lifecycle (process-wide, client_credentials) than user tokens.
374
+ */
375
+ private partnerFetch;
376
+ /**
377
+ * Fetches (or returns the cached) partner token via the
378
+ * `client_credentials` grant. Caches with a 60s safety margin so the
379
+ * token is renewed *before* the server-side expiry, never racing it.
380
+ *
381
+ * Side-effects: writes `this.partnerTokenCache` on success.
382
+ */
383
+ private getPartnerToken;
384
+ /**
385
+ * @internal Test-only: clears the cached partner token so the next call
386
+ * to `syncWebhookEvents` re-fetches it. Not part of the public API.
387
+ */
388
+ _resetPartnerTokenCache(): void;
389
+ private redactPartnerTokenResponseBody;
390
+ private redactPartnerSyncResponseBody;
391
+ private drainResponseBody;
119
392
  private getRetryDelay;
120
393
  private sleep;
121
394
  private fetchWithTimeout;
@@ -124,4 +397,169 @@ declare class EdgeConnectServer {
124
397
  private handleApiErrorFromBody;
125
398
  }
126
399
 
127
- export { EdgeConnectServer, type EdgeConnectServerConfig, EdgeUserClient, type MtlsConfig, type RequestInfo, type ResponseInfo, type RetryConfig, type TransferOptions };
400
+ type EdgeConnectEnv = Record<string, string | undefined>;
401
+ interface CreateEdgeConnectServerFromEnvOptions {
402
+ env?: EdgeConnectEnv;
403
+ requireMtls?: boolean;
404
+ timeout?: number;
405
+ retry?: RetryConfig;
406
+ onRequest?: (info: RequestInfo) => void;
407
+ onResponse?: (info: ResponseInfo) => void;
408
+ mle?: EdgeConnectServerConfig['mle'];
409
+ }
410
+ interface EdgeConnectServerFromEnvCapabilities {
411
+ mtlsEnabled: boolean;
412
+ webhookSyncConfigured: boolean;
413
+ customApiBaseUrl: boolean;
414
+ customOAuthBaseUrl: boolean;
415
+ customPartnerApiBaseUrl: boolean;
416
+ }
417
+ interface EdgeConnectServerFromEnvResult {
418
+ server: EdgeConnectServer;
419
+ config: EdgeConnectServerConfig;
420
+ capabilities: EdgeConnectServerFromEnvCapabilities;
421
+ warnings: string[];
422
+ }
423
+ declare function createEdgeConnectServerFromEnv(options?: CreateEdgeConnectServerFromEnvOptions): EdgeConnectServerFromEnvResult;
424
+
425
+ interface EdgeProblemJson {
426
+ type: string;
427
+ title: string;
428
+ status: number;
429
+ detail: string;
430
+ code: string;
431
+ details?: Record<string, unknown>;
432
+ }
433
+ interface EdgeHttpError {
434
+ status: number;
435
+ body: EdgeProblemJson;
436
+ }
437
+ declare function toEdgeHttpError(error: unknown): EdgeHttpError;
438
+ declare function toEdgeProblemJson(error: unknown): EdgeProblemJson;
439
+
440
+ interface PartnerIdentityAddress {
441
+ street?: string | null;
442
+ line1?: string | null;
443
+ address1?: string | null;
444
+ city?: string | null;
445
+ state?: string | null;
446
+ zip?: string | null;
447
+ postalCode?: string | null;
448
+ }
449
+ interface PartnerIdentityProfile {
450
+ firstName?: string | null;
451
+ givenName?: string | null;
452
+ lastName?: string | null;
453
+ familyName?: string | null;
454
+ address?: PartnerIdentityAddress | null;
455
+ }
456
+ interface IdentityPolicy {
457
+ minNameScore?: number;
458
+ minAddressScore?: number;
459
+ requireZipMatch?: boolean;
460
+ requireApiVerified?: boolean;
461
+ }
462
+ interface IdentityPolicyEvaluation {
463
+ verified: boolean;
464
+ reasons: string[];
465
+ scores?: VerifyIdentityResult['scores'];
466
+ }
467
+ declare function buildVerifyIdentityPayload(profile: PartnerIdentityProfile): VerifyIdentityOptions;
468
+ declare function evaluateIdentityResult(result: VerifyIdentityResult, policy?: IdentityPolicy): IdentityPolicyEvaluation;
469
+
470
+ interface TransferWebhookIntent {
471
+ transferId: string;
472
+ type?: TransferType;
473
+ amount?: string | number;
474
+ status?: TransferStatus;
475
+ }
476
+ declare function isTransferWebhookEvent(event: EdgeWebhookEvent): event is Extract<EdgeWebhookEvent, {
477
+ type: `transfer.${string}`;
478
+ }>;
479
+ declare function assertTransferEventMatchesIntent(event: EdgeWebhookEvent, intent: TransferWebhookIntent): void;
480
+
481
+ /**
482
+ * Webhook Signature Verification (SDK-001)
483
+ *
484
+ * HMAC-SHA256 verification for EDGE Connect webhook payloads.
485
+ *
486
+ * Signature header format: `t={timestamp},v1={hex_hmac}`
487
+ * Signed message: `{timestamp}.{raw_json_body}`
488
+ *
489
+ * The timestamp prefix prevents replay attacks — events older than the
490
+ * tolerance window (default 5 minutes) are rejected. The HMAC is verified
491
+ * with constant-time comparison to prevent timing attacks.
492
+ */
493
+ /**
494
+ * Options for {@link verifyWebhookSignature}.
495
+ */
496
+ interface VerifyWebhookSignatureOptions {
497
+ /**
498
+ * Maximum age of the signature timestamp, in seconds.
499
+ *
500
+ * Signatures older than this are rejected as potential replays.
501
+ * Defaults to 300 seconds (5 minutes), matching the producer.
502
+ */
503
+ toleranceSeconds?: number;
504
+ }
505
+ /**
506
+ * Verifies an EDGE Connect webhook signature.
507
+ *
508
+ * **Always pass the raw request body bytes** — never `JSON.stringify(req.body)`.
509
+ * Re-stringifying after the framework parses JSON will reorder keys, change
510
+ * whitespace, and silently break verification with no diagnostic. Capture
511
+ * the raw body via your framework's raw-body middleware:
512
+ *
513
+ * - Express: `express.raw({ type: 'application/json' })`
514
+ * - Fastify: built-in (use `request.rawBody`)
515
+ * - NestJS: `{ rawBody: true }` on `NestFactory.create` + `RawBodyRequest<Request>`
516
+ *
517
+ * @param header - The `X-Edge-Signature` header value (`t=...,v1=...`)
518
+ * @param body - The raw request body as a UTF-8 string (NOT a parsed object)
519
+ * @param secret - The partner's webhook signing secret
520
+ * @param options - Optional overrides
521
+ * @returns `true` if the signature is valid and within the tolerance window,
522
+ * `false` otherwise. Never throws — malformed input returns `false`.
523
+ *
524
+ * @example
525
+ * ```typescript
526
+ * import { verifyWebhookSignature } from '@edge-markets/connect-node'
527
+ *
528
+ * app.post('/webhooks/edge', express.raw({ type: 'application/json' }), (req, res) => {
529
+ * const ok = verifyWebhookSignature(
530
+ * req.headers['x-edge-signature'] as string,
531
+ * req.body.toString('utf8'),
532
+ * process.env.EDGE_WEBHOOK_SECRET!,
533
+ * )
534
+ *
535
+ * if (!ok) return res.status(401).send('invalid signature')
536
+ *
537
+ * const event = JSON.parse(req.body.toString('utf8'))
538
+ * // ... process event
539
+ * res.status(200).send('ok')
540
+ * })
541
+ * ```
542
+ */
543
+ declare function verifyWebhookSignature(header: string, body: string, secret: string, options?: VerifyWebhookSignatureOptions): boolean;
544
+
545
+ type EdgeWebhookRawBody = string | Buffer | Uint8Array;
546
+ type EdgeWebhookSignatureHeader = string | string[] | undefined | null;
547
+ interface ParseAndVerifyWebhookOptions extends VerifyWebhookSignatureOptions {
548
+ rawBody: EdgeWebhookRawBody;
549
+ signatureHeader: EdgeWebhookSignatureHeader;
550
+ secret: string;
551
+ receivedAt?: Date;
552
+ }
553
+ interface ParsedEdgeWebhook {
554
+ event: EdgeWebhookEvent;
555
+ eventId: string;
556
+ eventType: EdgeWebhookEventType;
557
+ signatureTimestamp: number;
558
+ receivedAt: Date;
559
+ }
560
+ declare function parseAndVerifyWebhook(options: ParseAndVerifyWebhookOptions): ParsedEdgeWebhook;
561
+ declare function extractWebhookSignatureTimestamp(header: EdgeWebhookSignatureHeader): number | undefined;
562
+ declare function isEdgeWebhookEvent(value: unknown): value is EdgeWebhookEvent;
563
+ declare function validateEdgeWebhookEvent(value: unknown): EdgeWebhookEvent;
564
+
565
+ export { type CreateEdgeConnectServerFromEnvOptions, type CreateTransferIdempotencyKeyOptions, type EdgeConnectEnv, EdgeConnectServer, type EdgeConnectServerConfig, type EdgeConnectServerFromEnvCapabilities, type EdgeConnectServerFromEnvResult, type EdgeHttpError, type EdgeProblemJson, type EdgeTokenStore, type EdgeTokenStoreSaveContext, type EdgeTokenStoreValue, EdgeTokenVault, type EdgeTokenVaultKey, type EdgeTokenVaultKeyMaterial, type EdgeTokenVaultOptions, EdgeUserClient, EdgeUserSession, type EdgeUserSessionOptions, type EdgeWebhookRawBody, EdgeWebhookReconciler, type EdgeWebhookSignatureHeader, type GetValidTokensOptions, type IdentityPolicy, type IdentityPolicyEvaluation, type MtlsConfig, type ParseAndVerifyWebhookOptions, type ParsedEdgeWebhook, type PartnerIdentityAddress, type PartnerIdentityProfile, type RequestInfo, type ResponseInfo, type RetryConfig, type StartTransferVerificationOptions, type StartTransferVerificationResult, type SyncWebhookEventsOptions, type SyncWebhookEventsResult, type TransferAmountLimits, type TransferIntent, type TransferOptions, type TransferWebhookIntent, type VerifyWebhookSignatureOptions, type WebhookReconcilerCursorStore, type WebhookReconcilerOptions, type WebhookReconcilerRunResult, assertSameTransferIntent, assertTransferEventMatchesIntent, buildVerifyIdentityPayload, createEdgeConnectServerFromEnv, createEdgeTokenVault, createEdgeUserSession, createTransferIdempotencyKey, createWebhookReconciler, evaluateIdentityResult, extractWebhookSignatureTimestamp, isEdgeTokenVaultEnvelope, isEdgeWebhookEvent, isTransferWebhookEvent, normalizeMoneyAmount, parseAndVerifyWebhook, startTransferVerification, toEdgeHttpError, toEdgeProblemJson, validateEdgeWebhookEvent, validateTransferAmount, verifyWebhookSignature };