diff --git a/config/install/payment.payment_method_configuration.payment_datatrans.yml b/config/install/payment.payment_method_configuration.payment_datatrans.yml index 3c474c0..43c6d6e 100644 --- a/config/install/payment.payment_method_configuration.payment_datatrans.yml +++ b/config/install/payment.payment_method_configuration.payment_datatrans.yml @@ -14,5 +14,6 @@ pluginConfiguration: hmac_key: '12345' hmac_key_2: '' use_hmac_2: FALSE + debug: FALSE pluginId: payment_datatrans status: true diff --git a/config/schema/payment_datatrans.schema.yml b/config/schema/payment_datatrans.schema.yml index dbf0563..8674ed0 100644 --- a/config/schema/payment_datatrans.schema.yml +++ b/config/schema/payment_datatrans.schema.yml @@ -32,3 +32,6 @@ payment.plugin_configuration.payment_method_configuration.payment_datatrans: use_hmac_2: type: boolean label: Use HMAC 2 + debug: + type: boolean + label: Log Datatrans responses diff --git a/src/Controller/DatatransResponseController.php b/src/Controller/DatatransResponseController.php index 0e6f33c..1c06a64 100644 --- a/src/Controller/DatatransResponseController.php +++ b/src/Controller/DatatransResponseController.php @@ -12,8 +12,9 @@ use Symfony\Component\HttpFoundation\Request; /** - * Class DatatransResponseController - * Datatrans Response Controller + * Class DatatransResponseController. + * + * Datatrans Response Controller * * @package Drupal\payment_datatrans\Controller */ @@ -22,15 +23,24 @@ class DatatransResponseController { /** * Page callback for processing successful Datatrans response. * - * @param Request $request - * Request - * @param PaymentInterface $payment + * @param \Symfony\Component\HttpFoundation\Request $request + * Request. + * @param \Drupal\payment\Entity\PaymentInterface $payment * The Payment entity type. * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Symfony\Component\HttpFoundation\RedirectResponse + * Returns the redirect response. */ public function processSuccessResponse(Request $request, PaymentInterface $payment) { try { + if (\Drupal::config('payment.payment_method_configuration.payment_datatrans')->get('pluginConfiguration')['debug']) { + if (\Drupal::moduleHandler()->moduleExists('past')) { + past_event_save('datatrans', 'response_success', 'Success response - Payment ' . $payment->id() . ': POST data', ['POST' => $request->request->all(), 'Payment' => $payment]); + } + else { + \Drupal::logger('datatrans')->debug(t('Payment success response: @response', ['@response' => $request->request->all()])); + } + } // This needs to be checked to match the payment method settings // ND being valid with its keys and data. $post_data = $request->request->all(); @@ -88,15 +98,24 @@ public function processSuccessResponse(Request $request, PaymentInterface $payme /** * Page callback for processing error Datatrans response. * - * @param PaymentInterface $payment - * The Payment entity type. - * - * @return \Symfony\Component\HttpFoundation\Response + * @param \Symfony\Component\HttpFoundation\Request $request + * Request. + * @param \Drupal\payment\Entity\PaymentInterface $payment + * The Payment entity type. * - * @throws \Exception + * @return \Symfony\Component\HttpFoundation\RedirectResponse + * Returns the redirect response. */ - public function processErrorResponse(PaymentInterface $payment) { - + public function processErrorResponse(Request $request, PaymentInterface $payment) { + if (\Drupal::config('payment.payment_method_configuration.payment_datatrans')->get('pluginConfiguration')['debug']) { + if (\Drupal::moduleHandler()->moduleExists('past')) { + past_event_save('datatrans', 'response_error', 'Error response - Payment ' . $payment->id() . ': POST data', ['POST' => $request->request->all(), 'Payment' => $payment]); + } + else { + \Drupal::logger('datatrans')->info(t('Payment error response: @response', ['@response' => $request->request->all()])); + } + drupal_set_message(t('Payment error response: @response', ['@response' => implode(', ', $request->request->all())])); + } $message = 'Datatrans communication failure. Invalid data received from Datatrans.'; \Drupal::logger('datatrans')->error('Processing failed with exception @e.', array('@e' => $message)); drupal_set_message(t('Payment processing failed.'), 'error'); @@ -106,14 +125,23 @@ public function processErrorResponse(PaymentInterface $payment) { /** * Page callback for processing cancellation Datatrans response. * - * @param PaymentInterface $payment - * The Payment entity type. + * @param \Symfony\Component\HttpFoundation\Request $request + * Request received from datatrans server. + * @param \Drupal\payment\Entity\PaymentInterface $payment + * The Payment entity type. * * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \Exception + * Returns the redirect response. */ - public function processCancelResponse(PaymentInterface $payment) { + public function processCancelResponse(Request $request, PaymentInterface $payment) { + if (\Drupal::config('payment.payment_method_configuration.payment_datatrans')->get('pluginConfiguration')['debug']) { + if (\Drupal::moduleHandler()->moduleExists('past')) { + past_event_save('datatrans', 'response_cancel', 'Cancel response - Payment ' . $payment->id() . ': POST data', ['POST' => $request->request->all(), 'Payment' => $payment]); + } + else { + \Drupal::logger('datatrans')->info(t('Payment cancel response: @response', ['@response' => $request->request->all()])); + } + } drupal_set_message(t('Payment cancelled.'), 'error'); return $this->savePayment($payment, 'payment_cancelled'); } @@ -128,9 +156,11 @@ public function processCancelResponse(PaymentInterface $payment) { * * @return string * The generated sign. + * * @throws \Exception + * Exception when generating the sign failed. */ - public function generateSign2($plugin_definition, $post_data) { + public function generateSign2(array $plugin_definition, array $post_data) { if ($plugin_definition['security']['hmac_key'] || $plugin_definition['security']['hmac_key_2']) { if ($plugin_definition['security']['use_hmac_2']) { $key = $plugin_definition['security']['hmac_key_2']; @@ -148,11 +178,12 @@ public function generateSign2($plugin_definition, $post_data) { * Saves success/cancelled/failed payment. * * @param \Drupal\payment\Entity\PaymentInterface $payment - * Payment entity. + * Payment entity. * @param string $status - * Payment status to set + * Payment status to set. * - * @return \Symfony\Component\HttpFoundation\RedirectResponse + * @return \Symfony\Component\HttpFoundation\Response + * Saves payment and returns a response. */ public function savePayment(PaymentInterface $payment, $status = 'payment_failed') { $payment->setPaymentStatus(\Drupal::service('plugin.manager.payment.status') @@ -166,12 +197,12 @@ public function savePayment(PaymentInterface $payment, $status = 'payment_failed * * No validation. * - * @param PaymentInterface $payment + * @param \Drupal\payment\Entity\PaymentInterface $payment * Payment Interface. - * @param $post_data + * @param array $post_data * Datatrans Post Data. */ - public function setPaymentConfiguration(PaymentInterface $payment, $post_data) { + public function setPaymentConfiguration(PaymentInterface $payment, array $post_data) { /** @var \Drupal\payment_datatrans\Plugin\Payment\Method\DatatransMethod $payment_method */ $payment_method = $payment->getPaymentMethod(); diff --git a/src/Plugin/Payment/MethodConfiguration/DatatransConfiguration.php b/src/Plugin/Payment/MethodConfiguration/DatatransConfiguration.php index 06911fd..8755cea 100644 --- a/src/Plugin/Payment/MethodConfiguration/DatatransConfiguration.php +++ b/src/Plugin/Payment/MethodConfiguration/DatatransConfiguration.php @@ -87,6 +87,7 @@ public function defaultConfiguration() { 'hmac_key_2' => '', 'use_hmac_2' => FALSE, ), + 'debug' => FALSE, ); } @@ -332,6 +333,23 @@ public function getHmacKeyTwo() { return $this->configuration['security']['hmac_key_2']; } + /** + * Enables logging the response from Datatrans. + * + * @param bool $state + * Whether debugging should be dis/enabled. + */ + public function setDebug($state = TRUE) { + $this->configuration['debug'] = $state; + } + + /** + * Disables logging the response from Datatrans. + */ + public function getDebug() { + return $this->configuration['debug']; + } + /** * {@inheritdoc} */ @@ -432,6 +450,12 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta ), ); + $form['debug'] = array( + '#type' => 'checkbox', + '#title' => 'Log response from Datatrans server', + '#default_value' => $this->getDebug(), + ); + return $form; } @@ -449,7 +473,8 @@ public function formElementsValidate(array $element, FormStateInterface $form_st ->setMerchantControlConstant($values['security']['merchant_control_constant']) ->setHmacKey($values['security']['hmac_key']) ->setUseHmacTwo($values['security']['use_hmac_2']) - ->setHmacKeyTwo($values['security']['hmac_key_2']); + ->setHmacKeyTwo($values['security']['hmac_key_2']) + ->setDebug($values['debug']); } } diff --git a/src/Tests/DatatransPaymentTest.php b/src/Tests/DatatransPaymentTest.php index 07ba26e..fa8e86f 100644 --- a/src/Tests/DatatransPaymentTest.php +++ b/src/Tests/DatatransPaymentTest.php @@ -27,7 +27,8 @@ class DatatransPaymentTest extends WebTestBase { 'payment_datatrans_test', 'node', 'field_ui', - 'config' + 'config', + 'dblog', ); /** @@ -95,7 +96,8 @@ protected function setUp() { 'access content', 'access administration pages', 'access user profiles', - 'payment.payment.view.any' + 'access site reports', + 'payment.payment.view.any', )); $this->drupalLogin($this->adminUser); } @@ -103,7 +105,7 @@ protected function setUp() { /** * Tests succesfull Datatrans payment. */ - function testDatatransSuccessPayment() { + public function testDatatransSuccessPayment() { // Modifies the datatrans configuration for testing purposes. $generator = \Drupal::urlGenerator(); $datatrans_configuration = array( @@ -118,23 +120,33 @@ function testDatatransSuccessPayment() { ); $this->drupalPostForm('admin/config/services/payment/method/configuration/payment_datatrans', $datatrans_configuration, t('Save')); - // Create datatrans payment + $this->drupalGet('admin/config/services/payment/method/configuration/payment_datatrans'); + // Test the debug checkbox. + $this->assertNoFieldChecked('edit-plugin-form-debug'); + $edit = [ + 'plugin_form[debug]' => TRUE, + ]; + $this->drupalPostForm(NULL, $edit, t('Save')); + $this->drupalGet('admin/config/services/payment/method/configuration/payment_datatrans'); + $this->assertFieldChecked('edit-plugin-form-debug'); + + // Create datatrans payment. $this->drupalPostForm('node/' . $this->node->id(), array(), t('Pay')); - // Retrieve plugin configuration of created node + // Retrieve plugin configuration of created node. $plugin_configuration = $this->node->{$this->fieldName}->plugin_configuration; // Array of Datatrans payment method configuration. $datatrans_payment_method_configuration = entity_load('payment_method_configuration', 'payment_datatrans')->getPluginConfiguration(); - // Check for correct Merchant ID + // Check for correct Merchant ID. $this->assertText('merchantId' . $datatrans_payment_method_configuration['merchant_id']); - // Check for correct amount + // Check for correct amount. $calculated_amount = $this->calculateAmount($plugin_configuration['amount'], $plugin_configuration['quantity'], $plugin_configuration['currency_code']); $this->assertText('amount' . $calculated_amount); - // Check for correct success, error and cancel url + // Check for correct success, error and cancel url. $this->assertText('successUrl' . $generator->generateFromRoute('payment_datatrans.response_success', array('payment' => 1), array('absolute' => TRUE))); $this->assertText('errorUrl' . $generator->generateFromRoute('payment_datatrans.response_error', array('payment' => 1), array('absolute' => TRUE))); $this->assertText('cancelUrl' . $generator->generateFromRoute('payment_datatrans.response_cancel', array('payment' => 1), array('absolute' => TRUE))); @@ -142,19 +154,19 @@ function testDatatransSuccessPayment() { // Check for correct sign with using hmac_key $this->assertText('sign' . '309dd30ad0cb07770d3a1ffda64585a9'); - // Finish and save payment + // Finish and save payment. $this->drupalPostForm(NULL, array(), t('Submit')); - // Check out the payment overview page + // Check out the payment overview page. $this->drupalGet('admin/content/payment'); - // Check for correct currency code and payment amount + // Check for correct currency code and payment amount. $this->assertText('CHF 246.00'); - // Check for correct Payment Method + // Check for correct Payment Method. $this->assertText('Datatrans'); - // Check payment configuration (city, street & zipcode) + // Check payment configuration (city, street & zipcode). /** @var \Drupal\payment\Entity\PaymentInterface $payment */ $payment = entity_load('payment', 1); $payment_method = $payment->getPaymentMethod(); @@ -167,20 +179,24 @@ function testDatatransSuccessPayment() { $this->assertTrue($payment_configuration['uppCustomerStreet'], 'street'); $this->assertTrue($payment_configuration['uppCustomerZipCode'], 'CHE'); - // Check for detailed payment information + // Check for detailed payment information. $this->drupalGet('payment/1'); $this->assertNoText('Failed'); $this->assertText('Payment description'); $this->assertText('CHF 123.00'); $this->assertText('CHF 246.00'); $this->assertText('Completed'); + + $this->drupalGet('/admin/reports/dblog'); + $this->assertText('Payment success response: POST'); } /** * Tests failing Datatrans payment. + * * The test fails by providing an incorrect hmac key. */ - function testDatatransFailedPayment() { + public function testDatatransFailedPayment() { // Modifies the datatrans configuration for testing purposes. $generator = \Drupal::urlGenerator(); $datatrans_configuration = array( @@ -193,35 +209,40 @@ function testDatatransFailedPayment() { 'plugin_form[security][hmac_key]' => '1234', // For failed test we give a wrong hmac_key 'plugin_form[security][hmac_key_2]' => '', + 'plugin_form[debug]' => TRUE, ); $this->drupalPostForm('admin/config/services/payment/method/configuration/payment_datatrans', $datatrans_configuration, t('Save')); - // Create datatrans payment + // Create datatrans payment. \Drupal::state()->set('datatrans.return_url_key', 'error'); $this->drupalPostForm('node/' . $this->node->id(), array(), t('Pay')); // Check for incorrect sign. $this->assertNoText('sign309dd30ad0cb07770d3a1ffda64585a9'); - // Finish and save payment + // Finish and save payment. $this->drupalPostForm(NULL, array(), t('Submit')); - // Check out the payment overview page + // Check out the payment overview page. $this->drupalGet('admin/content/payment'); $this->assertText('Failed'); $this->assertNoText('Success'); - // Check for detailed payment information + // Check for detailed payment information. $this->drupalGet('payment/1'); $this->assertText('Failed'); $this->assertNoText('Success'); + + $this->drupalGet('/admin/reports/dblog'); + $this->assertText('Payment error response: POST'); } /** * Tests failing Datatrans payment. + * * The test fails by providing an incorrect hmac key. */ - function testDatatransTestSignPayment() { + public function testDatatransTestSignPayment() { // Modifies the datatrans configuration for testing purposes. $generator = \Drupal::urlGenerator(); $datatrans_configuration = array( @@ -234,10 +255,11 @@ function testDatatransTestSignPayment() { 'plugin_form[security][hmac_key]' => '1234', // For failed test we give a wrong hmac_key 'plugin_form[security][hmac_key_2]' => '', + 'plugin_form[debug]' => TRUE, ); $this->drupalPostForm('admin/config/services/payment/method/configuration/payment_datatrans', $datatrans_configuration, t('Save')); - // Create datatrans payment + // Create datatrans payment. \Drupal::state()->set('datatrans.return_url_key', 'error'); \Drupal::state()->set('datatrans.identifier', rand(10, 100)); @@ -248,24 +270,27 @@ function testDatatransTestSignPayment() { \Drupal::state()->get('datatrans.identifier'), '24600', 'CHF'); $this->assertText($generated_sign); - // Finish and save payment + // Finish and save payment. $this->drupalPostForm(NULL, array(), t('Submit')); - // Check out the payment overview page + // Check out the payment overview page. $this->drupalGet('admin/content/payment'); $this->assertText('Failed'); $this->assertNoText('Success'); - // Check for detailed payment information + // Check for detailed payment information. $this->drupalGet('payment/1'); $this->assertText('Failed'); $this->assertNoText('Success'); + + $this->drupalGet('/admin/reports/dblog'); + $this->assertText('Payment error response: POST'); } /** * Tests cancelled Datatrans payment. */ - function testDatatransCancelPayment() { + public function testDatatransCancelPayment() { // Modifies the datatrans configuration for testing purposes. $generator = \Drupal::urlGenerator(); $datatrans_configuration = array( @@ -278,6 +303,7 @@ function testDatatransCancelPayment() { 'plugin_form[security][hmac_key]' => '1234', // For failed test we give a wrong hmac_key 'plugin_form[security][hmac_key_2]' => '', + 'plugin_form[debug]' => TRUE, ); $this->drupalPostForm('admin/config/services/payment/method/configuration/payment_datatrans', $datatrans_configuration, t('Save')); @@ -291,67 +317,73 @@ function testDatatransCancelPayment() { // Check for incorrect sign. $this->assertNoText('sign309dd30ad0cb07770d3a1ffda64585a9'); - // Check out the payment overview page + // Check out the payment overview page. $this->drupalGet('admin/content/payment'); $this->assertText('Cancelled'); $this->assertNoText('Failed'); $this->assertNoText('Success'); - // Check for detailed payment information + // Check for detailed payment information. $this->drupalGet('payment/1'); $this->assertText('Cancelled'); $this->assertNoText('Failed'); $this->assertNoText('Success'); + + $this->drupalGet('/admin/reports/dblog'); + $this->assertText('Payment cancel response: POST'); } /** - * Calculates the total amount + * Calculates the total amount. + * + * @param int $amount + * Base amount. + * @param int $quantity + * Quantity. + * @param string $currency_code + * Currency code. * - * @param $amount - * Base amount - * @param $quantity - * Quantity - * @param $currency_code - * Currency code * @return int - * Returns the total amount + * Returns the total amount. */ - function calculateAmount($amount, $quantity, $currency_code) { + public function calculateAmount($amount, $quantity, $currency_code) { $base_amount = $amount * $quantity; $currency = Currency::load($currency_code); return intval($base_amount * $currency->getSubunits()); } /** - * Generates the sign + * Generates the sign. + * + * @param string $hmac_key + * Hmac key. + * @param string $merchant_id + * Merchant ID. + * @param string $identifier + * The identifier. + * @param int $amount + * The order amount. + * @param string $currency + * Currency Code. * - * @param $hmac_key - * hmac key - * @param $merchant_id - * Merchant ID - * @param $identifier - * @param $amount - * The order amount - * @param $currency - * Currency Code * @return string - * Returns the sign + * Returns the sign. */ - function generateSign($hmac_key, $merchant_id, $identifier, $amount, $currency) { + public function generateSign($hmac_key, $merchant_id, $identifier, $amount, $currency) { $hmac_data = $merchant_id . $amount . $currency . $identifier; return hash_hmac('md5', $hmac_data, pack('H*', $hmac_key)); } /** - * Adds the payment field to the node + * Adds the payment field to the node. * * @param NodeTypeInterface $type - * Node type interface type - * + * Node type interface type. * @param string $label - * Field label + * Field label. * * @return \Drupal\Core\Entity\EntityInterface|static + * Returns the payment form field. */ function addPaymentFormField(NodeTypeInterface $type, $label = 'Payment Label') { $field_storage = FieldStorageConfig::create(array(