mirror of
https://gh.wpcy.net/https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2026-04-29 04:59:01 +08:00
330 lines
11 KiB
PHP
330 lines
11 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Helper class for the subscriptions. Contains methods to determine
|
|
* whether the cart contains a subscription, the current product is
|
|
* a subscription or the subscription plugin is activated in the first place.
|
|
*
|
|
* @package WooCommerce\PayPalCommerce\WcSubscriptions\Helper
|
|
*/
|
|
declare (strict_types=1);
|
|
namespace WooCommerce\PayPalCommerce\WcSubscriptions\Helper;
|
|
|
|
use WC_Order;
|
|
use WC_Product;
|
|
use WC_Product_Subscription_Variation;
|
|
use WC_Product_Variable;
|
|
use WC_Subscription;
|
|
use WC_Subscriptions;
|
|
use WC_Subscriptions_Product;
|
|
use WCS_Manual_Renewal_Manager;
|
|
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
|
use WP_Query;
|
|
/**
|
|
* Class SubscriptionHelper
|
|
*/
|
|
class SubscriptionHelper
|
|
{
|
|
public const SUBSCRIPTION_MODE_VALUE_VAULTING = 'vaulting_api';
|
|
public const SUBSCRIPTION_MODE_VALUE_SUBSCRIPTIONS = 'subscriptions_api';
|
|
public const SUBSCRIPTION_MODE_VALUE_DISABLED = 'disable_paypal_subscriptions';
|
|
/**
|
|
* Whether the current product is a subscription.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function current_product_is_subscription(): bool
|
|
{
|
|
if (!$this->plugin_is_active()) {
|
|
return \false;
|
|
}
|
|
$product = wc_get_product();
|
|
return $product && WC_Subscriptions_Product::is_subscription($product);
|
|
}
|
|
/**
|
|
* Whether the current cart contains subscriptions.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function cart_contains_subscription(): bool
|
|
{
|
|
if (!$this->plugin_is_active()) {
|
|
return \false;
|
|
}
|
|
$cart = WC()->cart;
|
|
/**
|
|
* Don't use `$cart->is_empty()` for checking for an empty cart.
|
|
* This is maybe called so early that it can corrupt it because it loads it than from session
|
|
*/
|
|
if (!$cart || empty($cart->cart_contents)) {
|
|
return \false;
|
|
}
|
|
foreach ($cart->get_cart() as $item) {
|
|
if (!isset($item['data']) || !is_a($item['data'], WC_Product::class)) {
|
|
continue;
|
|
}
|
|
if (WC_Subscriptions_Product::is_subscription($item['data'])) {
|
|
return \true;
|
|
}
|
|
}
|
|
return \false;
|
|
}
|
|
/**
|
|
* Whether pay for order contains subscriptions.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function order_pay_contains_subscription(): bool
|
|
{
|
|
if (!$this->plugin_is_active() || !is_wc_endpoint_url('order-pay')) {
|
|
return \false;
|
|
}
|
|
global $wp;
|
|
$order_id = (int) $wp->query_vars['order-pay'];
|
|
if (0 === $order_id) {
|
|
return \false;
|
|
}
|
|
return $this->has_subscription($order_id);
|
|
}
|
|
/**
|
|
* Whether manual renewals are accepted.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function accept_manual_renewals(): bool
|
|
{
|
|
if (!class_exists(WCS_Manual_Renewal_Manager::class)) {
|
|
return \false;
|
|
}
|
|
return WCS_Manual_Renewal_Manager::is_manual_renewal_enabled();
|
|
}
|
|
/**
|
|
* Whether the subscription plugin is active or not.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function plugin_is_active(): bool
|
|
{
|
|
return class_exists(WC_Subscriptions::class) && class_exists(WC_Subscriptions_Product::class);
|
|
}
|
|
/**
|
|
* Checks if order contains subscription.
|
|
*
|
|
* @param int $order_id The order Id.
|
|
* @return boolean Whether order is a subscription or not.
|
|
*/
|
|
public function has_subscription($order_id): bool
|
|
{
|
|
return function_exists('wcs_order_contains_subscription') && (wcs_order_contains_subscription($order_id) || wcs_is_subscription($order_id) || wcs_order_contains_renewal($order_id));
|
|
}
|
|
/**
|
|
* Checks if page is pay for order and change subscription payment page.
|
|
*
|
|
* @return bool Whether page is change subscription or not.
|
|
*/
|
|
public function is_subscription_change_payment(): bool
|
|
{
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
if (!isset($_GET['pay_for_order']) || !isset($_GET['change_payment_method'])) {
|
|
return \false;
|
|
}
|
|
return \true;
|
|
}
|
|
/**
|
|
* Checks whether subscription needs subscription intent.
|
|
*
|
|
* @param string $subscription_mode The subscription mode.
|
|
* @return bool
|
|
*/
|
|
public function need_subscription_intent(string $subscription_mode): bool
|
|
{
|
|
if ($subscription_mode === 'subscriptions_api') {
|
|
if ($this->current_product_is_subscription() || (is_cart() || is_checkout()) && $this->cart_contains_subscription()) {
|
|
return \true;
|
|
}
|
|
}
|
|
return \false;
|
|
}
|
|
/**
|
|
* Checks if subscription product is allowed.
|
|
*
|
|
* @return bool
|
|
* @throws NotFoundException If setting is not found.
|
|
*/
|
|
public function checkout_subscription_product_allowed(): bool
|
|
{
|
|
if (!$this->paypal_subscription_id() || !$this->cart_contains_only_one_item()) {
|
|
return \false;
|
|
}
|
|
return \true;
|
|
}
|
|
/**
|
|
* Returns PayPal subscription plan id from WC subscription product.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function paypal_subscription_id(): string
|
|
{
|
|
if ($this->current_product_is_subscription()) {
|
|
$product = wc_get_product();
|
|
assert($product instanceof WC_Product);
|
|
if ($product->get_type() === 'subscription' && $product->meta_exists('ppcp_subscription_plan')) {
|
|
return $product->get_meta('ppcp_subscription_plan')['id'];
|
|
}
|
|
}
|
|
$cart = WC()->cart ?? null;
|
|
if (!$cart || $cart->is_empty()) {
|
|
return '';
|
|
}
|
|
$items = $cart->get_cart_contents();
|
|
foreach ($items as $item) {
|
|
$product = wc_get_product($item['product_id']);
|
|
assert($product instanceof WC_Product);
|
|
if ($product->get_type() === 'subscription' && $product->meta_exists('ppcp_subscription_plan')) {
|
|
return $product->get_meta('ppcp_subscription_plan')['id'];
|
|
}
|
|
if ($product->get_type() === 'variable-subscription') {
|
|
assert($product instanceof WC_Product_Variable);
|
|
$product_variations = $product->get_available_variations();
|
|
foreach ($product_variations as $variation) {
|
|
/** @psalm-suppress UndefinedMethod */
|
|
$variation_product = wc_get_product($variation['variation_id']) ?? '';
|
|
if ($variation_product && $variation_product->meta_exists('ppcp_subscription_plan')) {
|
|
return $variation_product->get_meta('ppcp_subscription_plan')['id'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
/**
|
|
* Returns variations for variable PayPal subscription product.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function variable_paypal_subscription_variations(): array
|
|
{
|
|
$variations = array();
|
|
if (!$this->current_product_is_subscription()) {
|
|
return $variations;
|
|
}
|
|
$product = wc_get_product();
|
|
assert($product instanceof WC_Product);
|
|
if ($product->get_type() !== 'variable-subscription') {
|
|
return $variations;
|
|
}
|
|
$variation_ids = $product->get_children();
|
|
foreach ($variation_ids as $id) {
|
|
$product = wc_get_product($id);
|
|
if (!$product instanceof WC_Product_Subscription_Variation) {
|
|
continue;
|
|
}
|
|
$subscription_plan = $product->get_meta('ppcp_subscription_plan') ?? array();
|
|
$variations[] = array('id' => $product->get_id(), 'attributes' => $product->get_attributes(), 'subscription_plan' => $subscription_plan['id'] ?? '');
|
|
}
|
|
return $variations;
|
|
}
|
|
/**
|
|
* Checks if cart contains only one item.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function cart_contains_only_one_item(): bool
|
|
{
|
|
if (!$this->plugin_is_active()) {
|
|
return \false;
|
|
}
|
|
$cart = WC()->cart;
|
|
if (!$cart || $cart->is_empty()) {
|
|
return \false;
|
|
}
|
|
if (count($cart->get_cart()) > 1) {
|
|
return \false;
|
|
}
|
|
return \true;
|
|
}
|
|
/**
|
|
* Returns the locations on the page which have subscription products.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function locations_with_subscription_product(): array
|
|
{
|
|
return array('product' => is_product() && $this->current_product_is_subscription(), 'payorder' => is_wc_endpoint_url('order-pay') && $this->order_pay_contains_subscription(), 'cart' => $this->cart_contains_subscription());
|
|
}
|
|
/**
|
|
* Returns previous order transaction from the given subscription.
|
|
*
|
|
* @param WC_Subscription $subscription WooCommerce Subscription.
|
|
* @param string $vault_token_id Vault token id.
|
|
* @return string
|
|
*/
|
|
public function previous_transaction(WC_Subscription $subscription, string $vault_token_id): string
|
|
{
|
|
$orders = $subscription->get_related_orders('ids', array('parent', 'renewal'));
|
|
if (!$orders || !$vault_token_id) {
|
|
return '';
|
|
}
|
|
// Sort orders by order ID descending.
|
|
rsort($orders);
|
|
$current_order = wc_get_order(array_shift($orders));
|
|
if (!$current_order instanceof WC_Order) {
|
|
return '';
|
|
}
|
|
foreach ($orders as $order_id) {
|
|
$order = wc_get_order($order_id);
|
|
if ($order instanceof WC_Order && in_array($order->get_status(), array('processing', 'completed'), \true) && $current_order->get_payment_method() === $order->get_payment_method()) {
|
|
$transaction_id = $order->get_transaction_id();
|
|
$tokens = $order->get_payment_tokens();
|
|
foreach ($tokens as $token) {
|
|
$wc_token = \WC_Payment_Tokens::get($token);
|
|
if ($transaction_id && $wc_token instanceof \WC_Payment_Token && $wc_token->get_token() === $vault_token_id) {
|
|
return $transaction_id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
/**
|
|
* Returns the variation subscription plan id from the cart.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function paypal_subscription_variation_from_cart(): string
|
|
{
|
|
$cart = WC()->cart ?? null;
|
|
if (!$cart || $cart->is_empty()) {
|
|
return '';
|
|
}
|
|
$items = $cart->get_cart_contents();
|
|
foreach ($items as $item) {
|
|
$variation_id = $item['variation_id'] ?? 0;
|
|
if ($variation_id) {
|
|
$variation_product = wc_get_product($variation_id) ?? '';
|
|
if ($variation_product && $variation_product->meta_exists('ppcp_subscription_plan')) {
|
|
return $variation_product->get_meta('ppcp_subscription_plan')['id'];
|
|
}
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
/**
|
|
* Checks if any subscription products exist.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function has_subscription_products(): bool
|
|
{
|
|
// Query for subscription products.
|
|
$args = array(
|
|
'post_type' => 'product',
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => 1,
|
|
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
|
|
'tax_query' => array(array('taxonomy' => 'product_type', 'field' => 'slug', 'terms' => 'subscription')),
|
|
);
|
|
$subscription_products = new WP_Query($args);
|
|
return $subscription_products->have_posts();
|
|
}
|
|
}
|