Skip to content

Commit

Permalink
Merge branch 'develop' into fix/csv-export-localization-locale
Browse files Browse the repository at this point in the history
  • Loading branch information
mordeth authored Feb 13, 2024
2 parents c9ced32 + fc8c838 commit d599e88
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 3 deletions.
4 changes: 4 additions & 0 deletions changelog/fix-4444-payment-request-bookable-product-support
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Add support for bookable products to payment request buttons on product pages.
13 changes: 13 additions & 0 deletions client/checkout/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,19 @@ export default class WCPayAPI {
} );
}

/**
* Empty the cart.
*
* @param {number} bookingId Booking ID (optional).
* @return {Promise} Promise for the request to the server.
*/
paymentRequestEmptyCart( bookingId ) {
return this.request( getPaymentRequestAjaxURL( 'empty_cart' ), {
security: getPaymentRequestData( 'nonce' )?.empty_cart,
booking_id: bookingId,
} );
}

/**
* Get selected product data from variable product page.
*
Expand Down
48 changes: 46 additions & 2 deletions client/payment-request/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ jQuery( ( $ ) => {
.val();
}

if ( $( '.wc-bookings-booking-form' ).length ) {
productId = $( '.wc-booking-product-id' ).val();
}

const data = {
product_id: productId,
qty: $( '.quantity .qty' ).val(),
Expand All @@ -187,10 +191,10 @@ jQuery( ( $ ) => {
: [],
};

// Add addons data to the POST body
// Add extension data to the POST body
const formData = $( 'form.cart' ).serializeArray();
$.each( formData, ( i, field ) => {
if ( /^addon-/.test( field.name ) ) {
if ( /^(addon-|wc_)/.test( field.name ) ) {
if ( /\[\]$/.test( field.name ) ) {
const fieldName = field.name.substring(
0,
Expand Down Expand Up @@ -295,6 +299,10 @@ jQuery( ( $ ) => {
.val();
}

if ( $( '.wc-bookings-booking-form' ).length ) {
productId = $( '.wc-booking-product-id' ).val();
}

const addons =
$( '#product-addons-total' ).data( 'price_data' ) || [];
const addonValue = addons.reduce(
Expand Down Expand Up @@ -616,4 +624,40 @@ jQuery( ( $ ) => {
$( document.body ).on( 'updated_checkout', () => {
wcpayPaymentRequest.init();
} );

// Handle bookable products on the product page.
let wcBookingFormChanged = false;

$( document.body )
.off( 'wc_booking_form_changed' )
.on( 'wc_booking_form_changed', () => {
wcBookingFormChanged = true;
} );

// Listen for the WC Bookings wc_bookings_calculate_costs event to complete
// and add the bookable product to the cart, using the response to update the
// payment request request params with correct totals.
$( document ).ajaxComplete( function ( event, xhr, settings ) {
if ( wcBookingFormChanged ) {
if (
settings.url === window.booking_form_params.ajax_url &&
settings.data.includes( 'wc_bookings_calculate_costs' ) &&
xhr.responseText.includes( 'SUCCESS' )
) {
wcpayPaymentRequest.blockPaymentRequestButton();
wcBookingFormChanged = false;
return wcpayPaymentRequest.addToCart().then( ( response ) => {
wcpayPaymentRequestParams.product.total = response.total;
wcpayPaymentRequestParams.product.displayItems =
response.displayItems;
// Empty the cart to avoid having 2 products in the cart when payment request is not used.
api.paymentRequestEmptyCart( response.bookingId );

wcpayPaymentRequest.init();

wcpayPaymentRequest.unblockPaymentRequestButton();
} );
}
}
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,7 @@ public function scripts() {
'update_shipping' => wp_create_nonce( 'wcpay-update-shipping-method' ),
'checkout' => wp_create_nonce( 'woocommerce-process_checkout' ),
'add_to_cart' => wp_create_nonce( 'wcpay-add-to-cart' ),
'empty_cart' => wp_create_nonce( 'wcpay-empty-cart' ),
'get_selected_product_data' => wp_create_nonce( 'wcpay-get-selected-product-data' ),
'platform_tracker' => wp_create_nonce( 'platform_tracks_nonce' ),
'pay_for_order' => wp_create_nonce( 'pay_for_order' ),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public function __construct( WC_Payment_Gateway_WCPay $gateway, WC_Payments_Paym

if ( $is_woopay_enabled || $is_payment_request_enabled ) {
add_action( 'wc_ajax_wcpay_add_to_cart', [ $this->express_checkout_helper, 'ajax_add_to_cart' ] );
add_action( 'wc_ajax_wcpay_empty_cart', [ $this->express_checkout_helper, 'ajax_empty_cart' ] );

add_action( 'woocommerce_after_add_to_cart_form', [ $this, 'display_express_checkout_buttons' ], 1 );
add_action( 'woocommerce_proceed_to_checkout', [ $this, 'display_express_checkout_buttons' ], 21 );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,65 @@ public function ajax_add_to_cart() {
WC()->cart->add_to_cart( $product->get_id(), $quantity, $variation_id, $attributes );
}

if ( in_array( $product_type, [ 'simple', 'variation', 'subscription', 'subscription_variation', 'bundle', 'mix-and-match' ], true ) ) {
if ( in_array( $product_type, [ 'simple', 'variation', 'subscription', 'subscription_variation', 'booking', 'bundle', 'mix-and-match' ], true ) ) {
WC()->cart->add_to_cart( $product->get_id(), $quantity );
}

WC()->cart->calculate_totals();

if ( 'booking' === $product_type ) {
$booking_id = $this->get_booking_id_from_cart();
}

$data = [];
$data += $this->build_display_items();
$data['result'] = 'success';

if ( $booking_id ) {
$data['bookingId'] = $booking_id;
}

wp_send_json( $data );
}

/**
* Gets the booking id from the cart.
* It's expected that the cart only contains one item which was added via ajax_add_to_cart.
* Used to remove the booking from WC Bookings in-cart status.
*
* @return int|false
*/
public function get_booking_id_from_cart() {
$cart = WC()->cart->get_cart();
$cart_item = reset( $cart );

if ( $cart_item && isset( $cart_item['booking']['_booking_id'] ) ) {
return $cart_item['booking']['_booking_id'];
}

return false;
}

/**
* Empties the cart via AJAX. Used on the product page.
*/
public function ajax_empty_cart() {
check_ajax_referer( 'wcpay-empty-cart', 'security' );

$booking_id = isset( $_POST['booking_id'] ) ? absint( $_POST['booking_id'] ) : null;

WC()->cart->empty_cart();

if ( $booking_id ) {
// When a bookable product is added to the cart, a 'booking' is create with status 'in-cart'.
// This status is used to prevent the booking from being booked by another customer
// and should be removed when the cart is emptied for PRB purposes.
do_action( 'wc-booking-remove-inactive-cart', $booking_id ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
}

wp_send_json( [ 'result' => 'success' ] );
}

/**
* Builds the line items to pass to Payment Request
*
Expand Down

0 comments on commit d599e88

Please sign in to comment.