@xkova/sdk 0.1.0-alpha.2 → 0.1.0-alpha.4

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.
@@ -1,4 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
+ import { isAllowedProtocol } from './url.js';
2
3
  export class XKOVAMemberIntentError extends Error {
3
4
  code;
4
5
  status;
@@ -33,8 +34,7 @@ export class XKOVAMemberActionIntents {
33
34
  location.username ||
34
35
  location.password ||
35
36
  location.hash ||
36
- (location.protocol !== 'https:' &&
37
- !(location.protocol === 'http:' && location.hostname === 'localhost')))
37
+ !isAllowedProtocol(location))
38
38
  throw new TypeError('Action review requires the exact application origin');
39
39
  const snapshot = JSON.parse(canonicalJson({
40
40
  request: input.request,
@@ -350,20 +350,6 @@ export declare class XKOVAMemberBffClient {
350
350
  readonly redirectUri: string;
351
351
  readonly transactionStore: XKOVAMemberAuthorizationTransactionStore<TApplicationData>;
352
352
  }): Promise<XKOVAHostedMemberAuthorizationStart>;
353
- /**
354
- * Available only to sandbox applications enabled for demo sign-in. The stored
355
- * Hosted transaction determines the demo account and callback. An uncertain
356
- * response preserves the key so a retry recovers the same reservation.
357
- * Complete the returned callback using
358
- * completeHostedAuthorization and the original browser flow cookie.
359
- */
360
- completeSandboxDemoAuthorization<TApplicationData>(input: {
361
- readonly state: string;
362
- readonly transactionStore: XKOVAMemberAuthorizationTransactionStore<TApplicationData>;
363
- }): Promise<{
364
- readonly callbackUrl: string;
365
- readonly expiresAt: string;
366
- }>;
367
353
  /**
368
354
  * Completes the one-time Hosted Auth result-code exchange and persists the
369
355
  * provider-neutral XKOVA member session. The returned projection is safe for
@@ -492,8 +478,6 @@ export declare class XKOVAMemberBffClient {
492
478
  private readSessionMaterial;
493
479
  private requestSignal;
494
480
  }
495
- /** Only confirmed admission rejections may release a customer's pending demo reservation. */
496
- export declare function isDefinitiveXKOVASandboxDemoAdmissionFailure(error: unknown): boolean;
497
481
  export declare function projectXKOVAMemberSession(session: XKOVAMemberContext): XKOVABrowserMemberProjection;
498
482
  export declare function serializeXKOVAMemberSessionCookie(input: {
499
483
  readonly cookieName: string;
@@ -1,5 +1,6 @@
1
1
  import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
2
2
  import { XKOVAApiError } from './errors.js';
3
+ import { isAllowedProtocol } from './url.js';
3
4
  import { XKOVAClient } from './client.js';
4
5
  import { XKOVAMemberResources } from './member-resources.js';
5
6
  import { XKOVAMemberActionIntents, } from './member-action-intents.js';
@@ -195,54 +196,6 @@ export class XKOVAMemberBffClient {
195
196
  throw error;
196
197
  }
197
198
  }
198
- /**
199
- * Available only to sandbox applications enabled for demo sign-in. The stored
200
- * Hosted transaction determines the demo account and callback. An uncertain
201
- * response preserves the key so a retry recovers the same reservation.
202
- * Complete the returned callback using
203
- * completeHostedAuthorization and the original browser flow cookie.
204
- */
205
- async completeSandboxDemoAuthorization(input) {
206
- if (!this.options.apiKey.startsWith('xkv_test_') ||
207
- !this.options.publishableKey.startsWith('xkova_pk_sandbox_')) {
208
- throw new XKOVAMemberAuthorizationTerminalError('provider_rejected', 'Demo admission requires a sandbox application');
209
- }
210
- const stateHash = hashOpaque(input.state);
211
- return input.transactionStore.runExclusive(stateHash, async () => {
212
- const transaction = await input.transactionStore.get(stateHash);
213
- if (!transaction || transaction.expiresAt <= Date.now()) {
214
- throw new XKOVAMemberAuthorizationTerminalError('transaction_unavailable', 'Demo admission requires an active Hosted transaction');
215
- }
216
- assertMemberBffClientFingerprint(transaction.clientFingerprint, this.clientFingerprint);
217
- if (transaction.status !== 'pending' ||
218
- transaction.completionMode !== 'hosted_result_code' ||
219
- !transaction.authorizationUrl) {
220
- throw new XKOVAMemberAuthorizationTerminalError('transaction_unavailable', 'Demo admission requires a completed Hosted start');
221
- }
222
- const authorization = new URL(normalizeHostedAuthorizationUrl(transaction.authorizationUrl));
223
- const capability = authorization.searchParams.get('transaction');
224
- if (!/^\/authorize\/?$/.test(authorization.pathname) ||
225
- !capability ||
226
- !/^[A-Za-z0-9_-]{43}$/.test(capability)) {
227
- throw new XKOVAMemberAuthorizationTerminalError('transaction_unavailable', 'Demo admission capability is invalid');
228
- }
229
- const value = await this.postJson('/member-auth/hosted/authorizations/sandbox-demo', { transaction: capability }, undefined, `sandbox-demo-${stateHash}`);
230
- const body = requireObject(value, 'Sandbox demo admission');
231
- const { expiresAt } = boundedAuthorizationExpiry(body, transaction.expiresAt, 'Sandbox demo admission');
232
- const callback = new URL(requireString(body, 'redirect_url', 'Sandbox demo admission'));
233
- const code = callback.searchParams.get('code');
234
- if (!sameCallback(callback, new URL(transaction.redirectUri)) ||
235
- callback.searchParams.getAll('state').length !== 1 ||
236
- callback.searchParams.get('state') !== input.state ||
237
- callback.searchParams.getAll('code').length !== 1 ||
238
- !code ||
239
- callback.searchParams.has('error')) {
240
- throw new Error('Sandbox demo admission returned the wrong callback binding');
241
- }
242
- assertHostedAuthorizationResultCode(code);
243
- return { callbackUrl: callback.toString(), expiresAt };
244
- });
245
- }
246
199
  /**
247
200
  * Completes the one-time Hosted Auth result-code exchange and persists the
248
201
  * provider-neutral XKOVA member session. The returned projection is safe for
@@ -560,7 +513,7 @@ export class XKOVAMemberBffClient {
560
513
  clientFingerprint: this.clientFingerprint,
561
514
  codeVerifier,
562
515
  redirectUri: returnUri,
563
- exchangeAttemptId: generateXKOVAMemberOperationAttemptId(),
516
+ exchangeAttemptId: randomUUID(),
564
517
  sessionReference: input.sessionReference,
565
518
  intent: 'action',
566
519
  ...(applicationReturnUri !== undefined && { applicationReturnUri }),
@@ -1210,14 +1163,6 @@ export class XKOVAMemberBffClient {
1210
1163
  : AbortSignal.timeout(this.options.timeoutMs);
1211
1164
  }
1212
1165
  }
1213
- /** Only confirmed admission rejections may release a customer's pending demo reservation. */
1214
- export function isDefinitiveXKOVASandboxDemoAdmissionFailure(error) {
1215
- return (error instanceof XKOVAMemberAuthorizationTerminalError ||
1216
- (error instanceof XKOVAApiError &&
1217
- (error.status < 500 ||
1218
- error.code === 'demo_persona_unavailable' ||
1219
- error.code === 'demo_login_disabled')));
1220
- }
1221
1166
  export function projectXKOVAMemberSession(session) {
1222
1167
  return {
1223
1168
  signedIn: true,
@@ -1339,8 +1284,8 @@ function normalizeApiBaseUrl(baseUrl) {
1339
1284
  if (url.username || url.password || url.search || url.hash) {
1340
1285
  throw new TypeError('XKOVA member BFF baseUrl must be an API origin or /v1 base URL');
1341
1286
  }
1342
- if (url.protocol !== 'https:' && !(url.protocol === 'http:' && url.hostname === 'localhost')) {
1343
- throw new TypeError('XKOVA member BFF baseUrl must use HTTPS or localhost HTTP');
1287
+ if (!isAllowedProtocol(url)) {
1288
+ throw new TypeError('XKOVA member BFF baseUrl must use HTTPS or loopback HTTP');
1344
1289
  }
1345
1290
  const pathname = url.pathname.replace(/\/+$/, '');
1346
1291
  if (pathname !== '' && pathname !== '/v1') {
@@ -1353,16 +1298,15 @@ function normalizeCallbackUri(value) {
1353
1298
  if (url.username || url.password || url.search || url.hash) {
1354
1299
  throw new TypeError('member authorization redirectUri must not contain credentials, query, or fragment');
1355
1300
  }
1356
- if (url.protocol !== 'https:' && !(url.protocol === 'http:' && url.hostname === 'localhost')) {
1357
- throw new TypeError('member authorization redirectUri must use HTTPS or localhost HTTP');
1301
+ if (!isAllowedProtocol(url)) {
1302
+ throw new TypeError('member authorization redirectUri must use HTTPS or loopback HTTP');
1358
1303
  }
1359
1304
  return url.toString();
1360
1305
  }
1361
1306
  function normalizeBrowserOrigin(value) {
1362
1307
  const url = new URL(value);
1363
- if (url.origin !== value ||
1364
- (url.protocol !== 'https:' && !(url.protocol === 'http:' && url.hostname === 'localhost'))) {
1365
- throw new TypeError('member authorization browserOrigin must be an exact HTTPS or localhost origin');
1308
+ if (url.origin !== value || !isAllowedProtocol(url)) {
1309
+ throw new TypeError('member authorization browserOrigin must be an exact HTTPS or loopback origin');
1366
1310
  }
1367
1311
  return value;
1368
1312
  }
@@ -1372,18 +1316,14 @@ function normalizeApplicationReturnUri(value, browserOrigin) {
1372
1316
  url.username ||
1373
1317
  url.password ||
1374
1318
  url.hash ||
1375
- (url.protocol !== 'https:' && !(url.protocol === 'http:' && url.hostname === 'localhost'))) {
1319
+ !isAllowedProtocol(url)) {
1376
1320
  throw new TypeError('member action applicationReturnUri must use the exact browser origin');
1377
1321
  }
1378
1322
  return url.toString();
1379
1323
  }
1380
1324
  function normalizeHostedAuthorizationUrl(value) {
1381
1325
  const url = new URL(value);
1382
- if (url.username ||
1383
- url.password ||
1384
- url.hash ||
1385
- (url.protocol !== 'https:' &&
1386
- !(url.protocol === 'http:' && (url.hostname === 'localhost' || url.hostname === '127.0.0.1')))) {
1326
+ if (url.username || url.password || url.hash || !isAllowedProtocol(url)) {
1387
1327
  throw new Error('XKOVA member Hosted authorization URL must use HTTPS or loopback HTTP without credentials or a fragment');
1388
1328
  }
1389
1329
  return url.toString();
@@ -18,6 +18,56 @@ export declare class XKOVAMemberResources {
18
18
  get paymentRequests(): import("./resources/index.js").PaymentRequestsResource;
19
19
  get wallets(): import("./resources/index.js").WalletsResource;
20
20
  readonly self: {
21
+ profile: () => Promise<{
22
+ id: import("./generated/types.js").components["schemas"]["AccountHolderId"];
23
+ tenant_id?: string;
24
+ state: "active" | "held" | "closed" | "closed_uncooperative" | "provisioning_failed";
25
+ state_reason?: string | null;
26
+ state_changed_at?: import("./generated/types.js").components["schemas"]["TimestampUtc"] | null;
27
+ tier: "tier_1" | "tier_2";
28
+ tier_2_uplift_status?: "not_requested" | "pending" | "passed" | "failed";
29
+ first_use_completed: boolean;
30
+ cip_payload?: {
31
+ name_hash?: string;
32
+ dob_hash?: string;
33
+ id_hash?: string;
34
+ address_hash?: string;
35
+ } | null;
36
+ cip_accepted_at?: import("./generated/types.js").components["schemas"]["TimestampUtc"] | null;
37
+ merchant_role_state?: "none" | "pending_review" | "approved" | "rejected" | "revoked";
38
+ auto_save_contacts?: boolean;
39
+ created_at: import("./generated/types.js").components["schemas"]["TimestampUtc"];
40
+ updated_at?: import("./generated/types.js").components["schemas"]["TimestampUtc"];
41
+ email?: string | null;
42
+ first_name?: string | null;
43
+ last_name?: string | null;
44
+ phone?: string | null;
45
+ avatar_url?: string | null;
46
+ }>;
47
+ productEnrollment: (product: string, control?: XKOVARequestControl) => Promise<{
48
+ product: string;
49
+ status: "not_opened" | "setup_required" | "opening" | "active" | "requires_review";
50
+ terms_version: string | null;
51
+ accepted_at: string | null;
52
+ credit: {
53
+ amount: string;
54
+ token_deployment_id: string;
55
+ status: "pending" | "submitted" | "settled" | "requires_review";
56
+ transaction_hash: string | null;
57
+ } | null;
58
+ }>;
59
+ acceptProductEnrollment: (product: string, body: ReqBody<"acceptAccountHolderSelfProductEnrollment">, opts?: XKOVAMutationOptions) => Promise<{
60
+ product: string;
61
+ status: "not_opened" | "setup_required" | "opening" | "active" | "requires_review";
62
+ terms_version: string | null;
63
+ accepted_at: string | null;
64
+ credit: {
65
+ amount: string;
66
+ token_deployment_id: string;
67
+ status: "pending" | "submitted" | "settled" | "requires_review";
68
+ transaction_hash: string | null;
69
+ } | null;
70
+ }>;
21
71
  actionReadiness: (control?: XKOVARequestControl) => Promise<{
22
72
  authorization_method: "hosted_passkey" | "external_oidc";
23
73
  setup_status: "passkey_required" | "recovery_acknowledgement_required" | "recovery_required" | "ready" | "externally_managed";
@@ -31,6 +31,9 @@ export class XKOVAMemberResources {
31
31
  return this.client.wallets;
32
32
  }
33
33
  self = {
34
+ profile: () => this.client.accountHolders.retrieve(this.accountHolderId),
35
+ productEnrollment: (product, control) => this.client.accountHolders.selfProductEnrollment(this.context, product, control),
36
+ acceptProductEnrollment: (product, body, opts) => this.client.accountHolders.acceptSelfProductEnrollment(this.context, product, body, opts),
34
37
  actionReadiness: (control) => this.client.accountHolders.selfActionReadiness({ ...this.context, accountHolderId: this.accountHolderId }, control),
35
38
  sandboxBankingContext: (control) => this.client.accountHolders.selfSandboxBankingContext(this.context, control),
36
39
  verification: (control) => this.client.accountHolders.selfVerification(this.context, control),
@@ -118,6 +118,30 @@ export declare class AccountHoldersResource extends Resource {
118
118
  }[];
119
119
  verification_url: string | null;
120
120
  }>;
121
+ selfProductEnrollment(context: XKOVAMemberSessionContext, product: string, control?: XKOVARequestControl): Promise<{
122
+ product: string;
123
+ status: "not_opened" | "setup_required" | "opening" | "active" | "requires_review";
124
+ terms_version: string | null;
125
+ accepted_at: string | null;
126
+ credit: {
127
+ amount: string;
128
+ token_deployment_id: string;
129
+ status: "pending" | "submitted" | "settled" | "requires_review";
130
+ transaction_hash: string | null;
131
+ } | null;
132
+ }>;
133
+ acceptSelfProductEnrollment(context: XKOVAMemberSessionContext, product: string, body: ReqBody<'acceptAccountHolderSelfProductEnrollment'>, opts?: XKOVAMutationOptions): Promise<{
134
+ product: string;
135
+ status: "not_opened" | "setup_required" | "opening" | "active" | "requires_review";
136
+ terms_version: string | null;
137
+ accepted_at: string | null;
138
+ credit: {
139
+ amount: string;
140
+ token_deployment_id: string;
141
+ status: "pending" | "submitted" | "settled" | "requires_review";
142
+ transaction_hash: string | null;
143
+ } | null;
144
+ }>;
121
145
  selfBalance(context: XKOVAMemberSessionContext, control?: XKOVARequestControl): Promise<{
122
146
  balances: import("../generated/types.js").components["schemas"]["AccountHolderSelfBalanceEntry"][];
123
147
  }>;
@@ -1,5 +1,6 @@
1
1
  import { Resource } from './base.js';
2
2
  import { XKOVAUploadError } from '../errors.js';
3
+ import { isAllowedProtocol } from '../url.js';
3
4
  function memberSessionHeaders(context) {
4
5
  return {
5
6
  'X-Member-Session-Id': context.memberSessionId,
@@ -30,6 +31,12 @@ export class AccountHoldersResource extends Resource {
30
31
  selfVerification(context, control) {
31
32
  return this.getJson('/account-holders/self/verification', undefined, control, memberSessionHeaders(context));
32
33
  }
34
+ selfProductEnrollment(context, product, control) {
35
+ return this.getJson(`/account-holders/self/product-enrollments/${this.enc(product)}`, undefined, control, memberSessionHeaders(context));
36
+ }
37
+ acceptSelfProductEnrollment(context, product, body, opts) {
38
+ return this.postJson(`/account-holders/self/product-enrollments/${this.enc(product)}`, body, opts, undefined, memberSessionHeaders(context));
39
+ }
33
40
  selfBalance(context, control) {
34
41
  return this.getJson('/account-holders/self/balance', undefined, control, memberSessionHeaders(context));
35
42
  }
@@ -115,8 +122,7 @@ export class AccountHoldersResource extends Resource {
115
122
  if (target.username ||
116
123
  target.password ||
117
124
  target.hash ||
118
- (target.protocol !== 'https:' &&
119
- !(target.protocol === 'http:' && target.hostname === 'localhost')) ||
125
+ !isAllowedProtocol(target) ||
120
126
  !Number.isFinite(Date.parse(upload.expires_at)) ||
121
127
  Date.parse(upload.expires_at) <= Date.now()) {
122
128
  throw new XKOVAUploadError('The avatar upload capability is invalid or expired');
@@ -178,5 +178,18 @@ export declare class PaymentsResource extends Resource {
178
178
  created_at: import("../generated/types.js").components["schemas"]["TimestampUtc"];
179
179
  finalized_at: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
180
180
  }>;
181
+ /** Requires a resolved member session. Only the sender can continue or cancel. */
182
+ recovery(id: string): Promise<{
183
+ payment: import("../generated/types.js").components["schemas"]["Payment"];
184
+ state: "awaiting_approval" | "approval_expired" | "on_hold" | "processing" | "completed" | "cancelled" | "failed";
185
+ can_continue: boolean;
186
+ can_cancel: boolean;
187
+ }>;
188
+ cancelForMember(id: string, opts?: XKOVAMutationOptions): Promise<{
189
+ payment: import("../generated/types.js").components["schemas"]["Payment"];
190
+ state: "awaiting_approval" | "approval_expired" | "on_hold" | "processing" | "completed" | "cancelled" | "failed";
191
+ can_continue: boolean;
192
+ can_cancel: boolean;
193
+ }>;
181
194
  }
182
195
  export {};
@@ -30,4 +30,11 @@ export class PaymentsResource extends Resource {
30
30
  cancel(id, opts) {
31
31
  return this.postJson(`/payments/${this.enc(id)}/cancel`, undefined, opts);
32
32
  }
33
+ /** Requires a resolved member session. Only the sender can continue or cancel. */
34
+ recovery(id) {
35
+ return this.getJson(`/account-holders/self/payments/${this.enc(id)}/recovery`);
36
+ }
37
+ cancelForMember(id, opts) {
38
+ return this.postJson(`/account-holders/self/payments/${this.enc(id)}/cancel`, undefined, opts);
39
+ }
33
40
  }
@@ -11,31 +11,34 @@ export declare class RampsResource extends Resource {
11
11
  has_more: boolean;
12
12
  }>;
13
13
  createFunding(body: ReqBody<'createFundingRequest'>, opts?: RampRequestOptions): Promise<{
14
- id?: import("../generated/types.js").components["schemas"]["FundingRequestId"];
15
- tenant_id?: string;
16
- workspace_id?: string;
14
+ id: import("../generated/types.js").components["schemas"]["FundingRequestId"];
15
+ tenant_id: string;
16
+ workspace_id: string;
17
17
  chain: import("../generated/types.js").components["schemas"]["ChainDescriptor"];
18
- account_holder_id?: import("../generated/types.js").components["schemas"]["AccountHolderId"];
19
- amount?: import("../generated/types.js").components["schemas"]["Money"];
20
- currency?: string;
21
- state?: "pending_compliance" | "pending_issuance" | "settled" | "failed";
22
- failure_reason_code?: string | null;
23
- x_partner_reference?: string | null;
24
- quote_id?: string | null;
25
- bank_debit_transfer_id?: string | null;
26
- partner_status?: string | null;
27
- last_event_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
28
- metadata?: {
18
+ account_holder_id: import("../generated/types.js").components["schemas"]["AccountHolderId"];
19
+ amount: import("../generated/types.js").components["schemas"]["Money"];
20
+ currency: string;
21
+ state: "pending_compliance" | "pending_issuance" | "settled" | "failed";
22
+ failure_reason_code: string | null;
23
+ x_partner_reference: string | null;
24
+ quote_id: string | null;
25
+ quote_rate: string | null;
26
+ quote_stablecoin_amount: import("../generated/types.js").components["schemas"]["Money"] | null;
27
+ quote_partner_fee_fiat: import("../generated/types.js").components["schemas"]["Money"] | null;
28
+ bank_debit_transfer_id: string | null;
29
+ partner_status: string | null;
30
+ last_event_at: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
31
+ metadata: {
29
32
  [key: string]: unknown;
30
33
  } | null;
31
34
  settlement_order: "hold_first" | "debit_first" | null;
32
- idempotency_key?: string;
33
- correlation_id?: string;
34
- ramp_profile?: "mock" | "sandbox" | "coinbase";
35
- hosted_redirect_url?: string | null;
36
- created_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"];
37
- updated_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"];
38
- settled_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
35
+ idempotency_key: string;
36
+ correlation_id: string;
37
+ ramp_profile: "mock" | "sandbox" | "coinbase";
38
+ hosted_redirect_url: string | null;
39
+ created_at: import("../generated/types.js").components["schemas"]["TimestampUtc"];
40
+ updated_at: import("../generated/types.js").components["schemas"]["TimestampUtc"];
41
+ settled_at: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
39
42
  }>;
40
43
  /**
41
44
  * One-call sandbox funding convenience. This invokes the normal funding
@@ -43,58 +46,64 @@ export declare class RampsResource extends Resource {
43
46
  * resource, state transitions, webhooks, and idempotency behavior.
44
47
  */
45
48
  fundSandboxWallet(body: SandboxFundingBody, opts?: RampRequestOptions): Promise<{
46
- id?: import("../generated/types.js").components["schemas"]["FundingRequestId"];
47
- tenant_id?: string;
48
- workspace_id?: string;
49
+ id: import("../generated/types.js").components["schemas"]["FundingRequestId"];
50
+ tenant_id: string;
51
+ workspace_id: string;
49
52
  chain: import("../generated/types.js").components["schemas"]["ChainDescriptor"];
50
- account_holder_id?: import("../generated/types.js").components["schemas"]["AccountHolderId"];
51
- amount?: import("../generated/types.js").components["schemas"]["Money"];
52
- currency?: string;
53
- state?: "pending_compliance" | "pending_issuance" | "settled" | "failed";
54
- failure_reason_code?: string | null;
55
- x_partner_reference?: string | null;
56
- quote_id?: string | null;
57
- bank_debit_transfer_id?: string | null;
58
- partner_status?: string | null;
59
- last_event_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
60
- metadata?: {
53
+ account_holder_id: import("../generated/types.js").components["schemas"]["AccountHolderId"];
54
+ amount: import("../generated/types.js").components["schemas"]["Money"];
55
+ currency: string;
56
+ state: "pending_compliance" | "pending_issuance" | "settled" | "failed";
57
+ failure_reason_code: string | null;
58
+ x_partner_reference: string | null;
59
+ quote_id: string | null;
60
+ quote_rate: string | null;
61
+ quote_stablecoin_amount: import("../generated/types.js").components["schemas"]["Money"] | null;
62
+ quote_partner_fee_fiat: import("../generated/types.js").components["schemas"]["Money"] | null;
63
+ bank_debit_transfer_id: string | null;
64
+ partner_status: string | null;
65
+ last_event_at: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
66
+ metadata: {
61
67
  [key: string]: unknown;
62
68
  } | null;
63
69
  settlement_order: "hold_first" | "debit_first" | null;
64
- idempotency_key?: string;
65
- correlation_id?: string;
66
- ramp_profile?: "mock" | "sandbox" | "coinbase";
67
- hosted_redirect_url?: string | null;
68
- created_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"];
69
- updated_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"];
70
- settled_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
70
+ idempotency_key: string;
71
+ correlation_id: string;
72
+ ramp_profile: "mock" | "sandbox" | "coinbase";
73
+ hosted_redirect_url: string | null;
74
+ created_at: import("../generated/types.js").components["schemas"]["TimestampUtc"];
75
+ updated_at: import("../generated/types.js").components["schemas"]["TimestampUtc"];
76
+ settled_at: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
71
77
  }>;
72
78
  getFunding(id: string): Promise<{
73
- id?: import("../generated/types.js").components["schemas"]["FundingRequestId"];
74
- tenant_id?: string;
75
- workspace_id?: string;
79
+ id: import("../generated/types.js").components["schemas"]["FundingRequestId"];
80
+ tenant_id: string;
81
+ workspace_id: string;
76
82
  chain: import("../generated/types.js").components["schemas"]["ChainDescriptor"];
77
- account_holder_id?: import("../generated/types.js").components["schemas"]["AccountHolderId"];
78
- amount?: import("../generated/types.js").components["schemas"]["Money"];
79
- currency?: string;
80
- state?: "pending_compliance" | "pending_issuance" | "settled" | "failed";
81
- failure_reason_code?: string | null;
82
- x_partner_reference?: string | null;
83
- quote_id?: string | null;
84
- bank_debit_transfer_id?: string | null;
85
- partner_status?: string | null;
86
- last_event_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
87
- metadata?: {
83
+ account_holder_id: import("../generated/types.js").components["schemas"]["AccountHolderId"];
84
+ amount: import("../generated/types.js").components["schemas"]["Money"];
85
+ currency: string;
86
+ state: "pending_compliance" | "pending_issuance" | "settled" | "failed";
87
+ failure_reason_code: string | null;
88
+ x_partner_reference: string | null;
89
+ quote_id: string | null;
90
+ quote_rate: string | null;
91
+ quote_stablecoin_amount: import("../generated/types.js").components["schemas"]["Money"] | null;
92
+ quote_partner_fee_fiat: import("../generated/types.js").components["schemas"]["Money"] | null;
93
+ bank_debit_transfer_id: string | null;
94
+ partner_status: string | null;
95
+ last_event_at: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
96
+ metadata: {
88
97
  [key: string]: unknown;
89
98
  } | null;
90
99
  settlement_order: "hold_first" | "debit_first" | null;
91
- idempotency_key?: string;
92
- correlation_id?: string;
93
- ramp_profile?: "mock" | "sandbox" | "coinbase";
94
- hosted_redirect_url?: string | null;
95
- created_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"];
96
- updated_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"];
97
- settled_at?: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
100
+ idempotency_key: string;
101
+ correlation_id: string;
102
+ ramp_profile: "mock" | "sandbox" | "coinbase";
103
+ hosted_redirect_url: string | null;
104
+ created_at: import("../generated/types.js").components["schemas"]["TimestampUtc"];
105
+ updated_at: import("../generated/types.js").components["schemas"]["TimestampUtc"];
106
+ settled_at: import("../generated/types.js").components["schemas"]["TimestampUtc"] | null;
98
107
  }>;
99
108
  listOffRamp(query?: Query<'listOffRampRequests'>): Promise<{
100
109
  data: import("../generated/types.js").components["schemas"]["OffRampRequest"][];
@@ -75,10 +75,10 @@ export declare class RwaResource extends Resource {
75
75
  bound_at: string;
76
76
  }>;
77
77
  proposeSchemaRebind(body: ReqBody<'proposeRwaSchemaRebind'>, opts?: XKOVAMutationOptions): Promise<{
78
- ceremony_id: string;
78
+ ceremony_id: import("../generated/types.js").components["schemas"]["SchemaRebindId"];
79
79
  }>;
80
80
  getSchemaRebind(id: string): Promise<{
81
- id: string;
81
+ id: import("../generated/types.js").components["schemas"]["SchemaRebindId"];
82
82
  status: "proposed" | "dry_run_passed" | "dry_run_failed" | "attested" | "committed" | "failed";
83
83
  asset_schema_id: string;
84
84
  from_version: number;
@@ -119,7 +119,7 @@ export declare class RwaResource extends Resource {
119
119
  submitAttestedEntry(tokenId: string, fieldPath: string, body: ReqBody<'submitRwaAttestedEntry'>, opts?: XKOVAMutationOptions): Promise<{
120
120
  committed: boolean;
121
121
  deduped: boolean;
122
- attester_id: string;
122
+ attester_id: import("../generated/types.js").components["schemas"]["RwaAttesterId"];
123
123
  attester_address: string;
124
124
  attested_digest: string;
125
125
  }>;
@@ -183,7 +183,7 @@ export declare class RwaResource extends Resource {
183
183
  created_at: string;
184
184
  }>;
185
185
  createCorporateAction(tokenId: string, body: ReqBody<'createRwaCorporateAction'>, opts?: XKOVAMutationOptions): Promise<{
186
- id: string;
186
+ id: import("../generated/types.js").components["schemas"]["CorporateActionId"];
187
187
  customer_token_id: string;
188
188
  kind: "redemption" | "split" | "consolidation" | "additional_issuance" | "capital_call" | "mandatory_conversion";
189
189
  state: string;
@@ -201,7 +201,7 @@ export declare class RwaResource extends Resource {
201
201
  created_at: string;
202
202
  }>;
203
203
  getCorporateAction(tokenId: string, caId: string): Promise<{
204
- id: string;
204
+ id: import("../generated/types.js").components["schemas"]["CorporateActionId"];
205
205
  customer_token_id: string;
206
206
  kind: "redemption" | "split" | "consolidation" | "additional_issuance" | "capital_call" | "mandatory_conversion";
207
207
  state: string;
@@ -219,7 +219,7 @@ export declare class RwaResource extends Resource {
219
219
  created_at: string;
220
220
  }>;
221
221
  advanceCorporateAction(tokenId: string, caId: string, opts?: XKOVAMutationOptions): Promise<{
222
- id: string;
222
+ id: import("../generated/types.js").components["schemas"]["CorporateActionId"];
223
223
  customer_token_id: string;
224
224
  kind: "redemption" | "split" | "consolidation" | "additional_issuance" | "capital_call" | "mandatory_conversion";
225
225
  state: string;
@@ -285,7 +285,7 @@ export declare class RwaResource extends Resource {
285
285
  reason: string;
286
286
  }>;
287
287
  listAttesters(tokenId: string): Promise<{
288
- id: string;
288
+ id: import("../generated/types.js").components["schemas"]["RwaAttesterId"];
289
289
  customer_token_id: string | null;
290
290
  display_name: string;
291
291
  attester_address: string;
@@ -294,7 +294,7 @@ export declare class RwaResource extends Resource {
294
294
  created_at: string;
295
295
  }[]>;
296
296
  registerAttester(tokenId: string, body: ReqBody<'registerRwaAttester'>, opts?: XKOVAMutationOptions): Promise<{
297
- id: string;
297
+ id: import("../generated/types.js").components["schemas"]["RwaAttesterId"];
298
298
  customer_token_id: string | null;
299
299
  display_name: string;
300
300
  attester_address: string;
@@ -303,7 +303,7 @@ export declare class RwaResource extends Resource {
303
303
  created_at: string;
304
304
  }>;
305
305
  revokeAttester(tokenId: string, attesterId: string, opts?: XKOVAMutationOptions): Promise<{
306
- id: string;
306
+ id: import("../generated/types.js").components["schemas"]["RwaAttesterId"];
307
307
  customer_token_id: string | null;
308
308
  display_name: string;
309
309
  attester_address: string;
package/dist/url.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function isAllowedProtocol(url: URL): boolean;
package/dist/url.js ADDED
@@ -0,0 +1,4 @@
1
+ export function isAllowedProtocol(url) {
2
+ return (url.protocol === 'https:' ||
3
+ (url.protocol === 'http:' && (url.hostname === 'localhost' || url.hostname === '127.0.0.1')));
4
+ }
package/llms.txt CHANGED
@@ -74,6 +74,26 @@ API availability does not grant permission. Product entitlement, selected API-ke
74
74
  - Reuse one createMemberActionReference() from @xkova/browser for a durable review. Save the draft before setup detours. Expired or missing reviews require checking activity and an explicit new review.
75
75
  - Setup readiness is advisory. beginMemberAction() and completeMemberAction() obtain fresh approval for the exact resource. Browser code must not receive provider wallet credentials or internal signing capabilities. Completion of authorization is distinct from execution and settlement.
76
76
 
77
+ ## Payment recovery
78
+
79
+ Use resolved.resources.payments.recovery(paymentId) to read the current
80
+ member's payment and available actions. Only the sender can continue or cancel.
81
+ On explicit cancellation, call cancelForMember(paymentId) after BFF session,
82
+ Origin and CSRF checks. The principal needs payment.read and payment.cancel.
83
+ Cancellation sends no value and does not require a passkey.
84
+
85
+ Continue only when can_continue is true. Begin a fresh member action for the
86
+ same payment resourceId with action sign, then complete the registered proof
87
+ ceremony. Never create another payment, change its fees or deadline, or reuse
88
+ an expired signing credential. A new session for the same member can recover
89
+ the payment without its original draft. Expired approvals remain cancellable;
90
+ accepted signatures close cancellation. Keep cancelled payments in history.
91
+ After a conflict or uncertain cancellation, re-read state before another action.
92
+
77
93
  ## Before handing off code
78
94
 
79
95
  List the Console setup and stores the application needs. Test denied access, uncertain writes, pagination, cancellation, and terminal outcomes. Report a missing method or setup requirement explicitly. Do not invent methods, suppress type errors, or bypass authorization to make an example run.
96
+
97
+ ## Product opt-in
98
+
99
+ Use resolveResources().self.productEnrollment(product) to read acceptance and credit status. After explicit consent, call self.acceptProductEnrollment(product, { terms_version, sandbox_credit? }). The BFF chooses the product, terms, amount, and token. Reads do not enroll members. Acceptance needs no Idempotency-Key header; reuse the same product and parameters when retrying. Sandbox credits are dUSDC only and issued once per workspace/member/product; the workspace daily budget is 1000 dUSDC. Poll opening until active. Treat requires_review as unresolved delivery, never as permission to request a second product credit.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xkova/sdk",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0-alpha.4",
4
4
  "description": "TypeScript SDK for the XKOVA API.",
5
5
  "homepage": "https://docs.xkova.com",
6
6
  "repository": {