Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for WooCommerce Deposits when using Apple Pay and Google Pay #7910

Merged
merged 32 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7627f38
Send wc_ prefixed fields for express payments.
peterwilsoncc Dec 14, 2023
52293a3
Merge branch 'develop' into fix/7907-addon-express-pay-fields
gpressutto5 Jan 3, 2024
21cdddf
Reload payment request when the deposit option is changed
gpressutto5 Jan 3, 2024
f55654e
Handling selected deposit option
gpressutto5 Jan 3, 2024
fedb13b
Added changelog entry
gpressutto5 Jan 3, 2024
18af61d
Merge branch 'develop' into fix/7907-addon-express-pay-fields
gpressutto5 Jan 3, 2024
a9b2903
Fixed deposit tests class
gpressutto5 Jan 4, 2024
1f13e7e
Fixed deposit tests class
gpressutto5 Jan 4, 2024
4ce48d4
Merge branch 'develop' into fix/7907-addon-express-pay-fields
gpressutto5 Jan 5, 2024
e37202f
Removed filter + sanitizing POST string
gpressutto5 Jan 8, 2024
9014f3c
Added tests for payment request + deposits
gpressutto5 Jan 8, 2024
0899435
Merge branch 'develop' into fix/7907-addon-express-pay-fields
gpressutto5 Jan 8, 2024
63afd86
Apply suggestions from code review
gpressutto5 Jan 9, 2024
80c03fe
Merge branch 'develop' into fix/7907-addon-express-pay-fields
gpressutto5 Jan 9, 2024
128de05
Fixed existing tests
gpressutto5 Jan 9, 2024
36f9e95
Added deposit plans support
gpressutto5 Jan 9, 2024
8ba894b
JS style fix
gpressutto5 Jan 9, 2024
eff996e
Ensure payment plan is specified.
peterwilsoncc Jan 10, 2024
5f91139
Merge branch 'develop' into fix/7907-addon-express-pay-fields
gpressutto5 Jan 12, 2024
a933580
Merge branch 'develop' into fix/7907-addon-express-pay-fields
peterwilsoncc Jan 31, 2024
980c374
Fixed deposit plans frontend
gpressutto5 Jan 10, 2024
9c54d6b
Fixed itemized amount for deposits and apple pay
gpressutto5 Jan 31, 2024
8cb9622
Merge branch 'develop' into fix/7907-addon-express-pay-fields
gpressutto5 Feb 1, 2024
90a9c25
Checking if plan is available for product
gpressutto5 Feb 5, 2024
4f98928
Merge branch 'develop' into fix/7907-addon-express-pay-fields
gpressutto5 Feb 16, 2024
804ebed
Revert "Fixed itemized amount for deposits and apple pay"
gpressutto5 Feb 19, 2024
369509e
Fixed wcpay_payment_request_hide_itemization filter
gpressutto5 Feb 19, 2024
aa580db
Merge branch 'develop' into fix/7907-addon-express-pay-fields
gpressutto5 Feb 19, 2024
1663d76
Only checking if the plan is valid when using a plan
gpressutto5 Feb 19, 2024
5d8c4ae
Fixed tests
gpressutto5 Feb 20, 2024
fcfe4f5
Merge branch 'develop' into fix/7907-addon-express-pay-fields
gpressutto5 Feb 20, 2024
91ec6c3
Merge branch 'develop' into fix/7907-addon-express-pay-fields
gpressutto5 Feb 21, 2024
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
4 changes: 4 additions & 0 deletions changelog/7907-support-deposit-express-pay
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fix

Added support for WooCommerce Deposits when using Apple Pay and Google Pay
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* External dependencies
*/
import validator from 'validator';
import { applyFilters } from '@wordpress/hooks';

