Add apple transaction in product with auth

This commit is contained in:
carmenmaymo 2023-08-17 09:42:45 +02:00
parent 3ae22b3356
commit 10613711b9
No known key found for this signature in database
GPG key ID: 6023F686B0F3102E
10 changed files with 1247 additions and 299 deletions

View file

@ -15,6 +15,18 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
return array(
'wcgateway.settings.fields' => static function ( ContainerInterface $container, array $fields ): array {
$fields['applepay_heading'] = array(
'heading' => __( 'Apple Pay', 'woocommerce-paypal-payments' ),
'description' =>
__(
'Customize the behaviour of the ApplePay button.',
'woocommerce-paypal-payments'
),
'type' => 'ppcp-heading',
'screens' => array( State::STATE_ONBOARDED ),
'requirements' => array(),
'gateway' => 'paypal',
);
$fields['applepay_button_enabled_product'] = array(
'title' => __( 'Apple Pay Button on Product Page', 'woocommerce-paypal-payments' ),
'type' => 'checkbox',
@ -33,6 +45,26 @@ return array(
'gateway' => 'paypal',
'requirements' => array(),
);
$fields['applepay_live_validation_file'] = array(
'title' => __( 'Apple Pay Live Validation File', 'woocommerce-paypal-payments' ),
'type' => 'textarea',
'label' => __( 'Paste here the validation file content', 'woocommerce-paypal-payments' ),
'default' => null,
'screens' => array( State::STATE_ONBOARDED ),
'gateway' => 'paypal',
'requirements' => array(),
);
$fields['applepay_sandbox_validation_file'] = array(
'title' => __( 'Apple Pay Sandbox Validation File', 'woocommerce-paypal-payments' ),
'type' => 'textarea',
'label' => __( 'Paste here the validation file content', 'woocommerce-paypal-payments' ),
'default' => null,
'screens' => array( State::STATE_ONBOARDED ),
'gateway' => 'paypal',
'requirements' => array(),
);
return $fields;
},

View file

@ -1,23 +1,29 @@
//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'
import ErrorHandler from '../../../ppcp-button/resources/js/modules/ErrorHandler';
import SingleProductActionHandler
from '../../../ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler';
import UpdateCart from '../../../ppcp-button/resources/js/modules/Helper/UpdateCart';
import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading';
(
function ({ wc_ppcp_applepay, jQuery}) {
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
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()){
if (outOfStock || !maybeShowButton()) {
return;
}
const nonce = document.getElementById('woocommerce-process-checkout-nonce').value
@ -29,14 +35,17 @@ import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper
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) {
@ -58,20 +67,20 @@ import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper
disableButton(appleButton);
}
const amountWithoutTax = productQuantity * price
loadPaypalScript(PayPalCommerceGateway, () => {
bootstrapped = true;
//print the button
const applepay = paypal.Applepay();
console.log(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
appleContainer.innerHTML = '<apple-pay-button id="btn-appl" buttonstyle="black" type="buy" locale="en">';
const paymentRequest = {
countryCode: applepayConfig.countryCode,
merchantCapabilities:
applepayConfig.merchantCapabilities,
merchantCapabilities: applepayConfig.merchantCapabilities,
supportedNetworks: applepayConfig.supportedNetworks,
currencyCode: currencyCode,
requiredShippingContactFields: ["name", "phone",
@ -82,13 +91,13 @@ import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper
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){
console.log(session)
if (needShipping) {
session.onshippingmethodselected = function (event) {
jQuery.ajax({
url: ajaxUrl,
@ -155,6 +164,7 @@ import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper
selectedShippingMethod = response.newShippingMethods[0]
}
this.completeShippingContactSelection(response)
},
error: (jqXHR, textStatus, errorThrown) => {
console.warn(textStatus, errorThrown)
@ -169,89 +179,152 @@ import {loadPaypalScript} from '../../../ppcp-button/resources/js/modules/Helper
})
.then(validateResult => {
session.completeMerchantValidation(validateResult.merchantSession);
//call backend to update validation to true
console.log('validated')
})
.catch(validateError => {
console.error(validateError);
//call backend to update validation to false
session.abort();
});
};
/*session.onpaymentauthorized = (ApplePayPayment) => {
const {billingContact, shippingContact } = ApplePayPayment.payment
session.onpaymentauthorized = (event) => {
function form() {
return document.querySelector('form.cart');
}
const errorHandler = new ErrorHandler(
PayPalCommerceGateway.labels.error.generic,
document.querySelector('.woocommerce-notices-wrapper')
);
const actionHandler = new SingleProductActionHandler(
PayPalCommerceGateway,
new UpdateCart(
PayPalCommerceGateway.ajax.change_cart.endpoint,
PayPalCommerceGateway.ajax.change_cart.nonce,
),
form(),
errorHandler,
);
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
let createOrderInPayPal = actionHandler.createOrder()
const processInWooAndCapture = async (data) => {
try {
const billingContact = data.billing_contact
const shippingContact = data.shipping_contact
jQuery.ajax({
url: ajaxUrl,
method: 'POST',
data: {
action: 'ppcp_create_order',
'product_id': productId,
'product_quantity': productQuantity,
'shipping_contact': shippingContact,
'billing_contact': billingContact,
'token': event.payment.token,
'shipping_method': 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': 'ppcp-gateway',
'funding_source': 'applepay',
'_wp_http_referer': '/?wc-ajax=update_order_review',
'paypal_order_id': data.paypal_order_id,
},
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)
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()
},
})
} catch (error) {
console.log(error) // handle error
}
}
createOrderInPayPal([], []).then((orderId) => {
applepay.confirmOrder(
{
orderId: orderId,
token: event.payment.token,
billingContact: event.payment.billingContact
}
},
error: (jqXHR, textStatus, errorThrown) => {
console.warn(textStatus, errorThrown)
session.abort()
},
).then(
confirmResult => {
session.completePayment(ApplePaySession.STATUS_SUCCESS);
let data = {
billing_contact: event.payment.billingContact,
shipping_contact: event.payment.shippingContact,
paypal_order_id: orderId
}
processInWooAndCapture(data).then(
() => {
console.log('done in woo and capture')
let result = confirmResult.data
redirectionUrl = result['returnUrl'];
session.completePayment(result['responseToApple'])
window.location.href = redirectionUrl
}
)
}
)
.catch(err => {
session.completePayment(ApplePaySession.STATUS_FAILURE);
console.error('Error confirming order with applepay token');
console.error(err);
}
);
}).catch((error) => {
console.log(error)
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

@ -9,15 +9,19 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Applepay;
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
return array(
'apple.status-cache' => static function( ContainerInterface $container ): Cache {
return new Cache( 'ppcp-paypal-apple-status-cache' );
},
'applepay.apple-product-status' => static function( ContainerInterface $container ): AppleProductStatus {
return new AppleProductStatus(
$container->get( 'wcgateway.settings' ),
$container->get( 'api.endpoint.partners' ),
$container->get( 'pui.status-cache' ),
$container->get( 'apple.status-cache' ),
$container->get( 'onboarding.state' )
);
},
@ -41,8 +45,9 @@ return array(
'applepay.payment_method' => static function ( ContainerInterface $container ): ApplepayPaymentMethod {
$settings = $container->get( 'wcgateway.settings' );
$logger = $container->get( 'woocommerce.logger.woocommerce' );
$order_processor = $container->get( 'wcgateway.order-processor' );
return new ApplepayPaymentMethod( $settings, $logger );
return new ApplepayPaymentMethod( $settings, $logger, $order_processor );
},
'applepay.url' => static function ( ContainerInterface $container ): string {
$path = realpath( __FILE__ );
@ -60,7 +65,7 @@ return array(
'applepay.script_url' => static function ( ContainerInterface $container ): string {
return trailingslashit( $container->get( 'applepay.url' ) ) . '/assets/js/applePayDirect.js';
},
'applepay.style_url' => static function ( ContainerInterface $container ): string {
'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 {
@ -79,7 +84,7 @@ return array(
(bool) $settings->get( 'applepay_button_enabled_cart' ) :
false;
},
'applepay.data_to_scripts' => static function ( ContainerInterface $container ): DataToAppleButtonScripts {
'applepay.data_to_scripts' => static function ( ContainerInterface $container ): DataToAppleButtonScripts {
return new DataToAppleButtonScripts();
},
);

View file

@ -0,0 +1,548 @@
<?php
/**
* Builds the object containing the data to send to Apple.
*
* @package WooCommerce\PayPalCommerce\Applepay
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Applepay;
use Psr\Log\LoggerInterface as Logger;
/**
* Class ApplePayDataObjectHttp
*/
class ApplePayDataObjectHttp {
/**
* The nonce of the request.
*
* @var string
*/
protected $nonce;
/**
* The contact with less fields.
*
* @var mixed
*/
protected $simplified_contact;
/**
* If the product needs shipping.
*
* @var mixed|null
*/
protected $need_shipping;
/**
* The product id.
*
* @var mixed
*/
protected $product_id;
/**
* The product quantity.
*
* @var mixed
*/
protected $product_quantity;
/**
* The shipping methods.
*
* @var array|mixed
*/
protected $shipping_method = array();
/**
* The billing address.
*
* @var string[]
*/
protected $billing_address = array();
/**
* The shipping address.
*
* @var string[]
*/
protected $shipping_address = array();
/**
* The list of errors.
*
* @var array
*/
protected $errors = array();
/**
* The logger.
*
* @var Logger
*/
protected $logger;
/**
* ApplePayDataObjectHttp constructor.
*
* @param Logger $logger The logger.
*/
public function __construct( Logger $logger ) {
$this->logger = $logger;
}
/**
* Resets the errors array
*/
protected function reset_errors() {
$this->errors = array();
}
/**
* Returns if the object has any errors
*
* @return bool
*/
public function has_errors() {
return ! empty( $this->errors );
}
/**
* Returns errors
*
* @return array
*/
public function errors(): array {
return $this->errors;
}
/**
* Set the object with the data relevant to ApplePay on update shipping contact
* Required data depends on callerPage
*/
public function update_contact_data() {
$nonce = filter_input( INPUT_POST, 'woocommerce-process-checkout-nonce', FILTER_SANITIZE_SPECIAL_CHARS );
$is_nonce_valid = wp_verify_nonce(
$nonce,
'woocommerce-process_checkout'
);
if ( ! $is_nonce_valid ) {
return;
}
$data = $this->get_filtered_request_data();
$result = $this->update_required_data(
$data,
PropertiesDictionary::UPDATE_CONTACT_SINGLE_PROD_REQUIRED_FIELDS,
PropertiesDictionary::UPDATE_CONTACT_CART_REQUIRED_FIELDS
);
if ( ! $result ) {
return;
}
$this->update_simplified_contact( $data[ PropertiesDictionary::SIMPLIFIED_CONTACT ] );
}
/**
* Set the object with the data relevant to ApplePay on update shipping method
* Required data depends on callerPage
*/
public function update_method_data() {
$nonce = filter_input( INPUT_POST, 'woocommerce-process-checkout-nonce', FILTER_SANITIZE_SPECIAL_CHARS );
$is_nonce_valid = wp_verify_nonce(
$nonce,
'woocommerce-process_checkout'
);
if ( ! $is_nonce_valid ) {
return;
}
$data = $this->get_filtered_request_data();
$result = $this->update_required_data(
$data,
PropertiesDictionary::UPDATE_METHOD_SINGLE_PROD_REQUIRED_FIELDS,
PropertiesDictionary::UPDATE_METHOD_CART_REQUIRED_FIELDS
);
if ( ! $result ) {
return;
}
$this->update_simplified_contact( $data[ PropertiesDictionary::SIMPLIFIED_CONTACT ] );
$this->update_shipping_method( $data );
}
/**
* Set the object with the data relevant to ApplePay on authorized order
* Required data depends on callerPage
*
* @param string $caller_page The caller page.
*/
public function order_data( string $caller_page ) {
$nonce = filter_input( INPUT_POST, 'woocommerce-process-checkout-nonce', FILTER_SANITIZE_SPECIAL_CHARS );
$is_nonce_valid = wp_verify_nonce(
$nonce,
'woocommerce-process_checkout'
);
if ( ! $is_nonce_valid ) {
return;
}
$data = filter_var_array( $_POST, FILTER_SANITIZE_SPECIAL_CHARS );
$data[ PropertiesDictionary::CALLER_PAGE ] = $caller_page;
$result = $this->update_required_data(
$data,
PropertiesDictionary::CREATE_ORDER_SINGLE_PROD_REQUIRED_FIELDS,
PropertiesDictionary::CREATE_ORDER_CART_REQUIRED_FIELDS
);
if ( ! $result ) {
return;
}
if (
! array_key_exists( 'emailAddress', $data[ PropertiesDictionary::SHIPPING_CONTACT ] )
|| ! $data[ PropertiesDictionary::SHIPPING_CONTACT ]['emailAddress']
) {
$this->errors[] = array(
'errorCode' => PropertiesDictionary::SHIPPING_CONTACT_INVALID,
'contactField' => 'emailAddress',
);
return;
}
$filtered_shipping_contact = $data[ PropertiesDictionary::SHIPPING_CONTACT ];
$this->shipping_address = $this->complete_address(
$filtered_shipping_contact,
PropertiesDictionary::SHIPPING_CONTACT_INVALID
);
$filtered_billing_contact = $data[ PropertiesDictionary::BILLING_CONTACT ];
$this->billing_address = $this->complete_address(
$filtered_billing_contact,
PropertiesDictionary::BILLING_CONTACT_INVALID
);
$this->update_shipping_method( $data );
}
/**
* Checks if the array contains all required fields and if those
* are not empty.
* If not it adds an unkown error to the object's error list, as this errors
* are not supported by ApplePay.
*
* @param array $data The data.
* @param array $required The required fields.
*
* @return bool
*/
protected function has_required_fields_values_or_error( array $data, array $required ) {
foreach ( $required as $required_field ) {
if ( ! array_key_exists( $required_field, $data ) ) {
$this->logger->debug(
sprintf( 'ApplePay Data Error: Missing index %s', $required_field )
);
$this->errors[] = array( 'errorCode' => 'unknown' );
continue;
}
if ( $data[ $required_field ] === null || $data[ $required_field ] === '' ) {
$this->logger->debug(
sprintf( 'ApplePay Data Error: Missing value for %s', $required_field )
);
$this->errors[] = array( 'errorCode' => 'unknown' );
continue;
}
}
return ! $this->has_errors();
}
/**
* Sets the value to the appropriate field in the object.
*
* @param array $data The data.
*/
protected function assign_data_object_values( array $data ) {
foreach ( $data as $key => $value ) {
if ( $key === 'woocommerce-process-checkout-nonce' ) {
$key = 'nonce';
}
$this->$key = $value;
}
}
/**
* Returns the address details used in pre-authorization steps.
*
* @param array $contact_info
*
* @return string[]
*/
protected function simplified_address( array $contact_info ) {
$required = array(
'locality' => 'locality',
'postalCode' => 'postalCode',
'countryCode' => 'countryCode',
);
if (
! $this->address_has_required_fields_values(
$contact_info,
$required,
PropertiesDictionary::SHIPPING_CONTACT_INVALID
)
) {
return array();
}
return array(
'city' => $contact_info['locality'],
'postcode' => $contact_info['postalCode'],
'country' => strtoupper( $contact_info['countryCode'] ),
);
}
/**
* Checks if the address array contains all required fields and if those
* are not empty.
* If not it adds a contacField error to the object's error list.
*
* @param array $post The address to check
* @param array $required The required fields for the given address
* @param string $error_code Either shipping or billing kind
*
* @return bool
*/
protected function address_has_required_fields_values(
array $post,
array $required,
string $error_code
) {
foreach ( $required as $required_field => $error_value ) {
if ( ! array_key_exists( $required_field, $post ) ) {
$this->logger->debug(
sprintf( 'ApplePay Data Error: Missing index %s', $required_field )
);
$this->errors[] = array( 'errorCode' => 'unknown' );
continue;
}
if ( ! $post[ $required_field ] ) {
$this->logger->debug(
sprintf( 'ApplePay Data Error: Missing value for %s', $required_field )
);
$this->errors[]
= array(
'errorCode' => $error_code,
'contactField' => $error_value,
);
continue;
}
}
return ! $this->has_errors();
}
/**
* Returns the address details for after authorization steps.
*
* @param array $data The data.
* @param string $error_code differentiates between billing and shipping information
*
* @return string[]
*/
protected function complete_address( array $data, string $error_code ): array {
$required = array(
'givenName' => 'name',
'familyName' => 'name',
'addressLines' => 'addressLines',
'locality' => 'locality',
'postalCode' => 'postalCode',
'countryCode' => 'countryCode',
);
if (
! $this->address_has_required_fields_values(
$data,
$required,
$error_code
)
) {
return array();
}
return array(
'first_name' => $data['givenName'],
'last_name' => $data['familyName'],
'email' => $data['emailAddress'] ?? '',
'phone' => $data['phoneNumber'] ?? '',
'address_1' => $data['addressLines'][0] ?? '',
'address_2' => $data['addressLines'][1] ?? '',
'city' => $data['locality'],
'state' => $data['administrativeArea'],
'postcode' => $data['postalCode'],
'country' => strtoupper( $data['countryCode'] ),
);
}
/**
* Updates the object with the required data.
*
* @param array $data The data.
* @param array $required_product_fields
* @param array $required_cart_fields
* @return bool
*/
protected function update_required_data( array $data, array $required_product_fields, array $required_cart_fields ) {
$this->reset_errors();
$required_fields = $required_product_fields;
if (
isset( $data[ PropertiesDictionary::CALLER_PAGE ] )
&& $data[ PropertiesDictionary::CALLER_PAGE ] === 'cart'
) {
$required_fields = $required_cart_fields;
}
$has_required_fields_values = $this->has_required_fields_values_or_error(
$data,
$required_fields
);
if ( ! $has_required_fields_values ) {
return false;
}
$this->assign_data_object_values( $data );
return true;
}
/**
* Updates the data object with the contact values from the request.
*
* @param array $data The data.
* @return void
*/
protected function update_simplified_contact( array $data ) : void {
$simplified_contact_info = array_map( 'sanitize_text_field', $data );
$this->simplified_contact = $this->simplified_address(
$simplified_contact_info
);
}
/**
* Updates the data object with the shipping values from the request.
*
* @param array $data The data.
* @return void
*/
protected function update_shipping_method( array $data ): void {
if (
array_key_exists(
PropertiesDictionary::SHIPPING_METHOD,
$data
)
) {
$this->shipping_method = filter_var_array(
$data[ PropertiesDictionary::SHIPPING_METHOD ],
FILTER_SANITIZE_SPECIAL_CHARS
);
}
}
/**
* Returns the billing address.
*
* @return string[]
*/
public function billing_address(): array {
return $this->billing_address;
}
/**
* Returns the shipping address.
*
* @return string[]
*/
public function shipping_address(): array {
return $this->shipping_address;
}
/**
* Returns the shipping method.
*
* @return array
*/
public function shipping_method(): array {
return $this->shipping_method ?? array();
}
/**
* Returns if the shipping is needed.
*
* @return bool
*/
public function need_shipping(): bool {
return $this->need_shipping ?? false;
}
/**
* Returns the product id.
*
* @return string
*/
public function product_id(): string {
return $this->product_id;
}
/**
* Returns the product quantity.
*
* @return string
*/
public function product_quantity(): string {
return $this->product_quantity;
}
/**
* Returns the nonce.
*
* @return mixed
*/
public function nonce() {
return $this->nonce;
}
/**
* Returns the simplified contact.
*
* @return mixed
*/
public function simplified_contact() {
return $this->simplified_contact;
}
/**
* Returns the filtered request data.
*
* @return array|false|null
*/
public function get_filtered_request_data() {
return filter_input_array(
INPUT_POST,
array(
PropertiesDictionary::CALLER_PAGE => FILTER_SANITIZE_SPECIAL_CHARS,
'woocommerce-process-checkout-nonce' => FILTER_SANITIZE_SPECIAL_CHARS,
PropertiesDictionary::NEED_SHIPPING => FILTER_VALIDATE_BOOLEAN,
PropertiesDictionary::SIMPLIFIED_CONTACT => array(
'filter' => FILTER_SANITIZE_SPECIAL_CHARS,
'flags' => FILTER_REQUIRE_ARRAY,
),
PropertiesDictionary::SHIPPING_CONTACT => array(
'filter' => FILTER_SANITIZE_SPECIAL_CHARS,
'flags' => FILTER_REQUIRE_ARRAY,
),
PropertiesDictionary::BILLING_CONTACT => array(
'filter' => FILTER_SANITIZE_SPECIAL_CHARS,
'flags' => FILTER_REQUIRE_ARRAY,
),
PropertiesDictionary::SHIPPING_METHOD => array(
'filter' => FILTER_SANITIZE_SPECIAL_CHARS,
'flags' => FILTER_REQUIRE_ARRAY,
),
PropertiesDictionary::PRODUCT_ID => FILTER_SANITIZE_NUMBER_INT,
PropertiesDictionary::PRODUCT_QUANTITY => FILTER_SANITIZE_NUMBER_INT,
)
);
}
}

View file

@ -21,7 +21,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
*/
class AppleProductStatus {
const APPLE_STATUS_CACHE_KEY = 'apple_pay_status_cache';
const APPLE_STATUS_CACHE_KEY = 'apple_status_cache';
/**
* The Cache.

View file

@ -79,7 +79,7 @@ class ApplepayModule implements ModuleInterface {
}
$this->load_assets( $c );
$env = $c->get( 'onboarding.environment' );
assert($env instanceof Environment);
assert( $env instanceof Environment );
$is_sandobx = $env->current_environment_is( Environment::SANDBOX );
$this->load_domain_association_file( $is_sandobx );
$this->render_buttons( $c );
@ -100,7 +100,7 @@ class ApplepayModule implements ModuleInterface {
*
* @param boolean $is_sandbox The environment for this merchant.
*/
protected function load_domain_association_file($is_sandbox ): void {
protected function load_domain_association_file( $is_sandbox ): void {
if ( ! isset( $_SERVER['REQUEST_URI'] ) ) {
return;
}
@ -121,7 +121,7 @@ class ApplepayModule implements ModuleInterface {
* @param ContainerInterface $c The container.
* @return void
*/
public function load_assets(ContainerInterface $c ): void {
public function load_assets( ContainerInterface $c ): void {
add_action(
'wp',
function () use ( $c ) {
@ -146,9 +146,9 @@ class ApplepayModule implements ModuleInterface {
array(),
$c->get( 'ppcp.asset-version' )
);
wp_enqueue_style( 'wc-ppcp-applepay');
wp_enqueue_style( 'wc-ppcp-applepay' );
wp_enqueue_script( 'wc-ppcp-applepay' );
$data = $c->get( 'applepay.data_to_scripts' )->applePayScriptData();
$data = $c->get( 'applepay.data_to_scripts' )->apple_pay_script_data();
wp_localize_script(
'wc-ppcp-applepay',
'wc_ppcp_applepay',
@ -177,13 +177,16 @@ class ApplepayModule implements ModuleInterface {
* @param ContainerInterface $c The container.
* @return void
*/
public function render_buttons(ContainerInterface $c ): void {
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;
});
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

@ -10,14 +10,25 @@ declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Applepay;
use Psr\Log\LoggerInterface;
use WC_Cart;
use WC_Checkout;
use WC_Order;
use WC_Session_Handler;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\Session\MemoryWcSession;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\Webhooks\Handler\RequestHandlerTrait;
/**
* Class PayPalPaymentMethod
*/
class ApplepayPaymentMethod {
use RequestHandlerTrait;
/**
* The settings.
*
@ -34,20 +45,53 @@ class ApplepayPaymentMethod {
* @var ResponsesToApple
*/
private $response_templates;
/**
* @var array The old cart contents.
*/
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;
/**
* @var bool Whether to reload the cart after the order is processed.
*/
protected $reload_cart = false;
/**
* PayPalPaymentMethod constructor.
*
* @param Settings $plugin_settings The settings.
* @param LoggerInterface $logger The logger.
* @param OrderProcessor $order_processor The Order processor.
*/
public function __construct(
Settings $plugin_settings,
LoggerInterface $logger
LoggerInterface $logger,
OrderProcessor $order_processor
) {
$this->plugin_settings = $plugin_settings;
$this->plugin_settings = $plugin_settings;
$this->response_templates = new ResponsesToApple();
$this->logger = $logger;
$this->logger = $logger;
$this->id = 'applepay';
$this->method_title = __( 'Apple Pay', 'woocommerce-paypal-payments' );
$this->order_processor = $order_processor;
}
/**
@ -73,7 +117,7 @@ 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'];
$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(
@ -85,7 +129,7 @@ class ApplepayPaymentMethod {
'PAYMENT',
'REFUND',
),
'seller_nonce' => $nonce
'seller_nonce' => $nonce,
),
),
),
@ -163,26 +207,27 @@ class ApplepayPaymentMethod {
* 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
* @throws \Exception
*/
public function update_shipping_contact(): void {
$applepay_request_data_object = $this->applepay_data_object_http();
if (!$this->is_nonce_valid()) {
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());
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')) {
if ( ! class_exists( 'WC_Countries' ) ) {
return;
}
$countries = $this->create_wc_countries();
$allowed_selling_countries = $countries->get_allowed_countries();
$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'];
$user_country = $applepay_request_data_object->simplified_contact()['country'];
$is_allowed_selling_country = array_key_exists(
$user_country,
$allowed_selling_countries
@ -192,24 +237,27 @@ class ApplepayPaymentMethod {
$user_country,
$allowed_shipping_countries
);
$product_need_shipping = $applepay_request_data_object->need_shipping();
$product_need_shipping = $applepay_request_data_object->need_shipping();
if (!$is_allowed_selling_country) {
if ( ! $is_allowed_selling_country ) {
$this->response_templates->response_with_data_errors(
[['errorCode' => 'addressUnserviceable']]
array( array( 'errorCode' => 'addressUnserviceable' ) )
);
return;
}
if ($product_need_shipping && !$is_allowed_shipping_country) {
if ( $product_need_shipping && ! $is_allowed_shipping_country ) {
$this->response_templates->response_with_data_errors(
[['errorCode' => 'addressUnserviceable']]
array( array( '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);
$cart_item_key = $this->prepare_cart($applepay_request_data_object);
$cart = WC()->cart;
$payment_details = $this->which_calculate_totals( $cart, $applepay_request_data_object );
$this->clear_current_cart($cart, $cart_item_key);
$this->reload_cart( $cart );
$response = $this->response_templates->apple_formatted_response( $payment_details );
$this->response_templates->response_success( $response );
}
/**
@ -217,27 +265,55 @@ class ApplepayPaymentMethod {
* 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
* @throws \Exception
*/
public function update_shipping_method(): void {
$applepay_request_data_object = $this->applepay_data_object_http();
if (!$this->is_nonce_valid()) {
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());
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);
$cart_item_key = $this->prepare_cart($applepay_request_data_object);
$cart = WC()->cart;
$payment_details = $this->which_calculate_totals( $cart, $applepay_request_data_object );
$this->clear_current_cart($cart, $cart_item_key);
$this->reload_cart( $cart );
$response = $this->response_templates->apple_formatted_response( $payment_details );
$this->response_templates->response_success( $response );
}
/**
* 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
*/
public function create_wc_order(): void {
public function create_wc_order() {
$this->response_after_successful_result();
$applepay_request_data_object = $this->applepay_data_object_http();
$applepay_request_data_object->order_data('productDetail');
$cart_item_key = $this->prepare_cart($applepay_request_data_object);
$cart = WC()->cart;
$this->which_calculate_totals($cart, $applepay_request_data_object );
if (! $cart_item_key) {
$this->response_templates->response_with_data_errors(
array(
array(
'errorCode' => 'unableToProcess',
'message' => 'Unable to process the order',
),
)
);
return;
}
$this->add_addresses_to_order($applepay_request_data_object);
//add_action('woocommerce_checkout_order_processed', array($this, 'process_order_as_paid'), 10, 3);
WC()->checkout()->process_checkout();
$this->clear_current_cart($cart, $cart_item_key);
$this->reload_cart( $cart );
}
/**
@ -269,9 +345,8 @@ class ApplepayPaymentMethod {
* Data Object to collect and validate all needed data collected
* through HTTP
*/
protected function applepay_data_object_http(): ApplePayDataObjectHttp
{
return new ApplePayDataObjectHttp($this->logger);
protected function applepay_data_object_http(): ApplePayDataObjectHttp {
return new ApplePayDataObjectHttp( $this->logger );
}
/**
@ -279,8 +354,7 @@ class ApplepayPaymentMethod {
*
* @return \WC_Countries
*/
protected function create_wc_countries()
{
protected function create_wc_countries() {
return new \WC_Countries();
}
@ -292,20 +366,23 @@ class ApplepayPaymentMethod {
* @return array|bool
*/
protected function which_calculate_totals(
$cart,
$applepay_request_data_object
) {
if ($applepay_request_data_object->caller_page === 'productDetail') {
$address = $applepay_request_data_object->shipping_address() ?? $applepay_request_data_object->simplified_contact();
if ( $applepay_request_data_object->caller_page === 'productDetail' ) {
if (! assert($cart instanceof WC_Cart)) {
return false;
}
return $this->calculate_totals_single_product(
$applepay_request_data_object->product_id(),
$applepay_request_data_object->product_quantity(),
$applepay_request_data_object->simplified_contact(),
$cart,
$address,
$applepay_request_data_object->shipping_method()
);
}
if ($applepay_request_data_object->caller_page === 'cart') {
if ( $applepay_request_data_object->caller_page === 'cart' ) {
return $this->calculate_totals_cart_page(
$applepay_request_data_object->simplified_contact(),
$address,
$applepay_request_data_object->shipping_method()
);
}
@ -321,50 +398,37 @@ class ApplepayPaymentMethod {
* @param $product_id
* @param $product_quantity
* @param $customer_address
* @param null $shipping_method
* @param null $shipping_method
*/
protected function calculate_totals_single_product(
$product_id,
$product_quantity,
$cart,
$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;
}
$results = array();
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) {
// 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',
[$shipping_method_id]
array( $shipping_method_id )
);
}
$cart_item_key = $cart->add_to_cart($product_id, $product_quantity);
if ($cart->needs_shipping()) {
if ( $cart->needs_shipping() ) {
list(
$shipping_methods_array, $selected_shipping_method
) = $this->cart_shipping_methods(
$cart,
$customer_address,
$shipping_method,
$shipping_method_id
);
$cart,
$customer_address,
$shipping_method,
$shipping_method_id
);
}
$cart->calculate_shipping();
$cart->calculate_fees();
$cart->calculate_totals();
@ -374,15 +438,7 @@ class ApplepayPaymentMethod {
$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) {
} catch ( Exception $exception ) {
}
return $results;
}
@ -392,9 +448,8 @@ class ApplepayPaymentMethod {
* 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();
protected function customer_address( array $address = array() ) {
$base_location = wc_get_base_location();
$shop_country_code = $base_location['country'];
WC()->customer->set_shipping_country(
$address['country'] ?? $shop_country_code
@ -425,62 +480,62 @@ class ApplepayPaymentMethod {
$shipping_method_id
): array {
$shipping_methods_array = [];
$shipping_methods = WC()->shipping->calculate_shipping(
$shipping_methods_array = array();
$shipping_methods = WC()->shipping->calculate_shipping(
$this->getShippingPackages(
$customer_address,
$cart->get_total('edit')
$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;
$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',
[$shipping_method_id]
array( $shipping_method_id )
);
}
}
$selected_shipping_method = $shipping_methods_array[0];
if ($shipping_method) {
if ( $shipping_method ) {
$selected_shipping_method = $shipping_method;
}
return [$shipping_methods_array, $selected_shipping_method];
return array( $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)
{
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 = 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);
return apply_filters( 'woocommerce_cart_shipping_packages', $packages );
}
/**
@ -495,22 +550,22 @@ class ApplepayPaymentMethod {
$selected_shipping_method,
$shipping_methods_array
): array {
$total = $cart->get_total('edit');
$total = round($total, 2);
return [
'subtotal' => $cart->get_subtotal(),
'shipping' => [
$total = $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()
'label' => $cart->needs_shipping()
? $selected_shipping_method['label'] : null,
],
),
'shippingMethods' => $cart->needs_shipping()
? $shipping_methods_array : null,
'taxes' => $cart->get_total_tax(),
'total' => $total,
];
'taxes' => $cart->get_total_tax(),
'total' => $total,
);
}
/**
@ -519,39 +574,39 @@ class ApplepayPaymentMethod {
* method
*
* @param $customer_address
* @param null $shipping_method
* @param null $shipping_method
*/
protected function calculate_totals_cart_page(
$customer_address = null,
$shipping_method = null
): array {
$results = [];
if (WC()->cart->is_empty()) {
return [];
$results = array();
if ( WC()->cart->is_empty() ) {
return array();
}
try {
$shipping_methods_array = [];
$selected_shipping_method = [];
//I just care about apple address details
$this->customer_address($customer_address);
$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) {
if ( $shipping_method ) {
WC()->session->set(
'chosen_shipping_methods',
[$shipping_method['identifier']]
array( $shipping_method['identifier'] )
);
}
if ($cart->needs_shipping()) {
if ( $cart->needs_shipping() ) {
list(
$shipping_methods_array, $selected_shipping_method
) = $this->cart_shipping_methods(
$cart,
$customer_address,
$shipping_method,
$shipping_method['identifier']
);
$cart,
$customer_address,
$shipping_method,
$shipping_method['identifier']
);
}
$cart->calculate_shipping();
$cart->calculate_fees();
@ -564,7 +619,7 @@ class ApplepayPaymentMethod {
);
$this->customer_address();
} catch (Exception $e) {
} catch ( Exception $e ) {
}
return $results;
@ -575,24 +630,22 @@ class ApplepayPaymentMethod {
*
* @param ApplePayDataObjectHttp $applepay_request_data_object
* @param $order
*
*/
protected function addAddressesToOrder(
protected function add_addresses_to_order(
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();
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
// 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');
$order->set_address( $billing_address, 'billing' );
$order->set_address( $shipping_address, 'shipping' );
}
},
10,
@ -603,10 +656,14 @@ class ApplepayPaymentMethod {
* 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);
protected function save_old_cart() {
$cart = WC()->cart;
if ( $cart->is_empty() || ! assert($cart instanceof WC_Cart)) {
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;
}
@ -614,20 +671,21 @@ class ApplepayPaymentMethod {
/**
* @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 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 );
}
}
protected function response_after_successful_result(): void
{
protected function response_after_successful_result(): void {
add_filter(
'woocommerce_payment_successful_result',
function ($result, $order_id) {
function ( $result, $order_id ) {
if (
isset($result['result'])
isset( $result['result'] )
&& 'success' === $result['result']
) {
$this->response_templates->response_success(
@ -641,7 +699,7 @@ class ApplepayPaymentMethod {
$this->response_templates->authorization_result_response(
'STATUS_FAILURE',
0,
[['errorCode' => 'unknown']]
array( array( 'errorCode' => 'unknown' ) )
)
);
}
@ -651,4 +709,41 @@ class ApplepayPaymentMethod {
2
);
}
/**
* @param WC_Cart|null $cart
* @param $cart_item_key
* @return void
*/
public function clear_current_cart(?WC_Cart $cart, $cart_item_key): void
{
$cart->remove_cart_item($cart_item_key);
$this->customer_address();
}
/**
* Removes the old cart, saves it, and creates a new one
* @param ApplePayDataObjectHttp $applepay_request_data_object
* @return bool | string The cart item key after adding to the new cart
* @throws \Exception
*/
public function prepare_cart(ApplePayDataObjectHttp $applepay_request_data_object): string
{
$this->save_old_cart();
$cart = WC()->cart;
return $cart->add_to_cart(
(int) $applepay_request_data_object->product_id(),
(int) $applepay_request_data_object->product_quantity());
}
public function process_order_as_paid($order_id): void
{
$order = wc_get_order($order_id);
if (!assert($order instanceof WC_Order)) {
return;
}
$order->payment_complete();
wc_reduce_stock_levels($order_id);
$order->save();
}
}

View file

@ -1,34 +1,43 @@
<?php
/**
* Prepares the necessary data for the Apple button script.
*
* @package WooCommerce\PayPalCommerce\Applepay
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Applepay;
/**
* Class DataToAppleButtonScripts
*/
class DataToAppleButtonScripts {
/**
* Sets the appropriate data to send to ApplePay script
* Data differs between product page and cart page
*
* @param bool $is_block
* @return array
*/
public function applePayScriptData( bool $isBlock = false ): array {
public function apple_pay_script_data( bool $is_block = false ): array {
$base_location = wc_get_base_location();
$shopCountryCode = $base_location['country'];
$currencyCode = get_woocommerce_currency();
$totalLabel = get_bloginfo( 'name' );
$shop_country_code = $base_location['country'];
$currency_code = get_woocommerce_currency();
$total_label = get_bloginfo( 'name' );
if ( is_product() ) {
return $this->dataForProductPage(
$shopCountryCode,
$currencyCode,
$totalLabel
return $this->data_for_product_page(
$shop_country_code,
$currency_code,
$total_label
);
}
if ( is_cart() || $isBlock ) {
return $this->dataForCartPage(
$shopCountryCode,
$currencyCode,
$totalLabel
if ( is_cart() || $is_block ) {
return $this->data_for_cart_page(
$shop_country_code,
$currency_code,
$total_label
);
}
return array();
@ -41,7 +50,7 @@ class DataToAppleButtonScripts {
*
* @return bool
*/
protected function checkIfNeedShipping( $product ) {
protected function check_if_need_shipping($product ) {
if (
! wc_shipping_enabled()
|| 0 === wc_get_shipping_method_count(
@ -60,64 +69,64 @@ class DataToAppleButtonScripts {
}
/**
* @param $shopCountryCode
* @param $currencyCode
* @param $totalLabel
* @param $shop_country_code
* @param $currency_code
* @param $tota_label
*
* @return array
*/
protected function dataForProductPage(
$shopCountryCode,
$currencyCode,
$totalLabel
protected function data_for_product_page(
$shop_country_code,
$currency_code,
$total_label
) {
$product = wc_get_product( get_the_id() );
if ( ! $product ) {
return array();
}
$isVariation = false;
$is_variation = false;
if ( $product->get_type() === 'variable' || $product->get_type() === 'variable-subscription' ) {
$isVariation = true;
$is_variation = true;
}
$productNeedShipping = $this->checkIfNeedShipping( $product );
$productId = get_the_id();
$productPrice = $product->get_price();
$productStock = $product->get_stock_status();
$product_need_shipping = $this->check_if_need_shipping( $product );
$product_id = get_the_id();
$product_price = $product->get_price();
$product_stock = $product->get_stock_status();
return array(
'product' => array(
'needShipping' => $productNeedShipping,
'id' => $productId,
'price' => $productPrice,
'isVariation' => $isVariation,
'stock' => $productStock,
'needShipping' => $product_need_shipping,
'id' => $product_id,
'price' => $product_price,
'isVariation' => $is_variation,
'stock' => $product_stock,
),
'shop' => array(
'countryCode' => $shopCountryCode,
'currencyCode' => $currencyCode,
'totalLabel' => $totalLabel,
'countryCode' => $shop_country_code,
'currencyCode' => $currency_code,
'totalLabel' => $total_label,
),
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
);
}
/**
* @param $shopCountryCode
* @param $currencyCode
* @param $totalLabel
* @param $shop_country_code
* @param $currency_code
* @param $total_label
*
* @return array
*/
protected function dataForCartPage(
$shopCountryCode,
$currencyCode,
$totalLabel
protected function data_for_cart_page(
$shop_country_code,
$currency_code,
$total_label
) {
$cart = WC()->cart;
$nonce = wp_nonce_field( 'woocommerce-process_checkout', 'woocommerce-process-checkout-nonce' );
$buttonMarkup =
$button_markup =
'<div id="applepay-container">'
. $nonce
. '</div>';
@ -127,12 +136,12 @@ class DataToAppleButtonScripts {
'subtotal' => $cart->get_subtotal(),
),
'shop' => array(
'countryCode' => $shopCountryCode,
'currencyCode' => $currencyCode,
'totalLabel' => $totalLabel,
'countryCode' => $shop_country_code,
'currencyCode' => $currency_code,
'totalLabel' => $total_label,
),
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'buttonMarkup' => $buttonMarkup,
'buttonMarkup' => $button_markup,
);
}
}

View file

@ -0,0 +1,176 @@
<?php
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Applepay;
use Psr\Log\LoggerInterface;
use WC_Payment_Gateway;
class ResponsesToApple {
/**
* Returns the authorization response with according success/fail status
* Adds the error list if provided to be handled by the script
* On success it adds the redirection url
*
* @param $status 0 => success, 1 => error
* @param string $order_id
* @param array $error_list
*
* @return array
*/
public function authorization_result_response(
$status,
$order_id = '',
$error_list = array(),
$return_url = ''
) {
$response = array();
if ( $status === 'STATUS_SUCCESS' ) {
$response['returnUrl'] = $return_url;
$response['responseToApple'] = array( 'status' => 0 );
} else {
$response = array(
'status' => 1,
'errors' => $this->apple_pay_error( $error_list ),
);
}
return $response;
}
/**
* Returns an error response to be handled by the script
*
* @param array $errorList [['errorCode'=>required, 'contactField'=>'']]
*
* @return void
*/
public function response_with_data_errors( $errorList ) {
$response = array();
$response['errors'] = $this->apple_pay_error( $errorList );
$response['newTotal'] = $this->apple_new_total_response(
0,
'pending'
);
wp_send_json_error( $response );
}
/**
* Creates a response formatted for ApplePay
*
* @return array
*/
public function apple_formatted_response( array $payment_details ) {
$response = array();
if ( $payment_details['shippingMethods'] ) {
$response['newShippingMethods'] = $payment_details['shippingMethods'];
}
$response['newLineItems'] = $this->apple_new_line_items_response(
$payment_details
);
$response['newTotal'] = $this->apple_new_total_response(
$payment_details['total']
);
return $response;
}
/**
* Returns a success response to be handled by the script
*/
public function response_success( array $response ) {
wp_send_json_success( $response );
}
/**
* Creates an array of errors formatted
*
* @param array $error_list
* @param array $errors
*
* @return array
*/
protected function apple_pay_error( $error_list, $errors = array() ) {
foreach ( $error_list as $error ) {
$errors[] = array(
'code' => $error['errorCode'],
'contactField' => $error['contactField'] ?? null,
'message' => array_key_exists( 'contactField', $error )
? sprintf( 'Missing %s', $error['contactField'] ) : '',
);
}
return $errors;
}
/**
* Creates NewTotals line
*
* @param $total
*
* @param string $type
*
* @return array
*/
protected function apple_new_total_response( $total, string $type = 'final' ): array {
return $this->apple_item_format(
get_bloginfo( 'name' ),
$total,
$type
);
}
/**
* Creates item line
*
* @param $subtotal_label
* @param $subtotal
* @param $type
*
* @return array
*/
protected function apple_item_format( $subtotal_label, $subtotal, $type ): array {
return array(
'label' => $subtotal_label,
'amount' => $subtotal,
'type' => $type,
);
}
/**
* Creates NewLineItems line
*
* @return array[]
*/
protected function apple_new_line_items_response( array $payment_details ): array {
$type = 'final';
$response = array();
$response[] = $this->apple_item_format(
'Subtotal',
round( floatval( $payment_details['subtotal'] ), 2 ),
$type
);
if ( $payment_details['shipping']['amount'] ) {
$response[] = $this->apple_item_format(
$payment_details['shipping']['label'] ?: '',
round( floatval( $payment_details['shipping']['amount'] ), 2 ),
$type
);
}
$isset_fee_amount = isset( $payment_details['fee'] ) && isset( $payment_details['fee']['amount'] );
if ( $isset_fee_amount ) {
$response[] = $this->apple_item_format(
$payment_details['fee']['label'] ?: '',
round( floatval( $payment_details['fee']['amount'] ), 2 ),
$type
);
}
$response[] = $this->apple_item_format( 'Estimated Tax', round( floatval( $payment_details['taxes'] ), 2 ), $type );
return $response;
}
}

View file

@ -1162,7 +1162,14 @@ class SmartButton implements SmartButtonInterface {
if ( $this->dcc_is_enabled() ) {
$components[] = 'hosted-fields';
}
//return filterable array of components
/**
* Filter to add further components from the extensions.
*
* @internal Matches filter name in ApplePay extension.
* @since TODO
*
* @param array $components The array of components already registered.
*/
return apply_filters( 'woocommerce_paypal_payments_sdk_components_hook', $components );
}