<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'WC_Connect_PayPal_EC' ) ) {
/**
* Integrates with WooCommerce PayPal Checkout Payment Gateway,
* modifying that plugin's behavior to facilitate authenticating requests
* not by linking an account but via the WCS server through which we proxy.
*/
class WC_Connect_PayPal_EC {
/**
* @var WC_Connect_API_Client
*/
private $api_client;
/**
* @var WC_Connect_Nux
*/
private $nux;
/**
* Express Checkout API methods to proxy.
*/
private $methods_to_proxy = array( 'SetExpressCheckout', 'GetExpressCheckoutDetails', 'DoExpressCheckoutPayment' );
public function __construct( WC_Connect_API_Client $api_client, WC_Connect_Nux $nux ) {
$this->api_client = $api_client;
$this->nux = $nux;
}
public function init() {
if ( ! function_exists( 'wc_gateway_ppec' ) ) {
return;
}
$ppec_plugin = wc_gateway_ppec();
if ( ! property_exists( $ppec_plugin, 'settings' ) || empty( $ppec_plugin->settings ) ) {
return;
}
$this->maybe_set_reroute_requests();
add_filter( 'woocommerce_paypal_express_checkout_settings', array( $this, 'adjust_form_fields' ) );
$this->initialize_settings();
$settings = $ppec_plugin->settings;
// Don't modify any PPEC plugin behavior if WCS request proxying is not enabled
if ( 'yes' !== $settings->reroute_requests ) {
return;
}
// If empty, populate Sandbox and Live API Subject values with provided email
if (
empty( $settings->sandbox_api_subject ) &&
empty( $settings->sandbox_api_username ) &&
empty( $settings->api_username )
) {
$email = isset( $settings->email ) ? $settings->email : $settings->api_subject;
$settings->api_subject = $email;
$settings->sandbox_api_subject = $email;
$settings->save();
}
$username = $settings->get_active_api_credentials()->get_username();
$subject = $settings->get_active_api_credentials()->get_subject();
// Proceed to attach PPEC-related hooks if email address is present but credentials are missing
if ( empty( $username ) && ! empty( $subject ) ) {
add_filter( 'woocommerce_paypal_express_checkout_request_body', array( $this, 'request_body' ) );
add_filter( 'option_woocommerce_ppec_paypal_settings', array( $this, 'adjust_settings' ) );
add_filter( 'woocommerce_payment_gateway_supports', array( $this, 'ppec_supports' ), 10, 3 );
if ( 'live' === $settings->environment ) {
// If PPEC order comes in, activate prompt to connect a PayPal account
add_action( 'woocommerce_order_status_on-hold', array( $this, 'maybe_trigger_banner' ) );
add_action( 'woocommerce_payment_complete', array( $this, 'maybe_trigger_banner' ) );
// Once a payment is received, show prompt to connect a PayPal account on certain screens
add_action( 'admin_enqueue_scripts', array( $this, 'maybe_show_banner' ) );
add_filter( 'wc_services_pointer_post.php', array( $this, 'register_refund_pointer' ) );
}
add_filter( 'pre_option_wc_gateway_ppce_prompt_to_connect', '__return_empty_string' ); // Disable default PPEC notice.
}
}
/**
* Attach request proxying hook if it's an Express Checkout method
*/
public function request_body( $body ) {
if ( in_array( $body['METHOD'], $this->methods_to_proxy ) ) {
add_filter( 'pre_http_request', array( $this, 'proxy_request' ), 10, 3 );
} else {
remove_filter( 'pre_http_request', array( $this, 'proxy_request' ), 10, 3 );
}
return $body;
}
/**
* Reroute Express Checkout requests from the PPEC extension via WCS server to pick up API credentials
*/
public function proxy_request( $preempt, $r, $url ) {
if ( ! preg_match( '/paypal.com\/nvp$/', $url ) ) {
return $preempt;
}
$settings = wc_gateway_ppec()->settings;
return $this->api_client->proxy_request( 'paypal/nvp/' . $settings->environment, $r );
}
/**
* Limit supported payment gateway features to payments alone
*/
public function ppec_supports( $supported, $feature, $gateway ) {
return 'ppec_paypal' === $gateway->id ? 'products' === $feature : $supported;
}
/**
* Add a pointer clarifying the need to link an account before refunding payment
*/
public function register_refund_pointer( $pointers ) {
$pointers[] = array(
'id' => 'wc_services_refund_via_ppec',
'target' => '.refund-actions > button:first-child',
'options' => array(
'content' => sprintf(
'<h3>%s</h3><p>%s</p>',
__( 'Link a PayPal account', 'woocommerce-services' ),
sprintf(
wp_kses(
__( 'To issue refunds via PayPal Checkout, you will need to <a href="%s">link a PayPal account</a> with the email address that received this payment.', 'woocommerce-services' ),
array( 'a' => array( 'href' => array() ) )
),
wc_gateway_ppec()->ips->get_signup_url( wc_gateway_ppec()->settings->environment )
)
),
'position' => array(
'edge' => 'bottom',
'align' => 'top',
),
),
'delayed_opening' => array(
'show_button' => '.refund-items',
'hide_button' => '.cancel-action',
'animating_container' => '.wc-order-refund-items',
'delegation_container' => '#woocommerce-order-items',
),
);
return $pointers;
}
/**
* Trigger banner to appear based on order paid with PPEC
*/
public function maybe_trigger_banner( $order_id ) {
$order = wc_get_order( $order_id );
$payment_method = $order ? $order->get_payment_method() : false;
if ( 'ppec_paypal' === $payment_method ) {
WC_Connect_Options::update_option( 'banner_ppec', 'yes' );
}
}
/**
* Show banner if it has been triggered and if this screen is an appropriate place for it
*/
public function maybe_show_banner() {
if ( 'yes' !== WC_Connect_Options::get_option( 'banner_ppec', null ) ) {
return;
}
$screen = get_current_screen();
$order = wc_get_order();
$payment_method = $order ? $order->get_payment_method() : false;
if ( // Display if on any of these admin pages.
( // Orders list.
'shop_order' === $screen->post_type
&& 'edit' === $screen->base
)
|| ( // Edit order page.
'shop_order' === $screen->post_type
&& 'post' === $screen->base
&& 'ppec_paypal' === $payment_method
)
|| ( // WooCommerce » Settings » Payments.
'woocommerce_page_wc-settings' === $screen->base
&& isset( $_GET['tab'] ) && 'checkout' === $_GET['tab']
)
|| ( // WooCommerce » Extensions » Payments.
'woocommerce_page_wc-addons' === $screen->base
&& isset( $_GET['section'] ) && 'payment-gateways' === $_GET['section']
)
) {
wp_enqueue_style( 'wc_connect_banner' );
add_action( 'admin_notices', array( $this, 'banner' ) );
}
}
/**
* Show a NUX banner prompting the merchant to link a PayPal account
*/
public function banner() {
$this->nux->show_nux_banner(
array(
'title' => __( 'Link your PayPal account', 'woocommerce-services' ),
'description' => esc_html( __( 'Link a new or existing PayPal account to make sure future orders are marked “Processing” instead of “On hold”, and so refunds can be issued without leaving WooCommerce.', 'woocommerce-services' ) ),
'button_text' => __( 'Link account', 'woocommerce-services' ),
'button_link' => wc_gateway_ppec()->ips->get_signup_url( 'live' ),
'image_url' => plugins_url( 'images/cashier.svg', dirname( __FILE__ ) ),
'should_show_jp' => false,
'dismissible_id' => 'ppec',
)
);
}
/**
* Initialize PPEC settings to their default values
*/
public function initialize_settings() {
$settings = get_option( 'woocommerce_ppec_paypal_settings', array() );
if ( ! isset( $settings['reroute_requests'] ) ) {
$settings['reroute_requests'] = 'no';
} elseif ( 'no' === $settings['reroute_requests'] ) {
return;
} elseif ( ! isset( $settings['button_size'] ) ) { // Check if settings are initialized, represented by button_size as its absence would be first to affect the customer
$payment_gateways = WC()->payment_gateways->payment_gateways();
$gateway = $payment_gateways['ppec_paypal'];
foreach ( $gateway->form_fields as $key => $form_field ) {
if ( ! isset( $settings[ $key ] ) && isset( $form_field['default'] ) ) {
$settings[ $key ] = $form_field['default'];
}
}
}
update_option( 'woocommerce_ppec_paypal_settings', $settings );
wc_gateway_ppec()->settings->load( true );
}
/**
* Force setting values that will work when proxying requests
*/
public function adjust_settings( $settings ) {
$settings['paymentaction'] = 'sale';
return $settings;
}
/**
* Modify PPEC settings form to include a toggle (and other accommodations) for WCS request proxying
*/
public function adjust_form_fields( $form_fields ) {
$settings = wc_gateway_ppec()->settings;
// Modify form fields and descriptions depending on whether WCS request proxying is enabled
if ( 'yes' === $settings->reroute_requests ) {
$form_fields = $this->adjust_api_subject_form_field( $form_fields );
// Prevent user from changing Payment Action away from "Sale", the only option for which payments will work
$form_fields['paymentaction']['disabled'] = true;
$form_fields['paymentaction']['description'] = sprintf( __( '%s (Note that "authorizing payment only" requires linking a PayPal account.)', 'woocommerce-services' ), $form_fields['paymentaction']['description'] );
// Communicate WCS proxying and provide option to disable
$reset_link = add_query_arg(
array(
'reroute_requests' => 'no',
'nonce' => wp_create_nonce( 'reroute_requests' ),
),
wc_gateway_ppec()->get_admin_setting_link()
);
$api_creds_template = __( 'Payments will be authenticated by WooCommerce Shipping & Tax and directed to the following email address. To disable this feature and link a PayPal account, <a href="%s">click here</a>.', 'woocommerce-services' );
if ( empty( $settings->api_username ) ) {
$api_creds_text = sprintf( $api_creds_template, esc_url( add_query_arg( 'environment', 'live', $reset_link ) ) );
$form_fields['api_credentials']['description'] = $api_creds_text;
unset( $form_fields['api_username'], $form_fields['api_password'], $form_fields['api_signature'], $form_fields['api_certificate'] );
}
if ( empty( $settings->sandbox_api_username ) ) {
$api_creds_text = sprintf( $api_creds_template, esc_url( add_query_arg( 'environment', 'sandbox', $reset_link ) ) );
$form_fields['sandbox_api_credentials']['description'] = $api_creds_text;
unset( $form_fields['sandbox_api_username'], $form_fields['sandbox_api_password'], $form_fields['sandbox_api_signature'], $form_fields['sandbox_api_certificate'] );
}
} else {
// Provide option to enable request proxying
$reset_link = add_query_arg(
array(
'reroute_requests' => 'yes',
'nonce' => wp_create_nonce( 'reroute_requests' ),
),
wc_gateway_ppec()->get_admin_setting_link()
);
$api_creds_template = __( 'To authenticate payments with WooCommerce Shipping & Tax, <a href="%s">click here</a>.', 'woocommerce-services' );
if ( empty( $settings->api_username ) ) {
$api_creds_text = sprintf( $api_creds_template, esc_url( add_query_arg( 'environment', 'live', $reset_link ) ) );
$form_fields['api_credentials']['description'] .= '<br /><br />' . $api_creds_text;
}
if ( empty( $settings->sandbox_api_username ) ) {
$api_creds_text = sprintf( $api_creds_template, esc_url( add_query_arg( 'environment', 'sandbox', $reset_link ) ) );
$form_fields['sandbox_api_credentials']['description'] .= '<br /><br />' . $api_creds_text;
}
}
return $form_fields;
}
/**
* Present the "API Subject" setting in a way that's simpler, more comprehensible, and more appropriate to the way it's being used
*/
public function adjust_api_subject_form_field( $form_fields ) {
$api_subject_title = __( 'Payment Email', 'woocommerce-services' );
$form_fields['api_subject']['title'] = $api_subject_title;
$form_fields['sandbox_api_subject']['title'] = $api_subject_title;
$api_subject_description = __( 'Enter your email address at which to accept payments. You\'ll need to link your own account in order to perform anything other than "sale" transactions.', 'woocommerce-services' );
$form_fields['api_subject']['description'] = $api_subject_description;
$form_fields['sandbox_api_subject']['description'] = $api_subject_description;
$api_subject_placeholder = __( 'Required', 'woocommerce-services' );
$form_fields['api_subject']['placeholder'] = $api_subject_placeholder;
$form_fields['sandbox_api_subject']['placeholder'] = $api_subject_placeholder;
return $form_fields;
}
/**
* Handle reroute_requests setting change
*/
public function maybe_set_reroute_requests() {
if (
! isset( $_GET['page'] ) || 'wc-settings' !== $_GET['page'] ||
empty( $_GET['reroute_requests'] ) ||
empty( $_GET['nonce'] ) ||
! wp_verify_nonce( $_GET['nonce'], 'reroute_requests' ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
) {
return;
}
$settings = wc_gateway_ppec()->settings;
$settings->reroute_requests = 'yes' === $_GET['reroute_requests'] ? 'yes' : 'no';
if ( isset( $_GET['environment'] ) ) {
$settings->environment = 'sandbox' === $_GET['environment'] ? 'sandbox' : 'live';
}
$settings->save();
wp_safe_redirect( wc_gateway_ppec()->get_admin_setting_link() );
exit;
}
}
}