import {useState, useCallback} from 'react' import {Product} from '@shopify/shop-minis-platform' import {motion, AnimatePresence} from 'motion/react' import {useErrorToast, useShopNavigation} from '../../hooks' import {useShopCartActions} from '../../internal/useShopCartActions' import {cn} from '../../lib/utils' import {Button} from '../atoms/button' /** * Navigates directly to Shop's express checkout, bypassing the cart entirely. Use for single-item purchases, limited-time offers, or when reducing friction is critical. Best for impulse buys and focused conversion flows. * @publicDocs */ export interface BuyNowButtonProps { /** Whether the button is disabled */ disabled?: boolean /** CSS class name */ className?: string /** Button size variant */ size?: 'default' | 'sm' | 'lg' /** The discount code to apply to the purchase */ discountCode?: string /** The GID of the product variant. E.g. `gid://shopify/ProductVariant/456` */ productVariantId: string /** The product to buy now */ product?: Product } export function BuyNowButton({ disabled = false, className, size = 'default', productVariantId, discountCode, product, }: BuyNowButtonProps) { const {buyProduct} = useShopCartActions() const {navigateToProduct} = useShopNavigation() const [isPurchasing, setIsPurchasing] = useState(false) const {id, referral, variants, selectedVariant} = product ?? {} // Look up the matching variant from `variants` first (productLists/savedProducts // path); otherwise fall back to `selectedVariant` (useProduct/useProducts marketplace path). const matchingVariant = variants?.find(variant => variant.id === productVariantId) ?? (selectedVariant?.id === productVariantId ? selectedVariant : undefined) const isSoldOut = matchingVariant?.availableForSale === false // Referral products only navigate to the PDP — never gate them on stock so a // sold-out selected variant doesn't strand the user without a way to switch. const isDisabled = disabled || isPurchasing || (!referral && isSoldOut) const {showErrorToast} = useErrorToast() const handleClick = useCallback(async () => { if (isDisabled) return if (id && referral) { navigateToProduct({ productId: id, }) return } try { if (id && productVariantId) { setIsPurchasing(true) await buyProduct({ productId: id, productVariantId, quantity: 1, discountCode, }) setIsPurchasing(false) } } catch (error) { showErrorToast({message: 'Failed to complete purchase'}) setIsPurchasing(false) } }, [ isDisabled, id, referral, navigateToProduct, productVariantId, buyProduct, discountCode, showErrorToast, ]) const getButtonText = () => { if (referral) return 'View product' if (isSoldOut) return 'Sold out' return 'Buy now' } const buttonText = getButtonText() return ( ) }