init
This commit is contained in:
+283
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
/**
|
||||
* WooPay
|
||||
*
|
||||
* @package WCPay\WooPay
|
||||
*/
|
||||
|
||||
namespace WCPay\WooPay;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Integrations\IntegrationRegistry;
|
||||
use Automattic\WooCommerce\Blocks\Integrations\IntegrationInterface;
|
||||
|
||||
/**
|
||||
* WooPay
|
||||
*/
|
||||
class WooPay_Adapted_Extensions extends IntegrationRegistry {
|
||||
const POINTS_AND_REWARDS_PLUGIN = 'woocommerce-points-and-rewards';
|
||||
const POINTS_AND_REWARDS_API = 'points-and-rewards';
|
||||
const GIFT_CARDS_API = 'woocommerce-gift-cards';
|
||||
const GIFT_CARDS_BLOCKS = 'wc-gift-cards-blocks';
|
||||
|
||||
/**
|
||||
* Initializa WC Blocks regitered integrations.
|
||||
*/
|
||||
public function init() {
|
||||
do_action( 'woocommerce_blocks_checkout_block_registration', $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get WooPay adapted extensions settings and extra data needed on WooPay.
|
||||
*
|
||||
* @param string $email The user email the data will be loaded.
|
||||
*
|
||||
* @return array The extensions script data.
|
||||
*/
|
||||
public function get_adapted_extensions_data( $email ) {
|
||||
$enabled_adapted_extensions = get_option( WooPay_Scheduler::ENABLED_ADAPTED_EXTENSIONS_OPTION_NAME, [] );
|
||||
|
||||
if ( ( is_countable( $enabled_adapted_extensions ) ? count( $enabled_adapted_extensions ) : 0 ) === 0 ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$extension_settings = [];
|
||||
|
||||
$user = wp_get_current_user();
|
||||
|
||||
if ( ! is_user_logged_in() ) {
|
||||
// If the user is a guest and has an account on the merchant site, load data
|
||||
// from there to check if we need to verify their email on WooPay later.
|
||||
$user_by_email = get_user_by( 'email', $email );
|
||||
|
||||
if ( false !== $user_by_email ) {
|
||||
$user = $user_by_email;
|
||||
}
|
||||
}
|
||||
|
||||
// Points and Rewards.
|
||||
if ( in_array( self::POINTS_AND_REWARDS_PLUGIN, $enabled_adapted_extensions, true ) ) {
|
||||
$points_and_rewards_data = self::get_points_and_rewards_data( $user );
|
||||
|
||||
if ( null !== $points_and_rewards_data ) {
|
||||
$extension_settings[ self::POINTS_AND_REWARDS_API ] = $points_and_rewards_data;
|
||||
}
|
||||
}
|
||||
|
||||
if ( in_array( self::GIFT_CARDS_API, $enabled_adapted_extensions, true ) ) {
|
||||
$gift_cards_data = self::get_gift_cards_data( $user );
|
||||
|
||||
if ( null !== $gift_cards_data ) {
|
||||
$extension_settings[ self::GIFT_CARDS_API ] = $gift_cards_data;
|
||||
}
|
||||
}
|
||||
|
||||
return $extension_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Points and Rewards settings for WooPay.
|
||||
*
|
||||
* @param \WP_User $user The user the data will be loaded.
|
||||
*
|
||||
* @return array|null The Points and Rewards script data if installed.
|
||||
*/
|
||||
public function get_points_and_rewards_data( $user ) {
|
||||
if (
|
||||
empty( $this->registered_integrations[ self::POINTS_AND_REWARDS_API ] ) ||
|
||||
! class_exists( 'WC_Points_Rewards_Manager' ) ||
|
||||
! method_exists( 'WC_Points_Rewards_Manager', 'get_users_points' )
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$points_and_rewards_script_data = $this->registered_integrations[ self::POINTS_AND_REWARDS_API ]->get_script_data();
|
||||
|
||||
list( $points, $monetary_value ) = explode( ':', get_option( 'wc_points_rewards_redeem_points_ratio', '' ) );
|
||||
|
||||
$points = floatval( $points );
|
||||
$monetary_value = floatval( $monetary_value );
|
||||
|
||||
$points_and_rewards_script_data['points_ratio'] = [
|
||||
'points' => $points,
|
||||
'monetary_value' => $monetary_value,
|
||||
];
|
||||
|
||||
/**
|
||||
* Check if the user has points to show the verify email alert.
|
||||
*
|
||||
* @psalm-suppress UndefinedClass
|
||||
*/
|
||||
$available_points_for_user = \WC_Points_Rewards_Manager::get_users_points( $user->ID );
|
||||
|
||||
if ( $available_points_for_user > 0 && $available_points_for_user > $points_and_rewards_script_data['minimum_points_amount'] ) {
|
||||
// Only ask the user to verify email if they have available points.
|
||||
$points_and_rewards_script_data['should_verify_email'] = ! is_user_logged_in();
|
||||
$points_and_rewards_script_data['points_available'] = $available_points_for_user;
|
||||
}
|
||||
|
||||
return $points_and_rewards_script_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Gift Cards settings for WooPay.
|
||||
*
|
||||
* @param \WP_User $user The user the data will be loaded.
|
||||
*
|
||||
* @return array|null The Gift Cards script data if installed.
|
||||
*/
|
||||
public function get_gift_cards_data( $user ) {
|
||||
if (
|
||||
empty( $this->registered_integrations[ self::GIFT_CARDS_BLOCKS ] ) ||
|
||||
! function_exists( 'WC_GC' ) ||
|
||||
! property_exists( WC_GC(), 'account' ) ||
|
||||
! method_exists( WC_GC()->account, 'get_active_giftcards' )
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$gift_cards_script_data = $this->registered_integrations[ self::GIFT_CARDS_BLOCKS ]->get_script_data();
|
||||
$gift_cards_script_data['should_verify_email'] = false;
|
||||
|
||||
if ( ! is_user_logged_in() ) {
|
||||
// Verify if the user has Gift Card balance to ask them to verify email on WooPay.
|
||||
$gift_cards = WC_GC()->account->get_active_giftcards( $user->ID );
|
||||
$balance = 0;
|
||||
|
||||
foreach ( $gift_cards as $giftcard_data ) {
|
||||
$balance += (float) $giftcard_data->get_balance();
|
||||
}
|
||||
|
||||
if ( $balance > 0 ) {
|
||||
$gift_cards_script_data['should_verify_email'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $gift_cards_script_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* The custom data from plugins to be used on WooPay,
|
||||
* it's not an adapted extension because it doesn't
|
||||
* use the email verification integration.
|
||||
*
|
||||
* @return array The custom data.
|
||||
*/
|
||||
public function get_extension_data() {
|
||||
$extension_data = [];
|
||||
|
||||
if ( defined( 'WOOCOMMERCE_MULTICURRENCY_VERSION' ) ) {
|
||||
$extension_data['woocommerce-multicurrency'] = [
|
||||
'currency' => get_woocommerce_currency(),
|
||||
];
|
||||
}
|
||||
|
||||
if ( $this->is_affiliate_for_woocommerce_enabled() && function_exists( 'afwc_get_referrer_id' ) ) {
|
||||
/**
|
||||
* Suppress psalm warning.
|
||||
*
|
||||
* @psalm-suppress UndefinedFunction
|
||||
*/
|
||||
$extension_data['affiliate-for-woocommerce'] = [
|
||||
'affiliate-user' => afwc_get_referrer_id(),
|
||||
];
|
||||
}
|
||||
|
||||
if ( $this->is_automate_woo_referrals_enabled() ) {
|
||||
$advocate_id = $this->get_automate_woo_advocate_id_from_cookie();
|
||||
|
||||
$extension_data['automatewoo-referrals'] = [
|
||||
'advocate_id' => $advocate_id,
|
||||
];
|
||||
}
|
||||
|
||||
return $extension_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update order extension data after finishing
|
||||
* an order on WooPay, this usually is needed
|
||||
* for extensions which uses cookies when an
|
||||
* order is finished.
|
||||
*
|
||||
* @param int $order_id The successful WooPay order.
|
||||
*/
|
||||
public function update_order_extension_data( $order_id ) {
|
||||
if ( ! empty( $_GET['affiliate'] ) && // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$this->is_affiliate_for_woocommerce_enabled()
|
||||
) {
|
||||
$affiliate_id = (int) wc_clean( wp_unslash( $_GET['affiliate'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
|
||||
if ( class_exists( '\AFWC_API' ) ) {
|
||||
// phpcs:ignore
|
||||
/**
|
||||
* @psalm-suppress UndefinedClass
|
||||
*/
|
||||
$affiliate_api = \AFWC_API::get_instance();
|
||||
$affiliate_api->track_conversion( $order_id, $affiliate_id, '', [ 'is_affiliate_eligible' => true ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get WC Blocks registered integrations.
|
||||
*
|
||||
* @param IntegrationInterface $integration An instance of IntegrationInterface.
|
||||
*
|
||||
* @return boolean True means registered successfully.
|
||||
*/
|
||||
public function register( IntegrationInterface $integration ) {
|
||||
$name = $integration->get_name();
|
||||
|
||||
if ( self::GIFT_CARDS_BLOCKS === $name || self::POINTS_AND_REWARDS_API === $name ) {
|
||||
$this->registered_integrations[ $name ] = $integration;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Affiliate for WooCommerce is enabled and
|
||||
* its functions used on WCPay are available.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_affiliate_for_woocommerce_enabled() {
|
||||
return defined( 'AFWC_PLUGIN_FILE' ) &&
|
||||
function_exists( 'afwc_get_referrer_id' ) &&
|
||||
class_exists( 'AFWC_API' ) &&
|
||||
method_exists( 'AFWC_API', 'get_instance' ) &&
|
||||
method_exists( 'AFWC_API', 'track_conversion' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Automate Woo Referrals is enabled and
|
||||
* its functions used on WCPay are available.
|
||||
*
|
||||
* @psalm-suppress UndefinedClass
|
||||
* @psalm-suppress UndefinedFunction
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function is_automate_woo_referrals_enabled() {
|
||||
return function_exists( 'AW_Referrals' ) &&
|
||||
method_exists( AW_Referrals(), 'options' ) &&
|
||||
AW_Referrals()->options()->type === 'link' &&
|
||||
class_exists( '\AutomateWoo\Referrals\Referral_Manager' ) &&
|
||||
method_exists( \AutomateWoo\Referrals\Referral_Manager::class, 'get_advocate_key_from_cookie' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AutomateWoo advocate id from cookie.
|
||||
*
|
||||
* @psalm-suppress UndefinedClass
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private function get_automate_woo_advocate_id_from_cookie() {
|
||||
if ( class_exists( '\AutomateWoo\Referrals\Referral_Manager' ) ) {
|
||||
$advocate_from_key_cookie = \AutomateWoo\Referrals\Referral_Manager::get_advocate_key_from_cookie();
|
||||
return $advocate_from_key_cookie ? $advocate_from_key_cookie->get_advocate_id() : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WooPay_Webhooks
|
||||
*
|
||||
* @package WooCommerce\Payments
|
||||
*/
|
||||
|
||||
namespace WCPay\WooPay;
|
||||
|
||||
use WC_Payments_Account;
|
||||
use WC_Payments_API_Client;
|
||||
use WCPay\Exceptions\API_Exception;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* This class introduces webhooks to delivery order updates to the associated
|
||||
* orders in the woopay.
|
||||
*
|
||||
* WooPay Webhooks are enqueued to their associated actions, delivered, and logged.
|
||||
*/
|
||||
class WooPay_Order_Status_Sync {
|
||||
const WCPAY_WEBHOOK_WOOPAY_ORDER_STATUS_CHANGED = 'wcpay_webhook_platform_checkout_order_status_changed';
|
||||
|
||||
/**
|
||||
* WC_Payments_Account instance to get information about the account
|
||||
*
|
||||
* @var WC_Payments_Account
|
||||
*/
|
||||
private $account;
|
||||
|
||||
/**
|
||||
* Client for making requests to the WooCommerce Payments API
|
||||
*
|
||||
* @var WC_Payments_API_Client
|
||||
*/
|
||||
protected $payments_api_client;
|
||||
|
||||
/**
|
||||
* Setup webhook for the WooPay Order Status Sync.
|
||||
*
|
||||
* @param WC_Payments_API_Client $payments_api_client - WooCommerce Payments API client.
|
||||
* @param WC_Payments_Account $account - WooCommerce Payments account.
|
||||
*/
|
||||
public function __construct( WC_Payments_API_Client $payments_api_client, WC_Payments_Account $account ) {
|
||||
|
||||
$this->payments_api_client = $payments_api_client;
|
||||
$this->account = $account;
|
||||
|
||||
add_filter( 'woocommerce_webhook_topic_hooks', [ __CLASS__, 'add_topics' ], 20, 2 );
|
||||
add_filter( 'woocommerce_webhook_payload', [ __CLASS__, 'create_payload' ], 10, 4 );
|
||||
add_filter( 'woocommerce_valid_webhook_resources', [ __CLASS__, 'add_resource' ], 10, 1 );
|
||||
add_filter( 'woocommerce_valid_webhook_events', [ __CLASS__, 'add_event' ], 10, 1 );
|
||||
add_action( 'woocommerce_order_status_changed', [ __CLASS__, 'send_webhook' ], 10, 3 );
|
||||
|
||||
add_action( 'admin_init', [ $this, 'maybe_create_woopay_order_webhook' ], 10 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the webhook name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function get_webhook_name() {
|
||||
return __( 'WCPay woopay order status sync', 'woocommerce-payments' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe create the WooPay webhook under certain conditions.
|
||||
*/
|
||||
public function maybe_create_woopay_order_webhook() {
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) || self::is_webhook_created() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->account->is_stripe_account_valid() || $this->account->is_account_under_review() || $this->account->is_account_rejected() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->register_webhook();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if webhook was already created.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_webhook_created() {
|
||||
return ! empty( self::get_webhook() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array with the webhook id for the woopay order status sync.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_webhook() {
|
||||
$data_store = \WC_Data_Store::load( 'webhook' );
|
||||
|
||||
$args = [
|
||||
'search' => self::get_webhook_name(),
|
||||
'status' => 'active',
|
||||
'limit' => 1,
|
||||
];
|
||||
|
||||
$webhooks = $data_store->search_webhooks( $args );
|
||||
return $webhooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the webhook on WooCommerce.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function register_webhook() {
|
||||
$webhook = new \WC_Webhook();
|
||||
$webhook->set_name( self::get_webhook_name() );
|
||||
$webhook->set_user_id( get_current_user_id() );
|
||||
$webhook->set_topic( 'order.status_changed' );
|
||||
$webhook->set_secret( wp_generate_password( 50, false ) );
|
||||
$webhook->set_delivery_url( WooPay_Utilities::get_woopay_rest_url( 'merchant-notification' ) );
|
||||
$webhook->set_status( 'active' );
|
||||
$webhook->save();
|
||||
|
||||
try {
|
||||
$this->payments_api_client->update_woopay( [ 'webhook_secret' => $webhook->get_secret() ] );
|
||||
} catch ( API_Exception $e ) {
|
||||
$webhook->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add order webhook topic
|
||||
*
|
||||
* @param array $topic_hooks List of WooCommerce's standard webhook topics and hooks.
|
||||
*/
|
||||
public static function add_topics( $topic_hooks ) {
|
||||
$topic_hooks['order.status_changed'][] = self::WCPAY_WEBHOOK_WOOPAY_ORDER_STATUS_CHANGED;
|
||||
|
||||
return $topic_hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup payload for the webhook delivery.
|
||||
*
|
||||
* @param array $payload Data to be sent out by the webhook.
|
||||
* @param string $resource_name Type/name of the resource.
|
||||
* @param integer $resource_id ID of the resource.
|
||||
* @param integer $id ID of the webhook.
|
||||
*/
|
||||
public static function create_payload( $payload, $resource_name, $resource_id, $id ) {
|
||||
$webhook = wc_get_webhook( $id );
|
||||
if ( 0 !== strpos( $webhook->get_delivery_url(), WooPay_Utilities::get_woopay_rest_url( 'merchant-notification' ) ) ) {
|
||||
// This is not a WooPay webhook, so we don't need to modify the payload.
|
||||
return $payload;
|
||||
}
|
||||
|
||||
return [
|
||||
'blog_id' => \Jetpack_Options::get_option( 'id' ),
|
||||
'order_id' => $resource_id,
|
||||
'order_status' => $payload['status'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add webhook resource for order.
|
||||
*
|
||||
* @param array $resources List of available resources.
|
||||
*/
|
||||
public static function add_resource( $resources ) {
|
||||
$resources[] = 'order';
|
||||
|
||||
return $resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param array $topic_events List of available topic events.
|
||||
*/
|
||||
public static function add_event( $topic_events ) {
|
||||
$topic_events[] = 'status_changed';
|
||||
|
||||
return $topic_events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger webhook delivery.
|
||||
*
|
||||
* @param int $order_id Order id.
|
||||
* @param string $previous_status the old WooCommerce order status.
|
||||
* @param string $next_status the new WooCommerce order status.
|
||||
* @return void
|
||||
*/
|
||||
public static function send_webhook( $order_id, $previous_status, $next_status ) {
|
||||
$order = wc_get_order( $order_id );
|
||||
if ( $order->get_meta( 'is_woopay' ) ) {
|
||||
do_action( self::WCPAY_WEBHOOK_WOOPAY_ORDER_STATUS_CHANGED, $order_id, $next_status );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the webhook if woopay is disabled.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function remove_webhook() {
|
||||
|
||||
if ( self::is_webhook_created() ) {
|
||||
$webhook_id = self::get_webhook()[0];
|
||||
$webhook = new \WC_Webhook( $webhook_id );
|
||||
$webhook->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WooPay_Scheduler.
|
||||
*
|
||||
* @package WooCommerce\Payments
|
||||
*/
|
||||
|
||||
namespace WCPay\WooPay;
|
||||
|
||||
use WC_Payments_API_Client;
|
||||
use WCPay\Logger;
|
||||
|
||||
/**
|
||||
* This class adds a cron job that runs daily to check if is there any extensions incompatible with WooPay active,
|
||||
* if one is found WooPay is disabled, also updates the list of extensions adapted to work with WooPay and the
|
||||
* available countries.
|
||||
*/
|
||||
class WooPay_Scheduler {
|
||||
|
||||
const INVALID_EXTENSIONS_FOUND_OPTION_NAME = 'woopay_invalid_extension_found';
|
||||
const INCOMPATIBLE_EXTENSIONS_LIST_OPTION_NAME = 'woopay_incompatible_extensions';
|
||||
const ENABLED_ADAPTED_EXTENSIONS_OPTION_NAME = 'woopay_enabled_adapted_extensions';
|
||||
const ADAPTED_EXTENSIONS_LIST_OPTION_NAME = 'woopay_adapted_extensions';
|
||||
|
||||
/**
|
||||
* WC_Payments_API_Client instance.
|
||||
*
|
||||
* @var WC_Payments_API_Client
|
||||
*/
|
||||
private $payments_api_client;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param WC_Payments_API_Client $payments_api_client The Payments API Client.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( WC_Payments_API_Client $payments_api_client ) {
|
||||
$this->payments_api_client = $payments_api_client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the hooks.
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'init', [ $this, 'schedule' ] );
|
||||
add_action( 'validate_woopay_compatibility', [ $this, 'update_compatibility_and_maybe_show_incompatibility_warning' ] );
|
||||
add_action( 'activated_plugin', [ $this, 'show_warning_when_incompatible_extension_is_enabled' ] );
|
||||
add_action( 'deactivated_plugin', [ $this, 'hide_warning_when_incompatible_extension_is_disabled' ] );
|
||||
add_action( 'woocommerce_woocommerce_payments_updated', [ $this, 'remove_legacy_schedule_action_name_on_update' ] );
|
||||
|
||||
register_deactivation_hook( WCPAY_PLUGIN_FILE, [ $this, 'remove_scheduler' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the scheduler when the plugin is disabled.
|
||||
*/
|
||||
public function remove_scheduler() {
|
||||
wp_clear_scheduled_hook( 'validate_woopay_compatibility' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the cron job.
|
||||
*/
|
||||
public function schedule() {
|
||||
if ( ! wp_next_scheduled( 'validate_woopay_compatibility' ) ) {
|
||||
wp_schedule_event( time(), 'daily', 'validate_woopay_compatibility' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove legacy action name on WCPay update.
|
||||
*/
|
||||
public function remove_legacy_schedule_action_name_on_update() {
|
||||
if ( wp_next_scheduled( 'validate_incompatible_extensions' ) ) {
|
||||
wp_clear_scheduled_hook( 'validate_incompatible_extensions' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the compability list.
|
||||
*/
|
||||
public function update_compatibility_and_maybe_show_incompatibility_warning() {
|
||||
try {
|
||||
$compatibility = $this->payments_api_client->get_woopay_compatibility();
|
||||
$incompatible_extensions = isset( $compatibility['incompatible_extensions'] ) ? $compatibility['incompatible_extensions'] : [];
|
||||
$adapted_extensions = isset( $compatibility['adapted_extensions'] ) ? $compatibility['adapted_extensions'] : [];
|
||||
$available_countries = isset( $compatibility['available_countries'] ) ? $compatibility['available_countries'] : [];
|
||||
|
||||
$active_plugins = get_option( 'active_plugins', [] );
|
||||
|
||||
update_option( self::INCOMPATIBLE_EXTENSIONS_LIST_OPTION_NAME, $incompatible_extensions );
|
||||
delete_option( self::INVALID_EXTENSIONS_FOUND_OPTION_NAME );
|
||||
|
||||
update_option( self::ADAPTED_EXTENSIONS_LIST_OPTION_NAME, $adapted_extensions );
|
||||
delete_option( self::ENABLED_ADAPTED_EXTENSIONS_OPTION_NAME );
|
||||
|
||||
if ( ! empty( $active_plugins ) && is_array( $active_plugins ) ) {
|
||||
if ( count( $this->get_extensions_in_list( $active_plugins, $incompatible_extensions ) ) > 0 ) {
|
||||
update_option( self::INVALID_EXTENSIONS_FOUND_OPTION_NAME, true );
|
||||
}
|
||||
}
|
||||
|
||||
$this->update_enabled_adapted_extensions( $active_plugins, $adapted_extensions );
|
||||
$this->update_available_countries( $available_countries );
|
||||
} catch ( \Exception $e ) {
|
||||
Logger::error( 'Failed to decode WooPay incompatible extensions list. ' . $e );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the enable adapted extensions list.
|
||||
*
|
||||
* @param array $active_plugins The active plugins.
|
||||
* @param array $adapted_extensions The adapted extensions list.
|
||||
*/
|
||||
public function update_enabled_adapted_extensions( $active_plugins, $adapted_extensions ) {
|
||||
$enabled_adapted_extensions = $this->get_extensions_in_list( $active_plugins, $adapted_extensions );
|
||||
|
||||
if ( count( $enabled_adapted_extensions ) > 0 ) {
|
||||
update_option( '_wcpay_feature_woopay_first_party_auth', 0 );
|
||||
} else {
|
||||
update_option( '_wcpay_feature_woopay_first_party_auth', 1 );
|
||||
}
|
||||
|
||||
update_option( self::ENABLED_ADAPTED_EXTENSIONS_OPTION_NAME, $enabled_adapted_extensions );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the available countries list.
|
||||
*
|
||||
* @param array $available_countries The available countries list.
|
||||
*/
|
||||
public function update_available_countries( $available_countries ) {
|
||||
try {
|
||||
if ( is_array( $available_countries ) ) {
|
||||
update_option( WooPay_Utilities::AVAILABLE_COUNTRIES_OPTION_NAME, wp_json_encode( $available_countries ) );
|
||||
}
|
||||
} catch ( \Exception $e ) {
|
||||
Logger::error( 'Failed to decode WooPay available countries. ' . $e );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a warning to the WC Payments settings page.
|
||||
*
|
||||
* @param string $plugin The plugin being enabled.
|
||||
*/
|
||||
public function show_warning_when_incompatible_extension_is_enabled( $plugin ) {
|
||||
$incompatible_extensions = get_option( self::INCOMPATIBLE_EXTENSIONS_LIST_OPTION_NAME, [] );
|
||||
$adapted_extensions = get_option( self::ADAPTED_EXTENSIONS_LIST_OPTION_NAME, [] );
|
||||
$plugin = $this->format_extension_name( $plugin );
|
||||
$active_plugins = get_option( 'active_plugins', [] );
|
||||
|
||||
if ( count( $this->get_extensions_in_list( [ $plugin ], $incompatible_extensions ) ) > 0 ) {
|
||||
update_option( self::INVALID_EXTENSIONS_FOUND_OPTION_NAME, true );
|
||||
}
|
||||
|
||||
$this->update_enabled_adapted_extensions( $active_plugins, $adapted_extensions );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the warning when the last incompatible extension is removed.
|
||||
*
|
||||
* @param string $plugin_being_deactivated The plugin name.
|
||||
*/
|
||||
public function hide_warning_when_incompatible_extension_is_disabled( $plugin_being_deactivated ) {
|
||||
$incompatible_extensions = get_option( self::INCOMPATIBLE_EXTENSIONS_LIST_OPTION_NAME, [] );
|
||||
$adapted_extensions = get_option( self::ADAPTED_EXTENSIONS_LIST_OPTION_NAME, [] );
|
||||
$active_plugins = get_option( 'active_plugins', [] );
|
||||
|
||||
// Needs to remove the plugin being deactivated because WordPress only updates the list after this hook runs.
|
||||
$active_plugins = array_diff( $active_plugins, [ $plugin_being_deactivated ] );
|
||||
|
||||
// Only deactivates the warning if there are no other incompatible extensions.
|
||||
if ( count( $this->get_extensions_in_list( $active_plugins, $incompatible_extensions ) ) === 0 ) {
|
||||
delete_option( self::INVALID_EXTENSIONS_FOUND_OPTION_NAME );
|
||||
}
|
||||
|
||||
$this->update_enabled_adapted_extensions( $active_plugins, $adapted_extensions );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of extensions in a list.
|
||||
*
|
||||
* @param mixed $active_plugins list of active plugins.
|
||||
* @param mixed $extensions list of extensions to find in active plugins.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_extensions_in_list( $active_plugins, $extensions ) {
|
||||
$plugins_in_list = [];
|
||||
|
||||
foreach ( $active_plugins as $plugin ) {
|
||||
$plugin = $this->format_extension_name( $plugin );
|
||||
|
||||
if ( in_array( $plugin, $extensions, true ) ) {
|
||||
$plugins_in_list[] = $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
return $plugins_in_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the folder and file extension from the plugin name.
|
||||
*
|
||||
* @param string $plugin the plugin main file name.
|
||||
* @return string the plugin name.
|
||||
*/
|
||||
private function format_extension_name( $plugin ) {
|
||||
$plugin = explode( '/', $plugin );
|
||||
$plugin = end( $plugin );
|
||||
return str_replace( '.php', '', $plugin );
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+145
@@ -0,0 +1,145 @@
|
||||
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
|
||||
/**
|
||||
* Class SessionHandler
|
||||
*
|
||||
* This is a copy of the Automattic\WooCommerce\StoreApi\SessionHandler class with the addition of an `init_session_cookie` method.
|
||||
*
|
||||
* @package WooCommerce\Payments
|
||||
*/
|
||||
|
||||
namespace WCPay\Platform_Checkout;
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\StoreApi\Utilities\JsonWebToken;
|
||||
use WC_Session;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* SessionHandler class
|
||||
*/
|
||||
final class SessionHandler extends WC_Session {
|
||||
/**
|
||||
* Token from HTTP headers.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $token;
|
||||
|
||||
/**
|
||||
* Table name for session data.
|
||||
*
|
||||
* @var string Custom session table name
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* Expiration timestamp.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $session_expiration;
|
||||
|
||||
/**
|
||||
* Constructor for the session class.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->token = wc_clean( wp_unslash( $_SERVER['HTTP_CART_TOKEN'] ?? '' ) );
|
||||
$this->table = $GLOBALS['wpdb']->prefix . 'woocommerce_sessions';
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: This method was added to the original class for compatibility with WooPay.
|
||||
*/
|
||||
public function init_session_cookie() {
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init hooks and session data.
|
||||
*/
|
||||
public function init() {
|
||||
$this->init_session_from_token();
|
||||
add_action( 'shutdown', [ $this, 'save_data' ], 20 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the token header to load the correct session.
|
||||
*/
|
||||
protected function init_session_from_token() {
|
||||
$payload = JsonWebToken::get_parts( $this->token )->payload;
|
||||
|
||||
$this->_customer_id = $payload->user_id;
|
||||
$this->session_expiration = $payload->exp;
|
||||
$this->_data = (array) $this->get_session( $this->_customer_id, [] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the current user has an active session,.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_session() {
|
||||
return ! empty( $this->token );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session.
|
||||
*
|
||||
* @param string $customer_id Customer ID.
|
||||
* @param mixed $default Default session value.
|
||||
*
|
||||
* @return string|array|bool
|
||||
*/
|
||||
public function get_session( $customer_id, $default = false ) {
|
||||
global $wpdb;
|
||||
|
||||
// This mimics behaviour from default WC_Session_Handler class. There will be no sessions retrieved while WP setup is due.
|
||||
if ( Constants::is_defined( 'WP_SETUP_CONFIG' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT session_value FROM $this->table WHERE session_key = %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$customer_id
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_null( $value ) ) {
|
||||
$value = $default;
|
||||
}
|
||||
|
||||
return maybe_unserialize( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a cache prefix. This is used in session names so the entire cache can be invalidated with 1 function call.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_cache_prefix() {
|
||||
return \WC_Cache_Helper::get_cache_prefix( WC_SESSION_CACHE_GROUP );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save data and delete user session.
|
||||
*/
|
||||
public function save_data() {
|
||||
// Dirty if something changed - prevents saving nothing new.
|
||||
if ( $this->_dirty ) {
|
||||
global $wpdb;
|
||||
|
||||
$wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"INSERT INTO $this->table (`session_key`, `session_value`, `session_expiry`) VALUES (%s, %s, %d) ON DUPLICATE KEY UPDATE `session_value` = VALUES(`session_value`), `session_expiry` = VALUES(`session_expiry`)", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$this->_customer_id,
|
||||
maybe_serialize( $this->_data ),
|
||||
$this->session_expiration
|
||||
)
|
||||
);
|
||||
wp_cache_set( $this->get_cache_prefix() . $this->_customer_id, $this->_data, WC_SESSION_CACHE_GROUP, $this->session_expiration - time() );
|
||||
$this->_dirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* Class WooPay_Store_Api_Token
|
||||
*
|
||||
* @package WCPay\Platform_Checkout
|
||||
*/
|
||||
|
||||
namespace WCPay\Platform_Checkout;
|
||||
|
||||
use Automattic\WooCommerce\StoreApi\Routes\V1\AbstractCartRoute;
|
||||
|
||||
if ( class_exists( AbstractCartRoute::class ) ) {
|
||||
/**
|
||||
* This class is used to get the cart token from the cart route.
|
||||
*/
|
||||
class WooPay_Store_Api_Token extends AbstractCartRoute {
|
||||
|
||||
/**
|
||||
* Helper method to get the instance of the class.
|
||||
*
|
||||
* @return WooPay_Store_Api_Token The instance of the class.
|
||||
*
|
||||
* @psalm-suppress InvalidArgument Psalm thinks namespace is incorrect.
|
||||
*/
|
||||
public static function init() {
|
||||
$formatters = new \Automattic\WooCommerce\StoreApi\Formatters();
|
||||
$extend_schema = new \Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema( $formatters );
|
||||
$schema_controller = new \Automattic\WooCommerce\StoreApi\SchemaController( $extend_schema );
|
||||
return new self( $schema_controller, $schema_controller->get( 'cart' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path of this REST route.
|
||||
*
|
||||
* @throws \Exception Throws exception because this method is not meant to be implemented in this utility class.
|
||||
*/
|
||||
public function get_path() {
|
||||
throw new \Exception( 'Not implemented' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get arguments for this REST route.
|
||||
*
|
||||
* @throws \Exception Throws exception because this method is not meant to be implemented in this utility class.
|
||||
*/
|
||||
public function get_args() {
|
||||
throw new \Exception( 'Not implemented' );
|
||||
}
|
||||
|
||||
//phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod
|
||||
/**
|
||||
* This function is used to get the cart token from the cart route.
|
||||
*
|
||||
* @return string The cart token.
|
||||
* @psalm-suppress UndefinedMethod
|
||||
*/
|
||||
public function get_cart_token() {
|
||||
// @phpstan-ignore-next-line.
|
||||
return parent::get_cart_token();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
<?php
|
||||
/**
|
||||
* WooPay
|
||||
*
|
||||
* @package WCPay\WooPay
|
||||
*/
|
||||
|
||||
namespace WCPay\WooPay;
|
||||
|
||||
use WC_Payments_Features;
|
||||
use WC_Payments_Subscriptions_Utilities;
|
||||
use WCPay\Logger;
|
||||
use WC_Geolocation;
|
||||
use WC_Payments;
|
||||
use Jetpack_Options;
|
||||
|
||||
/**
|
||||
* WooPay
|
||||
*/
|
||||
class WooPay_Utilities {
|
||||
use WC_Payments_Subscriptions_Utilities;
|
||||
|
||||
const AVAILABLE_COUNTRIES_OPTION_NAME = 'woocommerce_woocommerce_payments_woopay_available_countries';
|
||||
const AVAILABLE_COUNTRIES_DEFAULT = '["US"]';
|
||||
|
||||
const DEFAULT_WOOPAY_URL = 'https://pay.woo.com';
|
||||
|
||||
/**
|
||||
* Check various conditions to determine if we should enable woopay.
|
||||
*
|
||||
* @param \WC_Payment_Gateway_WCPay $gateway Gateway instance.
|
||||
* @return boolean
|
||||
*/
|
||||
public function should_enable_woopay( $gateway ) {
|
||||
$is_woopay_eligible = WC_Payments_Features::is_woopay_eligible(); // Feature flag.
|
||||
$is_woopay_enabled = 'yes' === $gateway->get_option( 'platform_checkout', 'no' );
|
||||
|
||||
return $is_woopay_eligible && $is_woopay_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks various conditions to determine if WooPay should be enabled on the checkout page.
|
||||
*
|
||||
* This function should only be called when evaluating something for the checkout or cart page. The
|
||||
* function will return false if you're on any other page.
|
||||
*
|
||||
* @return bool True if WooPay should be enabled, false otherwise.
|
||||
*/
|
||||
public function should_enable_woopay_on_cart_or_checkout(): bool {
|
||||
if ( ! is_checkout() && ! has_block( 'woocommerce/checkout' ) && ! is_cart() && ! has_block( 'woocommerce/cart' ) ) {
|
||||
// Wrong usage, this should only be called for the checkout or cart page.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! is_user_logged_in() ) {
|
||||
// If there's a subscription product in the cart and the customer isn't logged in we
|
||||
// should not enable WooPay since that situation is currently not supported.
|
||||
// Note that this is mirrored in the WC_Payments_WooPay_Button_Handler class.
|
||||
if ( class_exists( 'WC_Subscriptions_Cart' ) && \WC_Subscriptions_Cart::cart_contains_subscription() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If guest checkout is disabled and the customer isn't logged in we should not enable
|
||||
// WooPay scripts since that situations is currently not supported.
|
||||
// Note that this is mirrored in the WC_Payments_WooPay_Button_Handler class.
|
||||
if ( ! $this->is_guest_checkout_enabled() ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check conditions to determine if woopay express checkout is enabled.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_woopay_express_checkout_enabled() {
|
||||
return WC_Payments_Features::is_woopay_express_checkout_enabled() && $this->is_country_available( WC_Payments::get_gateway() ); // Feature flag.
|
||||
}
|
||||
|
||||
/**
|
||||
* Check conditions to determine if woopay first party auth is enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_woopay_first_party_auth_enabled() {
|
||||
return WC_Payments_Features::is_woopay_first_party_auth_enabled() && $this->is_country_available( WC_Payments::get_gateway() ); // Feature flag.
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the WooPay email input hooks should be enabled.
|
||||
*
|
||||
* This function doesn't affect the appearance of the email input,
|
||||
* only whether or not the email exists check or auto-redirection should be enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_woopay_email_input_enabled() {
|
||||
return apply_filters( 'wcpay_is_woopay_email_input_enabled', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a hash based on the store's blog token, merchant ID, and the time step window.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_woopay_request_signature() {
|
||||
$store_blog_token = \Jetpack_Options::get_option( 'blog_token' );
|
||||
$time_step_window = floor( time() / 30 );
|
||||
|
||||
return hash_hmac( 'sha512', \Jetpack_Options::get_option( 'id' ) . $time_step_window, $store_blog_token );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check session to determine if we should create a platform customer.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function should_save_platform_customer() {
|
||||
$session_data = [];
|
||||
|
||||
if ( isset( WC()->session ) && method_exists( WC()->session, 'has_session' ) && WC()->session->has_session() ) {
|
||||
$session_data = WC()->session->get( WooPay_Session::WOOPAY_SESSION_KEY );
|
||||
}
|
||||
|
||||
$save_user_in_woopay_post = isset( $_POST['save_user_in_woopay'] ) && filter_var( wp_unslash( $_POST['save_user_in_woopay'] ), FILTER_VALIDATE_BOOLEAN ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$save_user_in_woopay_session = isset( $session_data['save_user_in_woopay'] ) && filter_var( $session_data['save_user_in_woopay'], FILTER_VALIDATE_BOOLEAN );
|
||||
|
||||
return $save_user_in_woopay_post || $save_user_in_woopay_session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if WooPay is available on the user country.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_country_available() {
|
||||
if ( WC_Payments::mode()->is_test() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$location_data = WC_Geolocation::geolocate_ip();
|
||||
|
||||
$available_countries = self::get_persisted_available_countries();
|
||||
|
||||
return in_array( $location_data['country'], $available_countries, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if WooPay is available on the store country.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_store_country_available() {
|
||||
$store_base_location = wc_get_base_location();
|
||||
|
||||
if ( empty( $store_base_location['country'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$available_countries = self::get_persisted_available_countries();
|
||||
|
||||
return in_array( $store_base_location['country'], $available_countries, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get phone number for creating woopay customer.
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function get_woopay_phone() {
|
||||
$session_data = WC()->session->get( WooPay_Session::WOOPAY_SESSION_KEY );
|
||||
|
||||
if ( ! empty( $_POST['woopay_user_phone_field']['full'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
return wc_clean( wp_unslash( $_POST['woopay_user_phone_field']['full'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
} elseif ( ! empty( $session_data['woopay_user_phone_field']['full'] ) ) {
|
||||
return $session_data['woopay_user_phone_field']['full'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url marketing where the user have chosen marketing options.
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function get_woopay_source_url() {
|
||||
$session_data = WC()->session->get( WooPay_Session::WOOPAY_SESSION_KEY );
|
||||
|
||||
if ( ! empty( $_POST['woopay_source_url'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
return wc_clean( wp_unslash( $_POST['woopay_source_url'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
} elseif ( ! empty( $session_data['woopay_source_url'] ) ) {
|
||||
return $session_data['woopay_source_url'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if the request comes from blocks checkout.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function get_woopay_is_blocks() {
|
||||
$session_data = WC()->session->get( WooPay_Session::WOOPAY_SESSION_KEY );
|
||||
|
||||
$woopay_is_blocks_post = isset( $_POST['woopay_is_blocks'] ) && filter_var( wp_unslash( $_POST['woopay_is_blocks'] ), FILTER_VALIDATE_BOOLEAN ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$woopay_is_blocks_session = isset( $session_data['woopay_is_blocks'] ) && filter_var( $session_data['woopay_is_blocks'], FILTER_VALIDATE_BOOLEAN );
|
||||
|
||||
return $woopay_is_blocks_post || $woopay_is_blocks_session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user viewport.
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function get_woopay_viewport() {
|
||||
$session_data = WC()->session->get( WooPay_Session::WOOPAY_SESSION_KEY );
|
||||
|
||||
if ( ! empty( $_POST['woopay_viewport'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
return wc_clean( wp_unslash( $_POST['woopay_viewport'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
} elseif ( ! empty( $session_data['woopay_viewport'] ) ) {
|
||||
return $session_data['woopay_viewport'];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if guest checkout is enabled, false otherwise.
|
||||
*
|
||||
* @return bool True if guest checkout is enabled, false otherwise.
|
||||
*/
|
||||
public function is_guest_checkout_enabled(): bool {
|
||||
return 'yes' === get_option( 'woocommerce_enable_guest_checkout', 'no' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the WooPay rest url for a given endpoint
|
||||
*
|
||||
* @param string $endpoint the end point.
|
||||
* @return string the endpoint full url.
|
||||
*/
|
||||
public static function get_woopay_rest_url( $endpoint ) {
|
||||
return self::get_woopay_url() . '/wp-json/platform-checkout/v1/' . $endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the WooPay url.
|
||||
*
|
||||
* @return string the WooPay url.
|
||||
*/
|
||||
public static function get_woopay_url() {
|
||||
return defined( 'PLATFORM_CHECKOUT_HOST' ) ? PLATFORM_CHECKOUT_HOST : self::DEFAULT_WOOPAY_URL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the store blog token.
|
||||
*
|
||||
* @return mixed|string the store blog token.
|
||||
*/
|
||||
public static function get_store_blog_token() {
|
||||
if ( self::get_woopay_url() === self::DEFAULT_WOOPAY_URL ) {
|
||||
// Using WooPay production: Use the blog token secret from the store blog.
|
||||
return Jetpack_Options::get_option( 'blog_token' );
|
||||
} elseif ( apply_filters( 'wcpay_woopay_use_blog_token', false ) ) {
|
||||
// Requested to use the blog token secret from the store blog.
|
||||
return Jetpack_Options::get_option( 'blog_token' );
|
||||
} elseif ( defined( 'DEV_BLOG_TOKEN_SECRET' ) ) {
|
||||
// Has a defined dev blog token secret: Use it.
|
||||
return DEV_BLOG_TOKEN_SECRET;
|
||||
} else {
|
||||
Logger::log( __( 'WooPay blog_token is currently misconfigured.', 'woocommerce-payments' ) );
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array with encrypted and signed data.
|
||||
*
|
||||
* @param array $data The data to be encrypted and signed.
|
||||
* @return array The encrypted and signed data.
|
||||
*/
|
||||
public static function encrypt_and_sign_data( $data ) {
|
||||
$store_blog_token = self::get_store_blog_token();
|
||||
|
||||
if ( empty( $store_blog_token ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$message = wp_json_encode( $data );
|
||||
|
||||
// Generate an initialization vector (IV) for encryption.
|
||||
$iv = openssl_random_pseudo_bytes( openssl_cipher_iv_length( 'aes-256-cbc' ) );
|
||||
|
||||
// Encrypt the JSON session.
|
||||
$session_encrypted = openssl_encrypt( $message, 'aes-256-cbc', $store_blog_token, OPENSSL_RAW_DATA, $iv );
|
||||
|
||||
// Create an HMAC hash for data integrity.
|
||||
$hash = hash_hmac( 'sha256', $session_encrypted, $store_blog_token );
|
||||
|
||||
$data = [
|
||||
'session' => $session_encrypted,
|
||||
'iv' => $iv,
|
||||
'hash' => $hash,
|
||||
];
|
||||
|
||||
return [
|
||||
'blog_id' => Jetpack_Options::get_option( 'id' ),
|
||||
'data' => array_map( 'base64_encode', $data ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode encrypted and signed data and return it.
|
||||
*
|
||||
* @param array $data The session, iv, and hash data for the encryption.
|
||||
* @return mixed The decoded data.
|
||||
*/
|
||||
public static function decrypt_signed_data( $data ) {
|
||||
$store_blog_token = self::get_store_blog_token();
|
||||
|
||||
if ( empty( $store_blog_token ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Decode the data.
|
||||
$decoded_data_request = array_map( 'base64_decode', $data );
|
||||
|
||||
// Verify the HMAC hash before decryption to ensure data integrity.
|
||||
$computed_hash = hash_hmac( 'sha256', $decoded_data_request['iv'] . $decoded_data_request['data'], $store_blog_token );
|
||||
|
||||
// If the hashes don't match, the message may have been tampered with.
|
||||
if ( ! hash_equals( $computed_hash, $decoded_data_request['hash'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Decipher the data using the blog token and the IV.
|
||||
$decrypted_data = openssl_decrypt( $decoded_data_request['data'], 'aes-256-cbc', $store_blog_token, OPENSSL_RAW_DATA, $decoded_data_request['iv'] );
|
||||
|
||||
if ( false === $decrypted_data ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decrypted_data = json_decode( $decrypted_data, true );
|
||||
|
||||
return $decrypted_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the persisted available countries.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function get_persisted_available_countries() {
|
||||
$available_countries = json_decode( get_option( self::AVAILABLE_COUNTRIES_OPTION_NAME, self::AVAILABLE_COUNTRIES_DEFAULT ), true );
|
||||
|
||||
if ( ! is_array( $available_countries ) ) {
|
||||
return json_decode( self::AVAILABLE_COUNTRIES_DEFAULT );
|
||||
}
|
||||
|
||||
return $available_countries;
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Create_And_Confirm_Intention_Test
|
||||
*
|
||||
* @package Checkout_Service
|
||||
*/
|
||||
|
||||
namespace WCPay\WooPay\Service;
|
||||
|
||||
use WC_Payments_Features;
|
||||
use WCPay\Core\Server\Request;
|
||||
use WCPay\Core\Server\Request\WooPay_Create_And_Confirm_Intention;
|
||||
use WCPay\Core\Server\Request\WooPay_Create_And_Confirm_Setup_Intention;
|
||||
use WCPay\Payment_Information;
|
||||
|
||||
/**
|
||||
* Checkout service class.
|
||||
*/
|
||||
class Checkout_Service {
|
||||
|
||||
/**
|
||||
* Create woopay request from base create and confirm request.
|
||||
*
|
||||
* @param Request $base_request Base request.
|
||||
* @param Payment_Information $payment_information Using saved payment method.
|
||||
*
|
||||
* @return WooPay_Create_And_Confirm_Intention
|
||||
* @throws \WCPay\Core\Exceptions\Server\Request\Extend_Request_Exception
|
||||
*/
|
||||
public function create_intention_request( Request $base_request, Payment_Information $payment_information ) {
|
||||
$request = WooPay_Create_And_Confirm_Intention::extend( $base_request );
|
||||
$request->set_has_woopay_subscription( '1' === $payment_information->get_order()->get_meta( '_woopay_has_subscription' ) );
|
||||
$request->set_save_payment_method_to_platform( $payment_information->should_save_payment_method_to_platform() );
|
||||
$request->set_is_platform_payment_method( $this->is_platform_payment_method( $payment_information ) );
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create woopay setup and confirm intent request from base create and confirm request.
|
||||
*
|
||||
* @param Request $base_request Base request.
|
||||
* @param Payment_Information $payment_information Using saved payment method.
|
||||
* @param bool $save_in_platform_account Should save in platform account.
|
||||
* @param bool $save_payment_method_to_platform Should save in platform.
|
||||
*
|
||||
* @return WooPay_Create_And_Confirm_Setup_Intention
|
||||
* @throws \WCPay\Core\Exceptions\Server\Request\Extend_Request_Exception
|
||||
*/
|
||||
public function create_and_confirm_setup_intention_request( Request $base_request, Payment_Information $payment_information, bool $save_in_platform_account, bool $save_payment_method_to_platform ) {
|
||||
$request = WooPay_Create_And_Confirm_Setup_Intention::extend( $base_request );
|
||||
$request->set_save_in_platform_account( $save_in_platform_account );
|
||||
$request->set_save_payment_method_to_platform( $save_payment_method_to_platform );
|
||||
$request->set_is_platform_payment_method( $this->is_platform_payment_method( $payment_information ) );
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if current payment method is a platform payment method.
|
||||
*
|
||||
* @param Payment_Information $payment_information Payment information object used to determine if a saved payment method is being used as well as helps to determine
|
||||
* if stripe platform account should be used or not.
|
||||
*
|
||||
* @return boolean True if it is a platform payment method.
|
||||
*/
|
||||
public function is_platform_payment_method( Payment_Information $payment_information ) {
|
||||
// Return false for express checkout method.
|
||||
if ( isset( $_POST['payment_request_type'] ) || isset( $_POST['express_payment_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
return false;
|
||||
}
|
||||
|
||||
$should_use_stripe_platform = \WC_Payments::get_payment_gateway_by_id( $payment_information->get_payment_method_stripe_id() )->should_use_stripe_platform_on_checkout_page();
|
||||
|
||||
// Make sure the payment method being charged was created in the platform.
|
||||
if (
|
||||
! $payment_information->is_using_saved_payment_method() &&
|
||||
$should_use_stripe_platform
|
||||
) {
|
||||
// This payment method was created under the platform account.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'wcpay_create_and_confirm_intent_request', [ $this, 'create_intention_request' ], 10, 3 );
|
||||
add_filter( 'wcpay_create_and_confirm_setup_intention_request', [ $this, 'create_and_confirm_setup_intention_request' ], 10, 4 );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user