@rokt/dcui-orchestrator 1.0.0 → 1.0.1-rc.1

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,5 +1,5 @@
1
1
  import React from 'react';
2
- import type { CatalogDevicePayButtonModel } from '@rokt/dcui-layout-schema';
2
+ import type { ThemeColor } from '@rokt/dcui-layout-schema';
3
3
  /**
4
4
  * Renders an Apple Pay button using Apple's native CSS styling.
5
5
  * Returns null if the browser does not support `-webkit-appearance: -apple-pay-button`.
@@ -11,12 +11,36 @@ export type ThemedApplePayButtonStyle = {
11
11
  dark?: ApplePayButtonStyle;
12
12
  };
13
13
  export type ButtonStyleInput = ApplePayButtonStyle | ThemedApplePayButtonStyle;
14
+ /**
15
+ * Structural shape of the `own` style hints this module reads. Declared
16
+ * structurally rather than against one schema node so both the layout-variant
17
+ * `CatalogDevicePayButton` and a child-layout escape hatch can pass their own
18
+ * schema without either owning the other's types.
19
+ */
20
+ export type ApplePayButtonStyleSource = {
21
+ styles?: {
22
+ elements?: {
23
+ own?: ReadonlyArray<{
24
+ default?: {
25
+ background?: {
26
+ backgroundColor?: ThemeColor | null;
27
+ } | null;
28
+ border?: {
29
+ borderWidth?: unknown;
30
+ borderStyle?: unknown;
31
+ } | null;
32
+ } | null;
33
+ } | null> | null;
34
+ } | null;
35
+ } | null;
36
+ };
14
37
  type ApplePayButtonProps = {
15
38
  buttonStyle?: ButtonStyleInput;
16
39
  className?: string;
17
40
  disabled?: boolean;
18
41
  onClick?: React.MouseEventHandler<HTMLButtonElement>;
19
42
  tabIndex?: number;
43
+ testId?: string;
20
44
  };
21
45
  /**
22
46
  * Derives an Apple Pay button style from the schema's `own` background and
@@ -31,6 +55,6 @@ type ApplePayButtonProps = {
31
55
  * Breakpoint blocks merge with last-non-null-wins. An unrecognized `dark` is
32
56
  * dropped, so the `light` style applies in both modes.
33
57
  */
34
- export declare function deriveApplePayButtonStyle<C, P>(schema: CatalogDevicePayButtonModel<C, P>): ThemedApplePayButtonStyle | undefined;
58
+ export declare function deriveApplePayButtonStyle(schema: ApplePayButtonStyleSource): ThemedApplePayButtonStyle | undefined;
35
59
  export declare const ApplePayButton: React.ForwardRefExoticComponent<ApplePayButtonProps & React.RefAttributes<HTMLButtonElement>>;
36
60
  export {};
@@ -0,0 +1 @@
1
+ export * from './apple-pay-button';
@@ -30,3 +30,4 @@ export * from './layout-timer';
30
30
  export * from './form';
31
31
  export * from './text-input';
32
32
  export * from './action-button';
33
+ export * from './apple-pay-button';
@@ -14,6 +14,7 @@ export declare const CHILD_LAYOUT_BRIDGE_EVENTS: {
14
14
  readonly STATE_SYNC: "CHILD_LAYOUT/STATE_SYNC";
15
15
  /** child → host: bridge sagas are subscribed; reply with a fresh snapshot */
16
16
  readonly READY: "CHILD_LAYOUT/READY";
17
+ readonly REQUEST_DEVICE_PAY: "CHILD_LAYOUT/REQUEST_DEVICE_PAY";
17
18
  };
18
19
  export declare const CHILD_LAYOUT_BRIDGE_VERSION = 1;
