import {useCallback} from 'react' import {useShopIntent} from '../../internal/useShopIntent' import type { BuyNowProductVariantParams, BuyNowProductVariantResult, IntentResult, } from '@shopify/shop-minis-platform/actions' /** * Params accepted by `useBuyNow().buyNow()`. Alias for the shared * `BuyNowProductVariantParams` intent payload. * * @publicDocs */ export type BuyNowParams = BuyNowProductVariantParams /** * Successful payload returned from `useBuyNow().buyNow()`. Alias for * `BuyNowProductVariantResult`. * * @publicDocs */ export type BuyNowResult = BuyNowProductVariantResult export interface UseBuyNowReturns { /** * Sends the user to express checkout for a product variant. * * If `productVariantId` is supplied, the host creates the checkout * directly. If only `productId` is supplied, the native variant selector * sheet opens over the WebView and the user picks the variant; the host * then creates the checkout on confirm. * * Resolves with: * - `{code: 'ok', data: {outcome: 'checkout_opened', ...}}` once the * user is navigated to express checkout. * - `{code: 'ok', data: {outcome: 'navigated_to_product', productId}}` * if the product is a referral (off-catalog) product and the host * sent the user to its PDP instead. The shopper is now on the PDP — * treat this as a successful hand-off, not a failure. * - `{code: 'closed'}` when the user dismisses the sheet. * - `{code: 'error', message}` if checkout creation fails. * * Always switch on `data.outcome` to distinguish the two `'ok'` shapes. */ buyNow: (params: BuyNowParams) => Promise> } /** * Sends the user to express checkout for a product variant, optionally * prompting them to pick a variant first via a native bottom sheet rendered * on top of the mini WebView. * * Behavior: * * - When `productVariantId` is supplied (and `forceShow` is `false`), the * host creates the checkout directly without showing UI. * - When `productVariantId` is omitted and the product has a single * selectable variant, the host short-circuits and creates the checkout * directly. The success payload sets `source: 'auto'`. * - Otherwise the sheet opens, the user picks a variant and quantity, and * the host creates the checkout on confirm. `source: 'user'`. * - For referral (off-catalog) products the host cannot create a checkout; * it navigates the user to the merchant's PDP and resolves with * `{outcome: 'navigated_to_product'}`. `BuyNowButton` also handles this * case at the component level so most callers never observe it. * * Internally this is a typed wrapper around the * `buy_now:shopify/ProductVariant` intent. * * @example * ```tsx * const {buyNow} = useBuyNow() * * // direct buy now \u2014 mini knows the variant * await buyNow({productId, productVariantId, quantity: 1}) * * // prompt the user to pick a variant * const result = await buyNow({productId}) * if (result.code === 'ok') { * if (result.data.outcome === 'checkout_opened') { * // result.data.productVariantId, .quantity, .source * } else { * // referral hand-off; user is now on the PDP * // result.data.outcome === 'navigated_to_product' * // result.data.productId * } * } * ``` * * @publicDocs */ export function useBuyNow(): UseBuyNowReturns { const {invokeQuery} = useShopIntent() const buyNow = useCallback( async (params: BuyNowParams): Promise> => { const result = await invokeQuery({ action: 'buy_now', type: 'shopify/ProductVariant', // Input context (productId, productVariantId, ...) lives in `data`. // `value` is omitted: no pre-existing checkout resource exists \u2014 // we're creating one. data: params, }) // The bridge returns untyped data; the host handler guarantees the // shape matches BuyNowResult for this action+type. return result as IntentResult }, [invokeQuery] ) return {buyNow} } /** * The `useBuyNow` hook sends the user to express checkout for a product * variant, optionally opening a native variant selector sheet first when * the mini only knows the product (not the variant). Returns an * `IntentResult` so the mini can distinguish success, user-dismiss, and * error. * * @publicDocs */ export type UseBuyNowGeneratedType = () => UseBuyNowReturns