@edge-markets/connect-node 1.7.0 → 1.9.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/README.md +323 -71
- package/dist/index.d.mts +539 -66
- package/dist/index.d.ts +539 -66
- package/dist/index.js +1881 -85
- package/dist/index.mjs +1826 -57
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import * as node_https from 'node:https';
|
|
2
|
-
import { EdgeEnvironment, TransferCategory, User, VerifyIdentityOptions, VerifyIdentityResult, Balance,
|
|
3
|
-
export { Balance, BaseWebhookEvent, ConsentRevokedEventData, CreateVerificationSessionRequest, EDGE_WEBHOOK_EVENT_TYPES, EdgeApiError, EdgeAuthenticationError, EdgeConsentRequiredError, EdgeEnvironment, EdgeError, EdgeIdentityVerificationError, EdgeInsufficientScopeError, EdgeNetworkError, EdgeNotFoundError, EdgeTokenExchangeError, EdgeTokens, 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';
|
|
2
|
+
import { EdgeEnvironment, TransferCategory, Transfer, VerificationSession, TransferType, TransferStatus, User, VerifyIdentityOptions, VerifyIdentityResult, Balance, ListTransfersParams, TransferList, CreateVerificationSessionRequest, VerificationSessionStatusResponse, EdgeTokens, EdgeWebhookEvent, 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.
|
|
@@ -63,9 +81,19 @@ interface EdgeConnectServerConfig {
|
|
|
63
81
|
}
|
|
64
82
|
interface MtlsConfig {
|
|
65
83
|
enabled: boolean;
|
|
84
|
+
/** Client certificate PEM used to authenticate this partner to EDGE Connect. */
|
|
66
85
|
cert: string;
|
|
86
|
+
/** Client private key PEM paired with `cert`. */
|
|
67
87
|
key: string;
|
|
68
|
-
|
|
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[];
|
|
69
97
|
}
|
|
70
98
|
interface TransferOptions {
|
|
71
99
|
type: 'debit' | 'credit';
|
|
@@ -75,6 +103,57 @@ interface TransferOptions {
|
|
|
75
103
|
category?: TransferCategory;
|
|
76
104
|
}
|
|
77
105
|
|
|
106
|
+
interface TransferAmountLimits {
|
|
107
|
+
min?: string | number;
|
|
108
|
+
max?: string | number;
|
|
109
|
+
}
|
|
110
|
+
interface MoneyAmountOptions {
|
|
111
|
+
allowNumber?: boolean;
|
|
112
|
+
}
|
|
113
|
+
interface CreateTransferIdempotencyKeyOptions {
|
|
114
|
+
partnerUserId: string;
|
|
115
|
+
type: TransferType;
|
|
116
|
+
amount: string | number;
|
|
117
|
+
category?: TransferCategory;
|
|
118
|
+
externalId?: string;
|
|
119
|
+
nonce?: string;
|
|
120
|
+
namespace?: string;
|
|
121
|
+
}
|
|
122
|
+
interface TransferIntent {
|
|
123
|
+
type: TransferType;
|
|
124
|
+
amount: string | number;
|
|
125
|
+
category?: TransferCategory | null;
|
|
126
|
+
}
|
|
127
|
+
type ConnectTransferDirection = 'user_to_partner' | 'partner_to_user';
|
|
128
|
+
interface TransferStatusMapping<TPartnerState extends string = string> {
|
|
129
|
+
pending_verification: TPartnerState;
|
|
130
|
+
processing: TPartnerState;
|
|
131
|
+
completed: TPartnerState;
|
|
132
|
+
failed: TPartnerState;
|
|
133
|
+
expired: TPartnerState;
|
|
134
|
+
}
|
|
135
|
+
interface StartTransferVerificationOptions {
|
|
136
|
+
transfer: TransferOptions;
|
|
137
|
+
origin: string;
|
|
138
|
+
}
|
|
139
|
+
interface StartTransferVerificationResult {
|
|
140
|
+
transfer: Transfer;
|
|
141
|
+
verificationSession: VerificationSession;
|
|
142
|
+
}
|
|
143
|
+
declare const CONNECT_TRANSFER_DIRECTIONS: {
|
|
144
|
+
readonly debit: "user_to_partner";
|
|
145
|
+
readonly credit: "partner_to_user";
|
|
146
|
+
};
|
|
147
|
+
declare function getConnectTransferDirection(type: TransferType): ConnectTransferDirection;
|
|
148
|
+
declare function normalizeMoneyAmount(value: string | number, options?: MoneyAmountOptions): string;
|
|
149
|
+
declare function validateTransferAmount(value: string | number, limits?: TransferAmountLimits, options?: MoneyAmountOptions): string;
|
|
150
|
+
declare function createTransferIdempotencyKey(options: CreateTransferIdempotencyKeyOptions): string;
|
|
151
|
+
declare function assertSameTransferIntent(existing: TransferIntent, requested: TransferIntent): void;
|
|
152
|
+
declare function fingerprintTransferIntent(intent: TransferIntent): string;
|
|
153
|
+
declare function moneyAmountsEqual(left: string | number, right: string | number, options?: MoneyAmountOptions): boolean;
|
|
154
|
+
declare function mapTransferStatusToPartnerState<TPartnerState extends string>(status: TransferStatus, mapping: TransferStatusMapping<TPartnerState>): TPartnerState;
|
|
155
|
+
declare function startTransferVerification(client: Pick<EdgeUserClient, 'initiateTransfer' | 'createVerificationSession'>, options: StartTransferVerificationOptions): Promise<StartTransferVerificationResult>;
|
|
156
|
+
|
|
78
157
|
/**
|
|
79
158
|
* A lightweight, user-scoped API client created via {@link EdgeConnectServer.forUser}.
|
|
80
159
|
*
|
|
@@ -89,6 +168,7 @@ declare class EdgeUserClient {
|
|
|
89
168
|
verifyIdentity(options: VerifyIdentityOptions): Promise<VerifyIdentityResult>;
|
|
90
169
|
getBalance(): Promise<Balance>;
|
|
91
170
|
initiateTransfer(options: TransferOptions): Promise<Transfer>;
|
|
171
|
+
startTransferVerification(options: StartTransferVerificationOptions): Promise<StartTransferVerificationResult>;
|
|
92
172
|
getTransfer(transferId: string): Promise<Transfer>;
|
|
93
173
|
listTransfers(params?: ListTransfersParams): Promise<TransferList>;
|
|
94
174
|
revokeConsent(): Promise<{
|
|
@@ -107,6 +187,317 @@ declare class EdgeUserClient {
|
|
|
107
187
|
getVerificationSessionStatus(transferId: string, sessionId: string): Promise<VerificationSessionStatusResponse>;
|
|
108
188
|
}
|
|
109
189
|
|
|
190
|
+
type EdgeTokenVaultKeyMaterial = string | Buffer | Uint8Array;
|
|
191
|
+
interface EdgeTokenVaultKey {
|
|
192
|
+
id: string;
|
|
193
|
+
key: EdgeTokenVaultKeyMaterial;
|
|
194
|
+
}
|
|
195
|
+
interface EdgeTokenVaultOptions {
|
|
196
|
+
currentKey: EdgeTokenVaultKey;
|
|
197
|
+
previousKeys?: EdgeTokenVaultKey[];
|
|
198
|
+
}
|
|
199
|
+
declare class EdgeTokenVault {
|
|
200
|
+
private readonly currentKey;
|
|
201
|
+
private readonly keysById;
|
|
202
|
+
constructor(options: EdgeTokenVaultOptions);
|
|
203
|
+
encryptTokens(tokens: EdgeTokens): string;
|
|
204
|
+
decryptTokens(envelope: string): EdgeTokens;
|
|
205
|
+
isEncrypted(value: unknown): value is string;
|
|
206
|
+
}
|
|
207
|
+
declare function createEdgeTokenVault(options: EdgeTokenVaultOptions): EdgeTokenVault;
|
|
208
|
+
declare function isEdgeTokenVaultEnvelope(value: unknown): value is string;
|
|
209
|
+
|
|
210
|
+
type EdgeTokenStoreValue = EdgeTokens | string;
|
|
211
|
+
interface EdgeTokenStoreSaveContext {
|
|
212
|
+
tokens: EdgeTokens;
|
|
213
|
+
refreshed: boolean;
|
|
214
|
+
}
|
|
215
|
+
interface EdgeTokenStore {
|
|
216
|
+
load: (subjectId: string) => Promise<EdgeTokenStoreValue | null | undefined> | EdgeTokenStoreValue | null | undefined;
|
|
217
|
+
save: (subjectId: string, value: EdgeTokenStoreValue, context: EdgeTokenStoreSaveContext) => Promise<void> | void;
|
|
218
|
+
}
|
|
219
|
+
interface EdgeUserSessionOptions {
|
|
220
|
+
edge: Pick<EdgeConnectServer, 'forUser' | 'refreshTokens'>;
|
|
221
|
+
subjectId: string;
|
|
222
|
+
tokenStore: EdgeTokenStore;
|
|
223
|
+
tokenVault?: Pick<EdgeTokenVault, 'encryptTokens' | 'decryptTokens' | 'isEncrypted'>;
|
|
224
|
+
refreshSkewMs?: number;
|
|
225
|
+
now?: () => number;
|
|
226
|
+
onRefresh?: (tokens: EdgeTokens) => Promise<void> | void;
|
|
227
|
+
onEvent?: (event: EdgeUserSessionEvent) => Promise<void> | void;
|
|
228
|
+
singleFlightRefresh?: boolean;
|
|
229
|
+
}
|
|
230
|
+
interface GetValidTokensOptions {
|
|
231
|
+
forceRefresh?: boolean;
|
|
232
|
+
}
|
|
233
|
+
type EdgeUserSessionEventType = 'session.loaded' | 'session.refresh_started' | 'session.refresh_succeeded' | 'session.refresh_failed' | 'session.saved';
|
|
234
|
+
interface EdgeUserSessionEvent {
|
|
235
|
+
type: EdgeUserSessionEventType;
|
|
236
|
+
subjectId: string;
|
|
237
|
+
refreshed?: boolean;
|
|
238
|
+
encrypted?: boolean;
|
|
239
|
+
expiresAt?: number;
|
|
240
|
+
scope?: string;
|
|
241
|
+
error?: string;
|
|
242
|
+
}
|
|
243
|
+
declare class EdgeUserSession {
|
|
244
|
+
private readonly edge;
|
|
245
|
+
private readonly subjectId;
|
|
246
|
+
private readonly tokenStore;
|
|
247
|
+
private readonly tokenVault?;
|
|
248
|
+
private readonly refreshSkewMs;
|
|
249
|
+
private readonly now;
|
|
250
|
+
private readonly onRefresh?;
|
|
251
|
+
private readonly onEvent?;
|
|
252
|
+
private readonly singleFlightRefresh;
|
|
253
|
+
private refreshPromise;
|
|
254
|
+
constructor(options: EdgeUserSessionOptions);
|
|
255
|
+
getValidTokens(options?: GetValidTokensOptions): Promise<EdgeTokens>;
|
|
256
|
+
getClient(options?: GetValidTokensOptions): Promise<EdgeUserClient>;
|
|
257
|
+
withClient<T>(callback: (client: EdgeUserClient, tokens: EdgeTokens) => Promise<T> | T, options?: GetValidTokensOptions): Promise<T>;
|
|
258
|
+
saveTokens(tokens: EdgeTokens, refreshed?: boolean): Promise<void>;
|
|
259
|
+
private loadTokens;
|
|
260
|
+
private refreshTokens;
|
|
261
|
+
private emitLoaded;
|
|
262
|
+
private emit;
|
|
263
|
+
}
|
|
264
|
+
declare function createEdgeUserSession(options: EdgeUserSessionOptions): EdgeUserSession;
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Webhook Signature Verification (SDK-001)
|
|
268
|
+
*
|
|
269
|
+
* HMAC-SHA256 verification for EDGE Connect webhook payloads.
|
|
270
|
+
*
|
|
271
|
+
* Signature header format: `t={timestamp},v1={hex_hmac}`
|
|
272
|
+
* Signed message: `{timestamp}.{raw_json_body}`
|
|
273
|
+
*
|
|
274
|
+
* The timestamp prefix prevents replay attacks — events older than the
|
|
275
|
+
* tolerance window (default 5 minutes) are rejected. The HMAC is verified
|
|
276
|
+
* with constant-time comparison to prevent timing attacks.
|
|
277
|
+
*/
|
|
278
|
+
/**
|
|
279
|
+
* Options for {@link verifyWebhookSignature}.
|
|
280
|
+
*/
|
|
281
|
+
interface VerifyWebhookSignatureOptions {
|
|
282
|
+
/**
|
|
283
|
+
* Maximum age of the signature timestamp, in seconds.
|
|
284
|
+
*
|
|
285
|
+
* Signatures older than this are rejected as potential replays.
|
|
286
|
+
* Defaults to 300 seconds (5 minutes), matching the producer.
|
|
287
|
+
*/
|
|
288
|
+
toleranceSeconds?: number;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Verifies an EDGE Connect webhook signature.
|
|
292
|
+
*
|
|
293
|
+
* **Always pass the raw request body bytes** — never `JSON.stringify(req.body)`.
|
|
294
|
+
* Re-stringifying after the framework parses JSON will reorder keys, change
|
|
295
|
+
* whitespace, and silently break verification with no diagnostic. Capture
|
|
296
|
+
* the raw body via your framework's raw-body middleware:
|
|
297
|
+
*
|
|
298
|
+
* - Express: `express.raw({ type: 'application/json' })`
|
|
299
|
+
* - Fastify: built-in (use `request.rawBody`)
|
|
300
|
+
* - NestJS: `{ rawBody: true }` on `NestFactory.create` + `RawBodyRequest<Request>`
|
|
301
|
+
*
|
|
302
|
+
* @param header - The `X-Edge-Signature` header value (`t=...,v1=...`)
|
|
303
|
+
* @param body - The raw request body as a UTF-8 string (NOT a parsed object)
|
|
304
|
+
* @param secret - The partner's webhook signing secret
|
|
305
|
+
* @param options - Optional overrides
|
|
306
|
+
* @returns `true` if the signature is valid and within the tolerance window,
|
|
307
|
+
* `false` otherwise. Never throws — malformed input returns `false`.
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* ```typescript
|
|
311
|
+
* import { verifyWebhookSignature } from '@edge-markets/connect-node'
|
|
312
|
+
*
|
|
313
|
+
* app.post('/webhooks/edge', express.raw({ type: 'application/json' }), (req, res) => {
|
|
314
|
+
* const ok = verifyWebhookSignature(
|
|
315
|
+
* req.headers['x-edge-signature'] as string,
|
|
316
|
+
* req.body.toString('utf8'),
|
|
317
|
+
* process.env.EDGE_WEBHOOK_SECRET!,
|
|
318
|
+
* )
|
|
319
|
+
*
|
|
320
|
+
* if (!ok) return res.status(401).send('invalid signature')
|
|
321
|
+
*
|
|
322
|
+
* const event = JSON.parse(req.body.toString('utf8'))
|
|
323
|
+
* // ... process event
|
|
324
|
+
* res.status(200).send('ok')
|
|
325
|
+
* })
|
|
326
|
+
* ```
|
|
327
|
+
*/
|
|
328
|
+
declare function verifyWebhookSignature(header: string, body: string, secret: string, options?: VerifyWebhookSignatureOptions): boolean;
|
|
329
|
+
|
|
330
|
+
type EdgeWebhookRawBody = string | Buffer | Uint8Array;
|
|
331
|
+
type EdgeWebhookSignatureHeader = string | string[] | undefined | null;
|
|
332
|
+
interface ParseAndVerifyWebhookOptions extends VerifyWebhookSignatureOptions {
|
|
333
|
+
rawBody: EdgeWebhookRawBody;
|
|
334
|
+
signatureHeader: EdgeWebhookSignatureHeader;
|
|
335
|
+
secret: string;
|
|
336
|
+
receivedAt?: Date;
|
|
337
|
+
}
|
|
338
|
+
interface ParsedEdgeWebhook {
|
|
339
|
+
event: EdgeWebhookEvent;
|
|
340
|
+
eventId: string;
|
|
341
|
+
eventType: EdgeWebhookEventType;
|
|
342
|
+
signatureTimestamp: number;
|
|
343
|
+
receivedAt: Date;
|
|
344
|
+
}
|
|
345
|
+
declare function parseAndVerifyWebhook(options: ParseAndVerifyWebhookOptions): ParsedEdgeWebhook;
|
|
346
|
+
declare function extractWebhookSignatureTimestamp(header: EdgeWebhookSignatureHeader): number | undefined;
|
|
347
|
+
declare function isEdgeWebhookEvent(value: unknown): value is EdgeWebhookEvent;
|
|
348
|
+
declare function validateEdgeWebhookEvent(value: unknown): EdgeWebhookEvent;
|
|
349
|
+
|
|
350
|
+
type WebhookInboxStatus = 'pending' | 'processing' | 'processed' | 'failed';
|
|
351
|
+
type WebhookInboxSource = 'live_webhook' | 'sync';
|
|
352
|
+
interface WebhookInboxRecord {
|
|
353
|
+
eventId: string;
|
|
354
|
+
eventType: EdgeWebhookEventType;
|
|
355
|
+
event: EdgeWebhookEvent;
|
|
356
|
+
payloadFingerprint: string;
|
|
357
|
+
status: WebhookInboxStatus;
|
|
358
|
+
source: WebhookInboxSource;
|
|
359
|
+
attempts: number;
|
|
360
|
+
receivedAt: Date;
|
|
361
|
+
updatedAt?: Date;
|
|
362
|
+
processedAt?: Date | null;
|
|
363
|
+
lastError?: string | null;
|
|
364
|
+
signatureTimestamp?: number | null;
|
|
365
|
+
signatureHeader?: string | null;
|
|
366
|
+
rawBody?: string | null;
|
|
367
|
+
}
|
|
368
|
+
interface WebhookInboxInsertRecord {
|
|
369
|
+
eventId: string;
|
|
370
|
+
eventType: EdgeWebhookEventType;
|
|
371
|
+
event: EdgeWebhookEvent;
|
|
372
|
+
payloadFingerprint: string;
|
|
373
|
+
source: WebhookInboxSource;
|
|
374
|
+
receivedAt: Date;
|
|
375
|
+
signatureTimestamp?: number | null;
|
|
376
|
+
signatureHeader?: string | null;
|
|
377
|
+
rawBody?: string | null;
|
|
378
|
+
}
|
|
379
|
+
type WebhookInboxInsertResult = {
|
|
380
|
+
inserted: true;
|
|
381
|
+
record: WebhookInboxRecord;
|
|
382
|
+
} | {
|
|
383
|
+
inserted: false;
|
|
384
|
+
record: WebhookInboxRecord;
|
|
385
|
+
};
|
|
386
|
+
interface WebhookInboxListOptions {
|
|
387
|
+
status?: WebhookInboxStatus;
|
|
388
|
+
updatedBefore?: Date;
|
|
389
|
+
limit?: number;
|
|
390
|
+
}
|
|
391
|
+
interface WebhookInboxStore {
|
|
392
|
+
insert: (record: WebhookInboxInsertRecord) => Promise<WebhookInboxInsertResult> | WebhookInboxInsertResult;
|
|
393
|
+
find: (eventId: string) => Promise<WebhookInboxRecord | null | undefined> | WebhookInboxRecord | null | undefined;
|
|
394
|
+
markProcessing: (eventId: string, context: {
|
|
395
|
+
now: Date;
|
|
396
|
+
}) => Promise<WebhookInboxRecord | null | undefined> | WebhookInboxRecord | null | undefined;
|
|
397
|
+
markProcessed: (eventId: string, context: {
|
|
398
|
+
now: Date;
|
|
399
|
+
}) => Promise<WebhookInboxRecord | null | undefined> | WebhookInboxRecord | null | undefined;
|
|
400
|
+
markFailed: (eventId: string, context: {
|
|
401
|
+
now: Date;
|
|
402
|
+
error: string;
|
|
403
|
+
}) => Promise<WebhookInboxRecord | null | undefined> | WebhookInboxRecord | null | undefined;
|
|
404
|
+
markPending?: (eventId: string, context: {
|
|
405
|
+
now: Date;
|
|
406
|
+
reason: string;
|
|
407
|
+
}) => Promise<WebhookInboxRecord | null | undefined> | WebhookInboxRecord | null | undefined;
|
|
408
|
+
list?: (options: WebhookInboxListOptions) => Promise<WebhookInboxRecord[]> | WebhookInboxRecord[];
|
|
409
|
+
}
|
|
410
|
+
interface WebhookInboxOptions {
|
|
411
|
+
store: WebhookInboxStore;
|
|
412
|
+
processEvent: (event: EdgeWebhookEvent, record: WebhookInboxRecord) => Promise<void> | void;
|
|
413
|
+
now?: () => Date;
|
|
414
|
+
storeRawBody?: boolean;
|
|
415
|
+
processingTimeoutMs?: number;
|
|
416
|
+
onEvent?: (event: WebhookInboxLifecycleEvent) => Promise<void> | void;
|
|
417
|
+
}
|
|
418
|
+
interface WebhookInboxLifecycleEvent {
|
|
419
|
+
type: 'webhook.accepted' | 'webhook.duplicate' | 'webhook.payload_mismatch' | 'webhook.processing_started' | 'webhook.processing_succeeded' | 'webhook.processing_failed' | 'webhook.recovered_stale';
|
|
420
|
+
eventId: string;
|
|
421
|
+
eventType?: EdgeWebhookEventType;
|
|
422
|
+
status?: WebhookInboxStatus;
|
|
423
|
+
source?: WebhookInboxSource;
|
|
424
|
+
attempts?: number;
|
|
425
|
+
error?: string;
|
|
426
|
+
}
|
|
427
|
+
interface WebhookInboxAcceptResult {
|
|
428
|
+
eventId: string;
|
|
429
|
+
eventType: EdgeWebhookEventType;
|
|
430
|
+
inserted: boolean;
|
|
431
|
+
duplicate: boolean;
|
|
432
|
+
status: WebhookInboxStatus;
|
|
433
|
+
record: WebhookInboxRecord;
|
|
434
|
+
}
|
|
435
|
+
interface WebhookInboxProcessResult {
|
|
436
|
+
eventId: string;
|
|
437
|
+
status: WebhookInboxStatus;
|
|
438
|
+
processed: boolean;
|
|
439
|
+
skipped: boolean;
|
|
440
|
+
reason?: 'already_processed' | 'not_found' | 'locked';
|
|
441
|
+
record?: WebhookInboxRecord;
|
|
442
|
+
}
|
|
443
|
+
interface WebhookInboxRecoverResult {
|
|
444
|
+
recovered: number;
|
|
445
|
+
skipped: boolean;
|
|
446
|
+
reason?: 'list_not_configured' | 'mark_pending_not_configured';
|
|
447
|
+
}
|
|
448
|
+
declare class EdgeWebhookInbox {
|
|
449
|
+
private readonly store;
|
|
450
|
+
private readonly processEvent;
|
|
451
|
+
private readonly now;
|
|
452
|
+
private readonly storeRawBody;
|
|
453
|
+
private readonly processingTimeoutMs;
|
|
454
|
+
private readonly onEvent?;
|
|
455
|
+
constructor(options: WebhookInboxOptions);
|
|
456
|
+
accept(parsed: ParsedEdgeWebhook): Promise<WebhookInboxAcceptResult>;
|
|
457
|
+
acceptRaw(parsed: ParsedEdgeWebhook, rawBody: string, signatureHeader: string): Promise<WebhookInboxAcceptResult>;
|
|
458
|
+
acceptSyncedEvent(event: EdgeWebhookEvent, receivedAt?: Date): Promise<WebhookInboxAcceptResult>;
|
|
459
|
+
process(eventId: string, options?: {
|
|
460
|
+
force?: boolean;
|
|
461
|
+
}): Promise<WebhookInboxProcessResult>;
|
|
462
|
+
replay(eventId: string): Promise<WebhookInboxProcessResult>;
|
|
463
|
+
recoverStaleProcessing(): Promise<WebhookInboxRecoverResult>;
|
|
464
|
+
private acceptEvent;
|
|
465
|
+
private emit;
|
|
466
|
+
}
|
|
467
|
+
declare function createWebhookInbox(options: WebhookInboxOptions): EdgeWebhookInbox;
|
|
468
|
+
declare function fingerprintWebhookEvent(event: EdgeWebhookEvent): string;
|
|
469
|
+
|
|
470
|
+
interface WebhookReconcilerCursorStore {
|
|
471
|
+
load: () => Promise<string | null | undefined> | string | null | undefined;
|
|
472
|
+
save: (cursor: string) => Promise<void> | void;
|
|
473
|
+
}
|
|
474
|
+
interface WebhookReconcilerOptions {
|
|
475
|
+
edge: Pick<EdgeConnectServer, 'syncWebhookEvents'>;
|
|
476
|
+
cursorStore: WebhookReconcilerCursorStore;
|
|
477
|
+
processEvent?: (event: EdgeWebhookEvent) => Promise<void> | void;
|
|
478
|
+
inbox?: Pick<EdgeWebhookInbox, 'acceptSyncedEvent' | 'process'>;
|
|
479
|
+
limit?: SyncWebhookEventsOptions['limit'];
|
|
480
|
+
maxPages?: number;
|
|
481
|
+
}
|
|
482
|
+
interface WebhookReconcilerRunResult {
|
|
483
|
+
processed: number;
|
|
484
|
+
accepted?: number;
|
|
485
|
+
duplicates?: number;
|
|
486
|
+
pages: number;
|
|
487
|
+
cursor?: string;
|
|
488
|
+
hasMore: boolean;
|
|
489
|
+
skipped: boolean;
|
|
490
|
+
reason?: 'already_running' | 'empty_page_with_has_more' | 'max_pages_reached';
|
|
491
|
+
}
|
|
492
|
+
declare class EdgeWebhookReconciler {
|
|
493
|
+
private readonly options;
|
|
494
|
+
private running;
|
|
495
|
+
constructor(options: WebhookReconcilerOptions);
|
|
496
|
+
run(): Promise<WebhookReconcilerRunResult>;
|
|
497
|
+
private runInternal;
|
|
498
|
+
}
|
|
499
|
+
declare function createWebhookReconciler(options: WebhookReconcilerOptions): EdgeWebhookReconciler;
|
|
500
|
+
|
|
110
501
|
/**
|
|
111
502
|
* Options for {@link EdgeConnectServer.syncWebhookEvents}.
|
|
112
503
|
*/
|
|
@@ -142,6 +533,7 @@ interface SyncWebhookEventsResult {
|
|
|
142
533
|
declare class EdgeConnectServer {
|
|
143
534
|
private readonly config;
|
|
144
535
|
private readonly apiBaseUrl;
|
|
536
|
+
private readonly partnerApiBaseUrl;
|
|
145
537
|
private readonly oauthBaseUrl;
|
|
146
538
|
private readonly timeout;
|
|
147
539
|
private readonly retryConfig;
|
|
@@ -163,6 +555,7 @@ declare class EdgeConnectServer {
|
|
|
163
555
|
* or per user session and discard it when the token changes.
|
|
164
556
|
*/
|
|
165
557
|
forUser(accessToken: string): EdgeUserClient;
|
|
558
|
+
createUserSession(options: Omit<EdgeUserSessionOptions, 'edge'>): EdgeUserSession;
|
|
166
559
|
exchangeCode(code: string, codeVerifier: string, redirectUri?: string): Promise<EdgeTokens>;
|
|
167
560
|
refreshTokens(refreshToken: string): Promise<EdgeTokens>;
|
|
168
561
|
/** @internal Called by {@link EdgeUserClient} — not part of the public API. */
|
|
@@ -205,9 +598,18 @@ declare class EdgeConnectServer {
|
|
|
205
598
|
* @throws {EdgeApiError} For other non-2xx responses after retries
|
|
206
599
|
*/
|
|
207
600
|
syncWebhookEvents(options?: SyncWebhookEventsOptions): Promise<SyncWebhookEventsResult>;
|
|
601
|
+
/**
|
|
602
|
+
* Creates a cursor-based webhook reconciler bound to this SDK instance.
|
|
603
|
+
*
|
|
604
|
+
* Use this from cron jobs to process events returned by
|
|
605
|
+
* {@link EdgeConnectServer.syncWebhookEvents} without hand-writing
|
|
606
|
+
* pagination, cursor advancement, or process-level concurrency guards.
|
|
607
|
+
*/
|
|
608
|
+
createWebhookReconciler(options: Omit<WebhookReconcilerOptions, 'edge'>): EdgeWebhookReconciler;
|
|
208
609
|
/**
|
|
209
610
|
* Builds the sync URL with normalized query parameters.
|
|
210
611
|
*/
|
|
612
|
+
private normalizeSyncWebhookEventsOptions;
|
|
211
613
|
private buildSyncUrl;
|
|
212
614
|
/**
|
|
213
615
|
* Issues a GET to the sync endpoint with the partner Bearer token. Kept
|
|
@@ -228,6 +630,9 @@ declare class EdgeConnectServer {
|
|
|
228
630
|
* to `syncWebhookEvents` re-fetches it. Not part of the public API.
|
|
229
631
|
*/
|
|
230
632
|
_resetPartnerTokenCache(): void;
|
|
633
|
+
private redactPartnerTokenResponseBody;
|
|
634
|
+
private redactPartnerSyncResponseBody;
|
|
635
|
+
private drainResponseBody;
|
|
231
636
|
private getRetryDelay;
|
|
232
637
|
private sleep;
|
|
233
638
|
private fetchWithTimeout;
|
|
@@ -236,68 +641,136 @@ declare class EdgeConnectServer {
|
|
|
236
641
|
private handleApiErrorFromBody;
|
|
237
642
|
}
|
|
238
643
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
* tolerance window (default 5 minutes) are rejected. The HMAC is verified
|
|
249
|
-
* with constant-time comparison to prevent timing attacks.
|
|
250
|
-
*/
|
|
251
|
-
/**
|
|
252
|
-
* Options for {@link verifyWebhookSignature}.
|
|
253
|
-
*/
|
|
254
|
-
interface VerifyWebhookSignatureOptions {
|
|
255
|
-
/**
|
|
256
|
-
* Maximum age of the signature timestamp, in seconds.
|
|
257
|
-
*
|
|
258
|
-
* Signatures older than this are rejected as potential replays.
|
|
259
|
-
* Defaults to 300 seconds (5 minutes), matching the producer.
|
|
260
|
-
*/
|
|
261
|
-
toleranceSeconds?: number;
|
|
644
|
+
type EdgeConnectEnv = Record<string, string | undefined>;
|
|
645
|
+
interface CreateEdgeConnectServerFromEnvOptions {
|
|
646
|
+
env?: EdgeConnectEnv;
|
|
647
|
+
requireMtls?: boolean;
|
|
648
|
+
timeout?: number;
|
|
649
|
+
retry?: RetryConfig;
|
|
650
|
+
onRequest?: (info: RequestInfo) => void;
|
|
651
|
+
onResponse?: (info: ResponseInfo) => void;
|
|
652
|
+
mle?: EdgeConnectServerConfig['mle'];
|
|
262
653
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
654
|
+
interface EdgeConnectServerFromEnvCapabilities {
|
|
655
|
+
mtlsEnabled: boolean;
|
|
656
|
+
webhookSyncConfigured: boolean;
|
|
657
|
+
customApiBaseUrl: boolean;
|
|
658
|
+
customOAuthBaseUrl: boolean;
|
|
659
|
+
customPartnerApiBaseUrl: boolean;
|
|
660
|
+
}
|
|
661
|
+
interface EdgeConnectServerFromEnvResult {
|
|
662
|
+
server: EdgeConnectServer;
|
|
663
|
+
config: EdgeConnectServerConfig;
|
|
664
|
+
capabilities: EdgeConnectServerFromEnvCapabilities;
|
|
665
|
+
warnings: string[];
|
|
666
|
+
}
|
|
667
|
+
declare function createEdgeConnectServerFromEnv(options?: CreateEdgeConnectServerFromEnvOptions): EdgeConnectServerFromEnvResult;
|
|
668
|
+
|
|
669
|
+
interface EdgeProblemJson {
|
|
670
|
+
type: string;
|
|
671
|
+
title: string;
|
|
672
|
+
status: number;
|
|
673
|
+
detail: string;
|
|
674
|
+
code: string;
|
|
675
|
+
details?: Record<string, unknown>;
|
|
676
|
+
}
|
|
677
|
+
interface EdgeHttpError {
|
|
678
|
+
status: number;
|
|
679
|
+
body: EdgeProblemJson;
|
|
680
|
+
}
|
|
681
|
+
declare function toEdgeHttpError(error: unknown): EdgeHttpError;
|
|
682
|
+
declare function toEdgeProblemJson(error: unknown): EdgeProblemJson;
|
|
683
|
+
|
|
684
|
+
interface EdgeWebhookHttpRequestLike {
|
|
685
|
+
headers?: Record<string, unknown>;
|
|
686
|
+
rawBody?: Buffer | Uint8Array | string;
|
|
687
|
+
body?: unknown;
|
|
688
|
+
}
|
|
689
|
+
interface ParseWebhookHttpRequestOptions extends Omit<ParseAndVerifyWebhookOptions, 'rawBody' | 'signatureHeader'> {
|
|
690
|
+
request: EdgeWebhookHttpRequestLike;
|
|
691
|
+
signatureHeaderName?: string;
|
|
692
|
+
}
|
|
693
|
+
declare function getEdgeWebhookRawBody(request: EdgeWebhookHttpRequestLike): EdgeWebhookRawBody;
|
|
694
|
+
declare function getEdgeWebhookSignatureHeader(request: EdgeWebhookHttpRequestLike, headerName?: string): EdgeWebhookSignatureHeader;
|
|
695
|
+
declare function parseWebhookHttpRequest(options: ParseWebhookHttpRequestOptions): ParsedEdgeWebhook;
|
|
696
|
+
|
|
697
|
+
interface PartnerIdentityAddress {
|
|
698
|
+
street?: string | null;
|
|
699
|
+
line1?: string | null;
|
|
700
|
+
address1?: string | null;
|
|
701
|
+
city?: string | null;
|
|
702
|
+
state?: string | null;
|
|
703
|
+
zip?: string | null;
|
|
704
|
+
postalCode?: string | null;
|
|
705
|
+
}
|
|
706
|
+
interface PartnerIdentityProfile {
|
|
707
|
+
firstName?: string | null;
|
|
708
|
+
givenName?: string | null;
|
|
709
|
+
lastName?: string | null;
|
|
710
|
+
familyName?: string | null;
|
|
711
|
+
address?: PartnerIdentityAddress | null;
|
|
712
|
+
}
|
|
713
|
+
interface IdentityPolicy {
|
|
714
|
+
minNameScore?: number;
|
|
715
|
+
minAddressScore?: number;
|
|
716
|
+
requireZipMatch?: boolean;
|
|
717
|
+
requireApiVerified?: boolean;
|
|
718
|
+
}
|
|
719
|
+
interface IdentityPolicyEvaluation {
|
|
720
|
+
verified: boolean;
|
|
721
|
+
reasons: string[];
|
|
722
|
+
scores?: VerifyIdentityResult['scores'];
|
|
723
|
+
}
|
|
724
|
+
declare function buildVerifyIdentityPayload(profile: PartnerIdentityProfile): VerifyIdentityOptions;
|
|
725
|
+
declare function evaluateIdentityResult(result: VerifyIdentityResult, policy?: IdentityPolicy): IdentityPolicyEvaluation;
|
|
726
|
+
|
|
727
|
+
interface EdgeSessionTokenRecord {
|
|
728
|
+
subjectId: string;
|
|
729
|
+
encryptedTokens: string;
|
|
730
|
+
expiresAt: number;
|
|
731
|
+
scopes?: string[];
|
|
732
|
+
edgeUserId?: string;
|
|
733
|
+
keyId?: string;
|
|
734
|
+
metadata?: Record<string, unknown>;
|
|
735
|
+
}
|
|
736
|
+
interface CreateSessionTokenRecordOptions {
|
|
737
|
+
scopes?: string[];
|
|
738
|
+
edgeUserId?: string;
|
|
739
|
+
keyId?: string;
|
|
740
|
+
metadata?: Record<string, unknown>;
|
|
741
|
+
}
|
|
742
|
+
interface LegacySessionTokenRecord {
|
|
743
|
+
subjectId?: string;
|
|
744
|
+
accessToken?: string | null;
|
|
745
|
+
refreshToken?: string | null;
|
|
746
|
+
idToken?: string | null;
|
|
747
|
+
expiresAt?: unknown;
|
|
748
|
+
expiresIn?: unknown;
|
|
749
|
+
scope?: unknown;
|
|
750
|
+
scopes?: unknown;
|
|
751
|
+
edgeUserId?: string | null;
|
|
752
|
+
metadata?: Record<string, unknown>;
|
|
753
|
+
}
|
|
754
|
+
interface EncryptLegacySessionTokensOptions {
|
|
755
|
+
subjectId: string;
|
|
756
|
+
keyId?: string;
|
|
757
|
+
allowPlaintext?: boolean;
|
|
758
|
+
}
|
|
759
|
+
declare function createSessionTokenRecord(subjectId: string, tokens: EdgeTokens, tokenVault: Pick<EdgeTokenVault, 'encryptTokens'>, options?: CreateSessionTokenRecordOptions): EdgeSessionTokenRecord;
|
|
760
|
+
declare function parseSessionTokenRecord(record: unknown): EdgeSessionTokenRecord;
|
|
761
|
+
declare function encryptLegacySessionTokens(legacyRecord: LegacySessionTokenRecord, tokenVault: Pick<EdgeTokenVault, 'encryptTokens' | 'isEncrypted'>, options: EncryptLegacySessionTokensOptions): EdgeSessionTokenRecord;
|
|
762
|
+
declare function normalizeSessionExpiresAt(value: unknown): number;
|
|
763
|
+
|
|
764
|
+
interface TransferWebhookIntent {
|
|
765
|
+
transferId: string;
|
|
766
|
+
type?: TransferType;
|
|
767
|
+
amount?: string | number;
|
|
768
|
+
status?: TransferStatus;
|
|
769
|
+
}
|
|
770
|
+
declare function isTransferWebhookEvent(event: EdgeWebhookEvent): event is Extract<EdgeWebhookEvent, {
|
|
771
|
+
type: `transfer.${string}`;
|
|
772
|
+
}>;
|
|
773
|
+
declare function assertTransferEventMatchesIntent(event: EdgeWebhookEvent, intent: TransferWebhookIntent): void;
|
|
774
|
+
declare const assertWebhookMatchesTransfer: typeof assertTransferEventMatchesIntent;
|
|
302
775
|
|
|
303
|
-
export { EdgeConnectServer, type EdgeConnectServerConfig, EdgeUserClient, type MtlsConfig, type RequestInfo, type ResponseInfo, type RetryConfig, type SyncWebhookEventsOptions, type SyncWebhookEventsResult, type TransferOptions, type VerifyWebhookSignatureOptions, verifyWebhookSignature };
|
|
776
|
+
export { CONNECT_TRANSFER_DIRECTIONS, type ConnectTransferDirection, type CreateEdgeConnectServerFromEnvOptions, type CreateSessionTokenRecordOptions, type CreateTransferIdempotencyKeyOptions, type EdgeConnectEnv, EdgeConnectServer, type EdgeConnectServerConfig, type EdgeConnectServerFromEnvCapabilities, type EdgeConnectServerFromEnvResult, type EdgeHttpError, type EdgeProblemJson, type EdgeSessionTokenRecord, type EdgeTokenStore, type EdgeTokenStoreSaveContext, type EdgeTokenStoreValue, EdgeTokenVault, type EdgeTokenVaultKey, type EdgeTokenVaultKeyMaterial, type EdgeTokenVaultOptions, EdgeUserClient, EdgeUserSession, type EdgeUserSessionEvent, type EdgeUserSessionEventType, type EdgeUserSessionOptions, type EdgeWebhookHttpRequestLike, EdgeWebhookInbox, type EdgeWebhookRawBody, EdgeWebhookReconciler, type EdgeWebhookSignatureHeader, type EncryptLegacySessionTokensOptions, type GetValidTokensOptions, type IdentityPolicy, type IdentityPolicyEvaluation, type LegacySessionTokenRecord, type MoneyAmountOptions, type MtlsConfig, type ParseAndVerifyWebhookOptions, type ParseWebhookHttpRequestOptions, 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 TransferStatusMapping, type TransferWebhookIntent, type VerifyWebhookSignatureOptions, type WebhookInboxAcceptResult, type WebhookInboxInsertRecord, type WebhookInboxInsertResult, type WebhookInboxLifecycleEvent, type WebhookInboxListOptions, type WebhookInboxOptions, type WebhookInboxProcessResult, type WebhookInboxRecord, type WebhookInboxRecoverResult, type WebhookInboxSource, type WebhookInboxStatus, type WebhookInboxStore, type WebhookReconcilerCursorStore, type WebhookReconcilerOptions, type WebhookReconcilerRunResult, assertSameTransferIntent, assertTransferEventMatchesIntent, assertWebhookMatchesTransfer, buildVerifyIdentityPayload, createEdgeConnectServerFromEnv, createEdgeTokenVault, createEdgeUserSession, createSessionTokenRecord, createTransferIdempotencyKey, createWebhookInbox, createWebhookReconciler, encryptLegacySessionTokens, evaluateIdentityResult, extractWebhookSignatureTimestamp, fingerprintTransferIntent, fingerprintWebhookEvent, getConnectTransferDirection, getEdgeWebhookRawBody, getEdgeWebhookSignatureHeader, isEdgeTokenVaultEnvelope, isEdgeWebhookEvent, isTransferWebhookEvent, mapTransferStatusToPartnerState, moneyAmountsEqual, normalizeMoneyAmount, normalizeSessionExpiresAt, parseAndVerifyWebhook, parseSessionTokenRecord, parseWebhookHttpRequest, startTransferVerification, toEdgeHttpError, toEdgeProblemJson, validateEdgeWebhookEvent, validateTransferAmount, verifyWebhookSignature };
|