@xkova/sdk 0.1.0-alpha.2 → 0.1.0-alpha.3
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 +81 -22
- package/dist/client.js +3 -2
- package/dist/generated/types.d.ts +231 -56
- package/dist/member-action-intents.js +2 -2
- package/dist/member-bff.d.ts +0 -16
- package/dist/member-bff.js +10 -70
- package/dist/member-resources.d.ts +50 -0
- package/dist/member-resources.js +3 -0
- package/dist/resources/account-holders.d.ts +24 -0
- package/dist/resources/account-holders.js +8 -2
- package/dist/resources/payments.d.ts +13 -0
- package/dist/resources/payments.js +7 -0
- package/dist/url.d.ts +1 -0
- package/dist/url.js +4 -0
- package/llms.txt +20 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -197,26 +197,17 @@ For your own OIDC provider, use `beginAuthorization` and
|
|
|
197
197
|
code unless your integration explicitly assigns that exchange to XKOVA. Both
|
|
198
198
|
sign-in paths use the same XKOVA session and store contracts.
|
|
199
199
|
|
|
200
|
-
###
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
`
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
Keep the same pending reservation and transaction after an ambiguous admission
|
|
213
|
-
failure. Repeating the method uses the same idempotency key and recovers the
|
|
214
|
-
same demo account reservation. The method never retries automatically.
|
|
215
|
-
`isDefinitiveXKOVASandboxDemoAdmissionFailure(error)` identifies confirmed
|
|
216
|
-
rejection before an account was reserved; network, malformed-response, and
|
|
217
|
-
unknown server failures can leave the reservation outcome uncertain.
|
|
218
|
-
API errors retain their status, correlation ID, and Retry-After. Both admission
|
|
219
|
-
and session requests reject HTTP redirects.
|
|
200
|
+
### Local development
|
|
201
|
+
|
|
202
|
+
The SDK accepts HTTP API URLs, browser origins, callbacks, and storage URLs on
|
|
203
|
+
`localhost` and `127.0.0.1`. Remote hosts require HTTPS. Register each origin
|
|
204
|
+
and callback with its exact scheme, host, port, and path. These two local hosts
|
|
205
|
+
are distinct origins; cookies and browser storage are not shared between them.
|
|
206
|
+
|
|
207
|
+
For local passkeys, serve the authentication page on `localhost` and use
|
|
208
|
+
`localhost` as its relying-party ID. An IP address cannot be a passkey
|
|
209
|
+
relying-party ID. Keep API keys and member sessions in your BFF, including
|
|
210
|
+
during local development.
|
|
220
211
|
|
|
221
212
|
### Stores and callback recovery
|
|
222
213
|
|
|
@@ -344,6 +335,10 @@ Business requests use `no-store` and reject redirects so credentials cannot
|
|
|
344
335
|
follow a redirect. Network failures preserve the application session. Reuse the
|
|
345
336
|
original idempotency key when reconciling an uncertain write.
|
|
346
337
|
|
|
338
|
+
`resolved.resources.self.profile()` returns the current member's profile,
|
|
339
|
+
including email and name. These fields are display metadata, not authentication
|
|
340
|
+
proof. Read the profile when displaying it instead of caching it in the session.
|
|
341
|
+
|
|
347
342
|
`resolved.resources.self.verification()` returns the member's verification
|
|
348
343
|
requirements and sessions through the public self-verification operation.
|
|
349
344
|
`resolved.resources.self.uploadAvatar(image)` performs presign, object upload,
|
|
@@ -351,7 +346,7 @@ confirmation, and refreshed avatar lookup. Pass a nonempty PNG, JPEG, WebP, or G
|
|
|
351
346
|
`Blob` of at most 5 MiB. Storage receives only the image bytes and their content
|
|
352
347
|
headers, never bearer credentials, request signatures, member context, or cookies.
|
|
353
348
|
The helper rejects redirects, expired capabilities, URL credentials, and non-HTTPS
|
|
354
|
-
storage URLs (localhost
|
|
349
|
+
storage URLs (HTTP on `localhost` or `127.0.0.1` is permitted for local development). It does not
|
|
355
350
|
retry an upload. `XKOVAUploadError` identifies the object-storage stage without
|
|
356
351
|
exposing the signed URL; API failures retain `XKOVAApiError` metadata.
|
|
357
352
|
|
|
@@ -371,7 +366,44 @@ Durable application cookies use the host-only `__Host-` prefix, `Secure`, `HttpO
|
|
|
371
366
|
|
|
372
367
|
The publishable key is a non-secret application registration identifier. It grants no member or tenant authority by itself. The registered application pins tenant, workspace, environment, provider kind, issuer, audience, origins, redirects, and session policy, and must also match the authenticated workspace principal.
|
|
373
368
|
|
|
374
|
-
|
|
369
|
+
### Recover an existing member payment
|
|
370
|
+
|
|
371
|
+
Read recovery state through the resolved member resources:
|
|
372
|
+
|
|
373
|
+
```ts
|
|
374
|
+
const recovery = await resolved.resources.payments.recovery(paymentId);
|
|
375
|
+
// Call only after the member confirms cancellation in your app.
|
|
376
|
+
if (recovery.can_cancel) {
|
|
377
|
+
await resolved.resources.payments.cancelForMember(paymentId);
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
The API permits reads by the sender or recipient. Only the sender can cancel
|
|
382
|
+
or continue. The API principal needs `payment.read` and `payment.cancel`
|
|
383
|
+
permissions for these operations. Your BFF must validate the session, exact
|
|
384
|
+
Origin, and CSRF token before cancellation. Cancellation does not require a
|
|
385
|
+
passkey because it sends no value.
|
|
386
|
+
|
|
387
|
+
When `can_continue` is true, use `beginMemberAction()` with
|
|
388
|
+
`resourceType: 'payment'`, the existing `resourceId`, and `action: 'sign'`.
|
|
389
|
+
Complete fresh authorization through the registered provider. Keep the original
|
|
390
|
+
amount, recipient, fees, and deadline. Do not create a second payment or reuse
|
|
391
|
+
an expired signing credential. Recovery uses the payment ID, so a new valid
|
|
392
|
+
session for the same member can recover it without the original form draft.
|
|
393
|
+
|
|
394
|
+
`approval_expired` means the signing window has closed. It remains cancellable
|
|
395
|
+
until a signature has been accepted. After acceptance, `processing` disables
|
|
396
|
+
both actions. `cancelled`, `completed`, and `failed` are separate outcomes;
|
|
397
|
+
retain all three in history. These actions apply to payments; escrow
|
|
398
|
+
cancellation has its own authorization and settlement rules.
|
|
399
|
+
|
|
400
|
+
Availability can change between a read and a click. After a conflict or an
|
|
401
|
+
uncertain cancellation response, read recovery state again. Repeating an
|
|
402
|
+
already completed member cancellation returns the cancelled payment. Aborting
|
|
403
|
+
an HTTP request or cancelling an authorization ceremony does not cancel the
|
|
404
|
+
payment.
|
|
405
|
+
|
|
406
|
+
## Request cancellation and deadlines
|
|
375
407
|
|
|
376
408
|
Set `timeoutMs` on the client for a default request deadline. Mutation options and workflow polling also accept an `AbortSignal`:
|
|
377
409
|
|
|
@@ -386,6 +418,33 @@ await xkova.payments.cancel('pay_...', {
|
|
|
386
418
|
|
|
387
419
|
The SDK does not automatically retry writes. Use `withRetry` with a stable idempotency key when retry behavior is appropriate for your application.
|
|
388
420
|
|
|
421
|
+
## Product enrollment
|
|
422
|
+
|
|
423
|
+
After a member accepts your displayed terms, call
|
|
424
|
+
`resources.self.acceptProductEnrollment(product, { terms_version })` from the
|
|
425
|
+
BFF. Select the product and terms version on your server. Read the saved result
|
|
426
|
+
with `resources.self.productEnrollment(product)` when the member returns.
|
|
427
|
+
Acceptance does not require an `Idempotency-Key` header. Repeating the same
|
|
428
|
+
acceptance preserves its timestamp. Changed terms or credit parameters with the
|
|
429
|
+
same product are rejected.
|
|
430
|
+
|
|
431
|
+
Sandbox applications can include `sandbox_credit: { token_deployment_id,
|
|
432
|
+
amount }` to issue a test-token welcome credit. Use an active dUSDC deployment.
|
|
433
|
+
The credit is issued once per member, workspace, and product, across application
|
|
434
|
+
registrations. The daily workspace budget is 1000 dUSDC. Keep amounts and token
|
|
435
|
+
selection in the BFF, and give each product a stable identifier.
|
|
436
|
+
|
|
437
|
+
For Hosted Auth, set `require_passkey: true` to hold activation and the credit
|
|
438
|
+
until the member has an active passkey and has acknowledged recovery codes.
|
|
439
|
+
`setup_required` means terms are saved and security setup remains incomplete.
|
|
440
|
+
Open Account Center with the `security` intent, then read the enrollment again
|
|
441
|
+
on return. The setup requirement is fixed when the enrollment is accepted.
|
|
442
|
+
|
|
443
|
+
Poll the enrollment after acceptance. `opening` means delivery is pending;
|
|
444
|
+
`active` means the credit has a successful receipt and an indexed balance.
|
|
445
|
+
`requires_review` needs operator investigation. Do not create another product
|
|
446
|
+
identifier to retry an uncertain credit. Credits do not debit bank accounts.
|
|
447
|
+
|
|
389
448
|
## Errors
|
|
390
449
|
|
|
391
450
|
`XKOVAApiError.retryAfter` preserves the upstream `Retry-After` header when
|
package/dist/client.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { XKOVAApiError } from './errors.js';
|
|
2
|
+
import { isAllowedProtocol } from './url.js';
|
|
2
3
|
import { generateIdempotencyKey } from './idempotency.js';
|
|
3
4
|
import { signXKOVARequest, validateXKOVARequestSigner, xkovaContentDigest, } from './request-signing.js';
|
|
4
5
|
import { AccountHoldersResource, } from './resources/account-holders.js';
|
|
@@ -240,8 +241,8 @@ function normalizeApiBaseUrl(baseUrl) {
|
|
|
240
241
|
if (url.username || url.password || url.search || url.hash) {
|
|
241
242
|
throw new TypeError('XKOVA SDK baseUrl must be an API origin or /v1 base URL');
|
|
242
243
|
}
|
|
243
|
-
if (
|
|
244
|
-
throw new TypeError('XKOVA SDK baseUrl must use HTTPS or
|
|
244
|
+
if (!isAllowedProtocol(url)) {
|
|
245
|
+
throw new TypeError('XKOVA SDK baseUrl must use HTTPS or loopback HTTP');
|
|
245
246
|
}
|
|
246
247
|
const pathname = url.pathname.replace(/\/+$/, '');
|
|
247
248
|
if (pathname !== '' && pathname !== '/v1') {
|
|
@@ -1115,6 +1115,24 @@ export interface paths {
|
|
|
1115
1115
|
patch?: never;
|
|
1116
1116
|
trace?: never;
|
|
1117
1117
|
};
|
|
1118
|
+
"/account-holders/self/product-enrollments/{product}": {
|
|
1119
|
+
parameters: {
|
|
1120
|
+
query?: never;
|
|
1121
|
+
header?: never;
|
|
1122
|
+
path?: never;
|
|
1123
|
+
cookie?: never;
|
|
1124
|
+
};
|
|
1125
|
+
/** Read the member's product enrollment */
|
|
1126
|
+
get: operations["getAccountHolderSelfProductEnrollment"];
|
|
1127
|
+
put?: never;
|
|
1128
|
+
/** Record product terms acceptance */
|
|
1129
|
+
post: operations["acceptAccountHolderSelfProductEnrollment"];
|
|
1130
|
+
delete?: never;
|
|
1131
|
+
options?: never;
|
|
1132
|
+
head?: never;
|
|
1133
|
+
patch?: never;
|
|
1134
|
+
trace?: never;
|
|
1135
|
+
};
|
|
1118
1136
|
"/account-holders/self/wallet-readiness": {
|
|
1119
1137
|
parameters: {
|
|
1120
1138
|
query?: never;
|
|
@@ -1234,6 +1252,50 @@ export interface paths {
|
|
|
1234
1252
|
patch?: never;
|
|
1235
1253
|
trace?: never;
|
|
1236
1254
|
};
|
|
1255
|
+
"/account-holders/self/payments/{id}/recovery": {
|
|
1256
|
+
parameters: {
|
|
1257
|
+
query?: never;
|
|
1258
|
+
header: {
|
|
1259
|
+
"X-Member-Session-Id": components["parameters"]["xMemberSessionId"];
|
|
1260
|
+
"X-XKOVA-Publishable-Key": components["parameters"]["xXkovaPublishableKey"];
|
|
1261
|
+
};
|
|
1262
|
+
path: {
|
|
1263
|
+
id: components["parameters"]["pathId"];
|
|
1264
|
+
};
|
|
1265
|
+
cookie?: never;
|
|
1266
|
+
};
|
|
1267
|
+
/** Read a member payment and its available actions */
|
|
1268
|
+
get: operations["getMemberPaymentRecovery"];
|
|
1269
|
+
put?: never;
|
|
1270
|
+
post?: never;
|
|
1271
|
+
delete?: never;
|
|
1272
|
+
options?: never;
|
|
1273
|
+
head?: never;
|
|
1274
|
+
patch?: never;
|
|
1275
|
+
trace?: never;
|
|
1276
|
+
};
|
|
1277
|
+
"/account-holders/self/payments/{id}/cancel": {
|
|
1278
|
+
parameters: {
|
|
1279
|
+
query?: never;
|
|
1280
|
+
header: {
|
|
1281
|
+
"X-Member-Session-Id": components["parameters"]["xMemberSessionId"];
|
|
1282
|
+
"X-XKOVA-Publishable-Key": components["parameters"]["xXkovaPublishableKey"];
|
|
1283
|
+
};
|
|
1284
|
+
path: {
|
|
1285
|
+
id: components["parameters"]["pathId"];
|
|
1286
|
+
};
|
|
1287
|
+
cookie?: never;
|
|
1288
|
+
};
|
|
1289
|
+
get?: never;
|
|
1290
|
+
put?: never;
|
|
1291
|
+
/** Cancel the current member's unsigned payment */
|
|
1292
|
+
post: operations["cancelMemberPayment"];
|
|
1293
|
+
delete?: never;
|
|
1294
|
+
options?: never;
|
|
1295
|
+
head?: never;
|
|
1296
|
+
patch?: never;
|
|
1297
|
+
trace?: never;
|
|
1298
|
+
};
|
|
1237
1299
|
"/account-holders/self/transactions": {
|
|
1238
1300
|
parameters: {
|
|
1239
1301
|
query?: never;
|
|
@@ -2711,23 +2773,6 @@ export interface paths {
|
|
|
2711
2773
|
patch?: never;
|
|
2712
2774
|
trace?: never;
|
|
2713
2775
|
};
|
|
2714
|
-
"/member-auth/hosted/authorizations/sandbox-demo": {
|
|
2715
|
-
parameters: {
|
|
2716
|
-
query?: never;
|
|
2717
|
-
header?: never;
|
|
2718
|
-
path?: never;
|
|
2719
|
-
cookie?: never;
|
|
2720
|
-
};
|
|
2721
|
-
get?: never;
|
|
2722
|
-
put?: never;
|
|
2723
|
-
/** Admit a platform-leased sandbox demo persona */
|
|
2724
|
-
post: operations["admitMemberAuthSandboxDemo"];
|
|
2725
|
-
delete?: never;
|
|
2726
|
-
options?: never;
|
|
2727
|
-
head?: never;
|
|
2728
|
-
patch?: never;
|
|
2729
|
-
trace?: never;
|
|
2730
|
-
};
|
|
2731
2776
|
"/member-auth/hosted/authorizations/exchange": {
|
|
2732
2777
|
parameters: {
|
|
2733
2778
|
query?: never;
|
|
@@ -4313,6 +4358,22 @@ export interface components {
|
|
|
4313
4358
|
/** Format: uri */
|
|
4314
4359
|
avatar_url?: string | null;
|
|
4315
4360
|
};
|
|
4361
|
+
MemberProductEnrollment: {
|
|
4362
|
+
product: string;
|
|
4363
|
+
/** @enum {string} */
|
|
4364
|
+
status: "not_opened" | "setup_required" | "opening" | "active" | "requires_review";
|
|
4365
|
+
terms_version: string | null;
|
|
4366
|
+
/** Format: date-time */
|
|
4367
|
+
accepted_at: string | null;
|
|
4368
|
+
credit: {
|
|
4369
|
+
amount: string;
|
|
4370
|
+
/** Format: uuid */
|
|
4371
|
+
token_deployment_id: string;
|
|
4372
|
+
/** @enum {string} */
|
|
4373
|
+
status: "pending" | "submitted" | "settled" | "requires_review";
|
|
4374
|
+
transaction_hash: string | null;
|
|
4375
|
+
} | null;
|
|
4376
|
+
};
|
|
4316
4377
|
CuPayoutAccountRef: {
|
|
4317
4378
|
routing_number: string;
|
|
4318
4379
|
account_number: string;
|
|
@@ -4328,6 +4389,13 @@ export interface components {
|
|
|
4328
4389
|
AccountHolderSelfBalance: {
|
|
4329
4390
|
balances: components["schemas"]["AccountHolderSelfBalanceEntry"][];
|
|
4330
4391
|
};
|
|
4392
|
+
MemberPaymentRecovery: {
|
|
4393
|
+
payment: components["schemas"]["Payment"];
|
|
4394
|
+
/** @enum {string} */
|
|
4395
|
+
state: "awaiting_approval" | "approval_expired" | "on_hold" | "processing" | "completed" | "cancelled" | "failed";
|
|
4396
|
+
can_continue: boolean;
|
|
4397
|
+
can_cancel: boolean;
|
|
4398
|
+
};
|
|
4331
4399
|
AccountHolderSelfTransactionList: {
|
|
4332
4400
|
data: components["schemas"]["AccountHolderSelfTransaction"][];
|
|
4333
4401
|
next_cursor: string | null;
|
|
@@ -7533,6 +7601,14 @@ export interface operations {
|
|
|
7533
7601
|
};
|
|
7534
7602
|
};
|
|
7535
7603
|
400: components["responses"]["BadRequest"];
|
|
7604
|
+
409: {
|
|
7605
|
+
headers: {
|
|
7606
|
+
[name: string]: unknown;
|
|
7607
|
+
};
|
|
7608
|
+
content: {
|
|
7609
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
7610
|
+
};
|
|
7611
|
+
};
|
|
7536
7612
|
422: {
|
|
7537
7613
|
headers: {
|
|
7538
7614
|
[name: string]: unknown;
|
|
@@ -8063,6 +8139,74 @@ export interface operations {
|
|
|
8063
8139
|
403: components["responses"]["Forbidden"];
|
|
8064
8140
|
};
|
|
8065
8141
|
};
|
|
8142
|
+
getAccountHolderSelfProductEnrollment: {
|
|
8143
|
+
parameters: {
|
|
8144
|
+
query?: never;
|
|
8145
|
+
header: {
|
|
8146
|
+
"X-Member-Session-Id": components["parameters"]["xMemberSessionId"];
|
|
8147
|
+
"X-XKOVA-Publishable-Key": components["parameters"]["xXkovaPublishableKey"];
|
|
8148
|
+
};
|
|
8149
|
+
path: {
|
|
8150
|
+
product: string;
|
|
8151
|
+
};
|
|
8152
|
+
cookie?: never;
|
|
8153
|
+
};
|
|
8154
|
+
requestBody?: never;
|
|
8155
|
+
responses: {
|
|
8156
|
+
200: {
|
|
8157
|
+
headers: {
|
|
8158
|
+
[name: string]: unknown;
|
|
8159
|
+
};
|
|
8160
|
+
content: {
|
|
8161
|
+
"application/json": components["schemas"]["MemberProductEnrollment"];
|
|
8162
|
+
};
|
|
8163
|
+
};
|
|
8164
|
+
401: components["responses"]["Unauthorized"];
|
|
8165
|
+
403: components["responses"]["Forbidden"];
|
|
8166
|
+
};
|
|
8167
|
+
};
|
|
8168
|
+
acceptAccountHolderSelfProductEnrollment: {
|
|
8169
|
+
parameters: {
|
|
8170
|
+
query?: never;
|
|
8171
|
+
header: {
|
|
8172
|
+
"X-Member-Session-Id": components["parameters"]["xMemberSessionId"];
|
|
8173
|
+
"X-XKOVA-Publishable-Key": components["parameters"]["xXkovaPublishableKey"];
|
|
8174
|
+
};
|
|
8175
|
+
path: {
|
|
8176
|
+
product: string;
|
|
8177
|
+
};
|
|
8178
|
+
cookie?: never;
|
|
8179
|
+
};
|
|
8180
|
+
requestBody: {
|
|
8181
|
+
content: {
|
|
8182
|
+
"application/json": {
|
|
8183
|
+
terms_version: string;
|
|
8184
|
+
/** @default false */
|
|
8185
|
+
require_passkey?: boolean;
|
|
8186
|
+
sandbox_credit?: {
|
|
8187
|
+
/** Format: uuid */
|
|
8188
|
+
token_deployment_id: string;
|
|
8189
|
+
amount: string;
|
|
8190
|
+
};
|
|
8191
|
+
};
|
|
8192
|
+
};
|
|
8193
|
+
};
|
|
8194
|
+
responses: {
|
|
8195
|
+
200: {
|
|
8196
|
+
headers: {
|
|
8197
|
+
[name: string]: unknown;
|
|
8198
|
+
};
|
|
8199
|
+
content: {
|
|
8200
|
+
"application/json": components["schemas"]["MemberProductEnrollment"];
|
|
8201
|
+
};
|
|
8202
|
+
};
|
|
8203
|
+
401: components["responses"]["Unauthorized"];
|
|
8204
|
+
403: components["responses"]["Forbidden"];
|
|
8205
|
+
409: components["responses"]["Conflict"];
|
|
8206
|
+
422: components["responses"]["UnprocessableEntity"];
|
|
8207
|
+
429: components["responses"]["TooManyRequests"];
|
|
8208
|
+
};
|
|
8209
|
+
};
|
|
8066
8210
|
getAccountHolderSelfWalletReadiness: {
|
|
8067
8211
|
parameters: {
|
|
8068
8212
|
query?: never;
|
|
@@ -8265,6 +8409,76 @@ export interface operations {
|
|
|
8265
8409
|
403: components["responses"]["Forbidden"];
|
|
8266
8410
|
};
|
|
8267
8411
|
};
|
|
8412
|
+
getMemberPaymentRecovery: {
|
|
8413
|
+
parameters: {
|
|
8414
|
+
query?: never;
|
|
8415
|
+
header: {
|
|
8416
|
+
"X-Member-Session-Id": components["parameters"]["xMemberSessionId"];
|
|
8417
|
+
"X-XKOVA-Publishable-Key": components["parameters"]["xXkovaPublishableKey"];
|
|
8418
|
+
};
|
|
8419
|
+
path: {
|
|
8420
|
+
id: components["parameters"]["pathId"];
|
|
8421
|
+
};
|
|
8422
|
+
cookie?: never;
|
|
8423
|
+
};
|
|
8424
|
+
requestBody?: never;
|
|
8425
|
+
responses: {
|
|
8426
|
+
200: {
|
|
8427
|
+
headers: {
|
|
8428
|
+
[name: string]: unknown;
|
|
8429
|
+
};
|
|
8430
|
+
content: {
|
|
8431
|
+
"application/json": components["schemas"]["MemberPaymentRecovery"];
|
|
8432
|
+
};
|
|
8433
|
+
};
|
|
8434
|
+
401: components["responses"]["Unauthorized"];
|
|
8435
|
+
403: components["responses"]["Forbidden"];
|
|
8436
|
+
404: components["responses"]["NotFound"];
|
|
8437
|
+
};
|
|
8438
|
+
};
|
|
8439
|
+
cancelMemberPayment: {
|
|
8440
|
+
parameters: {
|
|
8441
|
+
query?: never;
|
|
8442
|
+
header: {
|
|
8443
|
+
"X-Member-Session-Id": components["parameters"]["xMemberSessionId"];
|
|
8444
|
+
"X-XKOVA-Publishable-Key": components["parameters"]["xXkovaPublishableKey"];
|
|
8445
|
+
};
|
|
8446
|
+
path: {
|
|
8447
|
+
id: components["parameters"]["pathId"];
|
|
8448
|
+
};
|
|
8449
|
+
cookie?: never;
|
|
8450
|
+
};
|
|
8451
|
+
requestBody?: never;
|
|
8452
|
+
responses: {
|
|
8453
|
+
200: {
|
|
8454
|
+
headers: {
|
|
8455
|
+
[name: string]: unknown;
|
|
8456
|
+
};
|
|
8457
|
+
content: {
|
|
8458
|
+
"application/json": components["schemas"]["MemberPaymentRecovery"];
|
|
8459
|
+
};
|
|
8460
|
+
};
|
|
8461
|
+
401: components["responses"]["Unauthorized"];
|
|
8462
|
+
403: components["responses"]["Forbidden"];
|
|
8463
|
+
404: components["responses"]["NotFound"];
|
|
8464
|
+
409: {
|
|
8465
|
+
headers: {
|
|
8466
|
+
[name: string]: unknown;
|
|
8467
|
+
};
|
|
8468
|
+
content: {
|
|
8469
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
8470
|
+
};
|
|
8471
|
+
};
|
|
8472
|
+
422: {
|
|
8473
|
+
headers: {
|
|
8474
|
+
[name: string]: unknown;
|
|
8475
|
+
};
|
|
8476
|
+
content: {
|
|
8477
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
8478
|
+
};
|
|
8479
|
+
};
|
|
8480
|
+
};
|
|
8481
|
+
};
|
|
8268
8482
|
listAccountHolderSelfTransactions: {
|
|
8269
8483
|
parameters: {
|
|
8270
8484
|
query?: {
|
|
@@ -11574,45 +11788,6 @@ export interface operations {
|
|
|
11574
11788
|
429: components["responses"]["TooManyRequests"];
|
|
11575
11789
|
};
|
|
11576
11790
|
};
|
|
11577
|
-
admitMemberAuthSandboxDemo: {
|
|
11578
|
-
parameters: {
|
|
11579
|
-
query?: never;
|
|
11580
|
-
header: {
|
|
11581
|
-
"Idempotency-Key": components["parameters"]["idempotencyKey"];
|
|
11582
|
-
"X-XKOVA-Publishable-Key": components["parameters"]["xXkovaPublishableKey"];
|
|
11583
|
-
};
|
|
11584
|
-
path?: never;
|
|
11585
|
-
cookie?: never;
|
|
11586
|
-
};
|
|
11587
|
-
requestBody: {
|
|
11588
|
-
content: {
|
|
11589
|
-
"application/json": {
|
|
11590
|
-
transaction: string;
|
|
11591
|
-
};
|
|
11592
|
-
};
|
|
11593
|
-
};
|
|
11594
|
-
responses: {
|
|
11595
|
-
200: {
|
|
11596
|
-
headers: {
|
|
11597
|
-
[name: string]: unknown;
|
|
11598
|
-
};
|
|
11599
|
-
content: {
|
|
11600
|
-
"application/json": {
|
|
11601
|
-
/** Format: uri */
|
|
11602
|
-
redirect_url: string;
|
|
11603
|
-
/** Format: date-time */
|
|
11604
|
-
expires_at: string;
|
|
11605
|
-
};
|
|
11606
|
-
};
|
|
11607
|
-
};
|
|
11608
|
-
401: components["responses"]["Unauthorized"];
|
|
11609
|
-
403: components["responses"]["Forbidden"];
|
|
11610
|
-
409: components["responses"]["Conflict"];
|
|
11611
|
-
422: components["responses"]["UnprocessableEntity"];
|
|
11612
|
-
429: components["responses"]["TooManyRequests"];
|
|
11613
|
-
503: components["responses"]["ServiceUnavailable"];
|
|
11614
|
-
};
|
|
11615
|
-
};
|
|
11616
11791
|
completeMemberAuthHostedAuthorization: {
|
|
11617
11792
|
parameters: {
|
|
11618
11793
|
query?: never;
|
|
@@ -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
|
|
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,
|
package/dist/member-bff.d.ts
CHANGED
|
@@ -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;
|
package/dist/member-bff.js
CHANGED
|
@@ -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:
|
|
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 (
|
|
1343
|
-
throw new TypeError('XKOVA member BFF baseUrl must use HTTPS or
|
|
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 (
|
|
1357
|
-
throw new TypeError('member authorization redirectUri must use HTTPS or
|
|
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
|
-
|
|
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
|
-
|
|
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";
|
package/dist/member-resources.js
CHANGED
|
@@ -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
|
|
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
|
}
|
package/dist/url.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isAllowedProtocol(url: URL): boolean;
|
package/dist/url.js
ADDED
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.
|