const useExpressCheckoutProductHandler = ( api ) => {
const getAttributes = () => {
Expand Down Expand Up @@ -104,14 +105,23 @@ const useExpressCheckoutProductHandler = ( api ) => {
}

const addOnForm = document.querySelector( 'form.cart' );
let allowedFieldNames = applyFilters(
'wcpayPaymentRequestAllowedFieldNames',
[]
);
// Ensure allowedFieldNames is an array.
if ( ! Array.isArray( allowedFieldNames ) ) {
allowedFieldNames = [ allowedFieldNames ];
}

if ( addOnForm ) {
const formData = new FormData( addOnForm );

formData.forEach( ( value, name ) => {
if (
/^addon-/.test( name ) ||
/^wc_gc_giftcard_/.test( name )
/^wc_/.test( name ) ||
allowedFieldNames.includes( name )
) {
if ( /\[\]$/.test( name ) ) {
const fieldName = name.substring( 0, name.length - 2 );
Expand Down
32 changes: 30 additions & 2 deletions client/payment-request/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { doAction } from '@wordpress/hooks';
import { applyFilters, doAction } from '@wordpress/hooks';
import { debounce } from 'lodash';
/**
* Internal dependencies
Expand Down Expand Up @@ -189,8 +189,19 @@ jQuery( ( $ ) => {

// Add addons data to the POST body
const formData = $( 'form.cart' ).serializeArray();
let allowedFieldNames = applyFilters(
'wcpayPaymentRequestAllowedFieldNames',
[]
);
// Ensure allowedFieldNames is an array.
if ( ! Array.isArray( allowedFieldNames ) ) {
allowedFieldNames = [ allowedFieldNames ];
}
$.each( formData, ( i, field ) => {
if ( /^addon-/.test( field.name ) ) {
if (
allowedFieldNames.includes( field.name ) ||
/^(addon-|wc_)/.test( field.name )
) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking and nice-to-have (or a good candidate for a separate small issue): It'd be beneficial to streamline this data variable appending implementation with the identical one from woopay/express-button/use-express-checkout-product-handler.js, to avoid code duplication and all the potential future regressions because of it. Both are located in different places which might be complicating things, but just a little bit.

if ( /\[\]$/.test( field.name ) ) {
const fieldName = field.name.substring(
0,
Expand Down Expand Up @@ -302,13 +313,22 @@ jQuery( ( $ ) => {
0
);

// WC Deposits Support.
const depositObject = {};
if ( $( 'input[name=wc_deposit_option]' ).length ) {
depositObject.wc_deposit_option = $(
'input[name=wc_deposit_option]:checked'
).val();
}

const data = {
product_id: productId,
qty: $( '.quantity .qty' ).val(),
attributes: $( '.variations_form' ).length
? wcpayPaymentRequest.getAttributes().data
: [],
addon_value: addonValue,
...depositObject,
};

return api.paymentRequestGetSelectedProductData( data );
Expand Down Expand Up @@ -429,6 +449,14 @@ jQuery( ( $ ) => {
wcpayPaymentRequest.addToCart();
} );

// WooCommerce Deposits support.
// Trigger the "woocommerce_variation_has_changed" event when the deposit option is changed.
$( 'input[name=wc_deposit_option]' ).on( 'change', () => {
$( 'form:has(input[name=wc_deposit_option])' ).trigger(
'woocommerce_variation_has_changed'
);
} );

$( document.body ).on( 'woocommerce_variation_has_changed', () => {
wcpayPaymentRequest.blockPaymentRequestButton();

Expand Down
17 changes: 15 additions & 2 deletions includes/class-wc-payments-payment-request-button-handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,18 +202,30 @@ public function get_button_height() {
* Gets the product total price.
*
* @param object $product WC_Product_* object.
* @param bool $is_deposit Whether customer is paying a deposit.
* @return mixed Total price.
*
* @throws Invalid_Price_Exception Whenever a product has no price.
*/
public function get_product_price( $product ) {
public function get_product_price( $product, ?bool $is_deposit = null ) {
// If prices should include tax, using tax inclusive price.
if ( $this->express_checkout_helper->cart_prices_include_tax() ) {
$base_price = wc_get_price_including_tax( $product );
} else {
$base_price = wc_get_price_excluding_tax( $product );
}

// If WooCommerce Deposits is active, we need to get the correct price for the product.
if ( class_exists( 'WC_Deposits_Product_Manager' ) && WC_Deposits_Product_Manager::deposits_enabled( $product->get_id() ) ) {
// If is_deposit is null, we use the default deposit type for the product.
if ( is_null( $is_deposit ) ) {
$is_deposit = 'deposit' === WC_Deposits_Product_Manager::get_deposit_type( $product->get_id() );
}
if ( $is_deposit ) {
$base_price = WC_Deposits_Product_Manager::get_deposit_amount( $product, 0, 'display', $base_price );
}
}

// Add subscription sign-up fees to product price.
$sign_up_fee = 0;
$subscription_types = [
Expand Down Expand Up @@ -1050,6 +1062,7 @@ public function ajax_get_selected_product_data() {
$product = wc_get_product( $product_id );
$variation_id = null;
$currency = get_woocommerce_currency();
$is_deposit = isset( $_POST['wc_deposit_option'] ) ? 'yes' === $_POST['wc_deposit_option'] : null;

if ( ! is_a( $product, 'WC_Product' ) ) {
/* translators: product ID */
Expand Down Expand Up @@ -1077,7 +1090,7 @@ public function ajax_get_selected_product_data() {
throw new Exception( sprintf( __( 'You cannot add that amount of "%1$s"; to the cart because there is not enough stock (%2$s remaining).', 'woocommerce-payments' ), $product->get_name(), wc_format_stock_quantity_for_display( $product->get_stock_quantity(), $product ) ) );
}

$price = $this->get_product_price( $product );
$price = $this->get_product_price( $product, $is_deposit );
$total = $qty * $price + $addon_value;

$quantity_label = 1 < $qty ? ' (x' . $qty . ')' : '';
Expand Down
19 changes: 17 additions & 2 deletions tests/unit/helpers/class-wc-helper-deposit-product-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,25 @@
* This helper class should ONLY be used for unit tests!.
*/
class WC_Deposits_Product_Manager {
public static function get_deposit_type( WC_Product_Simple $product ) {
/**
* @param WC_Product_Simple|int $product
* @return mixed
*/
public static function get_deposit_type( $product ) {
if ( ! is_object( $product ) ) {
$product = apply_filters( 'test_deposit_get_product', wc_get_product( $product ) );
}
return $product->get_meta( '_wc_deposit_type' );
}
public static function deposits_enabled( WC_Product_Simple $product ) {

/**
* @param WC_Product_Simple|int $product
* @return bool
*/
public static function deposits_enabled( $product ) {
if ( ! is_object( $product ) ) {
$product = apply_filters( 'test_deposit_get_product', wc_get_product( $product ) );
}
return true === $product->get_meta( '_wc_deposits_enabled' );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,12 @@ public function provide_get_product_tax_tests() {

public function test_get_product_price_includes_subscription_sign_up_fee() {
$mock_product = $this->create_mock_subscription( 'subscription' );
add_filter(
'test_deposit_get_product',
function() use ( $mock_product ) {
return $mock_product;
}
);

// We have a helper because we are not loading subscriptions.
WC_Subscriptions_Product::set_sign_up_fee( 10 );
Expand All @@ -458,6 +464,12 @@ public function test_get_product_price_includes_subscription_sign_up_fee() {

public function test_get_product_price_includes_variable_subscription_sign_up_fee() {
$mock_product = $this->create_mock_subscription( 'subscription_variation' );
add_filter(
'test_deposit_get_product',
function() use ( $mock_product ) {
return $mock_product;
}
);

// We have a helper because we are not loading subscriptions.
WC_Subscriptions_Product::set_sign_up_fee( 10 );
Expand All @@ -483,6 +495,12 @@ public function test_get_product_price_throws_exception_for_products_without_pri

public function test_get_product_price_throws_exception_for_a_non_numeric_signup_fee() {
$mock_product = $this->create_mock_subscription( 'subscription' );
add_filter(
'test_deposit_get_product',
function() use ( $mock_product ) {
return $mock_product;
}
);
WC_Subscriptions_Product::set_sign_up_fee( 'a' );

$this->expectException( WCPay\Exceptions\Invalid_Price_Exception::class );
Expand Down
Loading