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 adapted extensions in Direct Checkout #8849

Merged
merged 21 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
23d5c9a
Add get email logic for the connect page
cesarcosta99 May 20, 2024
ad06dc9
Update email retrieval logic in the init session request
cesarcosta99 May 20, 2024
0862ff3
Remove 1st party auth check from direct checkout feature check
cesarcosta99 May 21, 2024
1edeeaa
Add args to WooPay session data endpoint
cesarcosta99 May 22, 2024
75f10d6
Update comment
cesarcosta99 May 23, 2024
454c6b3
Update WC Payments Features PHP tests
cesarcosta99 May 23, 2024
34b4c7c
Add WooPay eligibility check to direct checkout feature check
cesarcosta99 May 23, 2024
24b8551
Add changelog entry
cesarcosta99 May 23, 2024
dd3660c
Merge branch 'develop' into add/2648-adapted-extensions-compatibility
cesarcosta99 May 23, 2024
98503ac
Fix psalm error
cesarcosta99 May 23, 2024
a778dd3
Add blog ID to connect URL params
cesarcosta99 May 28, 2024
0907bd1
Create decrypt signed data helper
cesarcosta99 May 28, 2024
f45d41d
Decrypt email if it's passed as an array
cesarcosta99 May 28, 2024
ec911a3
Ensure WooPay merchant ID is registered where we need
cesarcosta99 May 31, 2024
fe9c01f
Fix redundant variable
cesarcosta99 May 31, 2024
2abe133
Remove unnecessary fallback
cesarcosta99 May 31, 2024
eb72230
Refactor encrypted data approach
cesarcosta99 May 31, 2024
5ca33b9
Merge branch 'develop' into add/2648-adapted-extensions-compatibility
cesarcosta99 May 31, 2024
e0b8be4
Update is user logged in condition
cesarcosta99 Jun 3, 2024
9840b26
Update HMAC hash
cesarcosta99 Jun 7, 2024
0e7be91
Merge branch 'develop' into add/2648-adapted-extensions-compatibility
cesarcosta99 Jun 7, 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/add-2648-adapted-extensions-compatibility
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Enable adapted extensions compatibility with Direct Checkout.
16 changes: 16 additions & 0 deletions client/checkout/woopay/connect/user-connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class WooPayUserConnect extends WoopayConnect {
this.listeners = {
...this.listeners,
getIsUserLoggedInCallback: () => {},
getEncryptedDataCallback: () => {},
};
}

Expand All @@ -26,6 +27,18 @@ class WooPayUserConnect extends WoopayConnect {
);
}

/**
* Retrieves encrypted data from WooPay.
*
* @return {Promise<Object>} Resolves to an object with encrypted data.
*/
async getEncryptedData() {
return await this.sendMessageAndListenWith(
{ action: 'getEncryptedData' },
'getEncryptedDataCallback'
);
}

/**
* Handles the callback from the WooPayConnectIframe.
*
Expand All @@ -38,6 +51,9 @@ class WooPayUserConnect extends WoopayConnect {
case 'get_is_user_logged_in_success':
this.listeners.getIsUserLoggedInCallback( data.value );
break;
case 'get_encrypted_data_success':
this.listeners.getEncryptedDataCallback( data.value );
break;
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion client/checkout/woopay/connect/woopay-connect-iframe.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ export const WooPayConnectIframe = () => {
const fetchConfigAndSetIframeUrl = async () => {
const testMode = getConfig( 'testMode' );
const woopayHost = getConfig( 'woopayHost' );
const blogId = getConfig( 'woopayMerchantId' );
lovo-h marked this conversation as resolved.
Show resolved Hide resolved
const urlParams = new URLSearchParams( {
testMode,
source_url: window.location.href,
source_url: window.location.href, // TODO: refactor this to camel case.
blogId,
} );

const tracksUserId = await getTracksIdentity();
Expand Down
13 changes: 12 additions & 1 deletion client/checkout/woopay/direct-checkout/woopay-direct-checkout.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,21 @@ class WooPayDirectCheckout {
/**
* Checks if the user is logged in.
*
* @return {Promise<*>} Resolves to true if the user is logged in.
* @return {Promise<bool>} Resolves to true if the user is logged in.
*/
static async isUserLoggedIn() {
return this.getUserConnect().isUserLoggedIn();
}

/**
* Retrieves encrypted data from WooPay.
*
* @return {Promise<Object>} Resolves to an object with encrypted data.
*/
static async getEncryptedData() {
return this.getUserConnect().getEncryptedData();
}

