Add apple transaction in product before auth

This commit is contained in:
carmenmaymo 2023-07-31 17:08:42 +02:00
parent 2d8e7576bc
commit 3ae22b3356
No known key found for this signature in database
GPG key ID: 6023F686B0F3102E
10 changed files with 1041 additions and 74 deletions

View file

@ -0,0 +1,10 @@
export function createAppleErrors(errors) {
const errorList = []
for (const error of errors) {
const {contactField = null, code = null, message = null} = error
const appleError = contactField ? new ApplePayError(code, contactField, message) : new ApplePayError(code)
errorList.push(appleError)
}
return errorList
}

View file

@ -0,0 +1,22 @@
export const request = (countryCode, currencyCode, totalLabel, subtotal) => {
return {
countryCode: countryCode,
currencyCode: currencyCode,
supportedNetworks: ['amex', 'maestro', 'masterCard', 'visa', 'vPay'],
merchantCapabilities: ['supports3DS'],
shippingType: 'shipping',
requiredBillingContactFields: [
'postalAddress',
'email'
],
requiredShippingContactFields: [
'postalAddress',
'email'
],
total: {
label: totalLabel,
amount: subtotal,
type: 'final'
}
}
}

View file

@ -0,0 +1,259 @@
//import {useEffect, useState} from '@wordpress/element';
import {createAppleErrors} from './Helper/applePayError.js';
import {maybeShowButton} from './Helper/maybeShowApplePayButton.js';
import {request} from './Helper/applePayRequest.js';
import {buttonID, endpoints} from "./Helper/utils";
import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading'
(
function ({ wc_ppcp_applepay, jQuery}) {
document.addEventListener(
'DOMContentLoaded',
() => {
if (PayPalCommerceGateway) {
let bootstrapped = false;
const {product: {id, needShipping = true, isVariation = false, price, stock}, shop: {countryCode, currencyCode = 'EUR', totalLabel = ''}, ajaxUrl} = wc_ppcp_applepay
if (!id || !price || !countryCode || !ajaxUrl) {
return
}
let outOfStock = stock === 'outofstock'
if(outOfStock || !maybeShowButton()){
return;
}
const nonce = document.getElementById('woocommerce-process-checkout-nonce').value
let productId = id
let productQuantity = 1
let updatedContactInfo = []
let selectedShippingMethod = []
let redirectionUrl = ''
document.querySelector('input.qty').addEventListener('change', event => {
productQuantity = event.currentTarget.value
})
function disableButton(appleButton) {
appleButton.disabled = true;
appleButton.classList.add("buttonDisabled");
}
function enableButton(appleButton) {
appleButton.disabled = false;
appleButton.classList.remove("buttonDisabled");
}
if (isVariation) {
let appleButton = document.querySelector('#applepay-container');
jQuery('.single_variation_wrap').on('hide_variation', function (event, variation) {
disableButton(appleButton);
return;
});
jQuery('.single_variation_wrap').on('show_variation', function (event, variation) {
// Fired when the user selects all the required dropdowns / attributes
// and a final variation is selected / shown
if (!variation.is_in_stock) {
disableButton(appleButton);
return;
}
if (variation.variation_id) {
productId = variation.variation_id
}
enableButton(appleButton);
});
disableButton(appleButton);
}
const amountWithoutTax = productQuantity * price
loadPaypalScript(PayPalCommerceGateway, () => {
bootstrapped = true;
//print the button
const applepay = paypal.Applepay();
applepay.config()
.then(applepayConfig => {
const appleContainer = document.getElementById("applepay-container");
if (applepayConfig.isEligible) {
appleContainer.innerHTML = '<apple-pay-button id="btn-appl"buttonstyle="black" type="buy" locale="en">';
//handle transaction
const paymentRequest = {
countryCode: applepayConfig.countryCode,
merchantCapabilities:
applepayConfig.merchantCapabilities,
supportedNetworks: applepayConfig.supportedNetworks,
currencyCode: currencyCode,
requiredShippingContactFields: ["name", "phone",
"email", "postalAddress"],
requiredBillingContactFields: ["name", "phone", "email",
"postalAddress"],
total: {
label: totalLabel,
type: "final",
amount: amountWithoutTax,
}}
console.log(paymentRequest)
console.log(wc_ppcp_applepay)
let applePaySession = () => {
const session = new ApplePaySession(4, paymentRequest)
session.begin()
if(needShipping){
session.onshippingmethodselected = function (event) {
jQuery.ajax({
url: ajaxUrl,
method: 'POST',
data: {
action: 'ppcp_update_shipping_method',
shipping_method: event.shippingMethod,
product_id: productId,
caller_page: 'productDetail',
product_quantity: productQuantity,
simplified_contact: updatedContactInfo,
'woocommerce-process-checkout-nonce': nonce,
},
complete: (jqXHR, textStatus) => {
},
success: (applePayShippingMethodUpdate, textStatus, jqXHR) => {
let response = applePayShippingMethodUpdate.data
console.log('onshippingmethod', response)
selectedShippingMethod = event.shippingMethod
console.log(selectedShippingMethod)
//order the response shipping methods, so that the selected shipping method is the first one
let orderedShippingMethods = response.newShippingMethods.sort((a, b) => {
if (a.label === selectedShippingMethod.label) {
return -1
}
return 1
})
//update the response.newShippingMethods with the ordered shipping methods
response.newShippingMethods = orderedShippingMethods
if (applePayShippingMethodUpdate.success === false) {
response.errors = createAppleErrors(response.errors)
}
this.completeShippingMethodSelection(response)
},
error: (jqXHR, textStatus, errorThrown) => {
console.warn(textStatus, errorThrown)
session.abort()
},
})
}
session.onshippingcontactselected = function (event) {
jQuery.ajax({
url: ajaxUrl,
method: 'POST',
data: {
action: 'ppcp_update_shipping_contact',
product_id: productId,
caller_page: 'productDetail',
product_quantity: productQuantity,
simplified_contact: event.shippingContact,
need_shipping: needShipping,
'woocommerce-process-checkout-nonce': nonce,
},
complete: (jqXHR, textStatus) => {
},
success: (applePayShippingContactUpdate, textStatus, jqXHR) => {
let response = applePayShippingContactUpdate.data
updatedContactInfo = event.shippingContact
console.log('onshippingcontact', response)
if (applePayShippingContactUpdate.success === false) {
response.errors = createAppleErrors(response.errors)
}
if (response.newShippingMethods) {
selectedShippingMethod = response.newShippingMethods[0]
}
this.completeShippingContactSelection(response)
},
error: (jqXHR, textStatus, errorThrown) => {
console.warn(textStatus, errorThrown)
session.abort()
},
})
}
}
session.onvalidatemerchant = (applePayValidateMerchantEvent) => {
applepay.validateMerchant({
validationUrl: applePayValidateMerchantEvent.validationURL
})
.then(validateResult => {
session.completeMerchantValidation(validateResult.merchantSession);
console.log('validated')
})
.catch(validateError => {
console.error(validateError);
session.abort();
});
};
/*session.onpaymentauthorized = (ApplePayPayment) => {
const {billingContact, shippingContact } = ApplePayPayment.payment
jQuery.ajax({
url: ajaxUrl,
method: 'POST',
data: {
action: 'mollie_apple_pay_create_order',
productId: productId,
productQuantity: productQuantity,
shippingContact: ApplePayPayment.payment.shippingContact,
billingContact: ApplePayPayment.payment.billingContact,
token: ApplePayPayment.payment.token,
shippingMethod: selectedShippingMethod,
'mollie-payments-for-woocommerce_issuer_applepay': 'applepay',
'woocommerce-process-checkout-nonce': nonce,
'billing_first_name': billingContact.givenName || '',
'billing_last_name' : billingContact.familyName || '',
'billing_company': '',
'billing_country' : billingContact.countryCode || '',
'billing_address_1' : billingContact.addressLines[0] || '',
'billing_address_2' : billingContact.addressLines[1] || '',
'billing_postcode' : billingContact.postalCode || '',
'billing_city': billingContact.locality || '',
'billing_state' : billingContact.administrativeArea || '',
'billing_phone' : billingContact.phoneNumber || '000000000000',
'billing_email' : shippingContact.emailAddress || '',
'shipping_first_name': shippingContact.givenName || '',
'shipping_last_name' : shippingContact.familyName || '',
'shipping_company': '',
'shipping_country' : shippingContact.countryCode || '',
'shipping_address_1' : shippingContact.addressLines[0] || '',
'shipping_address_2' : shippingContact.addressLines[1] || '',
'shipping_postcode' : shippingContact.postalCode || '',
'shipping_city': shippingContact.locality || '',
'shipping_state' : shippingContact.administrativeArea || '',
'shipping_phone' : shippingContact.phoneNumber || '000000000000',
'shipping_email' : shippingContact.emailAddress || '',
'order_comments' : '',
'payment_method' : 'mollie_wc_gateway_applepay',
'_wp_http_referer' : '/?wc-ajax=update_order_review'
},
complete: (jqXHR, textStatus) => {
},
success: (authorizationResult, textStatus, jqXHR) => {
let result = authorizationResult.data
if (authorizationResult.success === true) {
redirectionUrl = result['returnUrl'];
session.completePayment(result['responseToApple'])
window.location.href = redirectionUrl
} else {
result.errors = createAppleErrors(result.errors)
session.completePayment(result)
}
},
error: (jqXHR, textStatus, errorThrown) => {
console.warn(textStatus, errorThrown)
session.abort()
},
})
}*/
}
document.querySelector('#btn-appl').addEventListener('click', (evt) => {
evt.preventDefault()
applePaySession()
})
} })
.catch(applepayConfigError => {
console.error(applepayConfigError)
console.error('Error while fetching Apple Pay configuration.');
});
});
}
})
}
)(window)

