init
This commit is contained in:
+50
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* Class BaseCompatibility
|
||||
*
|
||||
* @package WCPay\MultiCurrency\Compatibility
|
||||
*/
|
||||
|
||||
namespace WCPay\MultiCurrency\Compatibility;
|
||||
|
||||
use WCPay\MultiCurrency\MultiCurrency;
|
||||
use WCPay\MultiCurrency\Utils;
|
||||
|
||||
/**
|
||||
* Class that sets up base options for compatibility classes.
|
||||
*/
|
||||
abstract class BaseCompatibility {
|
||||
|
||||
/**
|
||||
* MultiCurrency class.
|
||||
*
|
||||
* @var MultiCurrency
|
||||
*/
|
||||
protected $multi_currency;
|
||||
|
||||
/**
|
||||
* Utils class.
|
||||
*
|
||||
* @var Utils
|
||||
*/
|
||||
protected $utils;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param MultiCurrency $multi_currency MultiCurrency class.
|
||||
* @param Utils $utils Utils class.
|
||||
*/
|
||||
public function __construct( MultiCurrency $multi_currency, Utils $utils ) {
|
||||
$this->multi_currency = $multi_currency;
|
||||
$this->utils = $utils;
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function init();
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WooCommerceBookings
|
||||
*
|
||||
* @package WCPay\MultiCurrency\Compatibility
|
||||
*/
|
||||
|
||||
namespace WCPay\MultiCurrency\Compatibility;
|
||||
|
||||
use WCPay\MultiCurrency\FrontendCurrencies;
|
||||
use WCPay\MultiCurrency\MultiCurrency;
|
||||
use WCPay\MultiCurrency\Utils;
|
||||
use WC_Product;
|
||||
|
||||
/**
|
||||
* Class that controls Multi Currency Compatibility with WooCommerce Bookings Plugin.
|
||||
*/
|
||||
class WooCommerceBookings extends BaseCompatibility {
|
||||
/**
|
||||
* Front-end currencies.
|
||||
*
|
||||
* @var FrontendCurrencies
|
||||
*/
|
||||
private $frontend_currencies;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param MultiCurrency $multi_currency MultiCurrency class.
|
||||
* @param Utils $utils Utils class.
|
||||
* @param FrontendCurrencies $frontend_currencies FrontendCurrencies class.
|
||||
*/
|
||||
public function __construct( MultiCurrency $multi_currency, Utils $utils, FrontendCurrencies $frontend_currencies ) {
|
||||
parent::__construct( $multi_currency, $utils );
|
||||
$this->frontend_currencies = $frontend_currencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
// Add needed actions and filters if Bookings is active.
|
||||
if ( class_exists( 'WC_Bookings' ) ) {
|
||||
if ( ! is_admin() || wp_doing_ajax() ) {
|
||||
add_filter( 'woocommerce_bookings_calculated_booking_cost', [ $this, 'adjust_amount_for_calculated_booking_cost' ], 50, 1 );
|
||||
add_filter( 'woocommerce_product_get_block_cost', [ $this, 'get_price' ], 50, 1 );
|
||||
add_filter( 'woocommerce_product_get_cost', [ $this, 'get_price' ], 50, 1 );
|
||||
add_filter( 'woocommerce_product_get_display_cost', [ $this, 'get_price' ], 50, 1 );
|
||||
add_filter( 'woocommerce_product_booking_person_type_get_block_cost', [ $this, 'get_price' ], 50, 1 );
|
||||
add_filter( 'woocommerce_product_booking_person_type_get_cost', [ $this, 'get_price' ], 50, 1 );
|
||||
add_filter( 'woocommerce_product_get_resource_base_costs', [ $this, 'get_resource_prices' ], 50, 1 );
|
||||
add_filter( 'woocommerce_product_get_resource_block_costs', [ $this, 'get_resource_prices' ], 50, 1 );
|
||||
add_filter( MultiCurrency::FILTER_PREFIX . 'should_convert_product_price', [ $this, 'should_convert_product_price' ], 50, 2 );
|
||||
add_action( 'wp_ajax_wc_bookings_calculate_costs', [ $this, 'add_wc_price_args_filter_for_ajax' ], 9 );
|
||||
add_action( 'wp_ajax_nopriv_wc_bookings_calculate_costs', [ $this, 'add_wc_price_args_filter_for_ajax' ], 9 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the calculated booking cost for the selected currency, applying rounding and charm pricing as necessary.
|
||||
*
|
||||
* @param mixed $costs The original calculated booking costs.
|
||||
* @return mixed The booking cost adjusted for the selected currency.
|
||||
*/
|
||||
public function adjust_amount_for_calculated_booking_cost( $costs ) {
|
||||
/**
|
||||
* Prevents adjustment of the calculated booking cost during cart addition.
|
||||
*
|
||||
* When a booking is added to the cart, the Booking plugin calculates the booking cost and
|
||||
* overrides the cart item price with this calculated amount. To avoid interfering with this process,
|
||||
* this function skips any additional adjustments at this stage.
|
||||
*/
|
||||
if ( $this->utils->is_call_in_backtrace( [ 'WC_Cart->add_to_cart' ] ) ) {
|
||||
return $costs;
|
||||
}
|
||||
|
||||
return $this->multi_currency->adjust_amount_for_selected_currency( $costs );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the price for an item, converting it based on the selected currency and context.
|
||||
*
|
||||
* @param mixed $price The item's price.
|
||||
*
|
||||
* @return mixed The converted item's price.
|
||||
*/
|
||||
public function get_price( $price ) {
|
||||
if ( ! $price ) {
|
||||
return $price;
|
||||
}
|
||||
|
||||
// Skip conversion during specific booking cost calculations to avoid double conversion.
|
||||
if ( $this->utils->is_call_in_backtrace( [ 'WC_Cart->add_to_cart' ] ) && $this->utils->is_call_in_backtrace( [ 'WC_Bookings_Cost_Calculation::calculate_booking_cost' ] ) ) {
|
||||
return $price;
|
||||
}
|
||||
|
||||
/**
|
||||
* When showing the price in HTML, the function applies currency conversion, charm pricing,
|
||||
* and rounding. For internal calculations, it uses the raw exchange rate, with charm pricing
|
||||
* and rounding adjustments applied only to the final calculated amount (handled in
|
||||
* adjust_amount_for_calculated_booking_cost).
|
||||
*/
|
||||
return $this->multi_currency->get_price( $price, $this->utils->is_call_in_backtrace( [ 'WC_Product_Booking->get_price_html' ] ) ? 'product' : 'exchange_rate' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the prices for a resource.
|
||||
*
|
||||
* @param mixed $prices The resource's prices in array format.
|
||||
*
|
||||
* @return mixed The converted resource's prices.
|
||||
*/
|
||||
public function get_resource_prices( $prices ) {
|
||||
if ( is_array( $prices ) ) {
|
||||
foreach ( $prices as $key => $price ) {
|
||||
$prices[ $key ] = $this->get_price( $price );
|
||||
}
|
||||
}
|
||||
return $prices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the product's price should be converted.
|
||||
*
|
||||
* @param bool $return Whether to convert the product's price or not. Default is true.
|
||||
* @param WC_Product $product The product instance being checked.
|
||||
*
|
||||
* @return bool True if it should be converted.
|
||||
*/
|
||||
public function should_convert_product_price( bool $return, WC_Product $product ): bool {
|
||||
// If it's already false, or the product is not a booking, ignore it.
|
||||
if ( ! $return || $product->get_type() !== 'booking' ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
// Fixes price display on product page and in shop.
|
||||
if ( $this->utils->is_call_in_backtrace( [ 'WC_Product_Booking->get_price_html' ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a filter for when there is an ajax call to calculate the booking cost.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_wc_price_args_filter_for_ajax() {
|
||||
add_filter( 'wc_price_args', [ $this, 'filter_wc_price_args' ], 100 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatting arguments to use when a booking price is calculated on the product.
|
||||
*
|
||||
* @param array $args Original args from wc_price().
|
||||
*
|
||||
* @return array New arguments matching the selected currency.
|
||||
*/
|
||||
public function filter_wc_price_args( $args ): array {
|
||||
return wp_parse_args(
|
||||
[
|
||||
'currency' => $this->multi_currency->get_selected_currency()->get_code(),
|
||||
'decimal_separator' => $this->frontend_currencies->get_price_decimal_separator( $args['decimal_separator'] ),
|
||||
'thousand_separator' => $this->frontend_currencies->get_price_thousand_separator( $args['thousand_separator'] ),
|
||||
'decimals' => $this->frontend_currencies->get_price_decimals( $args['decimals'] ),
|
||||
'price_format' => $this->frontend_currencies->get_woocommerce_price_format( $args['price_format'] ),
|
||||
],
|
||||
$args
|
||||
);
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WooCommerceDeposits
|
||||
*
|
||||
* @package WCPay\MultiCurrency\Compatibility
|
||||
*/
|
||||
|
||||
namespace WCPay\MultiCurrency\Compatibility;
|
||||
|
||||
use WC_Product;
|
||||
use WCPay\MultiCurrency\MultiCurrency;
|
||||
|
||||
/**
|
||||
* Class that controls Multi Currency Compatibility with WooCommerce Deposits Plugin.
|
||||
*/
|
||||
class WooCommerceDeposits extends BaseCompatibility {
|
||||
|
||||
/**
|
||||
* Init the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
if ( class_exists( 'WC_Deposits' ) ) {
|
||||
/*
|
||||
* Multi-currency support was added to WooCommerce Deposits in version 2.0.1.
|
||||
*
|
||||
* This prevents the loading of the compatibility class for Deposits in versions
|
||||
* of Deposits that support multi-currency.
|
||||
*
|
||||
* @see https://github.com/woocommerce/woocommerce-deposits/pull/425
|
||||
* @see https://github.com/woocommerce/woocommerce-deposits/issues/506
|
||||
*/
|
||||
if ( version_compare( WC_DEPOSITS_VERSION, '2.0.1', '>=' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add compatibility filters here.
|
||||
add_action( 'woocommerce_deposits_create_order', [ $this, 'modify_order_currency' ] );
|
||||
add_filter( 'woocommerce_get_cart_contents', [ $this, 'modify_cart_item_deposit_amounts' ] );
|
||||
add_filter( 'woocommerce_product_get__wc_deposit_amount', [ $this, 'modify_cart_item_deposit_amount_meta' ], 10, 2 );
|
||||
add_filter( MultiCurrency::FILTER_PREFIX . 'should_convert_product_price', [ $this, 'maybe_convert_product_prices_for_deposits' ], 10, 2 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the currency for deposit amounts of each cart item, only applies if deposits are enabled on the product.
|
||||
*
|
||||
* @param array $cart_contents The items on the cart.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function modify_cart_item_deposit_amounts( $cart_contents ) {
|
||||
foreach ( $cart_contents as $cart_item_key => $cart_item ) {
|
||||
if ( ! empty( $cart_item['is_deposit'] ) && isset( $cart_item['deposit_amount'] ) ) {
|
||||
$deposit_amount = (float) $cart_item['deposit_amount'];
|
||||
$cart_contents[ $cart_item_key ]['deposit_amount'] = $this->multi_currency->get_price( $deposit_amount, 'product' );
|
||||
}
|
||||
}
|
||||
|
||||
return $cart_contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the currency for deposit amounts of each cart item, only applies if deposits are enabled on the product.
|
||||
*
|
||||
* @param float $amount The amount to convert.
|
||||
* @param \WC_Product $product The product to check for.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function modify_cart_item_deposit_amount_meta( $amount, $product ) {
|
||||
if ( 'percent' === $this->get_product_deposit_type( $product ) && $this->utils->is_call_in_backtrace( [ 'WC_Deposits_Cart_Manager->deposits_form_output' ] ) ) {
|
||||
return $this->multi_currency->get_price( $amount, 'product' );
|
||||
}
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if the product prices need to be converted when calculating totals,
|
||||
* if the product's deposit type is a payment plan, then it shouldn't convert it.
|
||||
*
|
||||
* @param bool $result The previous flag for converting the price.
|
||||
* @param \WC_Product $product The product to check for.
|
||||
*
|
||||
* @return bool Whether the price should be converted or not.
|
||||
*/
|
||||
public function maybe_convert_product_prices_for_deposits( $result, $product ) {
|
||||
if ( 'plan' === $this->get_product_deposit_type( $product ) && $this->utils->is_call_in_backtrace( [ 'WC_Cart->calculate_totals' ] ) ) {
|
||||
return false;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* When creating a new order for the remaining amount, forces the new order currency
|
||||
* to be the same with the deposited order currency.
|
||||
*
|
||||
* @param integer $order_id The created order ID.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function modify_order_currency( $order_id ) {
|
||||
// We need to get the original order from the first item meta.
|
||||
$order = wc_get_order( $order_id );
|
||||
$order_items = $order->get_items();
|
||||
$first_order_item = 0 < ( is_countable( $order_items ) ? count( $order_items ) : 0 ) ? reset( $order_items ) : null;
|
||||
|
||||
if ( ! $first_order_item ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the original order ID is attached to the order item.
|
||||
$original_order_id = wc_get_order_item_meta( $first_order_item->get_id(), '_original_order_id', true );
|
||||
if ( ! $original_order_id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the original order still exists.
|
||||
$original_order = wc_get_order( $original_order_id );
|
||||
if ( ! $original_order ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the new and old order currencies, and match them if unmatched.
|
||||
$saved_currency = $order->get_currency( 'view' );
|
||||
$original_currency = $original_order->get_currency( 'view' );
|
||||
if ( $saved_currency !== $original_currency ) {
|
||||
$order->set_currency( $original_currency );
|
||||
$order->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the deposit type of a product if deposits are enabled for the product.
|
||||
*
|
||||
* @param \WC_Product $product The product to check.
|
||||
*
|
||||
* @return string|false The product deposit type if deposits are enabled on it, or false.
|
||||
*/
|
||||
private function get_product_deposit_type( $product ) {
|
||||
$product_has_deposit = class_exists( 'WC_Deposits_Product_Manager' ) && call_user_func( [ 'WC_Deposits_Product_Manager', 'deposits_enabled' ], $product );
|
||||
if ( $product_has_deposit ) {
|
||||
return call_user_func( [ 'WC_Deposits_Product_Manager', 'get_deposit_type' ], $product );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WooCommerceFedEx
|
||||
*
|
||||
* @package WCPay\MultiCurrency\Compatibility
|
||||
*/
|
||||
|
||||
namespace WCPay\MultiCurrency\Compatibility;
|
||||
|
||||
use WCPay\MultiCurrency\MultiCurrency;
|
||||
|
||||
/**
|
||||
* Class that controls Multi Currency Compatibility with WooCommerce FedEx Plugin.
|
||||
*/
|
||||
class WooCommerceFedEx extends BaseCompatibility {
|
||||
|
||||
/**
|
||||
* Calls to look for in the backtrace when determining whether
|
||||
* to return store currency or skip converting product prices.
|
||||
*/
|
||||
private const WC_SHIPPING_FEDEX_CALLS = [
|
||||
'WC_Shipping_Fedex->set_settings',
|
||||
'WC_Shipping_Fedex->per_item_shipping',
|
||||
'WC_Shipping_Fedex->box_shipping',
|
||||
'WC_Shipping_Fedex->get_fedex_api_request',
|
||||
'WC_Shipping_Fedex->get_fedex_requests',
|
||||
'WC_Shipping_Fedex->process_result',
|
||||
];
|
||||
|
||||
/**
|
||||
* Init the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
// Add needed actions and filters if FedEx is active.
|
||||
if ( class_exists( 'WC_Shipping_Fedex_Init' ) ) {
|
||||
add_filter( MultiCurrency::FILTER_PREFIX . 'should_convert_product_price', [ $this, 'should_convert_product_price' ] );
|
||||
add_filter( MultiCurrency::FILTER_PREFIX . 'should_return_store_currency', [ $this, 'should_return_store_currency' ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the product's price should be converted.
|
||||
*
|
||||
* @param bool $return Whether to convert the product's price or not. Default is true.
|
||||
*
|
||||
* @return bool True if it should be converted.
|
||||
*/
|
||||
public function should_convert_product_price( bool $return ): bool {
|
||||
// If it's already false, return it.
|
||||
if ( ! $return ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
if ( $this->utils->is_call_in_backtrace( self::WC_SHIPPING_FEDEX_CALLS ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether to return the store currency or not.
|
||||
*
|
||||
* @param bool $return Whether to return the store currency or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_return_store_currency( bool $return ): bool {
|
||||
// If it's already true, return it.
|
||||
if ( $return ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
if ( $this->utils->is_call_in_backtrace( self::WC_SHIPPING_FEDEX_CALLS ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WooCommerceNameYourPrice
|
||||
*
|
||||
* @package WCPay\MultiCurrency\Compatibility
|
||||
*/
|
||||
|
||||
namespace WCPay\MultiCurrency\Compatibility;
|
||||
|
||||
use WCPay\MultiCurrency\MultiCurrency;
|
||||
|
||||
/**
|
||||
* Class that controls Multi Currency Compatibility with WooCommerce Name Your Price Plugin.
|
||||
*/
|
||||
class WooCommerceNameYourPrice extends BaseCompatibility {
|
||||
|
||||
const NYP_CURRENCY = '_wcpay_multi_currency_nyp_currency';
|
||||
|
||||
/**
|
||||
* Init the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
// Add needed actions and filters if Name Your Price is active.
|
||||
if ( class_exists( 'WC_Name_Your_Price' ) ) {
|
||||
// Convert meta prices.
|
||||
add_filter( 'wc_nyp_raw_minimum_price', [ $this, 'get_nyp_prices' ] );
|
||||
add_filter( 'wc_nyp_raw_maximum_price', [ $this, 'get_nyp_prices' ] );
|
||||
add_filter( 'wc_nyp_raw_suggested_price', [ $this, 'get_nyp_prices' ] );
|
||||
|
||||
// Maybe translate cart prices.
|
||||
add_action( 'woocommerce_add_cart_item_data', [ $this, 'add_initial_currency' ], 20, 3 );
|
||||
add_filter( 'woocommerce_get_cart_item_from_session', [ $this, 'convert_cart_currency' ], 20, 2 );
|
||||
add_filter( MultiCurrency::FILTER_PREFIX . 'should_convert_product_price', [ $this, 'should_convert_product_price' ], 50, 2 );
|
||||
|
||||
// Convert cart editing price.
|
||||
add_filter( 'wc_nyp_edit_in_cart_args', [ $this, 'edit_in_cart_args' ], 10, 2 );
|
||||
add_filter( 'wc_nyp_get_initial_price', [ $this, 'get_initial_price' ], 10, 3 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the min/max/suggested prices of Name Your Price extension.
|
||||
*
|
||||
* @param mixed $price The price to be filtered.
|
||||
* @return mixed The price as a string or float.
|
||||
*/
|
||||
public function get_nyp_prices( $price ) {
|
||||
return ! $price ? $price : $this->multi_currency->get_price( $price, 'product' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the inintial currency when item is added.
|
||||
*
|
||||
* @param array $cart_item Extra cart item data being passed to the cart item.
|
||||
* @param int $product_id The id of the product being added to the cart.
|
||||
* @param int $variation_id The id of the variation being added to the cart.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function add_initial_currency( $cart_item, $product_id, $variation_id ) {
|
||||
|
||||
$nyp_id = $variation_id ? $variation_id : $product_id;
|
||||
|
||||
if ( class_exists( '\WC_Name_Your_Price_Helpers' ) && \WC_Name_Your_Price_Helpers::is_nyp( $nyp_id ) && isset( $cart_item['nyp'] ) ) {
|
||||
$currency = $this->multi_currency->get_selected_currency();
|
||||
$cart_item['nyp_currency'] = $currency->get_code();
|
||||
$cart_item['nyp_original'] = $cart_item['nyp'];
|
||||
}
|
||||
|
||||
return $cart_item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch the cart price when currency changes.
|
||||
* Do not convert price if in the same currency as when added to the cart.
|
||||
* This prevevents USD > EUR > USD style conversions and potential rounding problems.
|
||||
*
|
||||
* @param array $cart_item Cart item array.
|
||||
* @param array $values Cart item values e.g. quantity and product_id.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function convert_cart_currency( $cart_item, $values ) {
|
||||
|
||||
if ( isset( $cart_item['nyp_original'] ) && isset( $cart_item['nyp_currency'] ) ) {
|
||||
|
||||
// Store the original currency in $product meta.
|
||||
$cart_item['data']->update_meta_data( self::NYP_CURRENCY, $cart_item['nyp_currency'] );
|
||||
|
||||
$selected_currency = $this->multi_currency->get_selected_currency();
|
||||
|
||||
// If the currency is currently the same as at time price entered, restore NYP to original value.
|
||||
if ( $cart_item['nyp_currency'] === $selected_currency->get_code() ) {
|
||||
$cart_item['nyp'] = $cart_item['nyp_original'];
|
||||
} else {
|
||||
|
||||
$from_currency = $cart_item['nyp_currency'];
|
||||
$raw_price = $cart_item['nyp_original'];
|
||||
|
||||
$cart_item['nyp'] = $this->multi_currency->get_raw_conversion( $raw_price, $selected_currency->get_code(), $from_currency );
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line.
|
||||
$cart_item = WC_Name_Your_Price()->cart->set_cart_item( $cart_item );
|
||||
}
|
||||
|
||||
return $cart_item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the product's price should be converted.
|
||||
*
|
||||
* @param bool $return Whether to convert the product's price or not. Default is true.
|
||||
* @param object $product Product object to test.
|
||||
*
|
||||
* @return bool True if it should be converted.
|
||||
*/
|
||||
public function should_convert_product_price( bool $return, $product ): bool {
|
||||
// If it's already false, return it.
|
||||
if ( ! $return ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
$currency = $this->multi_currency->get_selected_currency();
|
||||
|
||||
// Check for cart items to see if they are in the original currency.
|
||||
if ( $currency->get_code() === $product->get_meta( self::NYP_CURRENCY ) ) {
|
||||
$return = false;
|
||||
}
|
||||
|
||||
// Check to see if the product is a NYP product.
|
||||
if ( class_exists( '\WC_Name_Your_Price_Helpers' ) && \WC_Name_Your_Price_Helpers::is_nyp( $product ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add currency to cart edit link.
|
||||
*
|
||||
* @param array $args The cart args.
|
||||
* @param array $cart_item The current cart item.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function edit_in_cart_args( $args, $cart_item ) {
|
||||
$args['nyp_currency'] = $this->multi_currency->get_selected_currency()->get_code();
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe convert any prices being edited from the cart
|
||||
*
|
||||
* @param string $initial_price The initial price.
|
||||
* @param mixed $product The product being queried.
|
||||
* @param string $suffix The suffix needed for composites and bundles.
|
||||
*
|
||||
* @return float|string
|
||||
*/
|
||||
public function get_initial_price( $initial_price, $product, $suffix ) {
|
||||
|
||||
if ( isset( $_REQUEST[ 'nyp_raw' . $suffix ] ) && isset( $_REQUEST['nyp_currency'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
$from_currency = wc_clean( wp_unslash( $_REQUEST['nyp_currency'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$raw_price = (float) wc_clean( wp_unslash( $_REQUEST[ 'nyp_raw' . $suffix ] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
$selected_currency = $this->multi_currency->get_selected_currency();
|
||||
|
||||
if ( $from_currency !== $selected_currency->get_code() ) {
|
||||
$initial_price = $this->multi_currency->get_raw_conversion( $raw_price, $selected_currency->get_code(), $from_currency );
|
||||
}
|
||||
}
|
||||
|
||||
return $initial_price;
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WooCommercePointsAndRewards
|
||||
*
|
||||
* @package WCPay\MultiCurrency\Compatibility
|
||||
*/
|
||||
|
||||
namespace WCPay\MultiCurrency\Compatibility;
|
||||
|
||||
use WCPay\MultiCurrency\Currency;
|
||||
|
||||
/**
|
||||
* Class that controls Multi Currency Compatibility with WooCommerce Points & Rewards Plugin.
|
||||
*/
|
||||
class WooCommercePointsAndRewards extends BaseCompatibility {
|
||||
|
||||
/**
|
||||
* Default Currency Code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $default_currency_code;
|
||||
|
||||
/**
|
||||
* Selected Currency Code.
|
||||
*
|
||||
* @var Currency
|
||||
*/
|
||||
private $selected_currency;
|
||||
|
||||
/**
|
||||
* Init the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
// Add needed filters if Points & Rewards is active and it's not an admin request.
|
||||
if ( is_admin() || ! class_exists( 'WC_Points_Rewards' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'option_wc_points_rewards_earn_points_ratio', [ $this, 'convert_points_ratio' ], 50 );
|
||||
add_filter( 'option_wc_points_rewards_redeem_points_ratio', [ $this, 'convert_points_ratio' ], 50 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts points ratio applying selected currency rate to the monetary value.
|
||||
*
|
||||
* @param string $ratio Store currency points ratio.
|
||||
* @return string Converted points ratio.
|
||||
*/
|
||||
public function convert_points_ratio( string $ratio = '' ): string {
|
||||
// Skip conversion if selected and default currencies are the same.
|
||||
if ( $this->selected_and_default_currency_match() ) {
|
||||
return $ratio;
|
||||
}
|
||||
|
||||
// Skip conversion on discount to avoid doing it twice.
|
||||
if ( $this->utils->is_call_in_backtrace( [ 'WC_Points_Rewards_Discount->get_discount_data' ] ) ) {
|
||||
return $ratio;
|
||||
}
|
||||
|
||||
$ratio = explode( ':', $ratio );
|
||||
$points = (float) ( $ratio[0] ?? 0 );
|
||||
$value = (float) ( $ratio[1] ?? 0 );
|
||||
|
||||
$rate = $this->selected_currency->get_rate();
|
||||
$value = $value * $rate;
|
||||
|
||||
return "$points:$value";
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the selected and default currency are the same.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function selected_and_default_currency_match(): bool {
|
||||
if ( empty( $this->default_currency_code ) ) {
|
||||
$this->default_currency_code = $this->multi_currency->get_default_currency()->get_code();
|
||||
}
|
||||
if ( empty( $this->selected_currency ) ) {
|
||||
$this->selected_currency = $this->multi_currency->get_selected_currency();
|
||||
}
|
||||
|
||||
return $this->default_currency_code === $this->selected_currency->get_code();
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WooCommercePreOrders
|
||||
*
|
||||
* @package WCPay\MultiCurrency\Compatibility
|
||||
*/
|
||||
|
||||
namespace WCPay\MultiCurrency\Compatibility;
|
||||
|
||||
use WCPay\MultiCurrency\MultiCurrency;
|
||||
use WCPay\MultiCurrency\Utils;
|
||||
|
||||
/**
|
||||
* Class that controls Multi Currency Compatibility with WooCommerce Pre-Orders Plugin.
|
||||
*/
|
||||
class WooCommercePreOrders extends BaseCompatibility {
|
||||
|
||||
/**
|
||||
* Init the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
// Add needed actions and filters if Pre-Orders is active.
|
||||
if ( class_exists( 'WC_Pre_Orders' ) ) {
|
||||
add_filter( 'wc_pre_orders_fee', [ $this, 'wc_pre_orders_fee' ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the Pre-Orders fee.
|
||||
*
|
||||
* @param array $args Array of args for the Pre-Orders fee.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function wc_pre_orders_fee( array $args ): array {
|
||||
$args['amount'] = $this->multi_currency->get_price( $args['amount'], 'product' );
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WooCommerceProductAddOns
|
||||
*
|
||||
* @package WCPay\MultiCurrency\Compatibility
|
||||
*/
|
||||
|
||||
namespace WCPay\MultiCurrency\Compatibility;
|
||||
|
||||
use WC_Product;
|
||||
use WCPay\MultiCurrency\MultiCurrency;
|
||||
use WCPay\MultiCurrency\Utils;
|
||||
|
||||
/**
|
||||
* Class that controls Multi Currency Compatibility with WooCommerce Product Add Ons Plugin.
|
||||
*/
|
||||
class WooCommerceProductAddOns extends BaseCompatibility {
|
||||
|
||||
const ADDONS_CONVERTED_META_KEY = '_wcpay_multi_currency_addons_converted';
|
||||
|
||||
/**
|
||||
* Init the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
// Add needed actions and filters if Product Add Ons is active.
|
||||
if ( class_exists( 'WC_Product_Addons' ) ) {
|
||||
if ( ! is_admin() && ! defined( 'DOING_CRON' ) ) {
|
||||
add_filter( 'woocommerce_product_addons_option_price_raw', [ $this, 'get_addons_price' ], 50, 2 );
|
||||
add_filter( 'woocommerce_product_addons_price_raw', [ $this, 'get_addons_price' ], 50, 2 );
|
||||
add_filter( 'woocommerce_product_addons_params', [ $this, 'product_addons_params' ], 50, 1 );
|
||||
add_filter( 'woocommerce_product_addons_get_item_data', [ $this, 'get_item_data' ], 50, 3 );
|
||||
add_filter( 'woocommerce_product_addons_update_product_price', [ $this, 'update_product_price' ], 50, 4 );
|
||||
add_filter( 'woocommerce_product_addons_order_line_item_meta', [ $this, 'order_line_item_meta' ], 50, 4 );
|
||||
add_filter( MultiCurrency::FILTER_PREFIX . 'should_convert_product_price', [ $this, 'should_convert_product_price' ], 50, 2 );
|
||||
}
|
||||
|
||||
if ( wp_doing_ajax() ) {
|
||||
add_filter( 'woocommerce_product_addons_ajax_get_product_price_including_tax', [ $this, 'get_product_calculation_price' ], 50, 3 );
|
||||
add_filter( 'woocommerce_product_addons_ajax_get_product_price_excluding_tax', [ $this, 'get_product_calculation_price' ], 50, 3 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the product's price should be converted.
|
||||
*
|
||||
* @param bool $return Whether to convert the product's price or not. Default is true.
|
||||
* @param object $product Product object to test.
|
||||
*
|
||||
* @return bool True if it should be converted.
|
||||
*/
|
||||
public function should_convert_product_price( bool $return, $product ): bool {
|
||||
// If it's already false, return it.
|
||||
if ( ! $return ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
// Check for cart items to see if they have already been converted.
|
||||
if ( 1 === $product->get_meta( self::ADDONS_CONVERTED_META_KEY ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the price of an addon from WooCommerce Products Add-on extension.
|
||||
*
|
||||
* @param mixed $price The price to be filtered.
|
||||
* @param array $type The type of the addon.
|
||||
|
||||
* @return mixed The price as a string or float.
|
||||
*/
|
||||
public function get_addons_price( $price, $type ) {
|
||||
if ( 'percentage_based' === $type['price_type'] ) {
|
||||
// If the addon is a percentage_based type $price is actually a percentage
|
||||
// and doesn't need any conversion.
|
||||
return $price;
|
||||
}
|
||||
|
||||
return $this->multi_currency->get_price( $price, 'product' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes currency formatting issues in Product Add-Ons. PAO gets these values directly from the db options,
|
||||
* so those values aren't filtered. Luckily, there's a filter.
|
||||
*
|
||||
* @param array $params Product Add-Ons global parameters.
|
||||
*
|
||||
* @return array Adjust parameters.
|
||||
*/
|
||||
public function product_addons_params( array $params ): array {
|
||||
$params['currency_format_num_decimals'] = wc_get_price_decimals();
|
||||
$params['currency_format_decimal_sep'] = wc_get_price_decimal_separator();
|
||||
$params['currency_format_thousand_sep'] = wc_get_price_thousand_separator();
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the cart item data meta so we can provide the proper name with converted add on price.
|
||||
*
|
||||
* @param array $addon_data The addon data we are filtering/replacing.
|
||||
* @param array $addon The addon being processed.
|
||||
* @param array $cart_item The cart item being processed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_data( $addon_data, $addon, $cart_item ): array {
|
||||
$price = isset( $cart_item['addons_price_before_calc'] ) ? $cart_item['addons_price_before_calc'] : $addon['price'];
|
||||
$value = $addon['value'];
|
||||
|
||||
/*
|
||||
* 'woocommerce_addons_add_cart_price_to_value'
|
||||
*
|
||||
* Use this filter to display the price next to each selected add-on option.
|
||||
* By default, add-on prices show up only next to flat fee add-ons.
|
||||
*
|
||||
* @param boolean
|
||||
*/
|
||||
$add_price_to_value = apply_filters( 'woocommerce_addons_add_cart_price_to_value', false, $cart_item );
|
||||
|
||||
if ( 0.0 === (float) $addon['price'] ) {
|
||||
$value .= '';
|
||||
} elseif ( 'percentage_based' === $addon['price_type'] && 0.0 === (float) $price ) {
|
||||
$value .= '';
|
||||
} elseif ( 'custom_price' === $addon['field_type'] && $addon['price'] ) {
|
||||
if ( class_exists( 'WC_Product_Addons_Helper' ) ) {
|
||||
$addon_price = wc_price( \WC_Product_Addons_Helper::get_product_addon_price_for_display( $addon['price'], $cart_item['data'] ) );
|
||||
/* translators: %1$s custom addon price in cart */
|
||||
$value .= sprintf( _x( ' (%1$s)', 'custom price addon price in cart', 'woocommerce-payments' ), $addon_price );
|
||||
$addon['display'] = $value;
|
||||
}
|
||||
} elseif ( 'flat_fee' === $addon['price_type'] && $addon['price'] ) {
|
||||
if ( class_exists( 'WC_Product_Addons_Helper' ) ) {
|
||||
$addon_price = $this->multi_currency->get_price( $addon['price'], 'product' );
|
||||
if ( 'input_multiplier' === $addon['field_type'] ) {
|
||||
// Quantity/multiplier add on needs to be split, calculated, then multiplied by input value.
|
||||
$addon_price = $this->multi_currency->get_price( $addon['price'] / $addon['value'], 'product' ) * $addon['value'];
|
||||
}
|
||||
$addon_price = wc_price( \WC_Product_Addons_Helper::get_product_addon_price_for_display( $addon_price, $cart_item['data'] ) );
|
||||
/* translators: %1$s flat fee addon price in order */
|
||||
$value .= sprintf( _x( ' (+ %1$s)', 'flat fee addon price in cart', 'woocommerce-payments' ), $addon_price );
|
||||
}
|
||||
} elseif ( 'quantity_based' === $addon['price_type'] && $addon['price'] && $add_price_to_value ) {
|
||||
if ( class_exists( 'WC_Product_Addons_Helper' ) ) {
|
||||
$addon_price = $this->multi_currency->get_price( $addon['price'], 'product' );
|
||||
if ( 'input_multiplier' === $addon['field_type'] ) {
|
||||
// Quantity/multiplier add on needs to be split, calculated, then multiplied by input value.
|
||||
$addon_price = $this->multi_currency->get_price( $addon['price'] / $addon['value'], 'product' ) * $addon['value'];
|
||||
}
|
||||
$addon_price = wc_price( \WC_Product_Addons_Helper::get_product_addon_price_for_display( $addon_price, $cart_item['data'] ) );
|
||||
/* translators: %1$s addon price in order */
|
||||
$value .= sprintf( _x( ' (%1$s)', 'quantity based addon price in cart', 'woocommerce-payments' ), $addon_price );
|
||||
}
|
||||
} elseif ( 'percentage_based' === $addon['price_type'] && $addon['price'] && $add_price_to_value ) {
|
||||
// Get the percentage cost in the currency in use, and set the meta data on the product that the value was converted.
|
||||
$_product = wc_get_product( $cart_item['product_id'] );
|
||||
$price = $this->multi_currency->get_price( $price, 'product' );
|
||||
$_product->set_price( $price * ( $addon['price'] / 100 ) );
|
||||
$_product->update_meta_data( self::ADDONS_CONVERTED_META_KEY, 1 );
|
||||
/* translators: %1$s addon price in order */
|
||||
$value .= sprintf( _x( ' (%1$s)', 'percentage based addon price in cart', 'woocommerce-payments' ), WC()->cart->get_product_price( $_product ) );
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $addon['name'],
|
||||
'value' => $value,
|
||||
'display' => isset( $addon['display'] ) ? $addon['display'] : '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the product price according to converted add on values.
|
||||
*
|
||||
* @param array $updated_prices Prices updated by Product Add-Ons (unused).
|
||||
* @param array $cart_item Cart item meta data.
|
||||
* @param array $prices Original prices passed to Product Add-Ons for calculations.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function update_product_price( $updated_prices, $cart_item, $prices ): array {
|
||||
$price = $this->multi_currency->get_price( $prices['price'], 'product' );
|
||||
$regular_price = $this->multi_currency->get_price( $prices['regular_price'], 'product' );
|
||||
$sale_price = $this->multi_currency->get_price( $prices['sale_price'], 'product' );
|
||||
$flat_fees = 0;
|
||||
$quantity = $cart_item['quantity'];
|
||||
$price_before_addons = $price;
|
||||
$regular_price_before_addons = $regular_price;
|
||||
$sale_price_before_addons = $sale_price;
|
||||
|
||||
// TODO: Check compat with Smart Coupons.
|
||||
// Compatibility with Smart Coupons self declared gift amount purchase.
|
||||
$credit_called = ! empty( $_POST['credit_called'] ) ? $_POST['credit_called'] : null; // phpcs:ignore
|
||||
if ( empty( $price ) && ! empty( $credit_called ) ) {
|
||||
// Variable $_POST['credit_called'] is an array.
|
||||
if ( isset( $credit_called[ $cart_item['data']->get_id() ] ) ) {
|
||||
$price = (float) $credit_called[ $cart_item['data']->get_id() ];
|
||||
$regular_price = $price;
|
||||
$sale_price = $price;
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $price ) && ! empty( $cart_item['credit_amount'] ) ) {
|
||||
$price = (float) $cart_item['credit_amount'];
|
||||
$regular_price = $price;
|
||||
$sale_price = $price;
|
||||
}
|
||||
|
||||
foreach ( $cart_item['addons'] as $addon ) {
|
||||
// Percentage based and custom defined addon prices do not get converted, all others do.
|
||||
if ( 'percentage_based' === $addon['price_type'] || 'custom_price' === $addon['field_type'] ) {
|
||||
$addon_price = $addon['price'];
|
||||
} elseif ( 'input_multiplier' === $addon['field_type'] ) {
|
||||
// Quantity/multiplier add on needs to be split, calculated, then multiplied by input value.
|
||||
$addon_price = $this->multi_currency->get_price( $addon['price'] / $addon['value'], 'product' ) * $addon['value'];
|
||||
} else {
|
||||
$addon_price = $this->multi_currency->get_price( $addon['price'], 'product' );
|
||||
}
|
||||
|
||||
switch ( $addon['price_type'] ) {
|
||||
case 'percentage_based':
|
||||
$price += (float) ( $price_before_addons * ( $addon_price / 100 ) );
|
||||
$regular_price += (float) ( $regular_price_before_addons * ( $addon_price / 100 ) );
|
||||
$sale_price += (float) ( $sale_price_before_addons * ( $addon_price / 100 ) );
|
||||
break;
|
||||
case 'flat_fee':
|
||||
$flat_fee = $quantity > 0 ? (float) ( $addon_price / $quantity ) : 0;
|
||||
$price += $flat_fee;
|
||||
$regular_price += $flat_fee;
|
||||
$sale_price += $flat_fee;
|
||||
$flat_fees += $flat_fee;
|
||||
break;
|
||||
default:
|
||||
$price += (float) $addon_price;
|
||||
$regular_price += (float) $addon_price;
|
||||
$sale_price += (float) $addon_price;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Let ourselves know this item has had add ons converted.
|
||||
$cart_item['data']->update_meta_data( self::ADDONS_CONVERTED_META_KEY, 1 );
|
||||
|
||||
return [
|
||||
'price' => $price,
|
||||
'regular_price' => $regular_price,
|
||||
'sale_price' => $sale_price,
|
||||
'addons_flat_fees_sum' => $flat_fees,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the meta data for order line items so that we can properly set values in the names.
|
||||
*
|
||||
* @param array $meta_data A key/value for the meta data to be inserted for the line item.
|
||||
* @param array $addon The addon being processed.
|
||||
* @param \WC_Order_Item_Product $item Order item data.
|
||||
* @param array $values Order item values.
|
||||
*
|
||||
* @return array A key/value for the meta data to be inserted for the line item.
|
||||
*/
|
||||
public function order_line_item_meta( array $meta_data, array $addon, \WC_Order_Item_Product $item, array $values ): array {
|
||||
|
||||
$add_price_to_value = apply_filters( 'woocommerce_addons_add_order_price_to_value', false, $item );
|
||||
|
||||
$value = $addon['value'];
|
||||
|
||||
// Pass the timestamp as the add-on value in order to save the timestamp to the DB.
|
||||
if ( isset( $addon['timestamp'] ) ) {
|
||||
$value = $addon['timestamp'];
|
||||
}
|
||||
|
||||
// If there is an add-on price, add the price of the add-on to the label name.
|
||||
if ( $addon['price'] && $add_price_to_value ) {
|
||||
$product = $item->get_product();
|
||||
|
||||
if ( 'percentage_based' === $addon['price_type'] && 0.0 !== (float) $product->get_price() ) {
|
||||
// Calculate the percentage price.
|
||||
$addon_price = $product->get_price() * ( $addon['price'] / 100 );
|
||||
} elseif ( 'custom_price' === $addon['field_type'] ) {
|
||||
// Custom prices do not get converted.
|
||||
$addon_price = $addon['price'];
|
||||
} elseif ( 'input_multiplier' === $addon['field_type'] ) {
|
||||
// Quantity/multiplier add on needs to be split, calculated, then multiplied by input value.
|
||||
$addon_price = $this->multi_currency->get_price( $addon['price'] / $addon['value'], 'product' ) * $addon['value'];
|
||||
} else {
|
||||
// Convert all others.
|
||||
$addon_price = $this->multi_currency->get_price( $addon['price'], 'product' );
|
||||
}
|
||||
if ( class_exists( 'WC_Product_Addons_Helper' ) ) {
|
||||
$price = html_entity_decode(
|
||||
wp_strip_all_tags( wc_price( \WC_Product_Addons_Helper::get_product_addon_price_for_display( $addon_price, $values['data'] ) ) ),
|
||||
ENT_QUOTES,
|
||||
get_bloginfo( 'charset' )
|
||||
);
|
||||
}
|
||||
|
||||
if ( 'flat_fee' === $addon['price_type'] && $addon['price'] && $add_price_to_value ) {
|
||||
/* translators: %1$s flat fee addon price in order */
|
||||
$value .= sprintf( _x( ' (+ %1$s)', 'flat fee addon price in order', 'woocommerce-payments' ), $price );
|
||||
} elseif ( ( 'quantity_based' === $addon['price_type'] || 'percentage_based' === $addon['price_type'] ) && $addon['price'] && $add_price_to_value ) {
|
||||
/* translators: %1$s addon price in order */
|
||||
$value .= sprintf( _x( ' (%1$s)', 'addon price in order', 'woocommerce-payments' ), $price );
|
||||
} elseif ( 'custom_price' === $addon['field_type'] ) {
|
||||
/* translators: %1$s custom addon price in order */
|
||||
$value = sprintf( _x( ' (%1$s)', 'custom addon price in order', 'woocommerce-payments' ), $price );
|
||||
}
|
||||
|
||||
$meta_data['raw_price'] = $this->multi_currency->get_price( $addon['price'], 'product' );
|
||||
}
|
||||
|
||||
$meta_data['value'] = $value;
|
||||
return $meta_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the product price during ajax requests from the product page.
|
||||
*
|
||||
* @param float $price Price to get converted.
|
||||
* @param int $quantity Quantity of the product selected.
|
||||
* @param \WC_Product $product WC_Product related to the price.
|
||||
*
|
||||
* @return float Adjusted price.
|
||||
*/
|
||||
public function get_product_calculation_price( float $price, int $quantity, \WC_Product $product ): float {
|
||||
return $this->multi_currency->get_price( $price / $quantity, 'product' ) * $quantity;
|
||||
}
|
||||
}
|
||||
+566
@@ -0,0 +1,566 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WooCommerceSubscriptions
|
||||
*
|
||||
* @package WCPay\MultiCurrency\Compatibility
|
||||
*/
|
||||
|
||||
namespace WCPay\MultiCurrency\Compatibility;
|
||||
|
||||
use WC_Subscription;
|
||||
use WCPay\MultiCurrency\Logger;
|
||||
use WCPay\MultiCurrency\FrontendCurrencies;
|
||||
use WCPay\MultiCurrency\MultiCurrency;
|
||||
|
||||
/**
|
||||
* Class that controls Multi Currency Compatibility with WooCommerce Subscriptions Plugin and WCPay Subscriptions.
|
||||
*/
|
||||
class WooCommerceSubscriptions extends BaseCompatibility {
|
||||
|
||||
/**
|
||||
* Our allowed subscription types.
|
||||
*/
|
||||
const SUBSCRIPTION_TYPES = [ 'renewal', 'resubscribe', 'switch' ];
|
||||
|
||||
/**
|
||||
* Subscription switch cart item.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $switch_cart_item = '';
|
||||
|
||||
/**
|
||||
* The current subscription being iterated through on the My Account > Subscriptions page.
|
||||
*
|
||||
* Tell Psalm to ignore the WC_Subscription class, this class is only loaded if Subscriptions is active.
|
||||
*
|
||||
* @psalm-suppress UndefinedDocblockClass
|
||||
*
|
||||
* @var WC_Subscription|null
|
||||
*/
|
||||
public $current_my_account_subscription = null;
|
||||
|
||||
/**
|
||||
* The FrontendCurrencies object.
|
||||
*
|
||||
* @var FrontendCurrencies
|
||||
*/
|
||||
public $frontend_currencies;
|
||||
|
||||
/**
|
||||
* If we are running through our filters.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $running_override_selected_currency_filters = false;
|
||||
|
||||
/**
|
||||
* Init the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
// Add needed actions and filters if WC Subscriptions or WCPay Subscriptions are active.
|
||||
if ( class_exists( 'WC_Subscriptions' ) || class_exists( 'WC_Payments_Subscriptions' ) ) {
|
||||
if ( ! is_admin() && ! defined( 'DOING_CRON' ) ) {
|
||||
$this->frontend_currencies = $this->multi_currency->get_frontend_currencies();
|
||||
|
||||
add_filter( 'woocommerce_subscriptions_product_price', [ $this, 'get_subscription_product_price' ], 50, 2 );
|
||||
add_filter( 'woocommerce_product_get__subscription_sign_up_fee', [ $this, 'get_subscription_product_signup_fee' ], 50, 2 );
|
||||
add_filter( 'woocommerce_product_variation_get__subscription_sign_up_fee', [ $this, 'get_subscription_product_signup_fee' ], 50, 2 );
|
||||
add_filter( 'option_woocommerce_subscriptions_multiple_purchase', [ $this, 'maybe_disable_mixed_cart' ], 50 );
|
||||
add_filter( MultiCurrency::FILTER_PREFIX . 'override_selected_currency', [ $this, 'override_selected_currency' ], 50 );
|
||||
add_filter( MultiCurrency::FILTER_PREFIX . 'should_convert_product_price', [ $this, 'should_convert_product_price' ], 50, 2 );
|
||||
add_filter( MultiCurrency::FILTER_PREFIX . 'should_convert_coupon_amount', [ $this, 'should_convert_coupon_amount' ], 50, 2 );
|
||||
add_filter( MultiCurrency::FILTER_PREFIX . 'should_disable_currency_switching', [ $this, 'should_disable_currency_switching' ], 50 );
|
||||
add_filter( 'woocommerce_subscription_price_string_details', [ $this, 'maybe_set_current_my_account_subscription' ], 50, 2 );
|
||||
add_filter( 'woocommerce_get_formatted_subscription_total', [ $this, 'maybe_clear_current_my_account_subscription' ], 50, 2 );
|
||||
add_filter( 'wc_price', [ $this, 'maybe_get_explicit_format_for_subscription_total' ], 50, 5 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts subscription prices, if needed.
|
||||
*
|
||||
* @param mixed $price The price to be filtered.
|
||||
* @param object $product The product that will have a filtered price.
|
||||
*
|
||||
* @return mixed The price as a string or float.
|
||||
*/
|
||||
public function get_subscription_product_price( $price, $product ) {
|
||||
if ( ! $price || ! $this->should_convert_product_price( true, $product ) ) {
|
||||
return $price;
|
||||
}
|
||||
|
||||
return $this->multi_currency->get_price( $price, 'product' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts subscription sign up prices, if needed.
|
||||
*
|
||||
* @param mixed $price The price to be filtered.
|
||||
* @param object $product The product that will have a filtered price.
|
||||
*
|
||||
* @return mixed The price as a string or float.
|
||||
*/
|
||||
public function get_subscription_product_signup_fee( $price, $product ) {
|
||||
if ( ! $price ) {
|
||||
return $price;
|
||||
}
|
||||
|
||||
$item = $this->get_subscription_type_from_cart( 'switch' );
|
||||
if ( $item ) {
|
||||
$item_id = ! empty( $item['variation_id'] ) ? $item['variation_id'] : $item['product_id'];
|
||||
$switch_cart_item = $this->switch_cart_item;
|
||||
$this->switch_cart_item = $item['key'];
|
||||
|
||||
if ( $product->get_id() === $item_id ) {
|
||||
|
||||
/**
|
||||
* These tests get mildly complex due to, when switching, the sign up fee is queried
|
||||
* several times to determine prorated costs. This means we have to test to see when
|
||||
* the fee actually needs be converted.
|
||||
*/
|
||||
|
||||
if ( $this->utils->is_call_in_backtrace( [ 'WC_Subscriptions_Cart::set_subscription_prices_for_calculation' ] ) ) {
|
||||
return $price;
|
||||
}
|
||||
|
||||
// Check to see if it's currently determining prorated prices.
|
||||
if ( $this->utils->is_call_in_backtrace( [ 'WC_Subscriptions_Product::get_sign_up_fee' ] )
|
||||
&& $this->utils->is_call_in_backtrace( [ 'WC_Cart->calculate_totals' ] )
|
||||
&& $item['key'] === $switch_cart_item
|
||||
&& ! $this->utils->is_call_in_backtrace( [ 'WCS_Switch_Totals_Calculator->apportion_sign_up_fees' ] ) ) {
|
||||
return $price;
|
||||
}
|
||||
|
||||
// Check to see if the _subscription_sign_up_fee meta for the product has already been updated.
|
||||
if ( $item['key'] === $switch_cart_item ) {
|
||||
foreach ( $product->get_meta_data() as $meta ) {
|
||||
if ( '_subscription_sign_up_fee' === $meta->get_data()['key'] && ! empty( $meta->get_changes() ) ) {
|
||||
return $price;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->multi_currency->get_price( $price, 'product' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the mixed cart if needed.
|
||||
*
|
||||
* @param string|bool $value Option from the database, or false.
|
||||
*
|
||||
* @return mixed False, yes, or no.
|
||||
*/
|
||||
public function maybe_disable_mixed_cart( $value ) {
|
||||
// If there's a subscription switch in the cart, disable multiple items in the cart.
|
||||
// This is so that subscriptions with different currencies cannot be added to the cart.
|
||||
if ( $this->get_subscription_type_from_cart( 'switch' ) ) {
|
||||
return 'no';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the if the selected currency needs to be overridden.
|
||||
*
|
||||
* The running_override_selected_currency_filters property is used here to avoid infinite loops.
|
||||
*
|
||||
* @param mixed $return Default is false, but could be three letter currency code.
|
||||
*
|
||||
* @return mixed Three letter currency code or false if not.
|
||||
*/
|
||||
public function override_selected_currency( $return ) {
|
||||
// If it's not false, or we are already running filters, exit.
|
||||
if ( $return || $this->running_override_selected_currency_filters ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
// If we have a subscription in $current_my_account_subscription, we want to use the currency from that subscription.
|
||||
if ( $this->is_current_my_account_subscription_set() ) {
|
||||
/**
|
||||
* Tell Psalm to ignore the WC_Subscription class, this class is only loaded if Subscriptions is active.
|
||||
*
|
||||
* @psalm-suppress UndefinedDocblockClass
|
||||
*/
|
||||
return $this->current_my_account_subscription->get_currency();
|
||||
}
|
||||
|
||||
// Loop through subscription types and check for cart items.
|
||||
foreach ( self::SUBSCRIPTION_TYPES as $type ) {
|
||||
$cart_item = $this->get_subscription_type_from_cart( $type );
|
||||
if ( $cart_item ) {
|
||||
$this->running_override_selected_currency_filters = true;
|
||||
|
||||
// If we have a cart item, then we can get the order or subscription to pull the currency from.
|
||||
$subscription_type = 'subscription_' . $type;
|
||||
$subscription = $this->get_subscription( $cart_item[ $subscription_type ]['subscription_id'] );
|
||||
|
||||
$this->running_override_selected_currency_filters = false;
|
||||
/**
|
||||
* Tell Psalm to ignore the WC_Subscription class, this class is only loaded if Subscriptions is active.
|
||||
*
|
||||
* @psalm-suppress UndefinedDocblockClass
|
||||
*/
|
||||
return $subscription ? $subscription->get_currency() : $return;
|
||||
}
|
||||
}
|
||||
|
||||
// This instance is for when the customer lands on the product page to choose a new subscription tier.
|
||||
$switch_subscription = $this->get_subscription_from_superglobal_switch_id();
|
||||
/**
|
||||
* Tell Psalm to ignore the WC_Subscription class, this class is only loaded if Subscriptions is active.
|
||||
*
|
||||
* @psalm-suppress UndefinedDocblockClass
|
||||
*/
|
||||
return $switch_subscription ? $switch_subscription->get_currency() : $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the product's price should be converted.
|
||||
*
|
||||
* @param bool $return Whether to convert the product's price or not. Default is true.
|
||||
* @param object $product Product object to test.
|
||||
*
|
||||
* @return bool True if it should be converted.
|
||||
*/
|
||||
public function should_convert_product_price( bool $return, $product ): bool {
|
||||
// If it's already false, return it.
|
||||
if ( ! $return ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
$calls = [
|
||||
'WC_Cart_Totals->calculate_item_totals',
|
||||
'WC_Cart->get_product_subtotal',
|
||||
'wc_get_price_excluding_tax',
|
||||
'wc_get_price_including_tax',
|
||||
];
|
||||
|
||||
// Check for renewal.
|
||||
if ( $this->get_subscription_type_from_cart( 'renewal' ) ) {
|
||||
// When WCPay Subs programmatically sets up the cart, we need to return the
|
||||
// converted price so the user lands at the checkout with the correct price.
|
||||
if ( $this->utils->is_call_in_backtrace( [ 'WCS_Cart_Renewal->setup_cart' ] ) ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
if ( $this->utils->is_call_in_backtrace( $calls ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for resubscribe.
|
||||
if ( $this->get_subscription_type_from_cart( 'resubscribe' )
|
||||
&& $this->utils->is_call_in_backtrace( $calls ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// WCPay Subs does a check against the product price and the total, we need to return the actual product price for this check.
|
||||
if ( $this->utils->is_call_in_backtrace( [ 'WC_Payments_Subscription_Service->get_recurring_item_data_for_subscription' ] )
|
||||
&& $this->utils->is_call_in_backtrace( [ 'WC_Product->get_price' ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the coupon's amount should be converted.
|
||||
*
|
||||
* @param bool $return Whether to convert the coupon's price or not. Default is true.
|
||||
* @param object $coupon Coupon object to test.
|
||||
*
|
||||
* @return bool True if it should be converted.
|
||||
*/
|
||||
public function should_convert_coupon_amount( bool $return, $coupon ): bool {
|
||||
// If it's already false, return it.
|
||||
if ( ! $return ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
// We do not need to convert percentage coupons.
|
||||
if ( $this->is_coupon_type( $coupon, 'subscription_percent' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there's not a renewal in the cart, we can convert.
|
||||
$subscription_renewal = $this->get_subscription_type_from_cart( 'renewal' );
|
||||
if ( ! $subscription_renewal ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to allow the early renewal to convert the cost, as it pulls the original value of the coupon.
|
||||
* Subsequent queries for the amount use the first converted amount.
|
||||
* This also works for normal manual renewals.
|
||||
*/
|
||||
if ( ! $this->utils->is_call_in_backtrace( [ 'WCS_Cart_Early_Renewal->setup_cart' ] )
|
||||
&& $this->utils->is_call_in_backtrace( [ 'WC_Discounts->apply_coupon' ] )
|
||||
&& $this->is_coupon_type( $coupon, 'subscription_recurring' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if currency switching should be disabled.
|
||||
*
|
||||
* @param bool $return Whether widgets should be hidden or not. Default is false.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_disable_currency_switching( bool $return ): bool {
|
||||
// If it's already true, return it.
|
||||
if ( $return ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
if ( $this->get_subscription_type_from_cart( 'renewal' )
|
||||
|| $this->get_subscription_type_from_cart( 'resubscribe' )
|
||||
|| $this->get_subscription_type_from_cart( 'switch' )
|
||||
|| $this->get_subscription_from_superglobal_switch_id() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe sets the current_my_account_subscription var and clears the FrontendCurrencies cache.
|
||||
*
|
||||
* The my-subscriptions.php template file calls $subscription->get_formatted_order_total(), which then calls $subscription->get_price_string_details().
|
||||
* At that point in time, if we have certain calls in the backtrace, we need to add the subscription into $current_my_account_subscription so that we
|
||||
* are able to use it later on in the maybe_get_explicit_format_for_subscription_total filter.
|
||||
*
|
||||
* Tell Psalm to ignore the WC_Subscription class, this class is only loaded if Subscriptions is active.
|
||||
*
|
||||
* @psalm-suppress UndefinedDocblockClass
|
||||
*
|
||||
* @param array $subscription_details The details related to the subscription.
|
||||
* @param WC_Subscription $subscription The subscription being acted on.
|
||||
*
|
||||
* @return array The unmodified subscription details.
|
||||
*/
|
||||
public function maybe_set_current_my_account_subscription( $subscription_details, $subscription ): array {
|
||||
$calls = [
|
||||
'WCS_Template_Loader::get_my_subscriptions ',
|
||||
'WC_Subscription->get_formatted_order_total',
|
||||
];
|
||||
if ( $this->utils->is_call_in_backtrace( $calls ) ) {
|
||||
// If we have our calls in the backtrace, we set our cached sub and clear the FrontendCurrencies cache.
|
||||
$this->current_my_account_subscription = $subscription;
|
||||
$this->frontend_currencies->selected_currency_changed();
|
||||
}
|
||||
|
||||
return $subscription_details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe clears the current_my_account_subscription var and clears the FrontendCurrencies cache.
|
||||
*
|
||||
* During the $subscription->get_formatted_order_total() call we may set the current_my_account_subscription var, and we want to clear it as soon as
|
||||
* we no longer need it. The woocommerce_get_formatted_subscription_total filter is at the end of that call, so we check to see if the var is set,
|
||||
* and if it is, we clear it, and we also clear the FrontendCurrencies cache.
|
||||
*
|
||||
* Tell Psalm to ignore the WC_Subscription class, this class is only loaded if Subscriptions is active.
|
||||
*
|
||||
* @psalm-suppress UndefinedDocblockClass
|
||||
*
|
||||
* @param string $formatted The subscription formatted total.
|
||||
* @param WC_Subscription $subscription The subscription being acted on.
|
||||
*
|
||||
* @return string The unmodified subscription formatted total.
|
||||
*/
|
||||
public function maybe_clear_current_my_account_subscription( $formatted, $subscription ): string {
|
||||
if ( $this->is_current_my_account_subscription_set() ) {
|
||||
$this->current_my_account_subscription = null;
|
||||
$this->frontend_currencies->selected_currency_changed();
|
||||
}
|
||||
|
||||
return $formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current_my_account_subscription var is set, then we use that subscription's currency in order to set the explicit total.
|
||||
*
|
||||
* @param string $html_price Price HTML markup.
|
||||
* @param string $price Formatted price.
|
||||
* @param array $args Pass on the args.
|
||||
* @param float $unformatted_price Price as float to allow plugins custom formatting. Since 3.2.0.
|
||||
* @param float|string $original_price Original price as float, or empty string. Since 5.0.0.
|
||||
*
|
||||
* @return string The wc_price with HTML wrapping, possibly with the currency code added for explicit formatting.
|
||||
*/
|
||||
public function maybe_get_explicit_format_for_subscription_total( $html_price, $price, $args, $unformatted_price, $original_price ): string {
|
||||
if ( ! $this->is_current_my_account_subscription_set() ) {
|
||||
return $html_price;
|
||||
}
|
||||
|
||||
if ( ! $this->multi_currency->has_additional_currencies_enabled() ) {
|
||||
return $html_price;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currency code from the subscription, then return the explicit price.
|
||||
* Tell Psalm to ignore the WC_Subscription class, this class is only loaded if Subscriptions is active.
|
||||
*
|
||||
* @psalm-suppress UndefinedDocblockClass
|
||||
*/
|
||||
$currency_code = $this->current_my_account_subscription->get_currency() ?? get_woocommerce_currency();
|
||||
|
||||
// This is sourced from WC_Payments_Explicit_Price_Formatter::get_explicit_price_with_currency.
|
||||
$price_to_check = html_entity_decode( wp_strip_all_tags( $html_price ) );
|
||||
|
||||
if ( false === strpos( $price_to_check, trim( $currency_code ) ) ) {
|
||||
return $html_price . ' ' . $currency_code;
|
||||
}
|
||||
|
||||
return $html_price;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple check to see if the current_my_account_subscription is a WC_Subscription object.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_current_my_account_subscription_set(): bool {
|
||||
/**
|
||||
* Tell Psalm to ignore the WC_Subscription class, this class is only loaded if Subscriptions is active.
|
||||
*
|
||||
* @psalm-suppress UndefinedClass
|
||||
*/
|
||||
return is_a( $this->current_my_account_subscription, 'WC_Subscription' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the cart values to see if there are subscriptions with specific types present.
|
||||
*
|
||||
* This checks both the cart itself and the session. This is due to there are times when an item may be present in
|
||||
* one place and not the other. We need to make sure that if an item is in either we are not creating double conversions.
|
||||
*
|
||||
* @param string $type The type of subscription to look for in the cart.
|
||||
*
|
||||
* @return mixed False if none found, or the subscription cart item as an array.
|
||||
*/
|
||||
private function get_subscription_type_from_cart( $type ) {
|
||||
// Make sure we're looking for allowed types.
|
||||
if ( ! in_array( $type, self::SUBSCRIPTION_TYPES, true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the sub type cart key.
|
||||
$subscription_type = 'subscription_' . $type;
|
||||
|
||||
// Go through each cart item and if it matches the type, return that item.
|
||||
if ( isset( WC()->cart ) && is_array( WC()->cart->cart_contents ) && ! empty( WC()->cart->cart_contents ) ) {
|
||||
foreach ( WC()->cart->cart_contents as $cart_item ) {
|
||||
if ( isset( $cart_item[ $subscription_type ] ) ) {
|
||||
return $cart_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go through each session cart item and if it matches the type, return that item.
|
||||
if ( isset( WC()->session ) && is_array( WC()->session->get( 'cart' ) ) && ! empty( WC()->session->get( 'cart' ) ) ) {
|
||||
foreach ( WC()->session->get( 'cart' ) as $cart_item ) {
|
||||
if ( isset( $cart_item[ $subscription_type ] ) ) {
|
||||
return $cart_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for subscription objects.
|
||||
*
|
||||
* @param mixed $the_subscription Post object or post ID of the order.
|
||||
*
|
||||
* Tell Psalm to ignore the WC_Subscription class, this class is only loaded if Subscriptions is active.
|
||||
*
|
||||
* @psalm-suppress UndefinedDocblockClass
|
||||
*
|
||||
* @return WC_Subscription|bool The subscription object, or false if it cannot be found.
|
||||
*/
|
||||
private function get_subscription( $the_subscription ) {
|
||||
if ( ! function_exists( 'wcs_get_subscription' ) ) {
|
||||
return false;
|
||||
}
|
||||
return wcs_get_subscription( $the_subscription );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks $_GET superglobal for a switch ID from the `switch-subscription` param if it exists.
|
||||
* This `switch-subscription` param is added to the URL when a customer
|
||||
* has initiated a switch from the My Account → Subscription page.
|
||||
*
|
||||
* Tell Psalm to ignore the WC_Subscription class, this class is only loaded if Subscriptions is active.
|
||||
*
|
||||
* @psalm-suppress UndefinedDocblockClass
|
||||
*
|
||||
* @return WC_Subscription|bool The subscription object, or false if it cannot be found.
|
||||
*/
|
||||
private function get_subscription_from_superglobal_switch_id() {
|
||||
// Return false if there's no nonce, or if it fails.
|
||||
if ( ! isset( $_GET['_wcsnonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['_wcsnonce'] ), 'wcs_switch_request' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return false if the param isn't set, or if it isn't numeric.
|
||||
if ( ! isset( $_GET['switch-subscription'] ) || ! is_numeric( $_GET['switch-subscription'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the switch ID from the param.
|
||||
$switch_id = (int) sanitize_key( $_GET['switch-subscription'] );
|
||||
|
||||
// Get the sub, preventing an infinite loop with running_override_selected_currency_filters.
|
||||
$this->running_override_selected_currency_filters = true;
|
||||
$switch_subscription = $this->get_subscription( $switch_id );
|
||||
$this->running_override_selected_currency_filters = false;
|
||||
|
||||
// Confirm the sub user matches current user, and return the sub.
|
||||
if ( $switch_subscription && $switch_subscription->get_customer_id() === get_current_user_id() ) {
|
||||
return $switch_subscription;
|
||||
} else {
|
||||
Logger::notice( 'User (' . get_current_user_id() . ') attempted to switch a subscription (' . $switch_subscription->get_id() . ') not assigned to them.' );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the coupon passed is of a specified type.
|
||||
*
|
||||
* @param \WC_Coupon $coupon Coupon to test.
|
||||
* @param string $type Type of coupon to test for.
|
||||
*
|
||||
* @return bool True on match.
|
||||
*/
|
||||
private function is_coupon_type( $coupon, string $type ) {
|
||||
|
||||
$types = null;
|
||||
switch ( $type ) {
|
||||
case 'subscription_percent':
|
||||
$types = [ 'recurring_percent', 'sign_up_fee_percent', 'renewal_percent' ];
|
||||
break;
|
||||
|
||||
case 'subscription_recurring':
|
||||
$types = [ 'recurring_fee', 'recurring_percent', 'renewal_fee', 'renewal_percent', 'renewal_cart' ];
|
||||
break;
|
||||
}
|
||||
|
||||
if ( in_array( $coupon->get_discount_type(), $types, true ) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WooCommerceUPS
|
||||
*
|
||||
* @package WCPay\MultiCurrency\Compatibility
|
||||
*/
|
||||
|
||||
namespace WCPay\MultiCurrency\Compatibility;
|
||||
|
||||
use WCPay\MultiCurrency\MultiCurrency;
|
||||
use WCPay\MultiCurrency\Utils;
|
||||
|
||||
/**
|
||||
* Class that controls Multi Currency Compatibility with WooCommerce UPS Plugin.
|
||||
*/
|
||||
class WooCommerceUPS extends BaseCompatibility {
|
||||
|
||||
/**
|
||||
* Init the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
// Add needed actions and filters if UPS is active.
|
||||
if ( class_exists( 'WC_Shipping_UPS_Init' ) ) {
|
||||
add_filter( MultiCurrency::FILTER_PREFIX . 'should_return_store_currency', [ $this, 'should_return_store_currency' ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether to return the store currency or not.
|
||||
*
|
||||
* @param bool $return Whether to return the store currency or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_return_store_currency( bool $return ): bool {
|
||||
// If it's already true, return it.
|
||||
if ( $return ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
$calls = [
|
||||
'WC_Shipping_UPS->per_item_shipping',
|
||||
'WC_Shipping_UPS->box_shipping',
|
||||
'WC_Shipping_UPS->calculate_shipping',
|
||||
];
|
||||
if ( $this->utils->is_call_in_backtrace( $calls ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user