/**
* Checks if third-party cookies are enabled.
*
Expand Down Expand Up @@ -363,10 +372,12 @@ class WooPayDirectCheckout {
* @return {Promise<Promise<*>|*>} Resolves to the WooPay session response.
*/
static async getEncryptedSessionData() {
const encryptedData = await this.getEncryptedData();
return request(
buildAjaxURL( getConfig( 'wcAjaxUrl' ), 'get_woopay_session' ),
{
_ajax_nonce: getConfig( 'woopaySessionNonce' ),
...( encryptedData && { encrypted_data: encryptedData } ),
}
);
}
Expand Down
7 changes: 7 additions & 0 deletions includes/admin/class-wc-rest-woopay-session-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ public function register_routes() {
'methods' => WP_REST_Server::READABLE,
'callback' => [ $this, 'get_session_data' ],
'permission_callback' => [ $this, 'check_permission' ],
'args' => [
'email' => [
'type' => 'string',
'format' => 'email',
'required' => true,
],
],
]
);
}
Expand Down
2 changes: 1 addition & 1 deletion includes/class-wc-payments-features.php
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ public static function is_woopay_direct_checkout_enabled() {
$is_direct_checkout_eligible = is_array( $account_cache ) && ( $account_cache['platform_direct_checkout_eligible'] ?? false );
$is_direct_checkout_flag_enabled = '1' === get_option( self::WOOPAY_DIRECT_CHECKOUT_FLAG_NAME, '1' );

return $is_direct_checkout_eligible && $is_direct_checkout_flag_enabled && self::is_woopay_first_party_auth_enabled();
return $is_direct_checkout_eligible && $is_direct_checkout_flag_enabled && self::is_woopay_eligible();
}

