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 hmac verification on ipn #281

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
2 changes: 1 addition & 1 deletion src/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"type": "library",
"require": {
"php": "^5.6 || ~7.0 || ~7.1 || ~7.2 || ~7.3 || ~7.4 || ~8.0 || ~8.1",
"alma/alma-php-client": "2.*",
joyet-simon marked this conversation as resolved.
Show resolved Hide resolved
"alma/alma-php-client": ">=2.2.0",
"ext-openssl": "*"
},
"require-dev": {
Expand Down
38 changes: 38 additions & 0 deletions src/includes/Builders/Helpers/SecurityHelperBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
/**
* SecurityHelperBuilder
*
* @package Alma\Woocommerce\Builders\Helpers
*/

namespace Alma\Woocommerce\Builders\Helpers;

use Alma\Woocommerce\Helpers\ProductHelper;
use Alma\Woocommerce\Helpers\SecurityHelper;
use Alma\Woocommerce\Traits\BuilderTrait;

if ( ! defined( 'ABSPATH' ) ) {
die( 'Not allowed' ); // Exit if accessed directly.
}

/**
* Class SecurityHelperBuilder
*
* @package Alma\Woocommerce\Builders\Helpers
*/
class SecurityHelperBuilder {

use BuilderTrait;

/**
* Get the instance of the SecurityHelper class
*
* @return SecurityHelper
*/
public function get_instance() {
return new SecurityHelper(
$this->get_alma_logger(),
$this->get_payment_validator()
);
}
}
20 changes: 20 additions & 0 deletions src/includes/Exceptions/AlmaInvalidSignatureException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
/**
* AlmaInvalidSignatureException.
*
* @since 5.7.0
*
* @package Alma_Gateway_For_Woocommerce
* @subpackage Alma_Gateway_For_Woocommerce/includes/Exceptions
* @namespace Alma\Woocommerce\Exceptions
*/

namespace Alma\Woocommerce\Exceptions;

/**
* Class AlmaInvalidSignatureException.
*/
class AlmaInvalidSignatureException extends AlmaException {


}
77 changes: 57 additions & 20 deletions src/includes/Helpers/PaymentHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Alma\Woocommerce\AlmaSettings;
use Alma\Woocommerce\Builders\Helpers\CartHelperBuilder;
use Alma\Woocommerce\Builders\Helpers\ProductHelperBuilder;
use Alma\Woocommerce\Builders\Helpers\SecurityHelperBuilder;
use Alma\Woocommerce\Builders\Helpers\ToolsHelperBuilder;
use Alma\Woocommerce\Exceptions\AlmaException;
use Alma\Woocommerce\Exceptions\AmountMismatchException;
Expand All @@ -32,13 +33,15 @@
use Alma\Woocommerce\Exceptions\BuildOrderException;
use Alma\Woocommerce\Exceptions\IncorrectPaymentException;
use Alma\Woocommerce\Exceptions\PlansDefinitionException;
use Alma\Woocommerce\Exceptions\AlmaInvalidSignatureException;
use Alma\Woocommerce\Services\PaymentUponTriggerService;

/**
* PaymentHelper.
*/
class PaymentHelper {


/**
* The logger.
*
Expand Down Expand Up @@ -90,23 +93,42 @@ class PaymentHelper {
protected $product_helper;

/**
* Contructor.
* Security helper.
*
* @var SecurityHelper
*/
public function __construct() {
$this->logger = new AlmaLogger();
$this->payment_upon_trigger = new PaymentUponTriggerService();
$this->alma_settings = new AlmaSettings();

$tools_helper_builder = new ToolsHelperBuilder();
$this->tool_helper = $tools_helper_builder->get_instance();

$cart_helper_builder = new CartHelperBuilder();
$this->cart_helper = $cart_helper_builder->get_instance();

$this->order_helper = new OrderHelper();
protected $security_helper;

$product_helper_builder = new ProductHelperBuilder();
$this->product_helper = $product_helper_builder->get_instance();
/**
* PaymentHelper constructor.
*
* @param AlmaLogger|null $logger The logger.
* @param PaymentUponTriggerService|null $trigger The payment upon trigger service.
* @param AlmaSettings|null $settings The settings.
* @param ToolsHelper|null $tool_helper The tool helper.
* @param CartHelper|null $cart_helper The cart helper.
* @param OrderHelper|null $order_helper The order helper.
* @param ProductHelper|null $product_helper The product helper.
* @param SecurityHelper|null $security_helper The security helper.
*/
public function __construct(
$logger = null,
$trigger = null,
$settings = null,
$tool_helper = null,
$cart_helper = null,
$order_helper = null,
$product_helper = null,
$security_helper = null
) {
$this->logger = isset( $logger ) ? $logger : new AlmaLogger();
$this->payment_upon_trigger = isset( $trigger ) ? $trigger : new PaymentUponTriggerService();
$this->alma_settings = isset( $settings ) ? $settings : new AlmaSettings();
$this->tool_helper = isset( $tool_helper ) ? $tool_helper : ( new ToolsHelperBuilder() )->get_instance();
$this->cart_helper = isset( $cart_helper ) ? $tool_helper : ( new CartHelperBuilder() )->get_instance();
$this->order_helper = isset( $order_helper ) ? $order_helper : new OrderHelper();
$this->product_helper = isset( $product_helper ) ? $product_helper : ( new ProductHelperBuilder() )->get_instance();
$this->security_helper = isset( $security_helper ) ? $security_helper : ( new SecurityHelperBuilder() )->get_instance();
}

/**
Expand All @@ -116,11 +138,29 @@ public function __construct() {
*/
public function handle_ipn_callback() {
$payment_id = $this->get_payment_to_validate();

if ( ! array_key_exists( 'HTTP_X_ALMA_SIGNATURE', $_SERVER ) ) {
$this->logger->error( 'Header key X-Alma-Signature doesn\'t exist' );
wp_send_json( array( 'error' => 'Header key X-Alma-Signature doesn\'t exist' ), 500 );
}

try {
$this->security_helper->validate_ipn_signature(
$payment_id,
$this->alma_settings->get_active_api_key(),
$_SERVER['HTTP_X_ALMA_SIGNATURE']
Benjamin-Freoua-Alma marked this conversation as resolved.
Show resolved Hide resolved
);
$this->logger->info( '[ALMA] IPN signature is valid' );
} catch ( AlmaInvalidSignatureException $e ) {
$this->logger->error( $e->getMessage() );
wp_send_json( array( 'error' => $e->getMessage() ), 500 );
}

$this->validate_payment_from_ipn( $payment_id );
}

/**
* Webhooks handlers
* Webhooks handlers.
*
* PID comes from Alma IPN callback or Alma Checkout page,
* it is not a user form submission: Nonce usage is not suitable here.
Expand Down Expand Up @@ -373,7 +413,6 @@ public function get_payment_payload_from_order( $wc_order, $fee_plan, $payment_t
$wc_order->add_order_note( $payment_type );

$data = $this->build_data_for_alma( $wc_order, $fee_plan, $is_in_page );

} catch ( \Exception $e ) {
$this->logger->error(
sprintf(
Expand Down Expand Up @@ -550,7 +589,7 @@ protected function add_product_data( $item ) {
* Call the payment api and create.
*
* @param \WC_Order $wc_order The order .
* @param FeePlan $fee_plan The fee plan.
* @param FeePlan $fee_plan The fee plan.
* @param boolean $is_in_page In Page mode.
*
* @return Payment
Expand All @@ -565,7 +604,6 @@ public function create_payments( $wc_order, $fee_plan, $is_in_page = false ) {
$payload = $this->get_payment_payload_from_order( $wc_order, $fee_plan, $payment_type, $is_in_page );

return $this->alma_settings->create_payment( $payload, $wc_order, $fee_plan );

}

/**
Expand Down Expand Up @@ -602,5 +640,4 @@ public function get_payment_method( $fee_plan ) {

throw new PlansDefinitionException();
}

}
62 changes: 62 additions & 0 deletions src/includes/Helpers/SecurityHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php
/**
* SecurityHelper
*
* @package Alma\Woocommerce\Helpers
*/

namespace Alma\Woocommerce\Helpers;

use Alma\API\Lib\PaymentValidator;
use Alma\Woocommerce\Exceptions\AlmaInvalidSignatureException;

/**
* Class SecurityHelper
*/
class SecurityHelper {

/**
* The logger.
*
* @var AlmaLogger
*/
protected $logger;

/**
* The payment validator.
*
* @var PaymentValidator
*/
protected $payment_validator;

/**
* SecurityHelper constructor.
*
* @param AlmaLogger $logger The logger.
* @param PaymentValidator $payment_validator The payment validator.
*/
public function __construct( $logger, $payment_validator ) {
$this->logger = $logger;
$this->payment_validator = $payment_validator;
}

/**
* Validate the IPN signature
*
* @param string $payment_id The payment ID.
* @param string $api_key The API key.
* @param string $signature The signature.
*
* @return void
*
* @throws AlmaInvalidSignatureException If the signature is invalid.
*/
public function validate_ipn_signature( $payment_id, $api_key, $signature ) {
if ( empty( $payment_id ) || empty( $api_key ) || empty( $signature ) ) {
throw new AlmaInvalidSignatureException( sprintf( '[ALMA] Missing required parameters, payment_id: %s, api_key: %s, signature: %s', $payment_id, $api_key, $signature ) );
}
if ( ! $this->payment_validator->isHmacValidated( $payment_id, $api_key, $signature ) ) {
throw new AlmaInvalidSignatureException( '[ALMA] Invalid signature' );
}
}
}
10 changes: 10 additions & 0 deletions src/includes/Traits/BuilderTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Alma\Woocommerce\Traits;

use Alma\API\Lib\PaymentValidator;
use Alma\Woocommerce\AlmaLogger;
use Alma\Woocommerce\AlmaSettings;
use Alma\Woocommerce\Factories\CartFactory;
Expand Down Expand Up @@ -402,4 +403,13 @@ public function get_php_helper( $php_helper = null ) {
return new PHPHelper();
}

/**
* PaymentValidator.
*
* @return PaymentValidator
*/
public function get_payment_validator() {
return new PaymentValidator();
}

}
28 changes: 28 additions & 0 deletions src/tests/Builders/Helpers/SecurityHelperBuilderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Alma\Woocommerce\Tests\Builders\Helpers;

use Alma\Woocommerce\Builders\Helpers\SecurityHelperBuilder;
use Alma\Woocommerce\Helpers\SecurityHelper;
use WP_UnitTestCase;

/**
* @covers \Alma\Woocommerce\Builders\Helpers\SecurityHelperBuilder
*/
class SecurityHelperBuilderTest extends WP_UnitTestCase
{
/**
* The security helper builder.
*
* @var SecurityHelperBuilder $security_helper_builder
*/
protected $security_helper_builder;
public function set_up() {
$this->security_helper_builder = new SecurityHelperBuilder();
}

public function test_get_instance() {
$this->assertInstanceOf(SecurityHelper::class, $this->security_helper_builder->get_instance());
}

}
60 changes: 60 additions & 0 deletions src/tests/Helpers/PaymentHelperTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php
/**
* Class AssetsHelperTest
*
* @covers \Alma\Woocommerce\Helpers\PaymentHelper
*
* @package Alma_Gateway_For_Woocommerce
*/

namespace Alma\Woocommerce\Tests\Helpers;

use Alma\Woocommerce\AlmaLogger;
use Alma\Woocommerce\AlmaSettings;
use Alma\Woocommerce\Helpers\CartHelper;
use Alma\Woocommerce\Helpers\OrderHelper;
use Alma\Woocommerce\Helpers\PaymentHelper;
use Alma\Woocommerce\Helpers\ProductHelper;
use Alma\Woocommerce\Helpers\SecurityHelper;
use Alma\Woocommerce\Helpers\ToolsHelper;
use Alma\Woocommerce\Services\PaymentUponTriggerService;
use WP_UnitTestCase;

class PaymentHelperTest extends WP_UnitTestCase
{
/**
* @var PaymentHelper
*/
protected $payment_helper;
/**
* @var PaymentHelper
*/
protected $logger;
public function set_up() {
$this->logger = \Mockery::mock(AlmaLogger::class);
$trigger = \Mockery::mock(PaymentUponTriggerService::class);
joyet-simon marked this conversation as resolved.
Show resolved Hide resolved
$settings = \Mockery::mock(AlmaSettings::class);
$tool_helper = \Mockery::mock(ToolsHelper::class);
$cart_helper = \Mockery::mock(CartHelper::class);
$order_helper = \Mockery::mock(OrderHelper::class);
$product_helper = \Mockery::mock(ProductHelper::class);
$security_helper = \Mockery::mock(SecurityHelper::class);
$this->payment_helper = new PaymentHelper(
$this->logger,
$trigger,
$settings,
$tool_helper,
$cart_helper,
$order_helper,
$product_helper,
$security_helper
);
}
protected function tearDown()
{
\Mockery::close();
}


joyet-simon marked this conversation as resolved.
Show resolved Hide resolved

}
Loading
Loading