【问题标题】:Add Woocommerce Subscription switch to cart programmatically以编程方式将 Woocommerce 订阅开关添加到购物车
【发布时间】:2021-10-20 03:11:29
【问题描述】:

我正在寻找一种以编程方式将两个 Woocommerce 订阅变体之间的切换添加到购物车的方法。 我们正在构建一个无头 WP 站点,所以我不想用链接来做,如 this question

中所述

我尝试了以下简单的方法,但它不允许这样做,因为用户已经订阅:

WC()->cart->add_to_cart( 1907, 1, 1908 );

我想模拟用户进入升级/降级页面、选择新变体并按下切换订阅时发生的情况。通常这意味着用户通过购物车中的开关被发送到结帐,我只想将其添加到购物车中,因为我们有自己的结帐。

【问题讨论】:

  • @GrafiCode,我想要一些不需要单击 URL 的东西,因为 WP 部分只是后端。这是一个 Gatsby 前端向 WP 发出 graphql 请求,应该将订阅更改添加到购物车。最终用户永远不会看到 WP。
  • 为了清楚起见,您使用的是这个插件吗? woocommerce subscriptionswoocommerce.com/products/woocommerce-subscriptions
  • @GrafiCode,是的,正是
  • @GrafiCode 感谢您的坚持!我想我已经明白了,但我仍在测试一些东西。我可以使用 add_to_cart(),但我必须让 WC 相信该产品实际上是可购买的,并且还要向购物车项目添加一些元数据。当我完全弄清楚后,我会发布答案!

标签: php wordpress woocommerce woocommerce-subscriptions


【解决方案1】:

主要问题是 Woocommerce 认为订阅产品在以任何其他方式添加到购物车时是无法购买的,而不是通过订阅切换表单。该表单发送了几个 GET 参数,这些参数通过一些额外的验证发送 WC,然后如果其他所有内容都有效,则允许将该项目添加到购物车中。

我对此的解决方案是使用我自己的一些功能来模拟这种验证,这些功能主要是从订阅插件中窃取的。

/**
 * Must be set to true before programmatically adding subscription switch to cart.
 *
 * @var  bool
 */
$kbhl_adding_switch_to_cart = false;

/**
 * Array filled with data about current subscription.
 * Should only be retrieved by kbhl_get_current_subscription_data().
 *
 * @var  array
 */
$kbhl_global_current_subscription_data = array();


/**
 * Cache whether a given product is purchasable or not to save running lots of queries for the same product in the same request
 *
 *      $is_purchasable_cache taken from plugins\woocommerce-subscriptions\includes\class-wcs-limiter.php
 *
 * @var  array
 */
$kbhl_purchasable_cache = array();

/**
 * $user_subscriptions_to_product taken from plugins\woocommerce-subscriptions\includes\class-wcs-limiter.php
 *
 * @var  array
 */
$kbhl_user_subscriptions_to_product = array();


/**
 * If a product is being marked as not purchasable because it is limited and the customer has a subscription,
 * but the current request is to switch the subscription, then mark it as purchasable.
 *
 *        Function is_purchasable_switch() taken from plugins\woocommerce-subscriptions\includes\class-wcs-limiter.php
 *
 * @param   bool $is_purchasable  Current purchasable status.
 * @param   obj  $product         Product being checked.
 *
 * @return  bool                  New purchasable status.
 */
