woocommerce-paypal-payments/modules/ppcp-applepay/src/Assets/ApplePayButton.php
2025-02-28 18:08:18 +01:00

1129 lines
33 KiB
PHP

<?php
/**
* The Applepay module.
*
* @package WooCommerce\PayPalCommerce\Applepay
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Applepay\Assets;
use Exception;
use Psr\Log\LoggerInterface;
use WC_Cart;
use WC_Order;
use WooCommerce\PayPalCommerce\Button\Assets\ButtonInterface;
use WooCommerce\PayPalCommerce\Button\Helper\CartProductsHelper;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
use WooCommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\Webhooks\Handler\RequestHandlerTrait;
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
/**
* Class ApplePayButton
*/
class ApplePayButton implements ButtonInterface {
use RequestHandlerTrait, ContextTrait;
/**
* The settings.
*
* @var Settings
*/
private $settings;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* The response templates.
*
* @var ResponsesToApple
*/
private $response_templates;
/**
* The old cart contents.
*
* @var array
* @psalm-suppress PropertyNotSetInConstructor
*/
private $old_cart_contents;
/**
* The method id.
*
* @var string
*/
protected $id;
/**
* The method title.
*
* @var string
*/
protected $method_title;
/**
* The processor for orders.
*
* @var OrderProcessor
*/
protected $order_processor;
/**
* Whether to reload the cart after the order is processed.
*
* @var bool
*/
protected $reload_cart = false;
/**
* The module version.
*
* @var string
*/
private $version;
/**
* The module URL.
*
* @var string
*/
private $module_url;
/**
* The data to send to the ApplePay button script.
*
* @var DataToAppleButtonScripts
*/
private $script_data;
/**
* The Settings status helper.
*
* @var SettingsStatus
*/
private $settings_status;
/**
* The cart products helper.
*
* @var CartProductsHelper
*/
protected $cart_products;
/**
* PayPalPaymentMethod constructor.
*
* @param Settings $settings The settings.
* @param LoggerInterface $logger The logger.
* @param OrderProcessor $order_processor The Order processor.
* @param string $module_url The module URL.
* @param string $version The module version.
* @param DataToAppleButtonScripts $data The data to send to the ApplePay button script.
* @param SettingsStatus $settings_status The settings status helper.
* @param CartProductsHelper $cart_products The cart products helper.
*/
public function __construct(
Settings $settings,
LoggerInterface $logger,
OrderProcessor $order_processor,
string $module_url,
string $version,
DataToAppleButtonScripts $data,
SettingsStatus $settings_status,
CartProductsHelper $cart_products
) {
$this->settings = $settings;
$this->response_templates = new ResponsesToApple();
$this->logger = $logger;
$this->id = 'applepay';
$this->method_title = __( 'Apple Pay', 'woocommerce-paypal-payments' );
$this->order_processor = $order_processor;
$this->module_url = $module_url;
$this->version = $version;
$this->script_data = $data;
$this->settings_status = $settings_status;
$this->cart_products = $cart_products;
}
/**
* Initializes the class hooks.
*/
public function initialize(): void {
add_filter( 'ppcp_onboarding_options', array( $this, 'add_apple_onboarding_option' ), 10, 1 );
add_filter(
'ppcp_partner_referrals_option',
function ( array $option ): array {
if ( $option['valid'] ) {
return $option;
}
if ( $option['field'] === 'ppcp-onboarding-apple' ) {
$option['valid'] = true;
$option['value'] = ( $option['value'] ? '1' : '' );
}
return $option;
}
);
add_filter(
'ppcp_partner_referrals_data',
function ( array $data ): array {
try {
$onboard_with_apple = $this->settings->get( 'ppcp-onboarding-apple' );
if ( $onboard_with_apple !== '1' ) {
return $data;
}
} catch ( NotFoundException $exception ) {
return $data;
}
if ( in_array( 'PPCP', $data['products'], true ) ) {
$data['products'][] = 'PAYMENT_METHODS';
} elseif ( in_array( 'EXPRESS_CHECKOUT', $data['products'], true ) ) {
$data['products'][0] = 'PAYMENT_METHODS';
}
$data['capabilities'][] = 'APPLE_PAY';
return $data;
}
);
}
/**
* Adds the ApplePay onboarding option.
*
* @param string $options The options.
*
* @return string
*/
public function add_apple_onboarding_option( $options ): string {
if ( ! apply_filters( 'woocommerce_paypal_payments_apple_pay_onboarding_option', false ) ) {
return $options;
}
$checked = '';
try {
$onboard_with_apple = $this->settings->get( 'ppcp-onboarding-apple' );
if ( $onboard_with_apple === '1' ) {
$checked = 'checked';
}
} catch ( NotFoundException $exception ) {
$checked = '';
}
return $options . '<li><label><input type="checkbox" id="ppcp-onboarding-apple" ' . $checked . ' data-onboarding-option="ppcp-onboarding-apple"> ' .
__( 'Onboard with ApplePay', 'woocommerce-paypal-payments' ) . '
</label></li>';
}
/**
* Adds all the Ajax actions to perform the whole workflow
*/
public function bootstrap_ajax_request(): void {
add_action(
'wp_ajax_' . PropertiesDictionary::VALIDATE,
array( $this, 'validate' )
);
add_action(
'wp_ajax_nopriv_' . PropertiesDictionary::VALIDATE,
array( $this, 'validate' )
);
add_action(
'wp_ajax_' . PropertiesDictionary::CREATE_ORDER,
array( $this, 'create_wc_order' )
);
add_action(
'wp_ajax_nopriv_' . PropertiesDictionary::CREATE_ORDER,
array( $this, 'create_wc_order' )
);
add_action(
'wp_ajax_' . PropertiesDictionary::UPDATE_SHIPPING_CONTACT,
array( $this, 'update_shipping_contact' )
);
add_action(
'wp_ajax_nopriv_' . PropertiesDictionary::UPDATE_SHIPPING_CONTACT,
array( $this, 'update_shipping_contact' )
);
add_action(
'wp_ajax_' . PropertiesDictionary::UPDATE_SHIPPING_METHOD,
array( $this, 'update_shipping_method' )
);
add_action(
'wp_ajax_nopriv_' . PropertiesDictionary::UPDATE_SHIPPING_METHOD,
array( $this, 'update_shipping_method' )
);
}
/**
* Method to validate the merchant in the db flag
* On fail triggers and option that shows an admin notice showing the error
* On success removes such flag
*/
public function validate(): void {
$applepay_request_data_object = $this->applepay_data_object_http();
if ( ! $this->is_nonce_valid() ) {
return;
}
$applepay_request_data_object->validation_data();
$settings = $this->settings;
$settings->set( 'applepay_validated', $applepay_request_data_object->validated_flag() );
$settings->persist();
wp_send_json_success();
}
/**
* Method to validate and update the shipping contact of the user
* It updates the amount paying information if needed
* On error returns an array of errors to be handled by the script
* On success returns the new contact data
*/
public function update_shipping_contact(): void {
$applepay_request_data_object = $this->applepay_data_object_http();
if ( ! $this->is_nonce_valid() ) {
return;
}
$applepay_request_data_object->update_contact_data();
if ( $applepay_request_data_object->has_errors() ) {
$this->response_templates->response_with_data_errors( $applepay_request_data_object->errors() );
return;
}
if ( ! class_exists( 'WC_Countries' ) ) {
return;
}
$countries = $this->create_wc_countries();
$allowed_selling_countries = $countries->get_allowed_countries();
$allowed_shipping_countries = $countries->get_shipping_countries();
$user_country = $applepay_request_data_object->simplified_contact()['country'];
$is_allowed_selling_country = array_key_exists(
$user_country,
$allowed_selling_countries
);
$is_allowed_shipping_country = array_key_exists(
$user_country,
$allowed_shipping_countries
);
if ( ! $is_allowed_selling_country ) {
$this->response_templates->response_with_data_errors(
array( array( 'errorCode' => 'addressUnserviceable' ) )
);
return;
}
if ( $applepay_request_data_object->need_shipping() && ! $is_allowed_shipping_country ) {
$this->response_templates->response_with_data_errors(
array( array( 'errorCode' => 'addressUnserviceable' ) )
);
return;
}
try {
$payment_details = $this->which_calculate_totals( $applepay_request_data_object );
if ( ! is_array( $payment_details ) ) {
$this->response_templates->response_with_data_errors(
array(
array(
'errorCode' => 'addressUnserviceable',
'message' => __( 'Error processing cart', 'woocommerce-paypal-payments' ),
),
)
);
return;
}
$response = $this->response_templates->apple_formatted_response( $payment_details );
$this->response_templates->response_success( $response );
} catch ( Exception $e ) {
$this->response_templates->response_with_data_errors(
array(
array(
'errorCode' => 'addressUnserviceable',
'message' => $e->getMessage(),
),
)
);
}
}
/**
* Method to validate and update the shipping method selected by the user
* It updates the amount paying information if needed
* On error returns an array of errors to be handled by the script
* On success returns the new contact data
*/
public function update_shipping_method(): void {
$applepay_request_data_object = $this->applepay_data_object_http();
if ( ! $this->is_nonce_valid() ) {
return;
}
$applepay_request_data_object->update_method_data();
if ( $applepay_request_data_object->has_errors() ) {
$this->response_templates->response_with_data_errors( $applepay_request_data_object->errors() );
}
try {
$payment_details = $this->which_calculate_totals( $applepay_request_data_object );
if ( ! is_array( $payment_details ) ) {
$this->response_templates->response_with_data_errors(
array(
array(
'errorCode' => 'addressUnserviceable',
'message' => __( 'Error processing cart', 'woocommerce-paypal-payments' ),
),
)
);
return;
}
$response = $this->response_templates->apple_formatted_response( $payment_details );
$this->response_templates->response_success( $response );
} catch ( Exception $e ) {
$this->response_templates->response_with_data_errors(
array(
array(
'errorCode' => 'addressUnserviceable',
'message' => $e->getMessage(),
),
)
);
}
}
/**
* Method to create a WC order from the data received from the ApplePay JS
* On error returns an array of errors to be handled by the script
* On success returns the new order data
*
* @throws Exception When validation fails.
*/
public function create_wc_order(): void {
$applepay_request_data_object = $this->applepay_data_object_http();
//phpcs:disable WordPress.Security.NonceVerification
$context = wc_clean( wp_unslash( $_POST['caller_page'] ?? '' ) );
if ( ! is_string( $context ) ) {
$this->response_templates->response_with_data_errors(
array(
array(
'errorCode' => 'unableToProcess',
'message' => 'Unable to process the order',
),
)
);
return;
}
$applepay_request_data_object->order_data( $context );
$this->update_posted_data( $applepay_request_data_object );
if ( $context === 'product' ) {
$cart_item_key = $this->prepare_cart( $applepay_request_data_object );
$cart = WC()->cart;
$address = $applepay_request_data_object->shipping_address();
$this->calculate_totals_single_product(
$cart,
$address,
$applepay_request_data_object->shipping_method()
);
if ( ! $cart_item_key ) {
$this->response_templates->response_with_data_errors(
array(
array(
'errorCode' => 'unableToProcess',
'message' => 'Unable to process the order',
),
)
);
} else {
add_filter(
'woocommerce_payment_successful_result',
function ( array $result ) use ( $cart, $cart_item_key ) : array {
$this->clear_current_cart( $cart, $cart_item_key );
$this->reload_cart( $cart );
return $result;
}
);
}
}
WC()->checkout()->process_checkout();
}
/**
* Checks if the nonce in the data object is valid
*
* @return bool
*/
protected function is_nonce_valid(): bool {
$nonce = filter_input( INPUT_POST, 'woocommerce-process-checkout-nonce', FILTER_SANITIZE_SPECIAL_CHARS );
if ( ! $nonce ) {
return false;
}
// Return value 1 indicates "valid nonce, generated in past 12 hours".
// Return value 2 also indicated valid nonce, but older than 12 hours.
return 1 === wp_verify_nonce(
$nonce,
'woocommerce-process_checkout'
);
}
/**
* Data Object to collect and validate all needed data collected
* through HTTP
*/
protected function applepay_data_object_http(): ApplePayDataObjectHttp {
return new ApplePayDataObjectHttp( $this->logger );
}
/**
* Returns a WC_Countries instance to check shipping
*
* @return \WC_Countries
*/
protected function create_wc_countries(): \WC_Countries {
return new \WC_Countries();
}
/**
* Selector between product detail and cart page calculations
*
* @param ApplePayDataObjectHttp $applepay_request_data_object The data object.
*
* @return array|bool
* @throws Exception If cannot be added to cart.
*/
protected function which_calculate_totals(
$applepay_request_data_object
) {
$address = empty( $applepay_request_data_object->shipping_address() ) ? $applepay_request_data_object->simplified_contact() : $applepay_request_data_object->shipping_address();
if ( $applepay_request_data_object->caller_page() === 'productDetail' ) {
$cart_item_key = $this->prepare_cart( $applepay_request_data_object );
$cart = WC()->cart;
$totals = $this->calculate_totals_single_product(
$cart,
$address,
$applepay_request_data_object->shipping_method()
);
if ( $cart_item_key ) {
$this->clear_current_cart( $cart, $cart_item_key );
$this->reload_cart( $cart );
}
return $totals;
}
if ( $applepay_request_data_object->caller_page() === 'cart' ) {
return $this->calculate_totals_cart_page(
$address,
$applepay_request_data_object->shipping_method()
);
}
return false;
}
/**
* Calculates totals for the product with the given information
* Saves the previous cart to reload it after calculations
* If no shippingMethodId provided will return the first available shipping
* method
*
* @param WC_Cart $cart The cart.
* @param array $customer_address customer address to use.
* @param array|null $shipping_method shipping method to use.
*/
protected function calculate_totals_single_product(
$cart,
$customer_address,
$shipping_method = null
): array {
$results = array();
try {
// I just care about apple address details.
$shipping_method_id = '';
$shipping_methods_array = array();
$selected_shipping_method = array();
$this->customer_address( $customer_address );
if ( $shipping_method ) {
$shipping_method_id = $shipping_method['identifier'];
WC()->session->set(
'chosen_shipping_methods',
array( $shipping_method_id )
);
}
if ( $cart->needs_shipping() ) {
list(
$shipping_methods_array, $selected_shipping_method
) = $this->cart_shipping_methods(
$cart,
$customer_address,
$shipping_method,
$shipping_method_id
);
}
$cart->calculate_shipping();
$cart->calculate_fees();
$cart->calculate_totals();
$results = $this->cart_calculation_results(
$cart,
$selected_shipping_method,
$shipping_methods_array
);
} catch ( Exception $exception ) {
return array();
}
return $results;
}
/**
* Sets the customer address with ApplePay details to perform correct
* calculations
* If no parameter passed then it resets the customer to shop details
*
* @param array $address customer address.
*/
protected function customer_address( array $address = array() ): void {
$base_location = wc_get_base_location();
$shop_country_code = $base_location['country'];
WC()->customer->set_shipping_country(
$address['country'] ?? $shop_country_code
);
WC()->customer->set_billing_country(
$address['country'] ?? $shop_country_code
);
WC()->customer->set_shipping_postcode(
$address['postcode'] ?? ''
);
WC()->customer->set_shipping_city(
$address['city'] ?? ''
);
}
/**
* Add shipping methods to cart to perform correct calculations
*
* @param WC_Cart $cart WC Cart instance.
* @param array $customer_address Customer address.
* @param array|null $shipping_method Shipping method.
* @param string $shipping_method_id Shipping method id.
*/
protected function cart_shipping_methods(
$cart,
$customer_address,
$shipping_method = null,
$shipping_method_id = ''
): array {
$shipping_methods_array = array();
/**
* The argument is defined only in docblock.
*
* @psalm-suppress InvalidScalarArgument
*/
$shipping_methods = WC()->shipping->calculate_shipping(
$this->getShippingPackages(
$customer_address,
$cart->get_total( 'edit' )
)
);
$done = false;
foreach ( $shipping_methods[0]['rates'] as $rate ) {
$shipping_methods_array[] = array(
'label' => $rate->get_label(),
'detail' => '',
'amount' => $rate->get_cost(),
'identifier' => $rate->get_id(),
);
if ( ! $done ) {
$done = true;
$shipping_method_id = $shipping_method ? $shipping_method_id
: $rate->get_id();
WC()->session->set(
'chosen_shipping_methods',
array( $shipping_method_id )
);
}
}
$selected_shipping_method = $shipping_methods_array[0];
if ( $shipping_method ) {
$selected_shipping_method = $shipping_method;
}
return array( $shipping_methods_array, $selected_shipping_method );
}
/**
* Sets shipping packages for correct calculations
*
* @param array $customer_address ApplePay address details.
* @param float $total Total amount of the cart.
*
* @return mixed|void|null
*/
protected function getShippingPackages( $customer_address, $total ) {
// Packages array for storing 'carts'.
$packages = array();
$packages[0]['contents'] = WC()->cart->cart_contents;
$packages[0]['contents_cost'] = $total;
$packages[0]['applied_coupons'] = WC()->session->applied_coupon;
$packages[0]['destination']['country'] = $customer_address['country'] ?? '';
$packages[0]['destination']['state'] = '';
$packages[0]['destination']['postcode'] = $customer_address['postcode'] ?? '';
$packages[0]['destination']['city'] = $customer_address['city'] ?? '';
$packages[0]['destination']['address'] = '';
$packages[0]['destination']['address_2'] = '';
return apply_filters( 'woocommerce_cart_shipping_packages', $packages );
}
/**
* Returns the formatted results of the cart calculations
*
* @param WC_Cart $cart WC Cart object.
* @param array $selected_shipping_method Selected shipping method.
* @param array $shipping_methods_array Shipping methods array.
*/
protected function cart_calculation_results(
$cart,
$selected_shipping_method,
$shipping_methods_array
): array {
$total = (float) $cart->get_total( 'edit' );
$total = round( $total, 2 );
return array(
'subtotal' => $cart->get_subtotal(),
'shipping' => array(
'amount' => $cart->needs_shipping()
? $cart->get_shipping_total() : null,
'label' => $cart->needs_shipping()
? $selected_shipping_method['label'] : null,
),
'shippingMethods' => $cart->needs_shipping()
? $shipping_methods_array : null,
'taxes' => $cart->get_total_tax(),
'total' => $total,
);
}
/**
* Calculates totals for the cart page with the given information
* If no shippingMethodId provided will return the first available shipping
* method
*
* @param array $customer_address The customer address.
* @param array|null $shipping_method The shipping method.
*/
protected function calculate_totals_cart_page(
array $customer_address,
$shipping_method = null
): array {
$results = array();
if ( WC()->cart->is_empty() ) {
return array();
}
try {
$shipping_methods_array = array();
$selected_shipping_method = array();
// I just care about apple address details.
$this->customer_address( $customer_address );
$cart = WC()->cart;
if ( $shipping_method ) {
WC()->session->set(
'chosen_shipping_methods',
array( $shipping_method['identifier'] )
);
}
if ( $cart->needs_shipping() ) {
$shipping_method_id = $shipping_method['identifier'] ?? '';
list(
$shipping_methods_array, $selected_shipping_method
) = $this->cart_shipping_methods(
$cart,
$customer_address,
$shipping_method,
$shipping_method_id
);
}
$cart->calculate_shipping();
$cart->calculate_fees();
$cart->calculate_totals();
$results = $this->cart_calculation_results(
$cart,
$selected_shipping_method,
$shipping_methods_array
);
$this->customer_address();
} catch ( Exception $e ) {
return array();
}
return $results;
}
/**
* Empty the cart to use for calculations
* while saving its contents in a field
*/
protected function save_old_cart(): void {
$cart = WC()->cart;
if ( $cart->is_empty() ) {
return;
}
$this->old_cart_contents = $cart->get_cart_contents();
foreach ( $this->old_cart_contents as $cart_item_key => $value ) {
$cart->remove_cart_item( $cart_item_key );
}
$this->reload_cart = true;
}
/**
* Reloads the previous cart contents
*
* @param WC_Cart $cart The cart to reload.
*/
protected function reload_cart( WC_Cart $cart ): void {
if ( ! $this->reload_cart ) {
return;
}
foreach ( $this->old_cart_contents as $cart_item_key => $value ) {
$cart->restore_cart_item( $cart_item_key );
}
}
/**
* Clear the current cart
*
* @param WC_Cart|null $cart The cart object.
* @param string $cart_item_key The cart item key.
* @return void
*/
public function clear_current_cart( ?WC_Cart $cart, string $cart_item_key ): void {
if ( ! $cart ) {
return;
}
$cart->remove_cart_item( $cart_item_key );
$this->customer_address();
}
/**
* Removes the old cart, saves it, and creates a new one
*
* @throws Exception If it cannot be added to cart.
* @param ApplePayDataObjectHttp $applepay_request_data_object The request data object.
* @return string The cart item key after adding to the new cart.
*/
public function prepare_cart( ApplePayDataObjectHttp $applepay_request_data_object ): string {
$this->save_old_cart();
$this->cart_products->set_cart( WC()->cart );
$product = $this->cart_products->product_from_data(
array(
'id' => (int) $applepay_request_data_object->product_id(),
'quantity' => (int) $applepay_request_data_object->product_quantity(),
'variations' => $applepay_request_data_object->product_variations(),
'extra' => $applepay_request_data_object->product_extra(),
'booking' => $applepay_request_data_object->product_booking(),
)
);
$this->cart_products->add_products( array( $product ) );
return $this->cart_products->cart_item_keys()[0] ?? '';
}
/**
* Update the posted data to match the Apple Pay request data
*
* @param ApplePayDataObjectHttp $applepay_request_data_object The Apple Pay request data.
*/
protected function update_posted_data( $applepay_request_data_object ): void {
// TODO : get checkout form data in here to fill more fields like: ensure billing email and phone are filled.
add_filter(
'woocommerce_checkout_posted_data',
function ( array $data ) use ( $applepay_request_data_object ): array {
$data['payment_method'] = 'ppcp-gateway';
$data['shipping_method'] = $applepay_request_data_object->shipping_method();
$data['billing_first_name'] = $applepay_request_data_object->billing_address()['first_name'] ?? '';
$data['billing_last_name'] = $applepay_request_data_object->billing_address()['last_name'] ?? '';
$data['billing_company'] = $applepay_request_data_object->billing_address()['company'] ?? '';
$data['billing_country'] = $applepay_request_data_object->billing_address()['country'] ?? '';
$data['billing_address_1'] = $applepay_request_data_object->billing_address()['address_1'] ?? '';
$data['billing_address_2'] = $applepay_request_data_object->billing_address()['address_2'] ?? '';
$data['billing_city'] = $applepay_request_data_object->billing_address()['city'] ?? '';
$data['billing_state'] = $applepay_request_data_object->billing_address()['state'] ?? '';
$data['billing_postcode'] = $applepay_request_data_object->billing_address()['postcode'] ?? '';
$data['billing_email'] = $applepay_request_data_object->billing_address()['email'] ?? '';
$data['billing_phone'] = $applepay_request_data_object->billing_address()['phone'] ?? '';
// ApplePay doesn't send us a billing email or phone, use the shipping contacts instead.
if ( ! ( $data['billing_email'] ?? false ) ) {
$data['billing_email'] = $applepay_request_data_object->shipping_address()['email'] ?? '';
}
if ( ! ( $data['billing_phone'] ?? false ) ) {
$data['billing_phone'] = $applepay_request_data_object->shipping_address()['phone'] ?? '';
}
if ( ! empty( $applepay_request_data_object->shipping_method() ) ) {
$data['shipping_first_name'] = $applepay_request_data_object->shipping_address()['first_name'] ?? '';
$data['shipping_last_name'] = $applepay_request_data_object->shipping_address()['last_name'] ?? '';
$data['shipping_company'] = $applepay_request_data_object->shipping_address()['company'] ?? '';
$data['shipping_country'] = $applepay_request_data_object->shipping_address()['country'] ?? '';
$data['shipping_address_1'] = $applepay_request_data_object->shipping_address()['address_1'] ?? '';
$data['shipping_address_2'] = $applepay_request_data_object->shipping_address()['address_2'] ?? '';
$data['shipping_city'] = $applepay_request_data_object->shipping_address()['city'] ?? '';
$data['shipping_state'] = $applepay_request_data_object->shipping_address()['state'] ?? '';
$data['shipping_postcode'] = $applepay_request_data_object->shipping_address()['postcode'] ?? '';
$data['shipping_email'] = $applepay_request_data_object->shipping_address()['email'] ?? '';
$data['shipping_phone'] = $applepay_request_data_object->shipping_address()['phone'] ?? '';
}
return $data;
}
);
}
/**
* Renders the Apple Pay button on the page
*
* @return bool
*
* @psalm-suppress RedundantCondition
*/
public function render(): bool {
if ( ! $this->is_enabled() ) {
return true;
}
$button_enabled_product = $this->settings_status->is_smart_button_enabled_for_location( 'product' );
$button_enabled_cart = $this->settings_status->is_smart_button_enabled_for_location( 'cart' );
$button_enabled_checkout = true;
$button_enabled_payorder = true;
$button_enabled_minicart = $this->settings_status->is_smart_button_enabled_for_location( 'mini-cart' );
add_filter(
'woocommerce_paypal_payments_sdk_components_hook',
function( array $components ) {
$components[] = 'applepay';
return $components;
}
);
if ( $button_enabled_product ) {
$default_hookname = 'woocommerce_paypal_payments_single_product_button_render';
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_applepay_render_hook_product', $default_hookname );
$render_placeholder = is_string( $render_placeholder ) ? $render_placeholder : $default_hookname;
add_action(
$render_placeholder,
function () {
$this->applepay_button();
}
);
}
if ( $button_enabled_cart ) {
$default_hook_name = 'woocommerce_paypal_payments_cart_button_render';
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_applepay_cart_button_render_hook', $default_hook_name );
$render_placeholder = is_string( $render_placeholder ) ? $render_placeholder : $default_hook_name;
add_action(
$render_placeholder,
function () {
$this->applepay_button();
}
);
}
if ( $button_enabled_checkout ) {
$default_hook_name = 'woocommerce_paypal_payments_checkout_button_render';
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_applepay_checkout_button_render_hook', $default_hook_name );
$render_placeholder = is_string( $render_placeholder ) ? $render_placeholder : $default_hook_name;
add_action(
$render_placeholder,
function () {
$this->applepay_button();
$this->hide_gateway_until_eligible();
},
21
);
}
if ( $button_enabled_payorder ) {
$default_hook_name = 'woocommerce_paypal_payments_payorder_button_render';
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_applepay_payorder_button_render_hook', $default_hook_name );
$render_placeholder = is_string( $render_placeholder ) ? $render_placeholder : $default_hook_name;
add_action(
$render_placeholder,
function () {
$this->applepay_button();
$this->hide_gateway_until_eligible();
},
21
);
}
if ( $button_enabled_minicart ) {
$default_hook_name = 'woocommerce_paypal_payments_minicart_button_render';
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_applepay_minicart_button_render_hook', $default_hook_name );
$render_placeholder = is_string( $render_placeholder ) ? $render_placeholder : $default_hook_name;
add_action(
$render_placeholder,
function () {
echo '<span id="ppc-button-applepay-container-minicart" class="ppcp-button-apm ppcp-button-applepay ppcp-button-minicart"></span>';
},
21
);
}
return true;
}
/**
* ApplePay button markup
*/
protected function applepay_button(): void {
?>
<div id="ppc-button-applepay-container" class="ppcp-button-apm ppcp-button-applepay">
<?php wp_nonce_field( 'woocommerce-process_checkout', 'woocommerce-process-checkout-nonce' ); ?>
</div>
<?php
}
/**
* Outputs an inline CSS style that hides the Apple Pay gateway (on Classic Checkout).
* The style is removed by `ApplepayButton.js` once the eligibility of the payment method
* is confirmed.
*
* @return void
*/
protected function hide_gateway_until_eligible(): void {
?>
<style data-hide-gateway="ppcp-applepay">.wc_payment_method.payment_method_ppcp-applepay{display:none}</style>
<?php
}
/**
* Enqueues the scripts.
*
* @return void
*/
public function enqueue(): void {
if ( ! $this->is_enabled() ) {
return;
}
wp_register_script(
'wc-ppcp-applepay',
untrailingslashit( $this->module_url ) . '/assets/js/boot.js',
array(),
$this->version,
true
);
wp_enqueue_script( 'wc-ppcp-applepay' );
$this->enqueue_styles();
wp_localize_script(
'wc-ppcp-applepay',
'wc_ppcp_applepay',
$this->script_data()
);
add_action(
'wp_enqueue_scripts',
function () {
wp_enqueue_script( 'wc-ppcp-applepay' );
}
);
}
/**
* Enqueues styles.
*/
public function enqueue_styles(): void {
if ( ! $this->is_enabled() ) {
return;
}
wp_register_style(
'wc-ppcp-applepay',
untrailingslashit( $this->module_url ) . '/assets/css/styles.css',
array(),
$this->version
);
wp_enqueue_style( 'wc-ppcp-applepay' );
}
/**
* Enqueues scripts for admin.
*/
public function enqueue_admin(): void {
wp_register_script(
'wc-ppcp-applepay-admin',
untrailingslashit( $this->module_url ) . '/assets/js/boot-admin.js',
array(),
$this->version,
true
);
wp_enqueue_script( 'wc-ppcp-applepay-admin' );
wp_localize_script(
'wc-ppcp-applepay-admin',
'wc_ppcp_applepay_admin',
$this->script_data_for_admin()
);
}
/**
* Enqueues styles for admin.
*/
public function enqueue_admin_styles(): void {
wp_register_style(
'wc-ppcp-applepay-admin',
untrailingslashit( $this->module_url ) . '/assets/css/styles.css',
array(),
$this->version
);
wp_enqueue_style( 'wc-ppcp-applepay-admin' );
}
/**
* Returns the script data.
*
* @return array
*/
public function script_data(): array {
return $this->script_data->apple_pay_script_data();
}
/**
* Returns the admin script data.
*
* @return array
*/
public function script_data_for_admin(): array {
return $this->script_data->apple_pay_script_data_for_admin();
}
/**
* Returns true if the module is enabled.
*
* @return bool
*/
public function is_enabled(): bool {
try {
return $this->settings->has( 'applepay_button_enabled' ) && $this->settings->get( 'applepay_button_enabled' );
} catch ( Exception $e ) {
return false;
}
}
}