diff --git a/includes/class-wc-gateway-payfast.php b/includes/class-wc-gateway-payfast.php index a145d32..b92f496 100644 --- a/includes/class-wc-gateway-payfast.php +++ b/includes/class-wc-gateway-payfast.php @@ -194,6 +194,9 @@ public function __construct() { add_filter( 'woocommerce_currency', array( $this, 'filter_currency' ) ); add_filter( 'nocache_headers', array( $this, 'no_store_cache_headers' ) ); + + // Validate the gateway credentials. + add_action( 'update_option_woocommerce_payfast_settings', array( $this, 'validate_payfast_credentials' ), 10, 2 ); } /** @@ -330,16 +333,17 @@ public function add_testmode_admin_settings_notice() { * @return array */ public function check_requirements() { - $errors = array( // Check if the store currency is supported by Payfast. ! in_array( get_woocommerce_currency(), $this->available_currencies, true ) ? 'wc-gateway-payfast-error-invalid-currency' : null, // Check if user entered the merchant ID. - 'yes' !== $this->get_option( 'testmode' ) && empty( $this->get_option( 'merchant_id' ) ) ? 'wc-gateway-payfast-error-missing-merchant-id' : null, + empty( $this->get_option( 'merchant_id' ) ) ? 'wc-gateway-payfast-error-missing-merchant-id' : null, // Check if user entered the merchant key. - 'yes' !== $this->get_option( 'testmode' ) && empty( $this->get_option( 'merchant_key' ) ) ? 'wc-gateway-payfast-error-missing-merchant-key' : null, + empty( $this->get_option( 'merchant_key' ) ) ? 'wc-gateway-payfast-error-missing-merchant-key' : null, // Check if user entered a pass phrase. - 'yes' !== $this->get_option( 'testmode' ) && empty( $this->get_option( 'pass_phrase' ) ) ? 'wc-gateway-payfast-error-missing-pass-phrase' : null, + empty( $this->get_option( 'pass_phrase' ) ) ? 'wc-gateway-payfast-error-missing-pass-phrase' : null, + // Check if payfast credentials are valid. + ( 'yes' === get_option( 'woocommerce_payfast_invalid_credentials' ) ) ? 'wc-gateway-payfast-error-invalid-credentials' : null, ); return array_filter( $errors ); @@ -1685,6 +1689,8 @@ public function get_error_message( $key ) { return esc_html__( 'You forgot to fill your merchant key.', 'woocommerce-gateway-payfast' ); case 'wc-gateway-payfast-error-missing-pass-phrase': return esc_html__( 'Payfast requires a passphrase to work.', 'woocommerce-gateway-payfast' ); + case 'wc-gateway-payfast-error-invalid-credentials': + return esc_html__( 'Invalid Payfast credentials. Please verify and enter the correct details.', 'woocommerce-gateway-payfast' ); default: return ''; } @@ -1826,4 +1832,62 @@ public function filter_currency( $currency ) { return $currency; } + + /** + * Validate the Payfast credentials. + * This function is used to validate the Payfast credentials. + * + * @param array $old_settings Old Payfast settings. + * @param array $settings Payfast settings. + */ + public function validate_payfast_credentials( $old_settings, $settings ) { + $merchant_id = $settings['merchant_id'] ?? ''; + $pass_phrase = $settings['pass_phrase'] ?? ''; + $test_mode = $settings['testmode'] ?? 'no'; + $old_merchant_id = $old_settings['merchant_id'] ?? ''; + $old_pass_phrase = $old_settings['pass_phrase'] ?? ''; + $old_test_mode = $old_settings['testmode'] ?? 'no'; + + // Clear the invalid credentials notice. + delete_option( 'woocommerce_payfast_invalid_credentials' ); + + // Bail if no merchant ID or passphrase is set. + if ( empty( $merchant_id ) || empty( $pass_phrase ) ) { + return; + } + + // Bail if the merchant ID and passphrase are the same as the old settings, no need to validate again. + if ( $old_merchant_id === $merchant_id && $old_pass_phrase === $pass_phrase && $old_test_mode === $test_mode ) { + return; + } + + $api_endpoint = 'https://api.payfast.co.za/ping'; + $api_endpoint .= 'yes' === $test_mode ? '?testing=true' : ''; + + $timestamp = current_time( rtrim( DateTime::ATOM, 'P' ) ) . '+02:00'; + $api_args['timeout'] = 45; + $api_args['headers'] = array( + 'merchant-id' => $merchant_id, + 'timestamp' => $timestamp, + 'version' => 'v1', + ); + + // Generate signature. + $all_api_variables = $api_args['headers']; + $this->pass_phrase = $pass_phrase; + $api_args['headers']['signature'] = md5( $this->_generate_parameter_string( $all_api_variables ) ); + $api_args['method'] = strtoupper( 'GET' ); + + $results = wp_remote_request( $api_endpoint, $api_args ); + + // Bail if there is an error in the request. + if ( is_wp_error( $results ) ) { + return; + } + + // Check Payfast server response if the response code is not 200 then show an error message. + if ( 200 !== wp_remote_retrieve_response_code( $results ) ) { + update_option( 'woocommerce_payfast_invalid_credentials', 'yes' ); + } + } } diff --git a/tests/e2e/global-setup.js b/tests/e2e/global-setup.js index bd13841..9fa0449 100644 --- a/tests/e2e/global-setup.js +++ b/tests/e2e/global-setup.js @@ -74,7 +74,7 @@ module.exports = async ( config ) => { await usernameLocator.fill( admin.username ); const passwordLocator = await adminPage.locator( 'input[name="pwd"]' ); await passwordLocator.fill( admin.password ); - const submitButtonLocator = await adminPage.locator( 'text=Log In' ); + const submitButtonLocator = await adminPage.locator( '#wp-submit' ); await submitButtonLocator.click(); await waitForNavigationPromise; @@ -120,7 +120,7 @@ module.exports = async ( config ) => { await usernameLocator.fill( admin.username ); const passwordLocator = await customerPage.locator( 'input[name="pwd"]' ); await passwordLocator.fill( admin.password ); - const submitButtonLocator = await customerPage.locator( 'text=Log In' ); + const submitButtonLocator = await customerPage.locator( '#wp-submit' ); await submitButtonLocator.click(); await waitForNavigationPromise; diff --git a/tests/e2e/specs/admin/edit-settings.test.js b/tests/e2e/specs/admin/edit-settings.test.js index a905e98..82735ce 100644 --- a/tests/e2e/specs/admin/edit-settings.test.js +++ b/tests/e2e/specs/admin/edit-settings.test.js @@ -76,29 +76,36 @@ test.describe( 'Verify payfast setting - @foundational', async () => { } ); } ); - test( 'Checkout Block: Verify method title & description', async () => { - await addProductToCart( {page: checkoutBlockPage, productUrl: '/product/simple-product/'} ); - await checkoutBlockPage.goto( '/checkout/' , { waitUntil: 'networkidle' }); + test( 'Edit Setting: Verify required notice for the credentials', async () => { + await gotoPayfastSettingPage( {page: adminPage} ); + await editPayfastSetting( { + page: adminPage, + settings: { + merchant_id: '', + merchant_key: '', + passphrase: '', + } + } ); - const paymentMethodLocator = await checkoutBlockPage.locator( - 'label[for="radio-control-wc-payment-method-options-payfast"]' ); - await expect( await checkoutBlockPage.locator( - 'label[for="radio-control-wc-payment-method-options-payfast"] img' ).getAttribute( 'alt' ) ) - .toEqual( 'Payfast' ); - await paymentMethodLocator.click(); - await expect( await checkoutBlockPage.locator( '.wc-block-components-radio-control-accordion-content' ) ) - .toHaveText( /Pay with payfast/ ); + await gotoPayfastSettingPage( {page: adminPage} ); + await expect( await adminPage.locator( '.notice.notice-error' ).last() ).toHaveText( /You forgot to fill your merchant ID/ ); + await expect( await adminPage.locator( '.notice.notice-error' ).last() ).toHaveText( /You forgot to fill your merchant key/ ); + await expect( await adminPage.locator( '.notice.notice-error' ).last() ).toHaveText( /Payfast requires a passphrase to work/ ); } ); - test( 'Checkout Page: Verify method title & description', async () => { - await addProductToCart( {page: checkoutPage, productUrl: '/product/simple-product/'} ); - await checkoutPage.goto( '/shortcode-checkout/' ); - const paymentMethodLocator = await checkoutPage.locator( '.wc_payment_method.payment_method_payfast' ); - await expect( paymentMethodLocator ).toHaveText( /Payfast/ ); - await paymentMethodLocator.click(); - await expect( await checkoutPage.locator( '.payment_box.payment_method_payfast' ) ) - .toHaveText( /Pay with payfast/ ); + test( 'Edit Setting: Verify credentials and show notice for invalid credentials', async () => { + await gotoPayfastSettingPage( {page: adminPage} ); + await editPayfastSetting( { + page: adminPage, + settings: { + merchant_id: '1', + merchant_key: '1', + passphrase: '1', + } + } ); + + await expect( await adminPage.locator( '.notice.notice-error' ).last() ).toHaveText( /Invalid Payfast credentials/ ); } ); test( 'Edit Setting: Verify Merchant ID, Merchant Key, and Passphrase', async () => { @@ -122,6 +129,31 @@ test.describe( 'Verify payfast setting - @foundational', async () => { await expect( await passphraseSettingLocator.inputValue() ).toEqual( payfastSandboxCredentials.passPharse ); } ); + test( 'Checkout Block: Verify method title & description', async () => { + await addProductToCart( {page: checkoutBlockPage, productUrl: '/product/simple-product/'} ); + await checkoutBlockPage.goto( '/checkout/' , { waitUntil: 'networkidle' }); + + const paymentMethodLocator = await checkoutBlockPage.locator( + 'label[for="radio-control-wc-payment-method-options-payfast"]' ); + await expect( await checkoutBlockPage.locator( + 'label[for="radio-control-wc-payment-method-options-payfast"] img' ).getAttribute( 'alt' ) ) + .toEqual( 'Payfast' ); + await paymentMethodLocator.click(); + await expect( await checkoutBlockPage.locator( '.wc-block-components-radio-control-accordion-content' ) ) + .toHaveText( /Pay with payfast/ ); + } ); + + test( 'Checkout Page: Verify method title & description', async () => { + await addProductToCart( {page: checkoutPage, productUrl: '/product/simple-product/'} ); + await checkoutPage.goto( '/shortcode-checkout/' ); + + const paymentMethodLocator = await checkoutPage.locator( '.wc_payment_method.payment_method_payfast' ); + await expect( paymentMethodLocator ).toHaveText( /Payfast/ ); + await paymentMethodLocator.click(); + await expect( await checkoutPage.locator( '.payment_box.payment_method_payfast' ) ) + .toHaveText( /Pay with payfast/ ); + } ); + test( 'Edit Setting - Send Debug Emails - Verify when setting disabled', async () => { test.slow(); diff --git a/tests/e2e/utils/index.js b/tests/e2e/utils/index.js index e24c5f6..a914988 100644 --- a/tests/e2e/utils/index.js +++ b/tests/e2e/utils/index.js @@ -29,7 +29,7 @@ export async function changeCurrency( {page, currency} ) { * @return {Promise} */ export async function gotoPayfastSettingPage( {page} ) { - await page.goto( '/wp-admin/admin.php?page=wc-settings&tab=checkout§ion=wc_gateway_payfast' ); + await page.goto( '/wp-admin/admin.php?page=wc-settings&tab=checkout§ion=wc_gateway_payfast', {waitUntil: 'networkidle'} ); } /**