function kbhl_is_purchasable_switch( $is_purchasable, $product ) {
    global $kbhl_purchasable_cache;

    $kbhl_current_subscription_data = kbhl_get_current_subscription_data();

    // Only process this filter if running custom add switch function.
    if ( ! empty( $kbhl_current_subscription_data['id'] ) ) {
        return $is_purchasable;
    }

    $product_key = wcs_get_canonical_product_id( $product );

    // Set an empty cache if one isn't set yet.
    if ( ! isset( $kbhl_purchasable_cache[ $product_key ] ) ) {
        $kbhl_purchasable_cache[ $product_key ] = array();
    }

    // Exit early if we've already determined this product's purchasability via switching.
    if ( isset( $kbhl_purchasable_cache[ $product_key ]['switch'] ) ) {
        return $kbhl_purchasable_cache[ $product_key ]['switch'];
    }

    // If the product is already purchasble, we don't need to determine it's purchasibility via switching/auto-switching.
    if ( true === $is_purchasable || ! is_user_logged_in() || ! wcs_is_product_switchable_type( $product ) || ! WC_Subscriptions_Product::is_subscription( $product->get_id() ) ) {
        $kbhl_purchasable_cache[ $product_key ]['switch'] = $is_purchasable;
        return $kbhl_purchasable_cache[ $product_key ]['switch'];
    }

    $user_id            = get_current_user_id();
    $product_limitation = wcs_get_product_limitation( $product );

    if ( 'no' == $product_limitation || ! wcs_user_has_subscription( $user_id, $product->get_id(), wcs_get_product_limitation( $product ) ) ) {
        $kbhl_purchasable_cache[ $product_key ]['switch'] = $is_purchasable;
        return $kbhl_purchasable_cache[ $product_key ]['switch'];
    }

    // Adding to cart.
    if ( array_key_exists( $kbhl_current_subscription_data['id'], kbhl_get_user_subscriptions_to_product( $product, $user_id, $product_limitation ) ) ) {
        $is_purchasable = true;
    }

    $kbhl_purchasable_cache[ $product_key ]['switch'] = $is_purchasable;
    return $kbhl_purchasable_cache[ $product_key ]['switch'];
}
add_filter( 'woocommerce_subscription_is_purchasable', 'kbhl_is_purchasable_switch', 13, 2 );



/**
 * Gets a list of the customer subscriptions to a product with a particular limited status.
 *
 *        Function get_user_subscriptions_to_product() taken from plugins\woocommerce-subscriptions\includes\class-wcs-limiter.php
 *
 * @param WC_Product|int $product      The product object or product ID.
 * @param int            $user_id      The user's ID.
 * @param string         $limit_status The limit status.
 *
 * @return WC_Subscription[] An array of a customer's subscriptions with a specific status and product.
 */
function kbhl_get_user_subscriptions_to_product( $product, $user_id, $limit_status ) {
    global $user_subscriptions_to_product;
    $product_id = is_object( $product ) ? $product->get_id() : $product;
    $cache_key  = "{$product_id}_{$user_id}_{$limit_status}";

    if ( ! isset( $user_subscriptions_to_product[ $cache_key ] ) ) {
        // Getting all the customers subscriptions and removing ones without the product is more performant than querying for subscriptions with the product.
        $subscriptions = wcs_get_subscriptions(
            array(
                'customer_id' => $user_id,
                'status'      => $limit_status,
            )
        );

        foreach ( $subscriptions as $subscription_id => $subscription ) {
            if ( ! $subscription->has_product( $product_id ) ) {
                unset( $subscriptions[ $subscription_id ] );
            }
        }

        $user_subscriptions_to_product[ $cache_key ] = $subscriptions;
    }

    return $user_subscriptions_to_product[ $cache_key ];
}





/**
 * When a subscription switch is added to the cart, store a record of pertinent meta about the switch.
 *
 * @since 1.4
 */

/**
 * When a subscription switch is added to the cart, store a record of pertinent meta about the switch.
 *
 *       Function set_switch_details_in_cart() taken from plugins\woocommerce-subscriptions\includes\class-wc-subscriptions-switcher.php
 *
 * @param   array $cart_item_data    Current cart item data.
 * @param   int   $product_id        ID of current product.
 * @param   int   $variation_id      ID of current product variation.
 *
 * @return  array                   Updated cart item data.
 */
