Full transaction with apple

This commit is contained in:
carmenmaymo 2023-10-16 15:49:28 +02:00
parent 327a87dfe8
commit 0882095602
No known key found for this signature in database
GPG key ID: 6023F686B0F3102E
10 changed files with 160 additions and 264 deletions

View file

@ -27,25 +27,28 @@
}
}
.ppcp-has-googlepay-block {
.ppcp-has-applepay-block {
.wp-block-woocommerce-checkout {
#applepay-container {
margin: 0;
height: 40px;
--apple-pay-button-margin: 0;
--apple-pay-button-height: 40px;
&.ppcp-button-pill {
--apple-pay-button-border-radius: 50px;
}
}
}
.wp-block-woocommerce-cart {
#applepay-container {
margin: 0;
height: 40px;
--apple-pay-button-margin: 0;
--apple-pay-button-height: 40px;
}
/* Workaround for blocks grid */
.wc-block-components-express-payment__event-buttons {
display: block;
--apple-pay-button-display: block;
li[id*="express-payment-method-ppcp-"] {
padding-bottom: 0;
--apple-pay-button-padding-bottom: 0;
}
}
}

View file

@ -34,7 +34,6 @@ class ApplepayButton {
}
init(config) {
console.log('[ApplePayButton] init', config);
if (this.isInitialized) {
return;
}
@ -180,7 +179,6 @@ class ApplepayButton {
console.error(error);
}
const session = this.applePaySession(paymentDataRequest)
console.log("session", session)
const formValidator = PayPalCommerceGateway.early_checkout_validation_enabled ?
new FormValidator(
PayPalCommerceGateway.ajax.validate_checkout.endpoint,
@ -191,9 +189,7 @@ class ApplepayButton {
const errors = await formValidator.validate(document.querySelector(checkoutFormSelector));
if (errors.length > 0) {
errorHandler.messages(errors);
// fire WC event for other plugins
jQuery( document.body ).trigger( 'checkout_error' , [ errorHandler.currentHtml() ] );
// stop Apple Pay payment sheet from showing
session.abort();
return;
}
@ -222,8 +218,8 @@ class ApplepayButton {
countryCode: applepayConfig.countryCode,
merchantCapabilities: applepayConfig.merchantCapabilities,
supportedNetworks: applepayConfig.supportedNetworks,
requiredShippingContactFields: ["postalAddress"],
requiredBillingContactFields: ["postalAddress"]
requiredShippingContactFields: ["postalAddress", "email", "phone"],
requiredBillingContactFields: ["postalAddress", "email", "phone"],
}
const paymentDataRequest = Object.assign({}, baseRequest);
paymentDataRequest.currencyCode = buttonConfig.shop.currencyCode;
@ -242,7 +238,6 @@ class ApplepayButton {
//------------------------
onvalidatemerchant(session) {
console.log("onvalidatemerchant")
return (applePayValidateMerchantEvent) => {
paypal.Applepay().validateMerchant({
validationUrl: applePayValidateMerchantEvent.validationURL
@ -259,10 +254,8 @@ class ApplepayButton {
'woocommerce-process-checkout-nonce': this.nonce,
}
})
console.log('validated')
})
.catch(validateError => {
console.error(validateError);
//call backend to update validation to false
jQuery.ajax({
url: this.buttonConfig.ajax_url,
@ -279,7 +272,6 @@ class ApplepayButton {
}
onshippingmethodselected(session) {
const ajax_url = this.buttonConfig.ajax_url
console.log('[ApplePayButton] onshippingmethodselected');
return (event) => {
const data = this.getShippingMethodData(event);
jQuery.ajax({
@ -291,7 +283,6 @@ class ApplepayButton {
if (applePayShippingMethodUpdate.success === false) {
response.errors = createAppleErrors(response.errors)
}
console.log('shipping method update response', response, applePayShippingMethodUpdate)
this.selectedShippingMethod = event.shippingMethod
//order the response shipping methods, so that the selected shipping method is the first one
let orderedShippingMethods = response.newShippingMethods.sort((a, b) => {
@ -316,10 +307,8 @@ class ApplepayButton {
}
onshippingcontactselected(session) {
const ajax_url = this.buttonConfig.ajax_url
console.log('[ApplePayButton] onshippingcontactselected', ajax_url, session)
return (event) => {
const data = this.getShippingContactData(event);
console.log('shipping contact selected', data, event)
jQuery.ajax({
url: ajax_url,
method: 'POST',
@ -327,7 +316,6 @@ class ApplepayButton {
success: (applePayShippingContactUpdate, textStatus, jqXHR) => {
let response = applePayShippingContactUpdate.data
this.updated_contact_info = event.shippingContact
console.log('shipping contact update response', response, applePayShippingContactUpdate, this.updated_contact_info)
if (applePayShippingContactUpdate.success === false) {
response.errors = createAppleErrors(response.errors)
}
@ -399,166 +387,91 @@ class ApplepayButton {
}
onpaymentauthorized(session) {
/*return (event) => {
return async (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,
);
let createOrderInPayPal = actionHandler.createOrder()
const processInWooAndCapture = async (data) => {
return new Promise((resolve, reject) => {
try {
console.log('processInWooAndCapture', data)
const billingContact = data.billing_contact
const shippingContact = data.shipping_contact
jQuery.ajax({
url: ajaxUrl,
method: 'POST',
data: {
let request_data = {
action: 'ppcp_create_order',
'product_id': productId,
'product_quantity': this.productQuantity,
'caller_page': this.context,
'product_id': this.buttonConfig.product.id ?? null,
'product_quantity': this.productQuantity ?? null,
'shipping_contact': shippingContact,
'billing_contact': billingContact,
'token': event.payment.token,
'shipping_method': selectedShippingMethod,
'woocommerce-process-checkout-nonce': nonce,
'shipping_method': this.selectedShippingMethod,
'woocommerce-process-checkout-nonce': this.nonce,
'funding_source': 'applepay',
'_wp_http_referer': '/?wc-ajax=update_order_review',
'paypal_order_id': data.paypal_order_id,
},
};
jQuery.ajax({
url: this.buttonConfig.ajax_url,
method: 'POST',
data: request_data,
complete: (jqXHR, textStatus) => {
},
success: (authorizationResult, textStatus, jqXHR) => {
console.log('success authorizationResult', authorizationResult)
if (authorizationResult.result === "success") {
redirectionUrl = authorizationResult.redirect;
//session.completePayment(ApplePaySession.STATUS_SUCCESS)
window.location.href = redirectionUrl
} else {
//session.completePayment(ApplePaySession.STATUS_FAILURE)
}
resolve(authorizationResult)
},
error: (jqXHR, textStatus, errorThrown) => {
console.log('error authorizationResult', errorThrown)
session.completePayment(ApplePaySession.STATUS_FAILURE)
console.warn(textStatus, errorThrown)
session.abort()
reject(new Error(errorThrown));
},
})
} catch (error) {
console.log(error) // handle error
}
});
}
createOrderInPayPal([], []).then((orderId) => {
console.log('createOrderInPayPal', orderId)
paypal.Applepay().confirmOrder(
{
orderId: orderId,
let id = await this.contextHandler.createOrder();
try {
const confirmOrderResponse = await paypal.Applepay().confirmOrder({
orderId: id,
token: event.payment.token,
billingContact: event.payment.billingContact
}
).then(
() => {
session.completePayment(ApplePaySession.STATUS_SUCCESS);
billingContact: event.payment.billingContact,
});
if (confirmOrderResponse && confirmOrderResponse.approveApplePayPayment) {
if (confirmOrderResponse.approveApplePayPayment.status === "APPROVED") {
try {
let data = {
billing_contact: event.payment.billingContact,
shipping_contact: event.payment.shippingContact,
paypal_order_id: orderId
paypal_order_id: id,
};
let authorizationResult = await processInWooAndCapture(data);
if (authorizationResult.result === "success") {
session.completePayment(ApplePaySession.STATUS_SUCCESS)
window.location.href = authorizationResult.redirect
} else {
session.completePayment(ApplePaySession.STATUS_FAILURE)
}
processInWooAndCapture(data)
}
).catch(err => {
console.error('Error confirming order with applepay token');
} catch (error) {
session.completePayment(ApplePaySession.STATUS_FAILURE);
console.error(err);
}
);
}).catch((error) => {
console.log(error)
session.abort()
})
};*/
console.error(error);
}
/* onPaymentAuthorized(paymentData) {
console.log('[ApplePayButton] onPaymentAuthorized', this.context);
return this.processPayment(paymentData);
}
async processPayment(paymentData) {
console.log('[ApplePayButton] processPayment', this.context);
return new Promise(async (resolve, reject) => {
try {
let id = await this.contextHandler.createOrder();
console.log('[ApplePayButton] processPayment: createOrder', id, this.context);
const confirmOrderResponse = await paypal.Applepay().confirmOrder({
orderId: id,
paymentMethodData: paymentData.paymentMethodData
});
console.log('[ApplePayButton] processPayment: confirmOrder', confirmOrderResponse, this.context);
/!** Capture the Order on the Server *!/
if (confirmOrderResponse.status === "APPROVED") {
let approveFailed = false;
await this.contextHandler.approveOrderForContinue({
orderID: id
}, {
restart: () => new Promise((resolve, reject) => {
approveFailed = true;
resolve();
})
});
if (!approveFailed) {
resolve(this.processPaymentResponse('SUCCESS'));
} else {
resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', 'FAILED TO APPROVE'));
console.error('Error status is not APPROVED');
session.completePayment(ApplePaySession.STATUS_FAILURE);
}
} else {
resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', 'TRANSACTION FAILED'));
console.error('Invalid confirmOrderResponse');
session.completePayment(ApplePaySession.STATUS_FAILURE);
}
} catch(err) {
resolve(this.processPaymentResponse('ERROR', 'PAYMENT_AUTHORIZATION', err.message));
} catch (error) {
console.error('Error confirming order with applepay token', error);
session.completePayment(ApplePaySession.STATUS_FAILURE);
session.abort()
}
});
};
}
processPaymentResponse(state, intent = null, message = null) {
let response = {
transactionState: state,
}
if (intent || message) {
response.error = {
intent: intent,
message: message,
}
}
console.log('[ApplePayButton] processPaymentResponse', response, this.context);
return response;
}*/
fill_billing_contact(form_saved) {
return {
givenName: form_saved.billing_first_name ?? '',

View file

@ -8,7 +8,6 @@ class ApplepayManager {
this.buttonConfig = buttonConfig;
this.ppcpConfig = ppcpConfig;
this.ApplePayConfig = null;
console.log('Applepay manager', ppcpConfig, buttonConfig)
this.buttons = [];
buttonModuleWatcher.watchContextBootstrap((bootstrap) => {

View file

@ -1,22 +0,0 @@
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

@ -1,13 +0,0 @@
import {buttonID} from "./utils";
export const maybeShowButton = () => {
const {ApplePaySession} = window
const applePayMethodElement = document.querySelector(
'#' + buttonID,
)
const canShowButton = applePayMethodElement && (ApplePaySession && ApplePaySession.canMakePayments())
if (!canShowButton) {
console.error('This device does not support Apple Pay');
return false
}
return true
}

View file

@ -14,14 +14,7 @@ if (typeof window.PayPalCommerceGateway === 'undefined') {
window.PayPalCommerceGateway = ppcpConfig;
}
console.log('ppcpData', ppcpData);
console.log('ppcpConfig', ppcpConfig);
console.log('buttonData', buttonData);
console.log('buttonConfig', buttonConfig);
const ApplePayComponent = () => {
console.log('ApplePayComponent render');
const [bootstrapped, setBootstrapped] = useState(false);
const [paypalLoaded, setPaypalLoaded] = useState(false);
const [applePayLoaded, setApplePayLoaded] = useState(false);
@ -30,6 +23,12 @@ const ApplePayComponent = () => {
const manager = new ApplepayManager(buttonConfig, ppcpConfig);
manager.init();
};
useEffect(() => {
const bodyClass = 'ppcp-has-applepay-block';
if (!document.body.classList.contains(bodyClass)) {
document.body.classList.add(bodyClass);
}
}, []);
useEffect(() => {
// Load ApplePay SDK

View file

@ -25,7 +25,6 @@ import ApplepayManager from "./ApplepayManager";
}
const isMiniCart = ppcpConfig.mini_cart_buttons_enabled;
const isButton = jQuery('#' + buttonConfig.button.wrapper).length > 0;
console.log('isbutton' ,isButton, buttonConfig.button.wrapper)
// If button wrapper is not present then there is no need to load the scripts.
// minicart loads later?
if (!isMiniCart && !isButton) {

View file

@ -136,7 +136,8 @@ class ApplepayModule implements ModuleInterface {
assert( $button instanceof ApplePayButton );
$smart_button = $c->get( 'button.smart-button' );
assert( $smart_button instanceof SmartButtonInterface );
if ( $smart_button->should_load_ppcp_script() ) {
$page_has_block = has_block( 'woocommerce/checkout' ) || has_block( 'woocommerce/cart' );
if ( $smart_button->should_load_ppcp_script() || $page_has_block ) {
$button->enqueue();
}
}

View file

@ -232,14 +232,6 @@ class ApplePayButton implements ButtonInterface {
'wp_ajax_nopriv_' . PropertiesDictionary::CREATE_ORDER,
array( $this, 'create_wc_order' )
);
add_action(
'wp_ajax_' . PropertiesDictionary::CREATE_ORDER_CART,
array( $this, 'create_wc_order_from_cart' )
);
add_action(
'wp_ajax_nopriv_' . PropertiesDictionary::CREATE_ORDER_CART,
array( $this, 'create_wc_order_from_cart' )
);
add_action(
'wp_ajax_' . PropertiesDictionary::UPDATE_SHIPPING_CONTACT,
array( $this, 'update_shipping_contact' )
@ -400,8 +392,22 @@ class ApplePayButton implements ButtonInterface {
*/
public function create_wc_order(): void {
$applepay_request_data_object = $this->applepay_data_object_http();
$applepay_request_data_object->order_data( 'productDetail' );
//phpcs:disable WordPress.Security.NonceVerification
$context = wc_clean( wp_unslash( $_POST['caller_page'] ?? '' ) );
if ( ! is_string( $context ) ) {
$this->response_templates->response_with_data_errors(
array(
array(
'errorCode' => 'unableToProcess',
'message' => 'Unable to process the order',
),
)
);
return;
}
$applepay_request_data_object->order_data( $context );
$this->update_posted_data( $applepay_request_data_object );
if ( $context == 'product' ) {
$cart_item_key = $this->prepare_cart( $applepay_request_data_object );
$cart = WC()->cart;
$address = $applepay_request_data_object->shipping_address();
@ -421,7 +427,6 @@ class ApplePayButton implements ButtonInterface {
);
return;
}
$this->add_addresses_to_order( $applepay_request_data_object );
add_filter(
'woocommerce_payment_successful_result',
function ( array $result ) use ( $cart, $cart_item_key ) : array {
@ -433,15 +438,9 @@ class ApplePayButton implements ButtonInterface {
return $result;
}
);
WC()->checkout()->process_checkout();
}
/**
* 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
*/
public function create_wc_order_from_cart(): void {
$this->add_addresses_to_order( $applepay_request_data_object );
WC()->checkout()->process_checkout();
}
@ -655,10 +654,10 @@ class ApplePayButton implements ButtonInterface {
$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']['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']['postcode'] = $customer_address['postcode'] ?? '';
$packages[0]['destination']['city'] = $customer_address['city'] ?? '';
$packages[0]['destination']['address'] = '';
$packages[0]['destination']['address_2'] = '';
@ -1016,6 +1015,12 @@ class ApplePayButton implements ButtonInterface {
'wc_ppcp_applepay',
$this->script_data()
);
add_action(
'wp_enqueue_scripts',
function () {
wp_enqueue_script( 'wc-ppcp-applepay' );
}
);
}
/**

View file

@ -71,14 +71,14 @@ class ApplePayDataObjectHttp {
*
* @var string[]
*/
protected $billing_address = array();
protected $billing_contact = array();
/**
* The shipping address.
*
* @var string[]
*/
protected $shipping_address = array();
protected $shipping_contact = array();
/**
* The list of errors.
@ -217,17 +217,11 @@ class ApplePayDataObjectHttp {
* @param string $caller_page The caller page.
*/
public function order_data( string $caller_page ): void {
$nonce = filter_input( INPUT_POST, 'woocommerce-process-checkout-nonce', FILTER_SANITIZE_SPECIAL_CHARS );
if ( ! $nonce ) {
return;
}
$is_nonce_valid = wp_verify_nonce(
$nonce,
'woocommerce-process_checkout'
);
if ( ! $is_nonce_valid ) {
if ( ! $this->is_nonce_valid() ) {
return;
}
//phpcs:disable WordPress.Security.NonceVerification
$data = filter_var_array( $_POST, FILTER_SANITIZE_SPECIAL_CHARS );
if ( ! $data ) {
return;
@ -238,6 +232,7 @@ class ApplePayDataObjectHttp {
PropertiesDictionary::CREATE_ORDER_SINGLE_PROD_REQUIRED_FIELDS,
PropertiesDictionary::CREATE_ORDER_CART_REQUIRED_FIELDS
);
if ( ! $result ) {
return;
}
@ -254,12 +249,12 @@ class ApplePayDataObjectHttp {
}
$filtered_shipping_contact = $data[ PropertiesDictionary::SHIPPING_CONTACT ];
$this->shipping_address = $this->complete_address(
$this->shipping_contact = $this->complete_address(
$filtered_shipping_contact,
PropertiesDictionary::SHIPPING_CONTACT_INVALID
);
$filtered_billing_contact = $data[ PropertiesDictionary::BILLING_CONTACT ];
$this->billing_address = $this->complete_address(
$this->billing_contact = $this->complete_address(
$filtered_billing_contact,
PropertiesDictionary::BILLING_CONTACT_INVALID
);
@ -310,6 +305,7 @@ class ApplePayDataObjectHttp {
}
$this->$key = $value;
}
}
/**
@ -436,7 +432,7 @@ class ApplePayDataObjectHttp {
$required_fields = $required_product_fields;
if (
isset( $data[ PropertiesDictionary::CALLER_PAGE ] )
&& $data[ PropertiesDictionary::CALLER_PAGE ] === 'cart'
&& $data[ PropertiesDictionary::CALLER_PAGE ] !== 'product'
) {
$required_fields = $required_cart_fields;
}
@ -490,7 +486,7 @@ class ApplePayDataObjectHttp {
* @return string[]
*/
public function billing_address(): array {
return $this->billing_address;
return $this->billing_contact;
}
/**
@ -499,7 +495,7 @@ class ApplePayDataObjectHttp {
* @return string[]
*/
public function shipping_address(): array {
return $this->shipping_address;
return $this->shipping_contact;
}
/**
@ -607,4 +603,20 @@ class ApplePayDataObjectHttp {
)
);
}
/**
* Returns if the nonce is valid.
*
* @return bool
*/
public function is_nonce_valid():bool {
$nonce = filter_input( INPUT_POST, 'woocommerce-process-checkout-nonce', FILTER_SANITIZE_SPECIAL_CHARS );
if ( ! $nonce ) {
return false;
}
return (bool) wp_verify_nonce(
$nonce,
'woocommerce-process_checkout'
);
}
}