19
20
  export interface ChildBridgeEnvelope<Payload> {
@@ -23,6 +24,12 @@ export interface ChildBridgeEnvelope<Payload> {
23
24
  export type DispatchBehaviorEnvelope = ChildBridgeEnvelope<GlobalActions>;
24
25
  export type StateSyncEnvelope = ChildBridgeEnvelope<SyncedGlobalState>;
25
26
  export type ReadyEnvelope = ChildBridgeEnvelope<null>;
27
+ export interface ChildDevicePayRequest {
28
+ requestKey: string;
29
+ provider: 'ApplePay';
30
+ }
31
+ export declare function isValidChildDevicePayRequestKey(key: unknown): key is string;
32
+ export declare function isChildDevicePayRequest(payload: unknown): payload is ChildDevicePayRequest;
26
33
  export declare function makeBridgeEnvelope<Payload>(payload: Payload): ChildBridgeEnvelope<Payload>;
27
34
  /**
28
35
  * The behaviors a child layout may dispatch into its host (keys) and their
@@ -8,3 +8,18 @@ export interface SetCustomStatePayload {
8
8
  value: number;
9
9
  }
10
10
  export declare function isValidSetCustomStatePayload(payload: unknown): payload is SetCustomStatePayload;
11
+ /**
12
+ * Custom state the instant-purchase device-pay sagas raise while a payment is in
13
+ * flight. Declared here, not in the module, because it crosses the child-layout
14
+ * bridge: a child CTA reads it to disable itself, and a child-initiated payment
15
+ * listener reads it to refuse a second request. Both frames must agree on the key.
16
+ */
17
+ export declare const DEVICE_PAY_PROCESSING_STATE_KEY = "ProcessingState";
18
+ export declare const DEVICE_PAY_PROCESSING_IDLE = 0;
19
+ export declare const DEVICE_PAY_PROCESSING_IN_FLIGHT = 1;
20
+ export declare const DEVICE_PAY_APPLE_PAY_AVAILABLE_STATE_KEY = "applePayAvailable";
21
+ export declare const DEVICE_PAY_ERROR_STATE_KEY = "DevicePayErrorState";
22
+ export declare const DEVICE_PAY_ERROR_HIDDEN = 0;
23
+ export declare const DEVICE_PAY_ERROR_RETRYABLE_DECLINE = 1;
24
+ /** @deprecated Use DEVICE_PAY_APPLE_PAY_AVAILABLE_STATE_KEY for new layouts. */
25
+ export declare const DEVICE_PAY_APPLE_PAY_UNAVAILABLE_STATE_KEY = "applePayUnavailable";
@@ -0,0 +1,5 @@
1
+ import { ChildDevicePayRequest } from '../../../../domain/src/behaviors';
2
+ type DevicePayRequestHandler = (provider: ChildDevicePayRequest['provider']) => void;
3
+ export declare function registerChildDevicePayRequestHandler(creativeInstanceGuid: string, requestKey: string, handler: DevicePayRequestHandler): () => void;
4
+ export declare function dispatchChildDevicePayRequest(creativeInstanceGuid: string, request: ChildDevicePayRequest): boolean;
5
+ export {};
@@ -1 +1,2 @@
1
1
  export * from './sagas';
2
+ export * from './device-pay-requests';
@@ -0,0 +1,9 @@
1
+ export declare const DEVICE_PAY_REQUEST_CONSUMED = "DEVICE_PAY/REQUEST_CONSUMED";
2
+ export type DevicePayRequestConsumedPayload = {
3
+ requestKey: string;
4
+ requestValue: number;
5
+ };
6
+ export type DevicePayRequestConsumedAction = {
7
+ type: typeof DEVICE_PAY_REQUEST_CONSUMED;
8
+ payload: DevicePayRequestConsumedPayload;
9
+ };
@@ -3,3 +3,4 @@ export * from './checkout-lifecycle';
3
3
  export * from './instant-purchase';
4
4
  export * from './device-pay';
5
5
  export * from './paypal';
6
+ export * from './device-pay-requests';
@@ -12,6 +12,47 @@
12
12
  * }
13
13
  * }