function kbhl_set_switch_details_in_cart( $cart_item_data, $product_id, $variation_id ) {
    try {

        $kbhl_current_subscription_data = kbhl_get_current_subscription_data();
        if ( empty( $kbhl_current_subscription_data['id'] ) || empty( $kbhl_current_subscription_data['item'] ) ) {
            return $cart_item_data;
        }

        $subscription = wcs_get_subscription( $kbhl_current_subscription_data['id'] );

        // Requesting a switch for someone elses subscription.
        if ( ! current_user_can( 'switch_shop_subscription', $subscription->get_id() ) ) {
            wc_add_notice( __( 'You can not switch this subscription. It appears you do not own the subscription.', 'woocommerce-subscriptions' ), 'error' );
            WC()->cart->empty_cart( true );
            return array();
        }

        $item = wcs_get_order_item( absint( $kbhl_current_subscription_data['item'] ), $subscription );

        // Else it's a valid switch.
        $product         = wc_get_product( $item['product_id'] );
        $parent_products = WC_Subscriptions_Product::get_parent_ids( $product );
        $child_products  = array();

        if ( ! empty( $parent_products ) ) {
            foreach ( $parent_products as $parent_id ) {
                $child_products = array_unique( array_merge( $child_products, wc_get_product( $parent_id )->get_children() ) );
            }
        }

        if ( $product_id != $item['product_id'] && ! in_array( $item['product_id'], $child_products ) ) {
            return $cart_item_data;
        }

        $next_payment_timestamp = $subscription->get_time( 'next_payment' );

        // If there are no more payments due on the subscription, because we're in the last billing period, we need to use the subscription's expiration date, not next payment date.
        if ( false == $next_payment_timestamp ) {
            $next_payment_timestamp = $subscription->get_time( 'end' );
        }

        $cart_item_data['subscription_switch'] = array(
            'subscription_id'        => $subscription->get_id(),
            'item_id'                => absint( $kbhl_current_subscription_data['item'] ),
            'next_payment_timestamp' => $next_payment_timestamp,
            'upgraded_or_downgraded' => '',
        );

        return $cart_item_data;

    } catch ( Exception $e ) {

        wc_add_notice( __( 'There was an error locating the switch details.', 'woocommerce-subscriptions' ), 'error' );
        WC()->cart->empty_cart( true );
        return array();
    }
}
add_filter( 'woocommerce_add_cart_item_data', 'kbhl_set_switch_details_in_cart', 11, 3 );





/**
 * Gets subscription data for current user.
 *
 * @return  array  Subscription data, also stored to global variable $kbhl_global_current_subscription_data
 */
function kbhl_get_current_subscription_data() {
    global $kbhl_adding_switch_to_cart, $kbhl_global_current_subscription_data;

    if ( ! $kbhl_adding_switch_to_cart ) {
        return array();
    }

    if ( ! empty( $kbhl_global_current_subscription_data ) ) {
        return $kbhl_global_current_subscription_data;
    }

    $subscription_data = array();
    $subs              = wcs_get_users_subscriptions();

    if ( ! empty( $subs ) ) {
        foreach ( $subs as $sub ) {
            $subscription_data['id'] = $sub->get_id();

            foreach ( $sub->get_items() as $item_id => $item ) {
                $subscription_data['item'] = $item_id;
                break; // There should only be 1 order item.
            }

            break; // There should only be 1 subscription.
        }
    }

    $kbhl_global_current_subscription_data = $subscription_data;

    return $kbhl_global_current_subscription_data;
}

添加后,您可以将开关添加到购物车,如下所示:

global $kbhl_adding_switch_to_cart; // If run inside a function.
WC()->cart->empty_cart( true );
$kbhl_adding_switch_to_cart = true;
WC()->cart->add_to_cart( 1907, 1, 1927 );
$kbhl_adding_switch_to_cart = false; // Reset after to get back to default validation.

【讨论】:

  • 感谢分享您的解决方案。
  • @GrafiCode 谢谢!我可以在末尾添加函数kbhl_get_current_subscription_data(),这可能是针对我的情况的,因为它假定用户只有一个订阅,并且订阅只包含 1 个项目。
猜你喜欢
  • 1970-01-01
  • 2016-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-30
  • 2017-10-13
  • 2015-10-10
相关资源
最近更新 更多