@doswiftly/storefront-sdk 16.1.0 → 18.0.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/CHANGELOG.md +1255 -0
- package/README.md +16 -4
- package/dist/core/auth/auth-client.d.ts +39 -3
- package/dist/core/auth/auth-client.d.ts.map +1 -1
- package/dist/core/auth/auth-client.js +51 -3
- package/dist/core/auth/cookie-config.d.ts +52 -3
- package/dist/core/auth/cookie-config.d.ts.map +1 -1
- package/dist/core/auth/cookie-config.js +60 -6
- package/dist/core/auth/handlers.d.ts +46 -0
- package/dist/core/auth/handlers.d.ts.map +1 -1
- package/dist/core/auth/handlers.js +9 -2
- package/dist/core/auth/session-events.d.ts +38 -0
- package/dist/core/auth/session-events.d.ts.map +1 -0
- package/dist/core/auth/session-events.js +35 -0
- package/dist/core/cart/cart-client.d.ts +10 -1
- package/dist/core/cart/cart-client.d.ts.map +1 -1
- package/dist/core/cart/cart-client.js +17 -1
- package/dist/core/cart/cart-recovery.d.ts +23 -0
- package/dist/core/cart/cart-recovery.d.ts.map +1 -1
- package/dist/core/cart/cart-recovery.js +20 -3
- package/dist/core/cart/types.d.ts +2 -1
- package/dist/core/cart/types.d.ts.map +1 -1
- package/dist/core/cart/types.js +7 -1
- package/dist/core/client/create-client.d.ts.map +1 -1
- package/dist/core/client/create-client.js +7 -3
- package/dist/core/client/execute.d.ts +29 -3
- package/dist/core/client/execute.d.ts.map +1 -1
- package/dist/core/client/execute.js +174 -3
- package/dist/core/client/types.d.ts +50 -2
- package/dist/core/client/types.d.ts.map +1 -1
- package/dist/core/errors.d.ts +6 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +6 -0
- package/dist/core/generated/operation-types.d.ts +937 -182
- package/dist/core/generated/operation-types.d.ts.map +1 -1
- package/dist/core/generated/operation-types.js +560 -1
- package/dist/core/index.d.ts +6 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +12 -2
- package/dist/core/middleware/session-retry.d.ts +47 -0
- package/dist/core/middleware/session-retry.d.ts.map +1 -0
- package/dist/core/middleware/session-retry.js +71 -0
- package/dist/core/operations/auth.d.ts.map +1 -1
- package/dist/core/operations/auth.js +1 -0
- package/dist/core/operations/cart.d.ts +7 -0
- package/dist/core/operations/cart.d.ts.map +1 -1
- package/dist/core/operations/cart.js +54 -3
- package/dist/react/components/PaymentInstrumentSection.d.ts +56 -0
- package/dist/react/components/PaymentInstrumentSection.d.ts.map +1 -0
- package/dist/react/components/PaymentInstrumentSection.js +89 -0
- package/dist/react/components/PaymentInstrumentTile.d.ts +56 -0
- package/dist/react/components/PaymentInstrumentTile.d.ts.map +1 -0
- package/dist/react/components/PaymentInstrumentTile.js +41 -0
- package/dist/react/components/index.d.ts +2 -0
- package/dist/react/components/index.d.ts.map +1 -1
- package/dist/react/components/index.js +2 -0
- package/dist/react/helpers/browser-data.d.ts +89 -0
- package/dist/react/helpers/browser-data.d.ts.map +1 -0
- package/dist/react/helpers/browser-data.js +84 -0
- package/dist/react/hooks/use-cart-manager.d.ts +104 -13
- package/dist/react/hooks/use-cart-manager.d.ts.map +1 -1
- package/dist/react/hooks/use-cart-manager.js +144 -12
- package/dist/react/hooks/use-login.d.ts.map +1 -1
- package/dist/react/hooks/use-login.js +3 -3
- package/dist/react/hooks/use-refresh-token.d.ts.map +1 -1
- package/dist/react/hooks/use-refresh-token.js +6 -4
- package/dist/react/hooks/use-session-expired.d.ts +16 -0
- package/dist/react/hooks/use-session-expired.d.ts.map +1 -0
- package/dist/react/hooks/use-session-expired.js +26 -0
- package/dist/react/hooks/use-session-refresh.d.ts +32 -0
- package/dist/react/hooks/use-session-refresh.d.ts.map +1 -0
- package/dist/react/hooks/use-session-refresh.js +147 -0
- package/dist/react/index.d.ts +5 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +5 -1
- package/dist/react/providers/storefront-client-provider.d.ts +10 -1
- package/dist/react/providers/storefront-client-provider.d.ts.map +1 -1
- package/dist/react/providers/storefront-client-provider.js +38 -3
- package/dist/react/providers/storefront-provider.d.ts +51 -3
- package/dist/react/providers/storefront-provider.d.ts.map +1 -1
- package/dist/react/providers/storefront-provider.js +22 -5
- package/dist/react/server/create-storefront-auth-route.d.ts +63 -0
- package/dist/react/server/create-storefront-auth-route.d.ts.map +1 -0
- package/dist/react/server/create-storefront-auth-route.js +239 -0
- package/dist/react/server/get-initial-auth.d.ts +57 -0
- package/dist/react/server/get-initial-auth.d.ts.map +1 -0
- package/dist/react/server/get-initial-auth.js +55 -0
- package/dist/react/server/index.d.ts +3 -0
- package/dist/react/server/index.d.ts.map +1 -1
- package/dist/react/server/index.js +6 -0
- package/dist/react/stores/auth.store.d.ts +46 -2
- package/dist/react/stores/auth.store.d.ts.map +1 -1
- package/dist/react/stores/auth.store.js +19 -7
- package/package.json +4 -2
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-retry middleware — reactive 401 handling (must be OUTERMOST).
|
|
3
|
+
*
|
|
4
|
+
* When a request fails with HTTP 401 (the access token lapsed between the
|
|
5
|
+
* proactive refresh and this call):
|
|
6
|
+
*
|
|
7
|
+
* - **Read query** — refresh the session once and replay the request (R2.1).
|
|
8
|
+
* Because this middleware is the outermost one, the retry re-runs the auth
|
|
9
|
+
* middleware, which re-reads the freshly-stored token. Concurrent 401s share
|
|
10
|
+
* a single renewal because the injected `refresh` dedupes in-flight calls
|
|
11
|
+
* (R2.2). If the refresh also fails, the session is gone — signal and bail
|
|
12
|
+
* (R2.4).
|
|
13
|
+
*
|
|
14
|
+
* - **Mutation** — never auto-retry (mutations are sacred; client-side
|
|
15
|
+
* idempotency is not guaranteed). Bail and signal the session loss (R2.3).
|
|
16
|
+
*
|
|
17
|
+
* Auth operations (login / signup / refresh / logout) are exempt: they own
|
|
18
|
+
* their own error handling, and retrying the refresh op itself would recurse.
|
|
19
|
+
*
|
|
20
|
+
* 0-deps core: takes the renewal + notification as injected callbacks, so it
|
|
21
|
+
* stays framework-agnostic (the provider wires the React store + emitter).
|
|
22
|
+
*
|
|
23
|
+
* NOTE — this fires only on a genuine HTTP 401. The storefront GraphQL data
|
|
24
|
+
* transport returns HTTP 200 with GraphQL errors / `userErrors` for an expired
|
|
25
|
+
* customer token (not a 401), so on that path the proactive scheduler
|
|
26
|
+
* (`useSessionRefresh`) is the protection and this middleware stays inert. It
|
|
27
|
+
* activates wherever a request really does return 401 — e.g. the same-origin
|
|
28
|
+
* BFF refresh route — keeping reactive recovery forward-compatible with no
|
|
29
|
+
* backend change. Cart-level session loss is handled separately by cart-recovery
|
|
30
|
+
* (`CART_UNAUTHENTICATED`).
|
|
31
|
+
*/
|
|
32
|
+
import type { Middleware } from '../client/types';
|
|
33
|
+
import type { SessionExpiredEvent } from '../auth/session-events';
|
|
34
|
+
export interface SessionRetryOptions {
|
|
35
|
+
/**
|
|
36
|
+
* Renew the session (refresh token → cookie sync → store update). Resolves
|
|
37
|
+
* `true` on success. MUST dedupe concurrent calls so simultaneous 401s share
|
|
38
|
+
* a single renewal (R2.2).
|
|
39
|
+
*/
|
|
40
|
+
refresh: () => Promise<boolean>;
|
|
41
|
+
/** Notify that the session is gone (a mutation 401, or a query whose refresh-retry failed). */
|
|
42
|
+
onSessionExpired: (event: SessionExpiredEvent) => void;
|
|
43
|
+
/** Operation names exempt from 401 handling (default: the auth operations). */
|
|
44
|
+
skipOperations?: string[];
|
|
45
|
+
}
|
|
46
|
+
export declare function sessionRetryMiddleware(options: SessionRetryOptions): Middleware;
|
|
47
|
+
//# sourceMappingURL=session-retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-retry.d.ts","sourceRoot":"","sources":["../../../src/core/middleware/session-retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAQlE,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,+FAA+F;IAC/F,gBAAgB,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACvD,+EAA+E;IAC/E,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAMD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,mBAAmB,GAAG,UAAU,CAgC/E"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-retry middleware — reactive 401 handling (must be OUTERMOST).
|
|
3
|
+
*
|
|
4
|
+
* When a request fails with HTTP 401 (the access token lapsed between the
|
|
5
|
+
* proactive refresh and this call):
|
|
6
|
+
*
|
|
7
|
+
* - **Read query** — refresh the session once and replay the request (R2.1).
|
|
8
|
+
* Because this middleware is the outermost one, the retry re-runs the auth
|
|
9
|
+
* middleware, which re-reads the freshly-stored token. Concurrent 401s share
|
|
10
|
+
* a single renewal because the injected `refresh` dedupes in-flight calls
|
|
11
|
+
* (R2.2). If the refresh also fails, the session is gone — signal and bail
|
|
12
|
+
* (R2.4).
|
|
13
|
+
*
|
|
14
|
+
* - **Mutation** — never auto-retry (mutations are sacred; client-side
|
|
15
|
+
* idempotency is not guaranteed). Bail and signal the session loss (R2.3).
|
|
16
|
+
*
|
|
17
|
+
* Auth operations (login / signup / refresh / logout) are exempt: they own
|
|
18
|
+
* their own error handling, and retrying the refresh op itself would recurse.
|
|
19
|
+
*
|
|
20
|
+
* 0-deps core: takes the renewal + notification as injected callbacks, so it
|
|
21
|
+
* stays framework-agnostic (the provider wires the React store + emitter).
|
|
22
|
+
*
|
|
23
|
+
* NOTE — this fires only on a genuine HTTP 401. The storefront GraphQL data
|
|
24
|
+
* transport returns HTTP 200 with GraphQL errors / `userErrors` for an expired
|
|
25
|
+
* customer token (not a 401), so on that path the proactive scheduler
|
|
26
|
+
* (`useSessionRefresh`) is the protection and this middleware stays inert. It
|
|
27
|
+
* activates wherever a request really does return 401 — e.g. the same-origin
|
|
28
|
+
* BFF refresh route — keeping reactive recovery forward-compatible with no
|
|
29
|
+
* backend change. Cart-level session loss is handled separately by cart-recovery
|
|
30
|
+
* (`CART_UNAUTHENTICATED`).
|
|
31
|
+
*/
|
|
32
|
+
import { StorefrontError } from '../errors';
|
|
33
|
+
/**
|
|
34
|
+
* Auth operations that must NOT trigger the 401 retry/signal dance — they
|
|
35
|
+
* surface their own errors, and the refresh op would otherwise recurse.
|
|
36
|
+
*/
|
|
37
|
+
const DEFAULT_SKIP_OPERATIONS = ['CustomerLogin', 'CustomerSignup', 'CustomerRefreshToken', 'CustomerLogout'];
|
|
38
|
+
function isUnauthorized(err) {
|
|
39
|
+
return err instanceof StorefrontError && err.status === 401;
|
|
40
|
+
}
|
|
41
|
+
export function sessionRetryMiddleware(options) {
|
|
42
|
+
const { refresh, onSessionExpired } = options;
|
|
43
|
+
const skip = new Set(options.skipOperations ?? DEFAULT_SKIP_OPERATIONS);
|
|
44
|
+
return async (request, next) => {
|
|
45
|
+
if (request.operationName && skip.has(request.operationName)) {
|
|
46
|
+
return next(request);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
return await next(request);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
if (!isUnauthorized(err))
|
|
53
|
+
throw err;
|
|
54
|
+
if (request.isMutation) {
|
|
55
|
+
// R2.3 — mutations are sacred: bail, never auto-retry.
|
|
56
|
+
onSessionExpired({ reason: 'mutation-unauthorized', cause: err });
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
// R2.1 / R2.2 — read query: refresh once (deduped) and replay.
|
|
60
|
+
const renewed = await refresh();
|
|
61
|
+
if (!renewed) {
|
|
62
|
+
// R2.4 — the refresh also failed; the session is gone.
|
|
63
|
+
onSessionExpired({ reason: 'reactive-refresh-failed', cause: err });
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
// Retry once — this middleware is outermost, so the replay re-runs the
|
|
67
|
+
// auth middleware, which attaches the freshly-stored token.
|
|
68
|
+
return next(request);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/core/operations/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/core/operations/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAqHH,eAAO,MAAM,cAAc,QASzB,CAAC;AAEH,eAAO,MAAM,eAAe,QAS1B,CAAC;AAEH,eAAO,MAAM,sBAAsB,QASjC,CAAC;AAEH,eAAO,MAAM,eAAe,QAW1B,CAAC;AAMH,eAAO,MAAM,cAAc,QAOzB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,QAYnC,CAAC"}
|
|
@@ -73,6 +73,13 @@ export declare const CART_COMPLETE: string;
|
|
|
73
73
|
* `payment.flow` (redirect / embedded / instant) to launch the payment.
|
|
74
74
|
*/
|
|
75
75
|
export declare const PAYMENT_CREATE: string;
|
|
76
|
+
/**
|
|
77
|
+
* cartClearPaymentSelection Mutation (Adv-1 Phase A1-2) — explicit deselect
|
|
78
|
+
* dla storefront accordion UI ("wróć do wyboru metody"). Atomic NULL na
|
|
79
|
+
* wszystkich 4 payment selection fields, idempotent. Cart MUSI być ACTIVE
|
|
80
|
+
* (CONVERTED reject z `ALREADY_COMPLETED`).
|
|
81
|
+
*/
|
|
82
|
+
export declare const CART_CLEAR_PAYMENT_SELECTION: string;
|
|
76
83
|
/**
|
|
77
84
|
* cartValidateDiscountCode Query — read-only preview discount applicability
|
|
78
85
|
* (Decision D3). No cart side effects; storefront UI używa do inline feedback
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cart.d.ts","sourceRoot":"","sources":["../../../src/core/operations/cart.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"cart.d.ts","sourceRoot":"","sources":["../../../src/core/operations/cart.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA2aH,eAAO,MAAM,UAAU,QAOrB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,qCAAqC,QAehD,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,QAO1C,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAO/B,CAAC;AAMH,eAAO,MAAM,WAAW,QAWtB,CAAC;AAEH,eAAO,MAAM,cAAc,QAWzB,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAW5B,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAW5B,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAEH,eAAO,MAAM,gBAAgB,QAW3B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,QAWjC,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAMH,eAAO,MAAM,yBAAyB,QAWpC,CAAC;AAEH,eAAO,MAAM,wBAAwB,QAWnC,CAAC;AAEH,eAAO,MAAM,2BAA2B,QAWtC,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAEH,eAAO,MAAM,oBAAoB,QAW/B,CAAC;AAEH,eAAO,MAAM,qBAAqB,QAWhC,CAAC;AAEH,eAAO,MAAM,+BAA+B,QAW1C,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,QAWxB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,cAAc,QAWzB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,QAWvC,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,QAoBtC,CAAC"}
|
|
@@ -198,12 +198,13 @@ const CART_SELECTED_PAYMENT_METHOD_FRAGMENT = `
|
|
|
198
198
|
name
|
|
199
199
|
provider
|
|
200
200
|
type
|
|
201
|
-
icon
|
|
201
|
+
icon { ...ImageThumbnail }
|
|
202
202
|
description
|
|
203
203
|
isDefault
|
|
204
204
|
supportedCurrencies
|
|
205
205
|
position
|
|
206
206
|
}
|
|
207
|
+
${IMAGE_THUMBNAIL_FRAGMENT}
|
|
207
208
|
`;
|
|
208
209
|
const CART_FRAGMENT = `
|
|
209
210
|
fragment Cart on Cart {
|
|
@@ -227,6 +228,7 @@ const CART_FRAGMENT = `
|
|
|
227
228
|
billingAddress { ...MailingAddress }
|
|
228
229
|
selectedShippingMethod { ...CartShippingMethod }
|
|
229
230
|
selectedPaymentMethod { ...CartSelectedPaymentMethod }
|
|
231
|
+
selectedPaymentInstrument
|
|
230
232
|
appliedGiftCards { ...CartAppliedGiftCard }
|
|
231
233
|
requiresShipping
|
|
232
234
|
createdAt
|
|
@@ -296,9 +298,10 @@ const SHIPPING_CARRIER_FRAGMENT = `
|
|
|
296
298
|
fragment ShippingCarrier on ShippingCarrier {
|
|
297
299
|
id
|
|
298
300
|
name
|
|
299
|
-
|
|
301
|
+
logo { ...ImageThumbnail }
|
|
300
302
|
serviceCode
|
|
301
303
|
}
|
|
304
|
+
${IMAGE_THUMBNAIL_FRAGMENT}
|
|
302
305
|
`;
|
|
303
306
|
const DELIVERY_ESTIMATE_FRAGMENT = `
|
|
304
307
|
fragment DeliveryEstimate on DeliveryEstimate {
|
|
@@ -336,18 +339,39 @@ const AVAILABLE_SHIPPING_METHOD_FRAGMENT = `
|
|
|
336
339
|
${DELIVERY_ESTIMATE_FRAGMENT}
|
|
337
340
|
${FREE_SHIPPING_PROGRESS_FRAGMENT}
|
|
338
341
|
`;
|
|
342
|
+
const PAYMENT_INSTRUMENT_FRAGMENT = `
|
|
343
|
+
fragment PaymentInstrument on PaymentInstrument {
|
|
344
|
+
provider
|
|
345
|
+
code
|
|
346
|
+
type
|
|
347
|
+
displayName
|
|
348
|
+
displayHint
|
|
349
|
+
brandImage { ...ImageThumbnail }
|
|
350
|
+
enabled
|
|
351
|
+
}
|
|
352
|
+
${IMAGE_THUMBNAIL_FRAGMENT}
|
|
353
|
+
`;
|
|
339
354
|
const PAYMENT_METHOD_FRAGMENT = `
|
|
340
355
|
fragment PaymentMethod on PaymentMethod {
|
|
341
356
|
id
|
|
342
357
|
name
|
|
343
358
|
provider
|
|
344
359
|
type
|
|
345
|
-
icon
|
|
360
|
+
icon { ...ImageThumbnail }
|
|
346
361
|
description
|
|
347
362
|
isDefault
|
|
348
363
|
supportedCurrencies
|
|
349
364
|
position
|
|
365
|
+
providersAvailable
|
|
366
|
+
preferredProvider
|
|
367
|
+
available
|
|
368
|
+
unavailableReason
|
|
369
|
+
instruments {
|
|
370
|
+
...PaymentInstrument
|
|
371
|
+
}
|
|
350
372
|
}
|
|
373
|
+
${PAYMENT_INSTRUMENT_FRAGMENT}
|
|
374
|
+
${IMAGE_THUMBNAIL_FRAGMENT}
|
|
351
375
|
`;
|
|
352
376
|
const AVAILABLE_PAYMENT_METHODS_FRAGMENT = `
|
|
353
377
|
fragment AvailablePaymentMethods on AvailablePaymentMethods {
|
|
@@ -368,6 +392,13 @@ const PAYMENT_SESSION_FRAGMENT = `
|
|
|
368
392
|
expiresAt
|
|
369
393
|
}
|
|
370
394
|
`;
|
|
395
|
+
const PAYMENT_WARNING_FRAGMENT = `
|
|
396
|
+
fragment PaymentWarning on PaymentWarning {
|
|
397
|
+
message
|
|
398
|
+
code
|
|
399
|
+
retryHint
|
|
400
|
+
}
|
|
401
|
+
`;
|
|
371
402
|
// ---------------------------------------------------------------------------
|
|
372
403
|
// Queries
|
|
373
404
|
// ---------------------------------------------------------------------------
|
|
@@ -655,10 +686,30 @@ export const PAYMENT_CREATE = composeOperation(`
|
|
|
655
686
|
paymentCreate(input: $input) {
|
|
656
687
|
payment { ...PaymentSession }
|
|
657
688
|
userErrors { ...UserError }
|
|
689
|
+
warnings { ...PaymentWarning }
|
|
658
690
|
}
|
|
659
691
|
}
|
|
660
692
|
${PAYMENT_SESSION_FRAGMENT}
|
|
661
693
|
${USER_ERROR_FRAGMENT}
|
|
694
|
+
${PAYMENT_WARNING_FRAGMENT}
|
|
695
|
+
`);
|
|
696
|
+
/**
|
|
697
|
+
* cartClearPaymentSelection Mutation (Adv-1 Phase A1-2) — explicit deselect
|
|
698
|
+
* dla storefront accordion UI ("wróć do wyboru metody"). Atomic NULL na
|
|
699
|
+
* wszystkich 4 payment selection fields, idempotent. Cart MUSI być ACTIVE
|
|
700
|
+
* (CONVERTED reject z `ALREADY_COMPLETED`).
|
|
701
|
+
*/
|
|
702
|
+
export const CART_CLEAR_PAYMENT_SELECTION = composeOperation(`
|
|
703
|
+
mutation CartClearPaymentSelection($input: CartClearPaymentSelectionInput!) {
|
|
704
|
+
cartClearPaymentSelection(input: $input) {
|
|
705
|
+
cart { ...Cart }
|
|
706
|
+
userErrors { ...UserError }
|
|
707
|
+
warnings { ...CartWarning }
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
${CART_FRAGMENT}
|
|
711
|
+
${USER_ERROR_FRAGMENT}
|
|
712
|
+
${CART_WARNING_FRAGMENT}
|
|
662
713
|
`);
|
|
663
714
|
/**
|
|
664
715
|
* cartValidateDiscountCode Query — read-only preview discount applicability
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<PaymentInstrumentSection>` — radio-group container dla payment instruments
|
|
3
|
+
* w obrębie jednej method (np. lista bankow + BLIK code entry w BLIK method,
|
|
4
|
+
* lista wallets + brand cards w CARD method). Renders jeden `<PaymentInstrumentTile>`
|
|
5
|
+
* per item z keyboard nav (Arrow Up/Down, Home/End) + ARIA `role="radiogroup"`.
|
|
6
|
+
*
|
|
7
|
+
* Headless — section + tiles bez własnego stylingu. Pass `sectionClassName`
|
|
8
|
+
* dla layoutu (grid/flex), `tileClassName` dla per-tile styling, optional
|
|
9
|
+
* `iconClassName`/`labelClassName` dla wewnętrznej struktury tile.
|
|
10
|
+
*
|
|
11
|
+
* Auto-group **nie jest** implementowane w tym sub-sprincie — backend już
|
|
12
|
+
* zwraca instruments sorted (BLIK + wallets pierwsze → banks alpha → others)
|
|
13
|
+
* z `availablePaymentMethods.methods[].instruments[]`. Komponent renderuje
|
|
14
|
+
* w order'ze otrzymanym, bez własnej resort logic.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* const [code, setCode] = useState<string | undefined>(undefined);
|
|
19
|
+
* <PaymentInstrumentSection
|
|
20
|
+
* method={method}
|
|
21
|
+
* selectedInstrumentCode={code}
|
|
22
|
+
* onSelectInstrument={setCode}
|
|
23
|
+
* sectionClassName="grid grid-cols-2 gap-2"
|
|
24
|
+
* tileClassName="rounded border p-3 data-[selected=true]:border-blue-500"
|
|
25
|
+
* />
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Added by payment-instrument-preselection-advanced sub-sprint Adv-2 Req 9.
|
|
29
|
+
*/
|
|
30
|
+
import type { PaymentMethod } from '../../core/generated/operation-types';
|
|
31
|
+
/**
|
|
32
|
+
* Minimalna projekcja `PaymentMethod` używana przez section — wymaga tylko
|
|
33
|
+
* `instruments[]`. Caller może pass pełną `PaymentMethod` z `availablePaymentMethods`
|
|
34
|
+
* query — extra pola (id, name, providersAvailable, etc.) są ignorowane.
|
|
35
|
+
*/
|
|
36
|
+
export type PaymentInstrumentSectionMethod = Pick<PaymentMethod, 'instruments'>;
|
|
37
|
+
export interface PaymentInstrumentSectionProps {
|
|
38
|
+
/** Payment method z `instruments[]` listą (null/empty → komponent zwraca null). */
|
|
39
|
+
method: PaymentInstrumentSectionMethod;
|
|
40
|
+
/** Currently selected instrument code — controlled by parent. Undefined gdy buyer nie wybrał. */
|
|
41
|
+
selectedInstrumentCode?: string;
|
|
42
|
+
/** Callback gdy buyer selektuje instrument. Parent persistuje state + odpala `cartSelectPaymentMethod`. */
|
|
43
|
+
onSelectInstrument: (instrumentCode: string) => void;
|
|
44
|
+
/** Optional class dla outer section element — typically grid/flex layout. */
|
|
45
|
+
sectionClassName?: string;
|
|
46
|
+
/** Optional class dla each `<PaymentInstrumentTile>` — passed jako `className`. */
|
|
47
|
+
tileClassName?: string;
|
|
48
|
+
/** Optional class dla brand image inside tiles — passed jako `iconClassName`. */
|
|
49
|
+
iconClassName?: string;
|
|
50
|
+
/** Optional class dla label span inside tiles — passed jako `labelClassName`. */
|
|
51
|
+
labelClassName?: string;
|
|
52
|
+
/** Optional ARIA label dla radiogroup. Defaults to nothing (caller's responsibility — section typically nested w wider label structure). */
|
|
53
|
+
ariaLabel?: string;
|
|
54
|
+
}
|
|
55
|
+
export declare function PaymentInstrumentSection({ method, selectedInstrumentCode, onSelectInstrument, sectionClassName, tileClassName, iconClassName, labelClassName, ariaLabel, }: PaymentInstrumentSectionProps): import("react/jsx-runtime").JSX.Element | null;
|
|
56
|
+
//# sourceMappingURL=PaymentInstrumentSection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PaymentInstrumentSection.d.ts","sourceRoot":"","sources":["../../../src/react/components/PaymentInstrumentSection.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAqB,MAAM,sCAAsC,CAAC;AAG7F;;;;GAIG;AACH,MAAM,MAAM,8BAA8B,GAAG,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAEhF,MAAM,WAAW,6BAA6B;IAC5C,mFAAmF;IACnF,MAAM,EAAE,8BAA8B,CAAC;IACvC,iGAAiG;IACjG,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,2GAA2G;IAC3G,kBAAkB,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4IAA4I;IAC5I,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,wBAAwB,CAAC,EACvC,MAAM,EACN,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,cAAc,EACd,SAAS,GACV,EAAE,6BAA6B,kDAmF/B"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<PaymentInstrumentSection>` — radio-group container dla payment instruments
|
|
3
|
+
* w obrębie jednej method (np. lista bankow + BLIK code entry w BLIK method,
|
|
4
|
+
* lista wallets + brand cards w CARD method). Renders jeden `<PaymentInstrumentTile>`
|
|
5
|
+
* per item z keyboard nav (Arrow Up/Down, Home/End) + ARIA `role="radiogroup"`.
|
|
6
|
+
*
|
|
7
|
+
* Headless — section + tiles bez własnego stylingu. Pass `sectionClassName`
|
|
8
|
+
* dla layoutu (grid/flex), `tileClassName` dla per-tile styling, optional
|
|
9
|
+
* `iconClassName`/`labelClassName` dla wewnętrznej struktury tile.
|
|
10
|
+
*
|
|
11
|
+
* Auto-group **nie jest** implementowane w tym sub-sprincie — backend już
|
|
12
|
+
* zwraca instruments sorted (BLIK + wallets pierwsze → banks alpha → others)
|
|
13
|
+
* z `availablePaymentMethods.methods[].instruments[]`. Komponent renderuje
|
|
14
|
+
* w order'ze otrzymanym, bez własnej resort logic.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* const [code, setCode] = useState<string | undefined>(undefined);
|
|
19
|
+
* <PaymentInstrumentSection
|
|
20
|
+
* method={method}
|
|
21
|
+
* selectedInstrumentCode={code}
|
|
22
|
+
* onSelectInstrument={setCode}
|
|
23
|
+
* sectionClassName="grid grid-cols-2 gap-2"
|
|
24
|
+
* tileClassName="rounded border p-3 data-[selected=true]:border-blue-500"
|
|
25
|
+
* />
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Added by payment-instrument-preselection-advanced sub-sprint Adv-2 Req 9.
|
|
29
|
+
*/
|
|
30
|
+
'use client';
|
|
31
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
32
|
+
import { useCallback, useRef } from 'react';
|
|
33
|
+
import { PaymentInstrumentTile } from './PaymentInstrumentTile';
|
|
34
|
+
export function PaymentInstrumentSection({ method, selectedInstrumentCode, onSelectInstrument, sectionClassName, tileClassName, iconClassName, labelClassName, ariaLabel, }) {
|
|
35
|
+
const instruments = method.instruments ?? [];
|
|
36
|
+
const sectionRef = useRef(null);
|
|
37
|
+
/**
|
|
38
|
+
* Keyboard nav per WAI-ARIA radiogroup pattern:
|
|
39
|
+
* ArrowDown/ArrowRight → focus next tile, wrap to first.
|
|
40
|
+
* ArrowUp/ArrowLeft → focus previous tile, wrap to last.
|
|
41
|
+
* Home → focus first tile.
|
|
42
|
+
* End → focus last tile.
|
|
43
|
+
* Enter/Space → native button activation (no custom handler).
|
|
44
|
+
*/
|
|
45
|
+
const handleKeyDown = useCallback((event) => {
|
|
46
|
+
if (!sectionRef.current)
|
|
47
|
+
return;
|
|
48
|
+
const tiles = Array.from(sectionRef.current.querySelectorAll('button[role="radio"]:not([disabled])'));
|
|
49
|
+
if (tiles.length === 0)
|
|
50
|
+
return;
|
|
51
|
+
const currentIndex = tiles.findIndex((tile) => tile === document.activeElement);
|
|
52
|
+
let nextIndex = -1;
|
|
53
|
+
switch (event.key) {
|
|
54
|
+
case 'ArrowDown':
|
|
55
|
+
case 'ArrowRight':
|
|
56
|
+
nextIndex = currentIndex === -1 || currentIndex === tiles.length - 1 ? 0 : currentIndex + 1;
|
|
57
|
+
break;
|
|
58
|
+
case 'ArrowUp':
|
|
59
|
+
case 'ArrowLeft':
|
|
60
|
+
nextIndex = currentIndex <= 0 ? tiles.length - 1 : currentIndex - 1;
|
|
61
|
+
break;
|
|
62
|
+
case 'Home':
|
|
63
|
+
nextIndex = 0;
|
|
64
|
+
break;
|
|
65
|
+
case 'End':
|
|
66
|
+
nextIndex = tiles.length - 1;
|
|
67
|
+
break;
|
|
68
|
+
default:
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
tiles[nextIndex]?.focus();
|
|
73
|
+
}, []);
|
|
74
|
+
if (instruments.length === 0) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return (_jsx("div", { ref: sectionRef, role: "radiogroup", "aria-label": ariaLabel, onKeyDown: handleKeyDown, className: sectionClassName, children: instruments.map((instrument) => {
|
|
78
|
+
// Cast to tile-projection shape — instruments[] z generated types ma więcej pól
|
|
79
|
+
// niż tile potrzebuje (provider, type), tile picks subset jawnie.
|
|
80
|
+
const tileInstrument = {
|
|
81
|
+
code: instrument.code,
|
|
82
|
+
displayName: instrument.displayName,
|
|
83
|
+
displayHint: instrument.displayHint,
|
|
84
|
+
enabled: instrument.enabled,
|
|
85
|
+
brandImage: instrument.brandImage ?? undefined,
|
|
86
|
+
};
|
|
87
|
+
return (_jsx(PaymentInstrumentTile, { instrument: tileInstrument, selected: instrument.code === selectedInstrumentCode, onSelect: () => onSelectInstrument(instrument.code), className: tileClassName, iconClassName: iconClassName, labelClassName: labelClassName }, instrument.code));
|
|
88
|
+
}) }));
|
|
89
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<PaymentInstrumentTile>` — single payment instrument tile (BLIK code,
|
|
3
|
+
* bank logo, wallet button, card brand). Headless — buyer-facing button
|
|
4
|
+
* z accessibility wbudowanym (`role="radio"`, `aria-checked`, `aria-label`,
|
|
5
|
+
* `data-instrument-code`, `data-display-hint`). Pass className per part
|
|
6
|
+
* (button, icon, label) żeby zintegrować z Tailwind / CSS Modules /
|
|
7
|
+
* styled-components — komponent nie ma własnego stylingu.
|
|
8
|
+
*
|
|
9
|
+
* `displayHint` dispatch — backend hint jak storefront powinien renderować
|
|
10
|
+
* instrument. Komponent emituje hint jako `data-display-hint` attribute na
|
|
11
|
+
* `<button>` żeby caller mógł stylizować przez CSS attribute selektory:
|
|
12
|
+
*
|
|
13
|
+
* - `BRANDED_TILE` — bank logo / wallet icon dominuje, label jako caption.
|
|
14
|
+
* - `PROMINENT_BUTTON` — duże CTA (BLIK code entry), brand image pominięty.
|
|
15
|
+
* - `RADIO_OPTION` — radio-list look, image (jeśli is) + label inline.
|
|
16
|
+
* - `DROPDOWN_OPTION` — text-only dla dropdown integration (caller wrap).
|
|
17
|
+
*
|
|
18
|
+
* Headless = no opinion. CSS styling decyduje, komponent tylko hint'uje.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <PaymentInstrumentTile
|
|
23
|
+
* instrument={{ code: 'blik', displayName: 'BLIK', displayHint: 'PROMINENT_BUTTON', enabled: true }}
|
|
24
|
+
* selected={selectedCode === 'blik'}
|
|
25
|
+
* onSelect={() => setSelectedCode('blik')}
|
|
26
|
+
* className="rounded-lg border p-3 hover:bg-gray-50 data-[selected=true]:border-blue-500"
|
|
27
|
+
* labelClassName="font-semibold"
|
|
28
|
+
* />
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Added by payment-instrument-preselection-advanced sub-sprint Adv-2 Req 9.
|
|
32
|
+
*/
|
|
33
|
+
import type { PaymentInstrument } from '../../core/generated/operation-types';
|
|
34
|
+
/**
|
|
35
|
+
* Minimalna projekcja `PaymentInstrument` używana przez tile. Caller może
|
|
36
|
+
* pass pełny instrument z `availablePaymentMethods` query — extra pola
|
|
37
|
+
* (provider, type) są ignorowane przez komponent (UI nie branchuje na
|
|
38
|
+
* provider — gateway-agnostic, code + displayHint wystarczą).
|
|
39
|
+
*/
|
|
40
|
+
export type PaymentInstrumentTileInstrument = Pick<PaymentInstrument, 'code' | 'displayName' | 'displayHint' | 'enabled'> & Partial<Pick<PaymentInstrument, 'brandImage'>>;
|
|
41
|
+
export interface PaymentInstrumentTileProps {
|
|
42
|
+
/** Instrument projection — gateway code, label, display hint, enabled state. */
|
|
43
|
+
instrument: PaymentInstrumentTileInstrument;
|
|
44
|
+
/** True gdy ten tile jest currently selected w parent section. Controls `aria-checked` + `data-selected`. */
|
|
45
|
+
selected: boolean;
|
|
46
|
+
/** Click handler — caller updates selected state external (controlled component). */
|
|
47
|
+
onSelect: () => void;
|
|
48
|
+
/** Optional class for the outer button — header styling, layout, border. */
|
|
49
|
+
className?: string;
|
|
50
|
+
/** Optional class for the brand image (`<img>` element). Only applied when instrument has `brandImage`. */
|
|
51
|
+
iconClassName?: string;
|
|
52
|
+
/** Optional class for the label span. */
|
|
53
|
+
labelClassName?: string;
|
|
54
|
+
}
|
|
55
|
+
export declare function PaymentInstrumentTile({ instrument, selected, onSelect, className, iconClassName, labelClassName, }: PaymentInstrumentTileProps): import("react/jsx-runtime").JSX.Element;
|
|
56
|
+
//# sourceMappingURL=PaymentInstrumentTile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PaymentInstrumentTile.d.ts","sourceRoot":"","sources":["../../../src/react/components/PaymentInstrumentTile.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,MAAM,+BAA+B,GAAG,IAAI,CAChD,iBAAiB,EACjB,MAAM,GAAG,aAAa,GAAG,aAAa,GAAG,SAAS,CACnD,GACC,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC,CAAC;AAEjD,MAAM,WAAW,0BAA0B;IACzC,gFAAgF;IAChF,UAAU,EAAE,+BAA+B,CAAC;IAC5C,6GAA6G;IAC7G,QAAQ,EAAE,OAAO,CAAC;IAClB,qFAAqF;IACrF,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2GAA2G;IAC3G,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,qBAAqB,CAAC,EACpC,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,aAAa,EACb,cAAc,GACf,EAAE,0BAA0B,2CAuB5B"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<PaymentInstrumentTile>` — single payment instrument tile (BLIK code,
|
|
3
|
+
* bank logo, wallet button, card brand). Headless — buyer-facing button
|
|
4
|
+
* z accessibility wbudowanym (`role="radio"`, `aria-checked`, `aria-label`,
|
|
5
|
+
* `data-instrument-code`, `data-display-hint`). Pass className per part
|
|
6
|
+
* (button, icon, label) żeby zintegrować z Tailwind / CSS Modules /
|
|
7
|
+
* styled-components — komponent nie ma własnego stylingu.
|
|
8
|
+
*
|
|
9
|
+
* `displayHint` dispatch — backend hint jak storefront powinien renderować
|
|
10
|
+
* instrument. Komponent emituje hint jako `data-display-hint` attribute na
|
|
11
|
+
* `<button>` żeby caller mógł stylizować przez CSS attribute selektory:
|
|
12
|
+
*
|
|
13
|
+
* - `BRANDED_TILE` — bank logo / wallet icon dominuje, label jako caption.
|
|
14
|
+
* - `PROMINENT_BUTTON` — duże CTA (BLIK code entry), brand image pominięty.
|
|
15
|
+
* - `RADIO_OPTION` — radio-list look, image (jeśli is) + label inline.
|
|
16
|
+
* - `DROPDOWN_OPTION` — text-only dla dropdown integration (caller wrap).
|
|
17
|
+
*
|
|
18
|
+
* Headless = no opinion. CSS styling decyduje, komponent tylko hint'uje.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <PaymentInstrumentTile
|
|
23
|
+
* instrument={{ code: 'blik', displayName: 'BLIK', displayHint: 'PROMINENT_BUTTON', enabled: true }}
|
|
24
|
+
* selected={selectedCode === 'blik'}
|
|
25
|
+
* onSelect={() => setSelectedCode('blik')}
|
|
26
|
+
* className="rounded-lg border p-3 hover:bg-gray-50 data-[selected=true]:border-blue-500"
|
|
27
|
+
* labelClassName="font-semibold"
|
|
28
|
+
* />
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Added by payment-instrument-preselection-advanced sub-sprint Adv-2 Req 9.
|
|
32
|
+
*/
|
|
33
|
+
'use client';
|
|
34
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
35
|
+
export function PaymentInstrumentTile({ instrument, selected, onSelect, className, iconClassName, labelClassName, }) {
|
|
36
|
+
const { code, displayName, displayHint, brandImage, enabled } = instrument;
|
|
37
|
+
const brandImageUrl = brandImage?.url;
|
|
38
|
+
// PROMINENT_BUTTON hides brand image — instrument jest text-only CTA (BLIK code entry).
|
|
39
|
+
const showImage = displayHint !== 'PROMINENT_BUTTON' && brandImageUrl;
|
|
40
|
+
return (_jsxs("button", { type: "button", role: "radio", "aria-checked": selected, "aria-label": displayName, "data-instrument-code": code, "data-display-hint": displayHint, "data-selected": selected, disabled: !enabled, onClick: onSelect, className: className, children: [showImage && _jsx("img", { src: brandImageUrl, alt: brandImage?.altText ?? '', className: iconClassName }), _jsx("span", { className: labelClassName, children: displayName })] }));
|
|
41
|
+
}
|
|
@@ -12,4 +12,6 @@ export { CartCount, type CartCountProps } from './CartCount';
|
|
|
12
12
|
export { AddToCartButton, type AddToCartButtonProps } from './AddToCartButton';
|
|
13
13
|
export { PriceDisplay, type PriceDisplayProps } from './PriceDisplay';
|
|
14
14
|
export { CartTotals, type CartTotalsProps, type CartTotalsLabels } from './CartTotals';
|
|
15
|
+
export { PaymentInstrumentTile, type PaymentInstrumentTileProps, type PaymentInstrumentTileInstrument, } from './PaymentInstrumentTile';
|
|
16
|
+
export { PaymentInstrumentSection, type PaymentInstrumentSectionProps, type PaymentInstrumentSectionMethod, } from './PaymentInstrumentSection';
|
|
15
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/components/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,KAAK,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/components/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,KAAK,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACvF,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,EAC/B,KAAK,+BAA+B,GACrC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,wBAAwB,EACxB,KAAK,6BAA6B,EAClC,KAAK,8BAA8B,GACpC,MAAM,4BAA4B,CAAC"}
|
|
@@ -12,3 +12,5 @@ export { CartCount } from './CartCount';
|
|
|
12
12
|
export { AddToCartButton } from './AddToCartButton';
|
|
13
13
|
export { PriceDisplay } from './PriceDisplay';
|
|
14
14
|
export { CartTotals } from './CartTotals';
|
|
15
|
+
export { PaymentInstrumentTile, } from './PaymentInstrumentTile';
|
|
16
|
+
export { PaymentInstrumentSection, } from './PaymentInstrumentSection';
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `getBrowserDataForPayment()` — collects browser context required by PSD2/3DS2
|
|
3
|
+
* authentication flows (card-on-file z 3DS challenge, BLIK confirmation,
|
|
4
|
+
* Apple/Google Pay z risk scoring).
|
|
5
|
+
*
|
|
6
|
+
* **Browser-only** — throws gdy `typeof window === 'undefined'` (server-side
|
|
7
|
+
* rendering). Caller MUSI gate'ować call w useEffect / event handler / browser
|
|
8
|
+
* code path. NEVER call this w Server Component lub Route Handler.
|
|
9
|
+
*
|
|
10
|
+
* Wartości pochodzą z standard Web APIs:
|
|
11
|
+
* - `userAgent` — `navigator.userAgent`.
|
|
12
|
+
* - `language` — `navigator.language` (BCP 47, fallback `en-US`).
|
|
13
|
+
* - `screen{Width,Height}` — `window.screen.{width,height}`.
|
|
14
|
+
* - `colorDepth` — `window.screen.colorDepth`.
|
|
15
|
+
* - `timezoneOffset` — `new Date().getTimezoneOffset()` (signed integer, minutes,
|
|
16
|
+
* reverse signed per ECMA: dodatnie = behind UTC, ujemne = ahead).
|
|
17
|
+
* - `javaEnabled` — `navigator.javaEnabled?.()` (deprecated ale wymagane PSD2/EMV
|
|
18
|
+
* specification; fallback `false` gdy browser nie expose'uje).
|
|
19
|
+
* - `acceptHeader` — NIE dostępne client-side (`navigator` nie expose'uje request
|
|
20
|
+
* headers). Caller passes z server context lub omits (gateway-dependent
|
|
21
|
+
* requirement). Helper zwraca undefined.
|
|
22
|
+
*
|
|
23
|
+
* Shape matches PSD2/3DS2 BrowserData specification (EMVCo) — adapter na backend
|
|
24
|
+
* `IPaymentProvider.createPayment` może merge wprost do gateway-specific body
|
|
25
|
+
* (Adyen `browserInfo`, Stripe `payment_method_data.billing_details.browser_info`,
|
|
26
|
+
* Mollie `cardToken` z 3DS lookup).
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* // W event handler (browser-only):
|
|
31
|
+
* function handleCheckoutSubmit() {
|
|
32
|
+
* const browserData = getBrowserDataForPayment();
|
|
33
|
+
* await cart.createPayment({ ..., browserData });
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* // SSR-safe wrap:
|
|
37
|
+
* if (typeof window !== 'undefined') {
|
|
38
|
+
* const browserData = getBrowserDataForPayment();
|
|
39
|
+
* ...
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* Added by payment-instrument-preselection-advanced sub-sprint Adv-2 Req 9.5
|
|
44
|
+
* (carry-over z Adv-1 plan — moved here as standalone utility, no backend
|
|
45
|
+
* mutation impact yet — Adv-3 będzie consumer'em w `paymentCreate` input).
|
|
46
|
+
*/
|
|
47
|
+
/**
|
|
48
|
+
* PSD2/3DS2 browser context shape. All fields optional poza ones backed by
|
|
49
|
+
* standard API — gateway-specific requirements decyzują które pola są mandatory.
|
|
50
|
+
*/
|
|
51
|
+
export interface PaymentBrowserData {
|
|
52
|
+
/** Accept HTTP header value — NOT available client-side (caller passes from server context or omits). */
|
|
53
|
+
acceptHeader?: string;
|
|
54
|
+
/** Full user-agent string (`navigator.userAgent`). */
|
|
55
|
+
userAgent: string;
|
|
56
|
+
/** BCP 47 language tag (`navigator.language`). Fallback `'en-US'` gdy missing. */
|
|
57
|
+
language: string;
|
|
58
|
+
/** Screen width in pixels (`window.screen.width`). */
|
|
59
|
+
screenWidth: number;
|
|
60
|
+
/** Screen height in pixels (`window.screen.height`). */
|
|
61
|
+
screenHeight: number;
|
|
62
|
+
/** Color depth bits-per-pixel (`window.screen.colorDepth`). Typically `24` or `32`. */
|
|
63
|
+
colorDepth: number;
|
|
64
|
+
/**
|
|
65
|
+
* Local timezone offset w minutach (ECMA signed convention — dodatnie = behind UTC,
|
|
66
|
+
* ujemne = ahead UTC). Np. CET zima = `-60`, PST zima = `480`.
|
|
67
|
+
*/
|
|
68
|
+
timezoneOffset: number;
|
|
69
|
+
/**
|
|
70
|
+
* Java support (`navigator.javaEnabled?.()`). Deprecated API ale wymagane PSD2/EMV
|
|
71
|
+
* specification; fallback `false` gdy browser nie expose'uje.
|
|
72
|
+
*/
|
|
73
|
+
javaEnabled: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* IANA timezone name (`Intl.DateTimeFormat().resolvedOptions().timeZone`).
|
|
76
|
+
* Optional — niektóre gateways używają zamiast offsetu (Adyen risk scoring).
|
|
77
|
+
*/
|
|
78
|
+
timezone?: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Throw'd gdy helper wywołany w SSR context (Server Component, Route Handler,
|
|
82
|
+
* Node bez JSDOM). Caller MUSI guard'ować przez `typeof window` check albo
|
|
83
|
+
* wywołać tylko w event handler / `useEffect`.
|
|
84
|
+
*/
|
|
85
|
+
export declare class BrowserDataNotAvailableError extends Error {
|
|
86
|
+
constructor();
|
|
87
|
+
}
|
|
88
|
+
export declare function getBrowserDataForPayment(): PaymentBrowserData;
|
|
89
|
+
//# sourceMappingURL=browser-data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-data.d.ts","sourceRoot":"","sources":["../../../src/react/helpers/browser-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,yGAAyG;IACzG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,YAAY,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,WAAW,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,qBAAa,4BAA6B,SAAQ,KAAK;;CAKtD;AAED,wBAAgB,wBAAwB,IAAI,kBAAkB,CA6B7D"}
|