src/Page/Product.php
public function canPurchase($member = null, $quantity = 1)
    {
        $global = self::config()->global_allow_purchase;
        if (!$global || !$this->AllowPurchase) {
            return false;
        }
        $allowpurchase = false;
        $extension = self::has_extension(ProductVariationsExtension::class);
        if ($extension && Variation::get()->filter('ProductID', $this->ID)->first()) {
            foreach ($this->Variations() as $variation) {
                if ($variation->canPurchase($member, $quantity)) {
                    $allowpurchase = true;
                    break;
                }
            }
        } else {
            $allowpurchase = ($this->sellingPrice() > 0 || self::config()->allow_zero_price);
        }

        // Standard mechanism for accepting permission changes from decorators
        $permissions = $this->extend('canPurchase', $member, $quantity);
        $permissions[] = $allowpurchase;
        return min($permissions);
    }
public function canPurchase($member = null, $quantity = 1)
    {
        $global = self::config()->global_allow_purchase;
        if (!$global || !$this->AllowPurchase) {
            return false;
        }
        $allowpurchase = false;
        $extension = self::has_extension(ProductVariationsExtension::class);
        if ($extension && Variation::get()->filter('ProductID', $this->ID)->first()) {
            foreach ($this->Variations() as $variation) {
                if ($variation->canPurchase($member, $quantity)) {
                    $allowpurchase = true;
                    break;
                }
            }
        } else {
            $allowpurchase = ($this->sellingPrice() > 0 || self::config()->allow_zero_price);
        }

        // Standard mechanism for accepting permission changes from decorators
        $permissions = $this->extend('canPurchase', $member, $quantity);
        $permissions[] = $allowpurchase;
        return min($permissions);
    }
Conditions for whether a product can be purchased:
- global allow purchase is enabled
- product AllowPurchase field is true
- if variations, then one of them needs to be purchasable
- if not variations, selling price must be above 0

Other conditions may be added by decorating with the canPurchase function

@param Member $member
@param int    $quantity

@return boolean