diff --git a/assets/other-scripts/wc-cover-fees/index.js b/assets/other-scripts/wc-cover-fees/index.js new file mode 100644 index 0000000000..a9844083bc --- /dev/null +++ b/assets/other-scripts/wc-cover-fees/index.js @@ -0,0 +1,29 @@ +/* globals jQuery, newspack_wc_cover_fees */ +( function ( $ ) { + if ( ! $ ) { + return; + } + const $body = $( document.body ); + $body.on( 'init_checkout', function () { + const form = document.querySelector( 'form.checkout' ); + if ( ! form ) { + return; + } + let checked = document.getElementById( newspack_wc_cover_fees.custom_field_name )?.checked; + form.addEventListener( 'change', function () { + // Get element on every change because the DOM is replaced by AJAX. + const input = document.getElementById( newspack_wc_cover_fees.custom_field_name ); + if ( ! input ) { + return; + } + if ( checked !== input.checked ) { + checked = input.checked; + $body.trigger( 'update_checkout', { update_shipping_method: false } ); + } + } ); + // Trigger checkout update on payment method change so it updates the fee. + $( document ).on( 'payment_method_selected', function () { + $body.trigger( 'update_checkout', { update_shipping_method: false } ); + } ); + } ); +} )( jQuery ); diff --git a/includes/reader-revenue/woocommerce/class-woocommerce-cover-fees.php b/includes/reader-revenue/woocommerce/class-woocommerce-cover-fees.php index 1743794c9d..4679465d12 100644 --- a/includes/reader-revenue/woocommerce/class-woocommerce-cover-fees.php +++ b/includes/reader-revenue/woocommerce/class-woocommerce-cover-fees.php @@ -13,18 +13,16 @@ * WooCommerce Order UTM class. */ class WooCommerce_Cover_Fees { - const CUSTOM_FIELD_NAME = 'newspack-wc-pay-fees'; - const PRICE_ELEMENT_ID = 'newspack-wc-price'; - const WC_ORDER_META_NAME = 'newspack_donor_covers_fees'; + const CUSTOM_FIELD_NAME = 'newspack-wc-pay-fees'; /** * Initialize hooks. */ public static function init() { \add_filter( 'woocommerce_checkout_fields', [ __CLASS__, 'add_checkout_fields' ] ); - \add_filter( 'woocommerce_checkout_create_order', [ __CLASS__, 'set_total_with_fees' ], 1, 2 ); + \add_action( 'woocommerce_checkout_update_order_review', [ __CLASS__, 'persist_fee_selection' ] ); + \add_action( 'woocommerce_cart_calculate_fees', [ __CLASS__, 'add_transaction_fee' ] ); \add_action( 'woocommerce_checkout_order_processed', [ __CLASS__, 'add_order_note' ], 1, 3 ); - \add_filter( 'wc_price', [ __CLASS__, 'amend_price_markup' ], 1, 2 ); \add_action( 'wc_stripe_payment_fields_stripe', [ __CLASS__, 'render_stripe_input' ] ); \add_action( 'wp_enqueue_scripts', [ __CLASS__, 'print_checkout_helper_script' ] ); } @@ -50,19 +48,41 @@ public static function add_checkout_fields( $fields ) { } /** - * Set order total, taking the fee into account. + * Persist the transaction fee selection in the Woo sesion when updating the + * order review. * - * @param \WC_Order $order Order object. - * @param array $data Posted data. + * @param string $posted_data Posted posted_data. + */ + public static function persist_fee_selection( $posted_data ) { + $data = []; + parse_str( $posted_data, $data ); + if ( self::should_apply_fee( $data ) ) { + \WC()->session->set( self::CUSTOM_FIELD_NAME, 1 ); + } else { + \WC()->session->set( self::CUSTOM_FIELD_NAME, 0 ); + } + } + + /** + * Add fee. * - * @return \WC_Order + * @param \WC_Cart $cart Cart object. */ - public static function set_total_with_fees( $order, $data ) { - if ( isset( $data[ self::CUSTOM_FIELD_NAME ] ) && 1 === $data[ self::CUSTOM_FIELD_NAME ] ) { - $order->add_meta_data( self::WC_ORDER_META_NAME, 1 ); - $order->set_total( self::get_total_with_fee( $order->get_total() ) ); + public static function add_transaction_fee( $cart ) { + if ( is_admin() && ! defined( 'DOING_AJAX' ) ) { + return; + } + if ( ! \WC()->session->get( self::CUSTOM_FIELD_NAME ) ) { + return; } - return $order; + $cart->add_fee( + sprintf( + // Translators: %s is the fee percentage. + __( 'Transaction fee (%s)', 'newspack-plugin' ), + self::get_fee_display_value() + ), + self::get_fee_value() + ); } /** @@ -73,8 +93,8 @@ public static function set_total_with_fees( $order, $data ) { * @param \WC_Order $order Order object. */ public static function add_order_note( $order_id, $posted_data, $order ) { - if ( 1 === intval( $order->get_meta_data( self::WC_ORDER_META_NAME ) ) ) { - $order->add_order_note( __( 'The donor opted to cover Stripe\'s transaction fee. The total amount will be updated.', 'newspack-plugin' ) ); + if ( \WC()->session->get( self::CUSTOM_FIELD_NAME ) ) { + $order->add_order_note( __( 'The donor opted to cover Stripe\'s transaction fee.', 'newspack-plugin' ) ); } } @@ -100,16 +120,27 @@ private static function should_allow_covering_fees() { if ( true !== boolval( get_option( 'newspack_donations_allow_covering_fees' ) ) ) { return false; } - if ( \Newspack_Blocks\Modal_Checkout::is_modal_checkout() ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended - return true; + return true; + } + + /** + * Whether to apply the fee in the current request. + * + * @param array $data Posted data. + * + * @return bool + */ + private static function should_apply_fee( $data ) { + if ( ! self::should_allow_covering_fees() ) { + return false; } - if ( isset( $_POST['post_data'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing - parse_str( \sanitize_text_field( \wp_unslash( $_POST['post_data'] ) ), $post_data ); // phpcs:ignore WordPress.Security.NonceVerification.Missing - if ( isset( $post_data['modal_checkout'] ) && 1 === intval( $post_data['modal_checkout'] ) ) { - return true; - } + if ( ! isset( $data['payment_method'] ) || 'stripe' !== $data['payment_method'] ) { + return false; + } + if ( ! isset( $data[ self::CUSTOM_FIELD_NAME ] ) || '1' !== $data[ self::CUSTOM_FIELD_NAME ] ) { + return false; } - return false; + return true; } /** @@ -123,16 +154,15 @@ public static function render_stripe_input() {