以编程方式将 Woocommerce 订阅开关添加到购物车

2024-01-20

我正在寻找一种以编程方式将两个 Woocommerce 订阅变体之间的切换添加到购物车的方法。 我们正在构建一个无头 WP 网站,因此我不想使用链接来完成此操作,如中所述这个问题 https://stackoverflow.com/questions/48327381/switch-subscription-add-directly-to-cart

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

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

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


主要问题是,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.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

以编程方式将 Woocommerce 订阅开关添加到购物车 的相关文章

  • 当数据验证失败时保留表单字段中的值

    我在弄清楚验证失败时如何保留用户数据时遇到问题 我对 PHP 有点陌生 所以我的逻辑可能会犯一些巨大的错误 目前 如果验证失败 所有字段都会被清除 并且 Post 数据也会消失 这是一些代码 假设用户输入无效电子邮件 我希望保留 名称 字段
  • 在 php 中为类自动生成 getter 和 setter 的最佳方法是什么? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我经常创建一个包含一些私有变量的类 当设置此类的实例时 应该可以使用 getter 和 setter 填充该类的所有变量 有没有一种简单的方法可
  •  
    标记内删除

    我制作了简单的 BBCode 脚本 一切正常 但后来我使用了一个 javascript 库来美化我的代码 pre pre 现在我面临的唯一问题是 br 每行代码后面的标签 pre pre tags 所以问题是我怎样才能删除 br 标记哪些在
  • 生成大随机数 php [重复]

    这个问题在这里已经有答案了 我想使用 PHP 生成一个包含 75 个字符的数字 我到处寻找 但一无所获 除了这个 http dailycoding com tools RandomNumber aspx http dailycoding c
  • 检查 PHP 中“@”字符后面的单词

    我现在正在制作一个新闻和评论系统 但是我已经在一个部分上停留了一段时间了 我希望用户能够在 Twitter 上引用其他玩家的风格 例如 用户名 该脚本看起来像这样 不是真正的 PHP 只是想象脚本 3 string I loved the
  • 如何从父类函数访问子类中定义的常量?

    我从 php net 看到这个例子 但 c MY CONST 仅在 5 3
  • posts_search 中的自定义查询

    如何使用此查询作为我的自定义搜索查询 add filter posts search my search is perfect 20 2 function my search is perfect search wp query sWord
  • 扩展蓝图类?

    我想覆盖timestamps 函数中发现Blueprint班级 我怎样才能做到这一点 e g public function up Schema create users function Blueprint table table gt
  • 蛋糕控制台 2.2.1:烘焙错误

    运行 MAMP 的 OSX 机器 CakePHP 2 2 1 已正确安装和配置 这意味着当我浏览到 Index php 文件时 所有绿色条都显示出来 我已经完成了博客教程 并且正在开发我的第二个应用程序 其中脚手架已启动并运行 现在我第一次
  • Symfony2 Assetic 和 Less Sourcemaps

    我不确定如何破解 assetic less 过滤器以输出源映射文件 我这里指的是LessFilterhttps github com kriswallsmith assetic blob master src Assetic Filter
  • 如何在原则 2 迁移中删除外键

    我想在原则 2 迁移中删除外键 但没有 dropForeignKeyConstraint 有谁知道怎么丢掉吗 public function down Schema schema table schema gt getTable table
  • 从 .phar 存档中提取文件

    对于 Phar 文件 我完全错过了一些东西 我正在安装一个需要 phpunit pdepend 和其他依赖项的项目 我将它们作为 phar 文件获取 但是 我无法使用命令行工具 php 命令 从中提取文件 我用谷歌搜索了这个问题 但没有发现
  • 交换关联数组中的两个项目

    Example arr array apple gt sweet grapefruit gt bitter pear gt tasty banana gt yellow 我想调换一下柚子和梨的位置 这样数组就变成了 arr array ap
  • 为什么 iconv 在 php:7.4-fpm-alpine docker 中返回空字符串

    给出以下代码
  • PHP 脚本可以在终端中运行,但不能在浏览器中运行

    我正在尝试执行exec命令 但我遇到了问题 当我运行以下代码时 当我通过浏览器运行它时它不起作用 但如果我把输出 str将其复制并粘贴到终端中 它工作得很好 造成这种情况的原因是什么 我该如何解决 目前我正在运行localhost php
  • 在 Wordpress 站点中进行 AJAX 调用时出现问题

    我在使用 Wordpress 站点功能的 AJAX 部分时遇到了一些问题 该功能接受在表单上输入的邮政编码 使用 PHP 函数来查找邮政编码是否引用特定位置并返回到该位置的永久链接 我的第一个问题是关于我构建的表单 现在我的表单操作是空白的
  • PHP 中只保留数组的前 N ​​个元素? [复制]

    这个问题在这里已经有答案了 有没有办法只保留数组的前 N 个 例如 10 个 元素 我知道有array pop 但是有没有更好 更优雅的方法呢 您可以使用array slice http php net array slice or arr
  • php 错误 fopen(): 文件名不能为空

    发送带有附件代码的电子邮件工作正常 最近我们已将文件传输到另一个托管服务器 idk 发生了什么 它显示以下错误 警告 fopen 第 106 行 home hugerecruitmetnt public html validatecva p
  • 如何在 Laravel 中使用 PUT http 动词提交表单

    我知道这个问题可能已经提出 但我就是无法让它发挥作用 如果有人可以帮助我 我将非常感激 我安装了 colletive form 但答案也可以是 html 表单标签 现在列出我的表格 我的路线和我的例外情况 Form model array
  • 简单的 PHP 表单:电子邮件附件(代码 Golf)

    想象一下 一个用户想要在其网站上放置一个表单 该表单将允许网站访问者上传一个文件和一条简单的消息 该消息将立即通过电子邮件发送 即 该文件未存储在服务器上 或者如果该文件存储在服务器上 仅暂时 作为文件附件 并在邮件正文中添加注释 查看更多