/**
Expand Down
1 change: 1 addition & 0 deletions includes/class-wc-payments.php
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,7 @@ public static function enqueue_woopay_common_config_script() {
'testMode' => $is_test_mode,
'wcAjaxUrl' => WC_AJAX::get_endpoint( '%%endpoint%%' ),
'woopaySessionNonce' => wp_create_nonce( 'woopay_session_nonce' ),
'woopayMerchantId' => Jetpack_Options::get_option( 'id' ),
'isWooPayDirectCheckoutEnabled' => WC_Payments_Features::is_woopay_direct_checkout_enabled(),
'platformTrackerNonce' => wp_create_nonce( 'platform_tracks_nonce' ),
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
Expand Down
35 changes: 33 additions & 2 deletions includes/woopay/class-woopay-session.php
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,38 @@ private static function get_checkout_data( $woopay_request ) {
return $checkout_data;
}

/**
* Retrieves the user email from the current session.
*
* @param \WP_User $user The user object.
* @return string The user email.
*/
private static function get_user_email( $user ) {
if ( ! empty( $_POST['email'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
return sanitize_email( wp_unslash( $_POST['email'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
}

if ( ! empty( $_GET['email'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
return sanitize_email( wp_unslash( $_GET['email'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
}

if ( ! empty( $_POST['encrypted_data'] ) && is_array( $_POST['encrypted_data'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
// phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
$decrypted_data = WooPay_Utilities::decrypt_signed_data( $_POST['encrypted_data'] );

if ( ! empty( $decrypted_data['user_email'] ) ) {
return sanitize_email( wp_unslash( $decrypted_data['user_email'] ) );
}
}

// As a last resort, we try to get the email from the customer logged in the store.
if ( $user->ID > 0 ) {
cesarcosta99 marked this conversation as resolved.
Show resolved Hide resolved
return $user->user_email;
}

return '';
}

/**
* Returns the initial session request data.
*
Expand Down Expand Up @@ -424,13 +456,12 @@ public static function get_init_session_request( $order_id = null, $key = null,

$cart_data = self::get_cart_data( $is_pay_for_order, $order_id, $key, $billing_email, $woopay_request );
$checkout_data = self::get_checkout_data( $woopay_request );
$email = self::get_user_email( $user );

if ( $woopay_request ) {
$order_id = $checkout_data['order_id'] ?? null;
}

$email = ! empty( $_POST['email'] ) ? wc_clean( wp_unslash( $_POST['email'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification

$request = [
'wcpay_version' => WCPAY_VERSION_NUMBER,
'user_id' => $user->ID,
Expand Down
36 changes: 36 additions & 0 deletions includes/woopay/class-woopay-utilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,42 @@ public static function encrypt_and_sign_data( $data ) {
];
}

/**
* Decode encrypted and signed data and return it.
*
* @param array $data The session, iv, and hash data for the encryption.
* @return mixed The decoded data.
*/
public static function decrypt_signed_data( $data ) {
$store_blog_token = ( self::get_woopay_url() === self::DEFAULT_WOOPAY_URL ) ? Jetpack_Options::get_option( 'blog_token' ) : 'dev_mode';

if ( empty( $store_blog_token ) ) {
return null;
}

// Decode the data.
$decoded_data_request = array_map( 'base64_decode', $data );

// Verify the HMAC hash before decryption to ensure data integrity.
$computed_hash = hash_hmac( 'sha256', $decoded_data_request['data'], $store_blog_token );

// If the hashes don't match, the message may have been tampered with.
if ( ! hash_equals( $computed_hash, $decoded_data_request['hash'] ) ) {
return null;
}

// Decipher the data using the blog token and the IV.
$decrypted_data = openssl_decrypt( $decoded_data_request['data'], 'aes-256-cbc', $store_blog_token, OPENSSL_RAW_DATA, $decoded_data_request['iv'] );

if ( false === $decrypted_data ) {
return null;
}

$decrypted_data = json_decode( $decrypted_data, true );

return $decrypted_data;
}

/**
* Get the persisted available countries.
*
Expand Down
7 changes: 2 additions & 5 deletions tests/unit/test-class-wc-payments-features.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ public function test_is_woopay_express_checkout_enabled_returns_false_when_woopa

public function test_is_woopay_direct_checkout_enabled_returns_true() {
$this->set_feature_flag_option( WC_Payments_Features::WOOPAY_EXPRESS_CHECKOUT_FLAG_NAME, '1' );
$this->set_feature_flag_option( WC_Payments_Features::WOOPAY_FIRST_PARTY_AUTH_FLAG_NAME, '1' );
$this->set_feature_flag_option( WC_Payments_Features::WOOPAY_DIRECT_CHECKOUT_FLAG_NAME, '1' );
$this->mock_cache->method( 'get' )->willReturn(
[
Expand All @@ -196,7 +195,6 @@ public function test_is_woopay_direct_checkout_enabled_returns_true() {

public function test_is_woopay_direct_checkout_enabled_returns_false_when_flag_is_false() {
$this->set_feature_flag_option( WC_Payments_Features::WOOPAY_EXPRESS_CHECKOUT_FLAG_NAME, '1' );
$this->set_feature_flag_option( WC_Payments_Features::WOOPAY_FIRST_PARTY_AUTH_FLAG_NAME, '1' );
$this->set_feature_flag_option( WC_Payments_Features::WOOPAY_DIRECT_CHECKOUT_FLAG_NAME, '0' );
$this->mock_cache->method( 'get' )->willReturn(
[
Expand All @@ -209,7 +207,6 @@ public function test_is_woopay_direct_checkout_enabled_returns_false_when_flag_i

public function test_is_woopay_direct_checkout_enabled_returns_false_when_woopay_eligible_is_false() {
$this->set_feature_flag_option( WC_Payments_Features::WOOPAY_EXPRESS_CHECKOUT_FLAG_NAME, '1' );
$this->set_feature_flag_option( WC_Payments_Features::WOOPAY_FIRST_PARTY_AUTH_FLAG_NAME, '1' );
$this->set_feature_flag_option( WC_Payments_Features::WOOPAY_DIRECT_CHECKOUT_FLAG_NAME, '1' );
$this->mock_cache->method( 'get' )->willReturn(
[
Expand All @@ -220,7 +217,7 @@ public function test_is_woopay_direct_checkout_enabled_returns_false_when_woopay
$this->assertFalse( WC_Payments_Features::is_woopay_direct_checkout_enabled() );
}

public function test_is_woopay_direct_checkout_enabled_returns_false_when_first_party_auth_is_disabled() {
public function test_is_woopay_direct_checkout_enabled_returns_true_when_first_party_auth_is_disabled() {
$this->set_feature_flag_option( WC_Payments_Features::WOOPAY_EXPRESS_CHECKOUT_FLAG_NAME, '1' );
$this->set_feature_flag_option( WC_Payments_Features::WOOPAY_FIRST_PARTY_AUTH_FLAG_NAME, '0' );
$this->set_feature_flag_option( WC_Payments_Features::WOOPAY_DIRECT_CHECKOUT_FLAG_NAME, '1' );
Expand All @@ -230,7 +227,7 @@ public function test_is_woopay_direct_checkout_enabled_returns_false_when_first_
'platform_direct_checkout_eligible' => true,
]
);
$this->assertFalse( WC_Payments_Features::is_woopay_direct_checkout_enabled() );
$this->assertTrue( WC_Payments_Features::is_woopay_direct_checkout_enabled() );
}

public function test_is_wcpay_frt_review_feature_active_returns_true() {
Expand Down
Loading