includes_path;
// TODO: Use spl autoload to require on-demand maybe?
require_once( $includes_path . 'class-wc-gateway-ppec-settings.php' );
require_once( $includes_path . 'class-wc-gateway-ppec-session-data.php' );
require_once( $includes_path . 'class-wc-gateway-ppec-checkout-details.php' );
require_once( $includes_path . 'class-wc-gateway-ppec-api-error.php' );
require_once( $includes_path . 'exceptions/class-wc-gateway-ppec-api-exception.php' );
require_once( $includes_path . 'exceptions/class-wc-gateway-ppec-missing-session-exception.php' );
require_once( $includes_path . 'class-wc-gateway-ppec-payment-details.php' );
require_once( $includes_path . 'class-wc-gateway-ppec-address.php' );
class WC_Gateway_PPEC_Checkout_Handler {
/**
* Cached result from self::get_checkout_defails.
*
* @since 1.2.0
*
* @var PayPal_Checkout_Details
*/
protected $_checkout_details;
public function __construct() {
add_action( 'init', array( $this, 'init' ) );
add_filter( 'the_title', array( $this, 'endpoint_page_titles' ) );
add_action( 'woocommerce_checkout_init', array( $this, 'checkout_init' ) );
add_filter( 'woocommerce_default_address_fields', array( $this, 'filter_default_address_fields' ) );
add_filter( 'woocommerce_billing_fields', array( $this, 'filter_billing_fields' ) );
add_action( 'woocommerce_checkout_process', array( $this, 'copy_checkout_details_to_post' ) );
add_action( 'wp', array( $this, 'maybe_return_from_paypal' ) );
add_action( 'wp', array( $this, 'maybe_cancel_checkout_with_paypal' ) );
add_action( 'woocommerce_cart_emptied', array( $this, 'maybe_clear_session_data' ) );
add_action( 'woocommerce_available_payment_gateways', array( $this, 'maybe_disable_other_gateways' ) );
add_action( 'woocommerce_review_order_after_submit', array( $this, 'maybe_render_cancel_link' ) );
add_action( 'woocommerce_cart_shipping_packages', array( $this, 'maybe_add_shipping_information' ) );
}
/**
* If the buyer clicked on the "Check Out with PayPal" button, we need to wait for the cart
* totals to be available. Unfortunately that doesn't happen until
* woocommerce_before_cart_totals executes, and there is already output sent to the browser by
* this point. So, to get around this issue, we'll enable output buffering to prevent WP from
* sending anything back to the browser.
*/
public function init() {
if ( version_compare( WC_VERSION, '3.3', '<' ) ) {
add_filter( 'wc_checkout_params', array( $this, 'filter_wc_checkout_params' ), 10, 1 );
} else {
add_filter( 'woocommerce_get_script_data', array( $this, 'filter_wc_checkout_params' ), 10, 2 );
}
if ( isset( $_GET['startcheckout'] ) && 'true' === $_GET['startcheckout'] ) {
ob_start();
}
}
/**
* Handle endpoint page title
* @param string $title
* @return string
*/
public function endpoint_page_titles( $title ) {
if ( ! is_admin() && is_main_query() && in_the_loop() && is_page() && is_checkout() && $this->has_active_session() ) {
$title = __( 'Confirm your PayPal order', 'woocommerce-gateway-paypal-express-checkout' );
remove_filter( 'the_title', array( $this, 'endpoint_page_titles' ) );
}
return $title;
}
/**
* If there's an active PayPal session during checkout (e.g. if the customer started checkout
* with PayPal from the cart), import billing and shipping details from PayPal using the
* token we have for the customer.
*
* Hooked to the woocommerce_checkout_init action
*
* @param WC_Checkout $checkout
*/
function checkout_init( $checkout ) {
if ( ! $this->has_active_session() ) {
return;
}
// Since we've removed the billing and shipping checkout fields, we should also remove the
// billing and shipping portion of the checkout form
remove_action( 'woocommerce_checkout_billing', array( $checkout, 'checkout_form_billing' ) );
remove_action( 'woocommerce_checkout_shipping', array( $checkout, 'checkout_form_shipping' ) );
// Lastly, let's add back in 1) displaying customer details from PayPal, 2) allow for
// account registration and 3) shipping details from PayPal
add_action( 'woocommerce_checkout_billing', array( $this, 'paypal_billing_details' ) );
add_action( 'woocommerce_checkout_billing', array( $this, 'account_registration' ) );
add_action( 'woocommerce_checkout_shipping', array( $this, 'paypal_shipping_details' ) );
}
/**
* If the cart doesn't need shipping at all, don't require the address fields
* (this is unique to PPEC). This is one of two places we need to filter fields.
* See also filter_billing_fields below.
*
* @since 1.2.1
* @since 1.5.4 Check to make sure PPEC is even enable before continuing.
* @param $fields array
*
* @return array
*/
public function filter_default_address_fields( $fields ) {
if ( 'yes' !== wc_gateway_ppec()->settings->enabled ) {
return $fields;
}
if ( ! apply_filters( 'woocommerce_paypal_express_checkout_address_not_required', ! WC_Gateway_PPEC_Plugin::needs_shipping() ) ) {
return $fields;
}
if ( method_exists( WC()->cart, 'needs_shipping' ) && ! WC()->cart->needs_shipping() ) {
$not_required_fields = array( 'address_1', 'city', 'postcode', 'country' );
foreach ( $not_required_fields as $not_required_field ) {
if ( array_key_exists( $not_required_field, $fields ) ) {
$fields[ $not_required_field ]['required'] = false;
}
}
}
// Regardless of shipping, PP doesn't have the county required (e.g. using Ireland without a county is acceptable)
if ( array_key_exists( 'state', $fields ) ) {
$fields['state']['required'] = false;
}
return $fields;
}
/**
* Since PayPal doesn't always give us the phone number for the buyer, we need to make
* that field not required. Note that core WooCommerce adds the phone field after calling
* get_default_address_fields, so the woocommerce_default_address_fields cannot
* be used to make the phone field not required.
*
* This is one of two places we need to filter fields. See also filter_default_address_fields above.
*
* @since 1.2.0
* @since 1.5.4 Check to make sure PPEC is even enable before continuing.
* @param $billing_fields array
*
* @return array
*/
public function filter_billing_fields( $billing_fields ) {
if ( 'yes' !== wc_gateway_ppec()->settings->enabled ) {
return $billing_fields;
}
$require_phone_number = wc_gateway_ppec()->settings->require_phone_number;
if ( array_key_exists( 'billing_phone', $billing_fields ) ) {
$billing_fields['billing_phone']['required'] = 'yes' === $require_phone_number;
}
return $billing_fields;
}
/**
* When an active session is present, gets (from PayPal) the buyer details
* and replaces the appropriate checkout fields in $_POST
*
* Hooked to woocommerce_checkout_process
*
* @since 1.2.0
*/
public function copy_checkout_details_to_post() {
if ( ! $this->has_active_session() ) {
return;
}
// Make sure the selected payment method is ppec_paypal
if ( ! isset( $_POST['payment_method'] ) || ( 'ppec_paypal' !== $_POST['payment_method'] ) ) {
return;
}
// Get the buyer details from PayPal
try {
$session = WC()->session->get( 'paypal' );
$token = isset( $_GET['token'] ) ? $_GET['token'] : $session->token;
$checkout_details = $this->get_checkout_details( $token );
} catch ( PayPal_API_Exception $e ) {
wc_add_notice( $e->getMessage(), 'error' );
return;
}
$shipping_details = $this->get_mapped_shipping_address( $checkout_details );
$billing_details = $this->get_mapped_billing_address( $checkout_details );
// If the billing address is empty, copy address from shipping
if ( empty( $billing_details['address_1'] ) ) {
// Set flag so that WC copies billing to shipping
$_POST['ship_to_different_address'] = 0;
$copyable_keys = array( 'address_1', 'address_2', 'city', 'state', 'postcode', 'country' );
foreach ( $copyable_keys as $copyable_key ) {
if ( array_key_exists( $copyable_key, $shipping_details ) ) {
$billing_details[ $copyable_key ] = $shipping_details[ $copyable_key ];
}
}
} else {
// Shipping may be different from billing, so set flag to not copy address from billing
$_POST['ship_to_different_address'] = 1;
}
foreach ( $shipping_details as $key => $value ) {
$_POST[ 'shipping_' . $key ] = $value;
}
foreach ( $billing_details as $key => $value ) {
$_POST[ 'billing_' . $key ] = $value;
}
}
/**
* Show billing information obtained from PayPal. This replaces the billing fields
* that the customer would ordinarily fill in. Should only happen if we have an active
* session (e.g. if the customer started checkout with PayPal from their cart.)
*
* Is hooked to woocommerce_checkout_billing action by checkout_init
*/
public function paypal_billing_details() {
$session = WC()->session->get( 'paypal' );
$token = isset( $_GET['token'] ) ? $_GET['token'] : $session->token;
try {
$checkout_details = $this->get_checkout_details( $token );
} catch ( PayPal_API_Exception $e ) {
wc_add_notice( $e->getMessage(), 'error' );
return;
}
?>