14
14
  * ```
15
+ *
16
+ * ## Listener mode (`requestKey`)
17
+ *
18
+ * A child layout can show the visible Apple Pay CTA, but it cannot complete a
19
+ * purchase: a `dcui-child` frame has no `CatalogItemProvider`, no
20
+ * `ValidatorProvider` and no device-pay sagas. So the child synchronously sends
21
+ * an offer-scoped request, and an instance of this node authored with
22
+ * `requestKey` runs the *same* initiate path a click would while preserving
23
+ * Safari's trusted user gesture.
24
+ *
25
+ * With `requestKey` set the node renders nothing — it is a listener, not a
26
+ * button. Keeping both roles in one component is deliberate: the click path and
27
+ * the request path share `validateAndInitiate`, so validation, catalog item,
28
+ * quantity, provider and signals cannot drift between them.
29
+ *
30
+ * Authored as a first-class layout-schema node:
31
+ * ```json
32
+ * {
33
+ * "type": "CatalogDevicePayButton",
34
+ * "node": {
35
+ * "requestKey": "ApplePayRequest",
36
+ * "provider": "ApplePay",
37
+ * "children": []
38
+ * }
39
+ * }
40
+ * ```
41
+ *
42
+ * ### Legacy timestamp requests
43
+ *
44
+ * `CustomStateValidationTriggerButton` and `DismissInstantPurchaseButton` treat
45
+ * `requestKey === 1` as the request and write `0` back to consume it, tracking
46
+ * re-entrancy in a component ref. That is safe for advancing a view but not for
47
+ * a payment: a listener must be authored inside a catalog collection, and those
48
+ * sit under a `ProcessingState is-not 1` gate that unmounts them while the
49
+ * payment sheet is open. A ref resets on remount and would re-fire the request
50
+ * with no user interaction.
51
+ *
52
+ * Older child layouts can still send a monotonically increasing timestamp. The
53
+ * watermark of consumed legacy requests lives in the store
54
+ * (`devicePayRequests`), and anything stamped before this mount began is
55
+ * refused so remounting cannot reopen a payment sheet.
15
56
  */
16
57
  import React from 'react';
17
58
  import { LayoutVariantNonInteractableChildren, WhenPredicate, CatalogDevicePayButtonModel } from '@rokt/dcui-layout-schema';
@@ -1,4 +1,2 @@
1
- export declare const DEVICE_PAY_ERROR_STATE_KEY = "DevicePayErrorState";
2
- export declare const DEVICE_PAY_ERROR_HIDDEN = 0;
3
- export declare const DEVICE_PAY_ERROR_RETRYABLE_DECLINE = 1;
1
+ export { DEVICE_PAY_ERROR_STATE_KEY, DEVICE_PAY_ERROR_HIDDEN, DEVICE_PAY_ERROR_RETRYABLE_DECLINE, } from '../../../../libraries/domain/src/behaviors';
4
2
  export declare const RETRYABLE_DEVICE_PAY_ERROR_MESSAGE = "Payment failed. Choose another card and try again.";
@@ -0,0 +1 @@
1
+ export * from './reducer';
@@ -0,0 +1,14 @@
1
+ import { AnyAction } from '@reduxjs/toolkit';
2
+ /**
3
+ * Highest device-pay request stamp already acted on, per request key.
4
+ *
5
+ * A child layout requests a payment by stamping a synced custom state with the
6
+ * tap time. This ledger is what makes that request idempotent, and it lives in
7
+ * the store rather than in a component ref on purpose: the listener node is
8
+ * authored inside a catalog collection that a `ProcessingState` gate unmounts
9
+ * while the payment sheet is open. A ref would reset on remount and re-fire the
10
+ * request with no user interaction.
11
+ */
12
+ export type DevicePayRequestsState = Record<string, number>;
13
+ export declare function makeInitialDevicePayRequestsState(): DevicePayRequestsState;
14
+ export declare const devicePayRequestsReducer: (state: DevicePayRequestsState | undefined, action: AnyAction) => DevicePayRequestsState;
@@ -1,6 +1,7 @@
1
1
  import { GlobalActions } from '../../../../libraries/domain/src/behaviors';
2
2
  import { ChildLayoutActions, childLayoutsReducer, ChildLayoutsState, StaticData, staticDataReducer, DynamicData, dynamicDataReducer } from '../../../../libraries/module-utils/src/index';
3
3
  import { CatalogSelectionState, DevicePayAvailabilityState, FulfillmentDetailsState, OfferLifecycleEventState, TaxAndShippingState } from '../models';
4
+ import { DevicePayRequestsState } from './device-pay-requests';
4
5
  import { PurchaseIdState } from './instant-purchase';
5
6
  import { PayPalTransactionDetailsState } from './paypal-transaction-details';
6
7
  import { PayPalPurchaseContextState } from './paypal-purchase-context';
@@ -12,6 +13,7 @@ export type AppState = {
12
13
  fulfillmentDetails: FulfillmentDetailsState;
13
14
  offerLifecycleEvent: OfferLifecycleEventState;
14
15
  devicePayAvailability: DevicePayAvailabilityState;
16
+ devicePayRequests: DevicePayRequestsState;
15
17
  paypalTransactionDetails: PayPalTransactionDetailsState;
16
18
  paypalPurchaseContext: PayPalPurchaseContextState;
17
19
  taxAndShipping: TaxAndShippingState;
@@ -26,6 +28,7 @@ export declare const appReducers: {
26
28
  fulfillmentDetails: (state: FulfillmentDetailsState | undefined, action: import("redux").AnyAction) => FulfillmentDetailsState;
27
29
  offerLifecycleEvent: (state: OfferLifecycleEventState | undefined, action: import("redux").AnyAction) => OfferLifecycleEventState;
28
30
  devicePayAvailability: (state: DevicePayAvailabilityState | undefined, action: import("redux").AnyAction) => DevicePayAvailabilityState;
31
+ devicePayRequests: (state: DevicePayRequestsState | undefined, action: import("redux").AnyAction) => DevicePayRequestsState;
29
32
  paypalTransactionDetails: (state: PayPalTransactionDetailsState | undefined, action: import("redux").AnyAction) => PayPalTransactionDetailsState;
30
33
  paypalPurchaseContext: (state: PayPalPurchaseContextState | undefined, action: import("redux").AnyAction) => PayPalPurchaseContextState;
31
34
  taxAndShipping: (state: TaxAndShippingState | undefined, action: import("redux").AnyAction) => TaxAndShippingState;
@@ -6,7 +6,6 @@ import { OfferLifecycle } from '../../../../../libraries/domain/src/behaviors';
6
6
  import { DevicePayPurchaseContext, DevicePayInteractionPayload, InitiateDevicePayAction, DevicePayResponsePayload, DevicePayResponse, DevicePayAvailableAction, DevicePayAvailablePayload } from '../../actions';
7
7
  import { AppState } from '../../reducers';
8
8
  import { OfferLifecycleEventState } from '../../models';
9
- import { PrepareDevicePayEventPayload } from '../../../../../libraries/domain/src/models';
10
9
  export declare function isDevicePayInteraction(data: unknown): data is DevicePayInteractionPayload;
11
10
  export declare function listenForDevicePayInteraction(channel: EventChannel<DevicePayInteractionPayload>, context: DevicePayPurchaseContext): Generator<PutEffect<{
12
11
  type: string;
@@ -25,7 +24,7 @@ export declare function isDevicePayResponse(obj: unknown): obj is DevicePayRespo
25
24
  * than letting the bad afterpay value leak into reducer state.
26
25
  */
27
26
  export declare function isDevicePayAvailableResponse(obj: unknown): obj is DevicePayAvailablePayload;
28
- export declare function listenForDevicePayAvailable(channel: EventChannel<DevicePayAvailablePayload>, logger: (log: Log) => void): Generator<ChannelTakeEffect<DevicePayAvailablePayload> | CallEffect<void> | PutEffect<DevicePayAvailableAction>, void, DevicePayAvailablePayload>;
27
+ export declare function listenForDevicePayAvailable(channel: EventChannel<DevicePayAvailablePayload>, logger: (log: Log) => void): Generator<ChannelTakeEffect<DevicePayAvailablePayload> | CallEffect<void> | PutEffect, void, DevicePayAvailablePayload>;
29
28
  export declare function processDevicePayAvailable(logger: (log: Log) => void, action: DevicePayAvailableAction): Generator<CallEffect<void> | import("redux-saga/effects").SelectEffect | PutEffect<{
30
29
  type: string;
31
30
  payload: {
@@ -121,7 +120,12 @@ export declare function prepareDevicePay(gateway: GlobalGateway, logger: (log: L
121
120
  type: string;
122
121
  payload: {
123
122
  name: string;
124
- body: PrepareDevicePayEventPayload;
123
+ body: {
124
+ stripeAccountId: string | undefined;
125
+ merchantName: string | undefined;
126
+ countryCode: string | undefined;
127
+ currency: string | undefined;
128
+ };
125
129
  };
126
130
  }>, void, AppState | undefined>;
127
131
  export declare function processDevicePayResponse(logger: (log: Log) => void, action: DevicePayResponse): Generator<CallEffect<void> | PutEffect<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rokt/dcui-orchestrator",
3
- "version": "1.0.0",
3
+ "version": "1.0.1-rc.1",
4
4
  "description": "Rokt's DCUI rendering process",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",