Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c1017f4
feat(modal-checkout): support "My Account"
miguelpeixe May 13, 2025
63077fa
fix: change open checkout api approach
miguelpeixe May 20, 2025
0b6932d
chore: organize utility
miguelpeixe May 20, 2025
4a66e9f
fix: docblock
miguelpeixe May 20, 2025
43c3315
feat: move `getOrderDetails()`; dynamic `action_type`; `subscription_…
miguelpeixe May 20, 2025
b200baf
Merge branch 'trunk' into feat/modal-checkout-my-account
miguelpeixe May 20, 2025
edd0696
fix: set modal title early
miguelpeixe May 21, 2025
94d8631
fix: prevent emptyCart() from crashing js
miguelpeixe May 21, 2025
3e7ea64
fix: properly catch fetch error
miguelpeixe May 21, 2025
6e14ecb
feat: improve options
miguelpeixe May 21, 2025
31d84ba
fix: detach onClose after close
miguelpeixe May 21, 2025
bc49dea
chore: inline comment
miguelpeixe May 21, 2025
8b6dcd5
chore: fix docblock
miguelpeixe May 21, 2025
87b20f6
feat: include order id
miguelpeixe May 21, 2025
e711857
fix: typo
miguelpeixe May 21, 2025
4f5da8b
feat: support subscription renewal id
miguelpeixe May 21, 2025
a13264a
fix: consolidate tracking checkout data
miguelpeixe May 23, 2025
5f85c5e
fix: consolidate modal checkout data
miguelpeixe May 23, 2025
d0da095
Merge branch 'trunk' into feat/modal-checkout-my-account
miguelpeixe May 23, 2025
78527dc
fix: price summary method
miguelpeixe May 23, 2025
7d64661
fix: apply price summary
miguelpeixe May 23, 2025
de679f5
fix: avoid repeated event listeners
miguelpeixe May 23, 2025
14498e3
fix: consolidate form data and checkout data
miguelpeixe May 26, 2025
33f6b22
fix: account for membership product type
miguelpeixe May 26, 2025
8f11487
Merge branch 'trunk' into feat/modal-checkout-my-account
miguelpeixe May 28, 2025
a435ca4
fix: ensure container and summary text node exists
miguelpeixe May 28, 2025
5b08f3d
fix: fetch gate and popup data from cart too
miguelpeixe May 28, 2025
d734a65
fix: remove superfluous params
miguelpeixe May 29, 2025
0990e6e
Merge branch 'trunk' into feat/modal-checkout-my-account
miguelpeixe May 29, 2025
0a00a46
fix(checkout-button): action hook for hidden fields
miguelpeixe May 29, 2025
e74c77c
fix: control GA event keys
miguelpeixe May 29, 2025
8372718
fix: add `action` event key
miguelpeixe May 29, 2025
f2f2466
Merge branch 'trunk' into feat/modal-checkout-my-account
miguelpeixe May 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 32 additions & 106 deletions includes/class-modal-checkout.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Newspack_Blocks;

use Newspack_Blocks\Tracking\Data_Events;
use Newspack_Blocks\Modal_Checkout\Checkout_Data;

defined( 'ABSPATH' ) || exit;

Expand Down Expand Up @@ -710,8 +710,6 @@ class="<?php echo esc_attr( "$class_prefix {$class_prefix}__modal-container news
$variation_name = wc_get_formatted_variation( $variation, true );
$price = $variation->get_price();
$price_html = $variation->get_price_html();
$frequency = '';
$product_type = Data_Events::get_product_type( $product_id );

// Use suggested price if NYP is active and set for variation.
if ( \Newspack_Blocks::can_use_name_your_price() && \WC_Name_Your_Price_Helpers::is_nyp( $variation_id ) ) {
Expand All @@ -722,29 +720,6 @@ class="<?php echo esc_attr( "$class_prefix {$class_prefix}__modal-container news
}
}

if ( class_exists( '\WC_Subscriptions_Product' ) && \WC_Subscriptions_Product::is_subscription( $variation ) ) {
$frequency = \WC_Subscriptions_Product::get_period( $variation );
}

$name = sprintf(
/* translators: 1: variable product name, 2: product variation name */
__( '%1$s - %2$s', 'newspack-blocks' ),
$product_name,
$variation_name
);
$product_price_summary = self::get_summary_card_price_string( $name, $price, $frequency );
$product_data = [
'amount' => $price,
'action_type' => 'checkout_button',
'currency' => function_exists( 'get_woocommerce_currency' ) ? \get_woocommerce_currency() : 'USD',
'product_price_summary' => $product_price_summary,
'product_id' => (string) $product_id,
'product_type' => $product_type,
'recurrence' => ! empty( $frequency ) ? $frequency : 'once',
'referrer' => substr( \get_permalink(), strlen( home_url() ) ), // TODO: Is this OK?
'variation_id' => (string) $variation_id,
];

// Replace nyp price html for variations.
if ( class_exists( '\WC_Name_Your_Price_Helpers' ) && \WC_Name_Your_Price_Helpers::is_nyp( $variation->get_id() ) ) {
$price_html = str_replace( ':', '', $price_html );
Expand All @@ -757,7 +732,7 @@ class="<?php echo esc_attr( "$class_prefix {$class_prefix}__modal-container news
<span class="price"><?php echo $price_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
</div>
<div class="variation"><?php echo esc_html( $variation_name ); ?></div>
<form data-product="<?php echo esc_attr( wp_json_encode( $product_data ) ); ?>">
<form data-checkout="<?php echo esc_attr( wp_json_encode( Checkout_Data::get_checkout_data( $variation ) ) ); ?>">
<input type="hidden" name="newspack_checkout" value="1" />
<button type="submit" class="<?php echo esc_attr( "{$class_prefix}__button {$class_prefix}__button--primary" ); ?> newspack-modal-checkout-variation-selection"><?php echo esc_html( self::get_modal_checkout_labels( 'checkout_confirm_variation' ) ); ?></button>
</form>
Expand Down Expand Up @@ -1025,6 +1000,7 @@ public static function enqueue_modal( $product_id = null ) {
'newspack_class_prefix' => self::get_class_prefix(),
'is_registration_required' => self::is_registration_required(),
'has_unsupported_payment_gateway' => self::has_unsupported_payment_gateway(),
'checkout_url' => remove_query_arg( 'my_account_checkout', add_query_arg( 'modal_checkout', '1', wc_get_checkout_url() ) ),
'labels' => [
'auth_modal_title' => self::get_modal_checkout_labels( 'auth_modal_title' ),
'checkout_modal_title' => self::get_modal_checkout_labels( 'checkout_modal_title' ),
Expand Down Expand Up @@ -1111,29 +1087,23 @@ public static function get_checkout_template( $template ) {
* Get after success button params.
*/
private static function get_after_success_params() {
// Express checkout payment requests are separate requests, so they won't have the after_success attributes. We'll have to check the HTTP_REFERER instead.
$request_params = $_REQUEST; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( self::is_express_checkout() ) {
$request_params = [];
$referrer = isset( $_SERVER['HTTP_REFERER'] ) ? \esc_url_raw( \wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : false; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( $referrer ) {
$referrer_query = \wp_parse_url( $referrer, PHP_URL_QUERY );
\wp_parse_str( $referrer_query, $referrer_query_params );
return array_filter(
[
'after_success_behavior' => isset( $referrer_query_params['after_success_behavior'] ) ? sanitize_text_field( wp_unslash( $referrer_query_params['after_success_behavior'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
'after_success_url' => isset( $referrer_query_params['after_success_url'] ) ? sanitize_url( wp_unslash( $referrer_query_params['after_success_url'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
'after_success_button_label' => isset( $referrer_query_params['after_success_button_label'] ) ? sanitize_text_field( wp_unslash( $referrer_query_params['after_success_button_label'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
]
);
\wp_parse_str( $referrer_query, $request_params );
}
} else {
return array_filter(
[
'after_success_behavior' => isset( $_REQUEST['after_success_behavior'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['after_success_behavior'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
'after_success_url' => isset( $_REQUEST['after_success_url'] ) ? sanitize_url( wp_unslash( $_REQUEST['after_success_url'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
'after_success_button_label' => isset( $_REQUEST['after_success_button_label'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['after_success_button_label'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
]
);
}
return array_filter(
[
'after_success_behavior' => isset( $request_params['after_success_behavior'] ) ? sanitize_text_field( wp_unslash( $request_params['after_success_behavior'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
'after_success_url' => isset( $request_params['after_success_url'] ) ? sanitize_url( wp_unslash( $request_params['after_success_url'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
'after_success_button_label' => isset( $request_params['after_success_button_label'] ) ? sanitize_text_field( wp_unslash( $request_params['after_success_button_label'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
'action_type' => isset( $request_params['action_type'] ) ? sanitize_text_field( wp_unslash( $request_params['action_type'] ) ) : '', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
]
);
}

/**
Expand Down Expand Up @@ -1538,16 +1508,7 @@ public static function recaptcha_verify_captcha( $should_verify, $url, $context
*/
public static function pass_url_param_on_redirect( $location ) {
if ( self::is_modal_checkout() ) {
$params = [ 'modal_checkout' => 1 ];
$newspack_popup_id = filter_input( INPUT_GET, 'newspack_popup_id', FILTER_SANITIZE_NUMBER_INT );
$gate_post_id = filter_input( INPUT_GET, 'memberships_content_gate', FILTER_SANITIZE_NUMBER_INT );
if ( $newspack_popup_id ) {
$params['newspack_popup_id'] = $newspack_popup_id;
}
if ( $gate_post_id ) {
$params['memberships_content_gate'] = $gate_post_id;
}
$location = \add_query_arg( $params, $location );
$location = \add_query_arg( [ 'modal_checkout' => 1 ], $location );
}
return $location;
}
Expand Down Expand Up @@ -1707,29 +1668,28 @@ public static function render_before_checkout_form() {
if ( 1 !== $cart->get_cart_contents_count() ) {
return;
}
$cart_item_key = array_key_first( $cart->get_cart() );
$cart_item = $cart->get_cart_item( $cart_item_key );
$product_id = $cart_item['variation_id'] ? $cart_item['variation_id'] : $cart_item['product_id'];
$class_prefix = self::get_class_prefix();
?>
<div class="<?php echo esc_attr( "order-details-summary {$class_prefix}__box {$class_prefix}__box--text-center" ); ?>">
<?php
// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WooCommerce hooks.
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) :
$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_checkout_cart_item_visible', true, $cart_item, $cart_item_key ) ) :
// Create an array of order information to pass to GA4 via JavaScript.
$data_order_details = Data_Events::build_js_data_events( $_product->get_id(), $cart_item );
?>
<p id="modal-checkout-product-details" data-order-details='<?php echo wp_json_encode( $data_order_details ); ?>'>
<strong>
<?php
echo apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . ': '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wc_get_formatted_cart_item_data( $cart_item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
?>
<?php echo apply_filters( 'woocommerce_cart_item_subtotal', $cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</strong>
</p>
<?php
endif;
endforeach;
$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_checkout_cart_item_visible', true, $cart_item, $cart_item_key ) ) :
?>
<p id="modal-checkout-product-details" data-checkout='<?php echo wp_json_encode( Checkout_Data::get_checkout_data( $cart ) ); ?>'>
<strong>
<?php
echo apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . ': '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wc_get_formatted_cart_item_data( $cart_item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
?>
<?php echo apply_filters( 'woocommerce_cart_item_subtotal', $cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</strong>
</p>
<?php
endif;
// phpcs:enable
?>
</div>
Expand Down Expand Up @@ -2080,40 +2040,6 @@ public static function get_modal_checkout_labels( $key = null ) {
return self::$modal_checkout_labels[ $key ] ?? '';
}

/**
* Get price string for the price summary card to render in auth flow.
*
* @param string $name The name.
* @param string $price The price. Optional. If not provided, the price string will contain 0.
* @param string $frequency The frequency. Optional. If not provided, the price will be treated as a one-time payment.
*
* @return string The price string.
*/
public static function get_summary_card_price_string( $name, $price = '', $frequency = '' ) {
if ( ! $price ) {
$price = '0';
}

if ( function_exists( 'wcs_price_string' ) && function_exists( 'wc_price' ) ) {
if ( $frequency && $frequency !== 'once' ) {
$price = wp_strip_all_tags(
wcs_price_string(
[
'recurring_amount' => $price,
'subscription_period' => $frequency,
'use_per_slash' => true,
]
)
);
} else {
$price = wp_strip_all_tags( wc_price( $price ) );
}
}

// translators: 1 is the name of the item. 2 is the price of the item.
return sprintf( __( '%1$s: %2$s', 'newspack-blocks' ), $name, $price );
}

/**
* Set the checkout registration flag to WC session.
*/
Expand Down
Loading