随机推荐

  • 将 Crystal Reports 连接到 SQL Server

    所以我进入了 数据库专家 但我似乎不知道如何将数据库添加到报告中 有任何想法吗 附 我正在使用 CR 13 和 SQL Server 2012 我们正在使用 CR 11 R2 我不确定 CR 2013 的选项是否已更改 但在 CR 11 R
  • JavaScript 对象使用 .检索值[重复]

    这个问题在这里已经有答案了 可能的重复 如何使用动态键访问对象 https stackoverflow com questions 6921803 how to access object using dynamic key 我有多个具有不
  • Spring:类文件版本错误 61.0,应该是 55.0

    我正在使用 Java 11 的 Maven 和 IntelliJ IDEA 上的 Maven 我正在尝试创建一个 JsonTset 类 如下所示 import org junit BeforeClass import org junit T
  • Jersey - 有没有办法用参数实例化每个请求资源?

    假设我有这样的课程 Path test public class TestResource private TestService testService public TestResource TestService testServic
  • 如何解决 Argo 输出参数大小限制?

    我有一个在 JSON 数组上循环的 Argo 工作流程 当列表变得太大时 我会收到如下错误 time some time level fatal msg Pod some pod name is invalid metadata annot
  • MongoDB 副本集状态未从启动更改为辅助

    我已经设置了一个包含 3 个节点的 MongoDB 副本集 虚拟机运行 CentOS 一个节点成为主节点 另外 2 个节点陷入启动状态 当这两个节点将其状态从启动更改为辅助时 aryabhata PRIMARY gt rs status s
  • Python:使用 lineno 引发语法错误

    我正在为特定于域的语言实现一个解析器 并且希望能够引发语法错误 引发此异常时如何设置文件名 行号和偏移量 例外 语法错误 当解析器遇到语法时引发 错误 这可能发生在 import 语句 exec 语句 调用内置函数 eval 或 input
  • 树枝变量中的树枝变量

    我有一个树枝变量html 为了在树枝模板中显示它 我这样做 html 该变量看起来像 div region top div div region center div region 也是一个变量 当 Twig 解析我的html变量 它不解析
  • 检测 jQuery 中 input[type=text] 的值变化

    我想在每次特定输入框的值发生变化时执行一个函数 它almost与 input keyup function 但是例如 将文本粘贴到框中时什么也不会发生 input change function 仅在输入模糊时触发 那么我如何立即知道文本框
  • GCC 相当于 MS 的 /bigobj

    我们正在大量使用boost serialization和一般模板 一切似乎都很顺利 不过 我们在 Windows 构建上遇到了障碍 这似乎会导致目标文件太大的问题 我们使用 MinGW Msys 和 g 4 7 0 c mingw bin
  • Flutter 删除所有路由

    我想开发一个注销按钮 它将把我发送到登录路线并从登录路线中删除所有其他路线Navigator 该文档似乎没有解释如何制作RoutePredicate或具有任何类型的removeAll 功能 我能够使用以下代码来完成此操作 Navigator
  • 我可以将参数传递给 rake db:seed 吗?

    我的一部分seeds rb将大量数据加载到数据库中 我希望能够有选择地加载这些数据 例如 rake db seed or rake db seed 0 只会加载运行网站所需的数据 而 rake db seed 1 也会将我的大数据文件加载到
  • Ember 数据无法读取未定义的属性“async”

    将 Ember v1 8 beta 3 与 Ember Data 1 0 beta 10 一起使用 您会收到以下错误 Error while processing route index Cannot read property async
  • 生成指定范围内的随机数 - 各种情况(int、float、inclusive、exclusive)

    given a Math random 返回 0 1 之间的数字的函数min max值来指定范围 我们如何为以下情况生成数字 我们想要的案例integer A min max B min max return Math floor Math
  • 如何更改jquery对话框按钮

    我想用我自己的按钮图像替换 jquery 对话框按钮 这样做最简洁的方法是什么 按钮上不会覆盖任何文本 我正在使用 jquery 1 4 2 和 jquery ui 1 8 1 不要应用 jQuery UI 使用的 CSS 选择器 使用具有
  • Global.asax 事件:Application_OnPostAuthenticateRequest

    我在用Application OnPostAuthenticateRequest事件在global asax to get a 经过身份验证的用户的角色和权限我还制作了自定义主体类来获取用户详细信息以及角色和权限 b 获取对该用户而言保持不
  • Jquery 动画无法正确处理列表项

    我有一个垂直的项目列表 每个项目都有一个删除按钮 当我点击其中一个的删除时 我希望下面的那些能够平滑地向上滑动 此时它们正在跳跃 下面是代码 http codepen io ovesyan19 pen chDgy http codepen
  • 这个&符号是什么意思? [复制]

    这个问题在这里已经有答案了 可能的重复 只需观看一些 Railscast 即可看到如下代码 Category Product Person each delete all 我知道它会删除这些模型的所有记录 但我不知道这是什么 delete
  • 在 iPad 上的 SwiftUI 中呈现 ActionSheet

    我已经得到了一个可以在 iPhone 设备上很好地呈现的 ActionSheet 但它在 iPad 上会崩溃 说它需要弹出窗口的位置 有人对这段代码感到幸运吗 我正在使用 iOS 13 beta 3 和 Xcode 11 beta 3 这使
  • 以编程方式将 Woocommerce 订阅开关添加到购物车

    我正在寻找一种以编程方式将两个 Woocommerce 订阅变体之间的切换添加到购物车的方法 我们正在构建一个无头 WP 网站 因此我不想使用链接来完成此操作 如中所述这个问题 https stackoverflow com questio