diff --git a/packages/checkout/sdk/src/widgets/definitions/parameters/commerce.ts b/packages/checkout/sdk/src/widgets/definitions/parameters/commerce.ts index 16f221a5f6..3ea5aa6cdc 100644 --- a/packages/checkout/sdk/src/widgets/definitions/parameters/commerce.ts +++ b/packages/checkout/sdk/src/widgets/definitions/parameters/commerce.ts @@ -7,6 +7,7 @@ import { SwapWidgetParams } from './swap'; import { OnRampWidgetParams } from './onramp'; import { SaleWidgetParams } from './sale'; import { AddTokensWidgetParams } from './addTokens'; +import { PurchaseWidgetParams } from './purchase'; export enum CommerceFlowType { CONNECT = 'CONNECT', @@ -16,6 +17,7 @@ export enum CommerceFlowType { BRIDGE = 'BRIDGE', ONRAMP = 'ONRAMP', ADD_TOKENS = 'ADD_TOKENS', + PURCHASE = 'PURCHASE', } export type CommerceWidgetConnectFlowParams = { @@ -46,6 +48,10 @@ export type CommerceWidgetAddTokensFlowParams = { flow: CommerceFlowType.ADD_TOKENS; } & AddTokensWidgetParams; +export type CommerceWidgetPurchaseFlowParams = { + flow: CommerceFlowType.PURCHASE; +} & PurchaseWidgetParams; + export type CommerceWidgetFlowParams = | CommerceWidgetConnectFlowParams | CommerceWidgetWalletFlowParams @@ -53,7 +59,8 @@ export type CommerceWidgetFlowParams = | CommerceWidgetBridgeFlowParams | CommerceWidgetOnRampFlowParams | CommerceWidgetSaleFlowParams - | CommerceWidgetAddTokensFlowParams; + | CommerceWidgetAddTokensFlowParams + | CommerceWidgetPurchaseFlowParams; export type CommerceWidgetParams = { /** The language to use for the Commerce Widget */ diff --git a/packages/checkout/widgets-lib/src/context/view-context/CheckoutWidgetViewContextTypes.ts b/packages/checkout/widgets-lib/src/context/view-context/CheckoutWidgetViewContextTypes.ts index 88b34874d7..0327961478 100644 --- a/packages/checkout/widgets-lib/src/context/view-context/CheckoutWidgetViewContextTypes.ts +++ b/packages/checkout/widgets-lib/src/context/view-context/CheckoutWidgetViewContextTypes.ts @@ -14,6 +14,8 @@ import { ConnectWidgetConfiguration, WalletWidgetParams, WalletWidgetConfiguration, + PurchaseWidgetConfiguration, + PurchaseWidgetParams, } from '@imtbl/checkout-sdk'; import { ViewType } from './ViewType'; @@ -24,7 +26,8 @@ export type CheckoutWidgetView = | SaleView | SwapView | OnRampView - | BrdigeView; + | BridgeView + | PurchaseView; interface ConnectView extends ViewType { type: CommerceFlowType.CONNECT; @@ -50,6 +53,14 @@ interface AddTokensView extends ViewType { }; } +interface PurchaseView extends ViewType { + type: CommerceFlowType.PURCHASE; + data: { + params: PurchaseWidgetParams; + config: PurchaseWidgetConfiguration; + }; +} + interface SaleView extends ViewType { type: CommerceFlowType.SALE; data: { @@ -74,7 +85,7 @@ interface OnRampView extends ViewType { }; } -interface BrdigeView extends ViewType { +interface BridgeView extends ViewType { type: CommerceFlowType.BRIDGE; data: { params: BridgeWidgetParams; diff --git a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/CommerceWidget.tsx b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/CommerceWidget.tsx index e28d884d0f..a18656ed20 100644 --- a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/CommerceWidget.tsx +++ b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/CommerceWidget.tsx @@ -47,6 +47,7 @@ import { commerceReducer, initialCommerceState, } from './context/CommerceContext'; +import PurchaseWidget from '../purchase/PurchaseWidget'; export type CommerceWidgetInputs = { checkout: Checkout; @@ -82,7 +83,10 @@ export default function CommerceWidget(props: CommerceWidgetInputs) { [view, checkout, provider, web3Provider], ); - const connectLoaderSuccessEvent = flowParams.flow === CommerceFlowType.ADD_TOKENS ? () => {} : undefined; + const connectLoaderSuccessEvent = ( + flowParams.flow === CommerceFlowType.ADD_TOKENS + || flowParams.flow === CommerceFlowType.PURCHASE + ) ? () => {} : undefined; const goToPreviousView = useCallback(() => { const sharedViews = [ @@ -255,6 +259,16 @@ export default function CommerceWidget(props: CommerceWidgetInputs) { /> )} + {shouldWrapWithProvidersContext && view.type === CommerceFlowType.PURCHASE && ( + + + + )} {/* --- Widgets that require connect loader --- */} {shouldWrapWithConnectLoader && ( { return validatedParams; } + protected getValidPurchaseFlowParams(params: CommerceWidgetPurchaseFlowParams) { + const validatedParams = { ...params }; + + if (!Array.isArray(validatedParams.items)) { + // eslint-disable-next-line no-console + console.warn('[IMTBL]: invalid "items" widget input.'); + validatedParams.items = []; + } + + if (!params.environmentId) { + // eslint-disable-next-line no-console + console.warn('[IMTBL]: invalid "environmentId" widget input'); + validatedParams.environmentId = ''; + } + + return { + ...validatedParams, + items: deduplicateSaleItemsArray(params.items), + }; + } + protected getValidSwapFlowParams(params: CommerceWidgetSwapFlowParams) { const validatedParams = { ...params }; @@ -238,6 +260,8 @@ export class CommerceWidgetRoot extends Base { return this.getValidOnRampFlowParams(params); case CommerceFlowType.ADD_TOKENS: return this.getValidAddTokensFlowParams(params); + case CommerceFlowType.PURCHASE: + return this.getValidPurchaseFlowParams(params); default: // eslint-disable-next-line no-console console.warn( diff --git a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/getCommerceWidgetEvent.ts b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/getCommerceWidgetEvent.ts index b1fccae2f3..87d5c3e144 100644 --- a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/getCommerceWidgetEvent.ts +++ b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/getCommerceWidgetEvent.ts @@ -9,6 +9,7 @@ import { IMTBLWidgetEvents, OnRampEventType, ProviderEventType, + PurchaseEventType, SaleEventType, SwapEventType, WalletEventType, @@ -315,6 +316,38 @@ function mapSaleWidgetEvent( } } +function mapPurchaseWidgetEvent( + event: CustomEvent<{ type: PurchaseEventType; data: Record }>, +): CommerceEventDetail { + const { type, data } = event.detail; + + switch (type) { + case PurchaseEventType.SUCCESS: + return { + type: CommerceEventType.SUCCESS, + data: { + type: CommerceSuccessEventType.SALE_SUCCESS, + data, + }, + }; + case PurchaseEventType.FAILURE: + return { + type: CommerceEventType.FAILURE, + data: { + type: CommerceFailureEventType.SALE_FAILED, + data, + }, + }; + case PurchaseEventType.CLOSE_WIDGET: + return { + type: CommerceEventType.CLOSE, + data: {}, + }; + default: + throw new Error(`Unknown purchase event type "${event.detail.type}"`); + } +} + /** * Map widget events to commerce widget event detail */ @@ -343,6 +376,8 @@ export function getCommerceWidgetEvent( return mapOnrampWidgetEvent(event); case IMTBLWidgetEvents.IMTBL_SALE_WIDGET_EVENT: return mapSaleWidgetEvent(event); + case IMTBLWidgetEvents.IMTBL_PURCHASE_WIDGET_EVENT: + return mapPurchaseWidgetEvent(event); default: throw new Error(`Unknown widget event type "${event.type}"`); } diff --git a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/getFlowRequiresContext.ts b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/getFlowRequiresContext.ts index b56a767934..3bdde8ed82 100644 --- a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/getFlowRequiresContext.ts +++ b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/getFlowRequiresContext.ts @@ -15,6 +15,7 @@ const connectLoaderFlows = [ */ const providersContextFlows = [ CommerceFlowType.ADD_TOKENS, + CommerceFlowType.PURCHASE, ] as unknown[]; /** diff --git a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/getViewFromOrchestrationEventType.ts b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/getViewFromOrchestrationEventType.ts index 4fab8cc7fd..0159cb1893 100644 --- a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/getViewFromOrchestrationEventType.ts +++ b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/getViewFromOrchestrationEventType.ts @@ -19,6 +19,8 @@ export function getViewFromOrchestrationEventType( return CommerceFlowType.ONRAMP; case OrchestrationEventType.REQUEST_ADD_TOKENS: return CommerceFlowType.ADD_TOKENS; + case OrchestrationEventType.REQUEST_PURCHASE: + return CommerceFlowType.PURCHASE; default: return null; } diff --git a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/isOrchestrationEvent.ts b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/isOrchestrationEvent.ts index 49828d9e43..0e7216dac3 100644 --- a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/isOrchestrationEvent.ts +++ b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/isOrchestrationEvent.ts @@ -8,6 +8,7 @@ const orchestrationEvents = [ OrchestrationEventType.REQUEST_BRIDGE, OrchestrationEventType.REQUEST_ONRAMP, OrchestrationEventType.REQUEST_ADD_TOKENS, + OrchestrationEventType.REQUEST_PURCHASE, OrchestrationEventType.REQUEST_GO_BACK, ]; diff --git a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/isValidCommerceFlow.ts b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/isValidCommerceFlow.ts index a108bfb7be..b8cbff9d01 100644 --- a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/isValidCommerceFlow.ts +++ b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/functions/isValidCommerceFlow.ts @@ -9,6 +9,7 @@ export const commerceFlows = [ CommerceFlowType.BRIDGE, CommerceFlowType.ONRAMP, CommerceFlowType.ADD_TOKENS, + CommerceFlowType.PURCHASE, ]; /** diff --git a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/hooks/useWidgetEvents.ts b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/hooks/useWidgetEvents.ts index 69c7510c3a..1b12a7b87f 100644 --- a/packages/checkout/widgets-lib/src/widgets/immutable-commerce/hooks/useWidgetEvents.ts +++ b/packages/checkout/widgets-lib/src/widgets/immutable-commerce/hooks/useWidgetEvents.ts @@ -24,6 +24,7 @@ const widgetEvents = [ IMTBLWidgetEvents.IMTBL_ONRAMP_WIDGET_EVENT, IMTBLWidgetEvents.IMTBL_SALE_WIDGET_EVENT, IMTBLWidgetEvents.IMTBL_ADD_TOKENS_WIDGET_EVENT, + IMTBLWidgetEvents.IMTBL_PURCHASE_WIDGET_EVENT, ]; /** diff --git a/packages/checkout/widgets-sample-app/src/components/ui/checkout/checkout.tsx b/packages/checkout/widgets-sample-app/src/components/ui/checkout/checkout.tsx index 8567104647..a51c801077 100644 --- a/packages/checkout/widgets-sample-app/src/components/ui/checkout/checkout.tsx +++ b/packages/checkout/widgets-sample-app/src/components/ui/checkout/checkout.tsx @@ -140,6 +140,7 @@ const flows: Array = [ CommerceFlowType.BRIDGE, CommerceFlowType.SALE, CommerceFlowType.ADD_TOKENS, + CommerceFlowType.PURCHASE, ]; function CheckoutUI() { @@ -206,6 +207,11 @@ function CheckoutUI() { toAmount: "1", toTokenAddress: "native", }, + PURCHASE: { + flow: CommerceFlowType.PURCHASE, + items: itemsMock.slice(0, 1), + environmentId: "82a81049-8c41-4ae3-91ca-0bd82a283abc", + }, }); // set a state to keep widget event results @@ -474,6 +480,26 @@ function CheckoutUI() { > Primary Sale + { + setParams({ + flow: CommerceFlowType.PURCHASE, + items: [ + { + productId: "kangaroo", + qty: 1, + name: "Kangaroo", + image: + "https://iguanas.mystagingwebsite.com/wp-content/uploads/2024/05/character-image-10-1.png", + description: "Pixel Art Kangaroo", + }, + ], + environmentId: "82a81049-8c41-4ae3-91ca-0bd82a283abc", + }); + }} + > + Direct NFT Purchase +