View file

@ -40,8 +40,9 @@ return array(
},
'applepay.payment_method' => static function ( ContainerInterface $container ): ApplepayPaymentMethod {
$settings = $container->get( 'wcgateway.settings' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
return new ApplepayPaymentMethod( $settings );
return new ApplepayPaymentMethod( $settings, $logger );
},
'applepay.url' => static function ( ContainerInterface $container ): string {
$path = realpath( __FILE__ );
@ -56,21 +57,11 @@ return array(
'applepay.sdk_script_url' => static function ( ContainerInterface $container ): string {
return 'https://applepay.cdn-apple.com/jsapi/v1/apple-pay-sdk.js';
},
'applepay.paypal_sdk_url' => static function ( ContainerInterface $container ): string {
$settings = $container->get( 'wcgateway.settings' );
if ( ! $settings->has( 'client_id' ) || ! $settings->has( 'merchant_id' ) ) {
return 'https://www.paypal.com/sdk/js?components=applepay';
}
$client_id = $settings->get( 'client_id' );
$shop_currency = get_woocommerce_currency();
$merchant_id = $settings->get( 'merchant_id' );
return 'https://www.paypal.com/sdk/js?client-id='
. $client_id . '&currency='
. $shop_currency . '&merchant-id='
. $merchant_id . '&components=applepay';
},
'applepay.script_url' => static function ( ContainerInterface $container ): string {
return trailingslashit( $container->get( 'applepay.url' ) ) . '/assets/js/paypal-applepay-direct.js';
return trailingslashit( $container->get( 'applepay.url' ) ) . '/assets/js/applePayDirect.js';
},
'applepay.style_url' => static function ( ContainerInterface $container ): string {
return trailingslashit( $container->get( 'applepay.url' ) ) . '/assets/css/applepaydirect.css';
},
'applepay.setting_button_enabled_product' => static function ( ContainerInterface $container ): bool {
$settings = $container->get( 'wcgateway.settings' );
@ -88,4 +79,7 @@ return array(
(bool) $settings->get( 'applepay_button_enabled_cart' ) :
false;
},
'applepay.data_to_scripts' => static function ( ContainerInterface $container ): DataToAppleButtonScripts {
return new DataToAppleButtonScripts();
},
);

View file

@ -84,7 +84,7 @@ class ApplepayModule implements ModuleInterface {
$this->load_domain_association_file( $is_sandobx );
$this->render_buttons( $c );
$apple_payment_method->bootstrapAjaxRequest();
$apple_payment_method->bootstrap_ajax_request();
}
/**
@ -123,7 +123,7 @@ class ApplepayModule implements ModuleInterface {
*/
public function load_assets(ContainerInterface $c ): void {
add_action(
'init',
'wp',
function () use ( $c ) {
wp_register_script(
'wc-ppcp-applepay-sdk',
@ -133,26 +133,26 @@ class ApplepayModule implements ModuleInterface {
true
);
wp_enqueue_script( 'wc-ppcp-applepay-sdk' );
wp_register_script(
'wc-ppcp-paypal-sdk',
$c->get( 'applepay.paypal_sdk_url' ),
array(),
$c->get( 'ppcp.asset-version' ),
true
);
wp_enqueue_script( 'wc-ppcp-paypal-sdk' );
wp_register_script(
'wc-ppcp-applepay',
$c->get( 'applepay.script_url' ),
array( 'wc-ppcp-applepay-sdk', 'wc-ppcp-paypal-sdk' ),
array( 'wc-ppcp-applepay-sdk' ),
$c->get( 'ppcp.asset-version' ),
true
);
wp_register_style(
'wc-ppcp-applepay',
$c->get( 'applepay.style_url' ),
array(),
$c->get( 'ppcp.asset-version' )
);
wp_enqueue_style( 'wc-ppcp-applepay');
wp_enqueue_script( 'wc-ppcp-applepay' );
$data = $c->get( 'applepay.data_to_scripts' )->applePayScriptData();
wp_localize_script(
'wc-ppcp-applepay',
'wc_ppcp_applepay',
array()
$data
);
}
);
@ -164,7 +164,7 @@ class ApplepayModule implements ModuleInterface {
protected function apple_pay_direct_button(): void {
?>
<div class="ppc-button-wrapper">
<div id="woocommerce_paypal_payments_applepayDirect-button">
<div id="applepay-container">
<?php wp_nonce_field( 'woocommerce-process_checkout', 'woocommerce-process-checkout-nonce' ); ?>
</div>
</div>
@ -180,6 +180,10 @@ class ApplepayModule implements ModuleInterface {
public function render_buttons(ContainerInterface $c ): void {
$button_enabled_product = $c->get( 'applepay.setting_button_enabled_product' );
$button_enabled_cart = $c->get( 'applepay.setting_button_enabled_cart' );
add_filter('woocommerce_paypal_payments_sdk_components_hook', function($components) {
$components[] = 'applepay';
return $components;
});
if ( $button_enabled_product ) {
$render_placeholder = apply_filters( 'woocommerce_paypal_payments_applepay_render_hook_product', 'woocommerce_after_add_to_cart_form' );
$render_placeholder = is_string( $render_placeholder ) ? $render_placeholder : 'woocommerce_after_add_to_cart_form';

View file

@ -9,6 +9,7 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Applepay;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
@ -23,6 +24,17 @@ class ApplepayPaymentMethod {
* @var Settings
*/
private $plugin_settings;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* @var ResponsesToApple
*/
private $response_templates;
private $old_cart_contents;
/**
* PayPalPaymentMethod constructor.
@ -30,9 +42,12 @@ class ApplepayPaymentMethod {
* @param Settings $plugin_settings The settings.
*/
public function __construct(
Settings $plugin_settings
Settings $plugin_settings,
LoggerInterface $logger
) {
$this->plugin_settings = $plugin_settings;
$this->response_templates = new ResponsesToApple();
$this->logger = $logger;
}
/**
@ -58,6 +73,23 @@ class ApplepayPaymentMethod {
$data['products'][0] = 'PAYMENT_METHODS';
}
$data['capabilities'][] = 'APPLE_PAY';
$nonce = $data['operations'][0]['api_integration_preference']['rest_api_integration']['first_party_details']['seller_nonce'];
$data['operations'][] = array(
'operation' => 'API_INTEGRATION',
'api_integration_preference' => array(
'rest_api_integration' => array(
'integration_method' => 'PAYPAL',
'integration_type' => 'THIRD_PARTY',
'third_party_details' => array(
'features' => array(
'PAYMENT',
'REFUND',
),
'seller_nonce' => $nonce
),
),
),
);
return $data;
}
@ -92,14 +124,6 @@ class ApplepayPaymentMethod {
* Adds all the Ajax actions to perform the whole workflow
*/
public function bootstrap_ajax_request(): void {
add_action(
'wp_ajax_' . PropertiesDictionary::VALIDATION,
array( $this, 'validate_merchant' )
);
add_action(
'wp_ajax_nopriv_' . PropertiesDictionary::VALIDATION,
array( $this, 'validate_merchant' )
);
add_action(
'wp_ajax_' . PropertiesDictionary::CREATE_ORDER,
array( $this, 'create_wc_order' )
@ -133,16 +157,6 @@ class ApplepayPaymentMethod {
array( $this, 'update_shipping_method' )
);
}
/**
* Method to validate the merchant against Apple system
* On fail triggers and option that shows an admin notice showing the error
* On success returns the validation data to the script
*/
public function validate_merchant(): void {
// TODO validate merchant.
// $this->plugin_settings->set('applepay_validated', 'yes');
}
/**
* Method to validate and update the shipping contact of the user
@ -151,6 +165,51 @@ class ApplepayPaymentMethod {
* 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
);
$product_need_shipping = $applepay_request_data_object->need_shipping();
if (!$is_allowed_selling_country) {
$this->response_templates->response_with_data_errors(
[['errorCode' => 'addressUnserviceable']]
);
return;
}
if ($product_need_shipping && !$is_allowed_shipping_country) {
$this->response_templates->response_with_data_errors(
[['errorCode' => 'addressUnserviceable']]
);
return;
}
$payment_details = $this->which_calculate_totals($applepay_request_data_object);
$response = $this->response_templates->apple_formatted_response($payment_details);
$this->response_templates->response_success($response);
}
/**
@ -160,6 +219,17 @@ class ApplepayPaymentMethod {
* 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());
}
$paymentDetails = $this->which_calculate_totals($applepay_request_data_object);
$response = $this->response_templates->apple_formatted_response($paymentDetails);
$this->response_templates->response_success($response);
}
/**
@ -192,6 +262,393 @@ class ApplepayPaymentMethod {
return wp_verify_nonce(
$nonce,
'woocommerce-process_checkout'
) === 1;
}
/**
* 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()
{
return new \WC_Countries();
}
/**
* Selector between product detail and cart page calculations
*
* @param $applepay_request_data_object
*
* @return array|bool
*/
protected function which_calculate_totals(
$applepay_request_data_object
) {
if ($applepay_request_data_object->caller_page === 'productDetail') {
return $this->calculate_totals_single_product(
$applepay_request_data_object->product_id(),
$applepay_request_data_object->product_quantity(),
$applepay_request_data_object->simplified_contact(),
$applepay_request_data_object->shipping_method()
);
}
if ($applepay_request_data_object->caller_page === 'cart') {
return $this->calculate_totals_cart_page(
$applepay_request_data_object->simplified_contact(),
$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 $product_id
* @param $product_quantity
* @param $customer_address
* @param null $shipping_method
*/
protected function calculate_totals_single_product(
$product_id,
$product_quantity,
$customer_address,
$shipping_method = null
): array {
$results = [];
$reload_cart = false;
if (!WC()->cart->is_empty()) {
$old_cart_contents = WC()->cart->get_cart_contents();
foreach (array_keys($old_cart_contents) as $cart_item_key) {
WC()->cart->remove_cart_item($cart_item_key);
}
$reload_cart = true;
}
try {
//I just care about apple address details
$shipping_method_id = '';
$shipping_methods_array = [];
$selected_shipping_method = [];
$this->customer_address($customer_address);
$cart = WC()->cart;
if ($shipping_method) {
$shipping_method_id = $shipping_method['identifier'];
WC()->session->set(
'chosen_shipping_methods',
[$shipping_method_id]
);
}
$cart_item_key = $cart->add_to_cart($product_id, $product_quantity);
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
);
$cart->remove_cart_item($cart_item_key);
$this->customer_address();
if ($reload_cart) {
foreach (array_keys($old_cart_contents) as $cart_item_key) {
$cart->restore_cart_item($cart_item_key);
}
}
} catch (Exception $exception) {
}
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
*/
protected function customer_address(array $address = [])
{
$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'] ?? $shop_country_code
);
WC()->customer->set_shipping_city(
$address['city'] ?? $shop_country_code
);
}
/**
* Add shipping methods to cart to perform correct calculations
*
* @param $cart
* @param $customer_address
* @param $shipping_method
* @param $shipping_method_id
*/
protected function cart_shipping_methods(
$cart,
$customer_address,
$shipping_method,
$shipping_method_id
): array {
$shipping_methods_array = [];
$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[] = [
"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',
[$shipping_method_id]
);
}
}
$selected_shipping_method = $shipping_methods_array[0];
if ($shipping_method) {
$selected_shipping_method = $shipping_method;
}
return [$shipping_methods_array, $selected_shipping_method];
}
/**
* Sets shipping packages for correct calculations
* @param $customer_address
* @param $total
*
* @return mixed|void|null
*/
protected function getShippingPackages($customer_address, $total)
{
// Packages array for storing 'carts'
$packages = [];
$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 $cart
* @param $selected_shipping_method
* @param $shipping_methods_array
*/
protected function cart_calculation_results(
$cart,
$selected_shipping_method,
$shipping_methods_array
): array {
$total = $cart->get_total('edit');
$total = round($total, 2);
return [
'subtotal' => $cart->get_subtotal(),
'shipping' => [
'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 $customer_address
* @param null $shipping_method
*/
protected function calculate_totals_cart_page(
$customer_address = null,
$shipping_method = null
): array {
$results = [];
if (WC()->cart->is_empty()) {
return [];
}
try {
$shipping_methods_array = [];
$selected_shipping_method = [];
//I just care about apple address details
$this->customer_address($customer_address);
$cart = WC()->cart;
if ($shipping_method) {
WC()->session->set(
'chosen_shipping_methods',
[$shipping_method['identifier']]
);
}
if ($cart->needs_shipping()) {
list(
$shipping_methods_array, $selected_shipping_method
) = $this->cart_shipping_methods(
$cart,
$customer_address,
$shipping_method,
$shipping_method['identifier']
);
}
$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 $results;
}
/**
* Add address billing and shipping data to order
*
* @param ApplePayDataObjectHttp $applepay_request_data_object
* @param $order
*
*/
protected function addAddressesToOrder(
ApplePayDataObjectHttp $applepay_request_data_object
) {
add_action(
'woocommerce_checkout_create_order',
static function ($order, $data) use ($applepay_request_data_object) {
if ($applepay_request_data_object->shipping_method() !== null) {
$billing_address = $applepay_request_data_object->billing_address();
$shipping_address = $applepay_request_data_object->shipping_address();
//apple puts email in shipping_address while we get it from WC's billing_address
$billing_address['email'] = $shipping_address['email'];
$billing_address['phone'] = $shipping_address['phone'];
$order->set_address($billing_address, 'billing');
$order->set_address($shipping_address, 'shipping');
}
},
10,
2
);
}
/**
* Empty the cart to use for calculations
* while saving its contents in a field
*/
protected function empty_current_cart()
{
foreach ($this->old_cart_contents as $cart_item_key => $value) {
WC()->cart->remove_cart_item($cart_item_key);
}
$this->reload_cart = true;
}
/**
* @param WC_Cart $cart
*/
protected function reload_cart(WC_Cart $cart): void
{
foreach ($this->old_cart_contents as $cart_item_key => $value) {
$cart->restore_cart_item($cart_item_key);
}
}
protected function response_after_successful_result(): void
{
add_filter(
'woocommerce_payment_successful_result',
function ($result, $order_id) {
if (
isset($result['result'])
&& 'success' === $result['result']
) {
$this->response_templates->response_success(
$this->response_templates->authorization_result_response(
'STATUS_SUCCESS',
$order_id
)
);
} else {
wp_send_json_error(
$this->response_templates->authorization_result_response(
'STATUS_FAILURE',
0,
[['errorCode' => 'unknown']]
)
);
}
return $result;
},
10,
2
);
}
}

View file

@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Applepay;
class DataToAppleButtonScripts {
/**
* Sets the appropriate data to send to ApplePay script
* Data differs between product page and cart page
*
* @return array
*/
public function applePayScriptData( bool $isBlock = false ): array {
$base_location = wc_get_base_location();
$shopCountryCode = $base_location['country'];
$currencyCode = get_woocommerce_currency();
$totalLabel = get_bloginfo( 'name' );
if ( is_product() ) {
return $this->dataForProductPage(
$shopCountryCode,
$currencyCode,
$totalLabel
);
}
if ( is_cart() || $isBlock ) {
return $this->dataForCartPage(
$shopCountryCode,
$currencyCode,
$totalLabel
);
}
return array();
}
/**
* Check if the product needs shipping
*
* @param $product
*
* @return bool
*/
protected function checkIfNeedShipping( $product ) {
if (
! wc_shipping_enabled()
|| 0 === wc_get_shipping_method_count(
true
)
) {
return false;
}
$needs_shipping = false;
if ( $product->needs_shipping() ) {
$needs_shipping = true;
}
return $needs_shipping;
}
/**
* @param $shopCountryCode
* @param $currencyCode
* @param $totalLabel
*
* @return array
*/
protected function dataForProductPage(
$shopCountryCode,
$currencyCode,
$totalLabel
) {
$product = wc_get_product( get_the_id() );
if ( ! $product ) {
return array();
}
$isVariation = false;
if ( $product->get_type() === 'variable' || $product->get_type() === 'variable-subscription' ) {
$isVariation = true;
}
$productNeedShipping = $this->checkIfNeedShipping( $product );
$productId = get_the_id();
$productPrice = $product->get_price();
$productStock = $product->get_stock_status();
return array(
'product' => array(
'needShipping' => $productNeedShipping,
'id' => $productId,
'price' => $productPrice,
'isVariation' => $isVariation,
'stock' => $productStock,
),
'shop' => array(
'countryCode' => $shopCountryCode,
'currencyCode' => $currencyCode,
'totalLabel' => $totalLabel,
),
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
);
}
/**
* @param $shopCountryCode
* @param $currencyCode
* @param $totalLabel
*
* @return array
*/
protected function dataForCartPage(
$shopCountryCode,
$currencyCode,
$totalLabel
) {
$cart = WC()->cart;
$nonce = wp_nonce_field( 'woocommerce-process_checkout', 'woocommerce-process-checkout-nonce' );
$buttonMarkup =
'<div id="applepay-container">'
. $nonce
. '</div>';
return array(
'product' => array(
'needShipping' => $cart->needs_shipping(),
'subtotal' => $cart->get_subtotal(),
),
'shop' => array(
'countryCode' => $shopCountryCode,
'currencyCode' => $currencyCode,
'totalLabel' => $totalLabel,
),
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'buttonMarkup' => $buttonMarkup,
);
}
}

View file

@ -14,13 +14,6 @@ namespace WooCommerce\PayPalCommerce\Applepay;
*/
class PropertiesDictionary {
public const VALIDATION_REQUIRED_FIELDS =
array(
self::WCNONCE,
self::VALIDATION_URL,
);
public const BILLING_CONTACT_INVALID = 'billing Contact Invalid';
public const CREATE_ORDER_SINGLE_PROD_REQUIRED_FIELDS =
@ -58,8 +51,6 @@ class PropertiesDictionary {
self::NEED_SHIPPING,
);
public const VALIDATION_URL = 'validationUrl';
public const UPDATE_METHOD_SINGLE_PROD_REQUIRED_FIELDS =
array(
self::WCNONCE,
@ -70,13 +61,13 @@ class PropertiesDictionary {
self::SIMPLIFIED_CONTACT,
);
public const PRODUCT_ID = 'productId';
public const PRODUCT_ID = 'product_id';
public const SIMPLIFIED_CONTACT = 'simplifiedContact';
public const SIMPLIFIED_CONTACT = 'simplified_contact';
public const SHIPPING_METHOD = 'shippingMethod';
public const SHIPPING_METHOD = 'shipping_method';
public const SHIPPING_CONTACT = 'shippingContact';
public const SHIPPING_CONTACT = 'shipping_contact';
public const SHIPPING_CONTACT_INVALID = 'shipping Contact Invalid';
@ -91,23 +82,21 @@ class PropertiesDictionary {
self::SHIPPING_CONTACT,
);
public const PRODUCT_QUANTITY = 'productQuantity';
public const PRODUCT_QUANTITY = 'product_quantity';
public const CALLER_PAGE = 'callerPage';
public const CALLER_PAGE = 'caller_page';
public const BILLING_CONTACT = 'billingContact';
public const BILLING_CONTACT = 'billing_contact';
public const NEED_SHIPPING = 'needShipping';
public const NEED_SHIPPING = 'need_shipping';
public const UPDATE_SHIPPING_CONTACT = 'woocommerce_paypal_payments_update_shipping_contact';
public const UPDATE_SHIPPING_CONTACT = 'ppcp_update_shipping_contact';
public const UPDATE_SHIPPING_METHOD = 'woocommerce_paypal_payments_update_shipping_method';
public const UPDATE_SHIPPING_METHOD = 'ppcp_update_shipping_method';
public const VALIDATION = 'woocommerce_paypal_payments_validation';
public const CREATE_ORDER = 'ppcp_create_order';
public const CREATE_ORDER = 'woocommerce_paypal_payments_create_order';
public const CREATE_ORDER_CART = 'ppcp_create_order_cart';
public const CREATE_ORDER_CART = 'woocommerce_paypal_payments_create_order_cart';
public const REDIRECT = 'woocommerce_paypal_payments_redirect';
public const REDIRECT = 'ppcp_redirect';
}

View file

@ -963,7 +963,7 @@
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
"@babel/runtime@^7.8.4":
"@babel/runtime@^7.13.10", "@babel/runtime@^7.16.0", "@babel/runtime@^7.8.4":
version "7.22.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438"
integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==
@ -1090,6 +1090,32 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.3.tgz#329842940042d2b280897150e023e604d11657d6"
integrity sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==
"@types/prop-types@*":
version "15.7.5"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
"@types/react-dom@^16.9.0":
version "16.9.19"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.19.tgz#6a139c26b02dec533a7fa131f084561babb10a8f"
integrity sha512-xC8D280Bf6p0zguJ8g62jcEOKZiUbx9sIe6O3tT/lKfR87A7A6g65q13z6D5QUMIa/6yFPkNhqjF5z/VVZEYqQ==
dependencies:
"@types/react" "^16"
"@types/react@^16", "@types/react@^16.9.0":
version "16.14.43"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.43.tgz#bc6e7a0e99826809591d38ddf1193955de32c446"
integrity sha512-7zdjv7jvoLLQg1tTvpQsm+hyNUMT2mPlNV1+d0I8fbGhkJl82spopMyBlu4wb1dviZAxpGdk5eHu/muacknnfw==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/scheduler@*":
version "0.16.3"
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5"
integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==
"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5":
version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24"
@ -1243,6 +1269,26 @@
json2php "^0.0.4"
webpack-sources "^3.2.2"
"@wordpress/element@^3.0.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@wordpress/element/-/element-3.2.0.tgz#531720b3a023a1509f13d2464b2e1d670153116e"
integrity sha512-YXJhtBF8FnFYwA9X6Dvs4k6yJf5wy1lhU04VNJVzoUDwCt/pK747RGePIPDdUWVd3X/TlyNH2yLRtcCyOC/SzQ==
dependencies:
"@babel/runtime" "^7.13.10"
"@types/react" "^16.9.0"
"@types/react-dom" "^16.9.0"
"@wordpress/escape-html" "^2.2.0"
lodash "^4.17.21"
react "^17.0.1"
react-dom "^17.0.1"
"@wordpress/escape-html@^2.2.0":
version "2.38.0"
resolved "https://registry.yarnpkg.com/@wordpress/escape-html/-/escape-html-2.38.0.tgz#72dd11ba66e26498b4379a4d8b7d020ba26d879b"
integrity sha512-q7wg1JvXVPpyddMnEl6A8ALn9U3mA4LvyQpkDNLonntU+Q8JbvW1r91HdzoFh396rHoNJWGzDGORUTlDlb5jOw==
dependencies:
"@babel/runtime" "^7.16.0"
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@ -1467,6 +1513,11 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
csstype@^3.0.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
debug@^4.1.0, debug@^4.1.1:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
@ -1723,7 +1774,7 @@ jest-worker@^27.4.5:
merge-stream "^2.0.0"
supports-color "^8.0.0"
js-tokens@^4.0.0:
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
@ -1794,6 +1845,18 @@ lodash.debounce@^4.0.8:
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loose-envify@^1.1.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
lru-cache@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
@ -1845,6 +1908,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
@ -1908,6 +1976,23 @@ randombytes@^2.1.0:
dependencies:
safe-buffer "^5.1.0"
react-dom@^17.0.1:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler "^0.20.2"
react@^17.0.1:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
@ -2008,6 +2093,14 @@ sass@^1.42.1:
immutable "^4.0.0"
source-map-js ">=0.6.2 <2.0.0"
scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
schema-utils@^2.6.5:
version "2.7.1"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"