From 5aa044557cc4f42777a8ea722604b777daeb4990 Mon Sep 17 00:00:00 2001 From: eileen Date: Fri, 5 Jun 2020 13:48:11 +1200 Subject: [PATCH] Convert Authorize.net doDirectPayment to use guzzle & add test This brings the main Authorize.net payment function under testing, putting us in a better position to bring it into line with more recommended coding practices. Note the trick to writing tests for Guzzle is to first add the lines ``` $this->setUpClientWithHistoryContainer(); $this->processor->setGuzzleClient($this->getGuzzleClient()); ... do the stuff $responses = $this->getResponseBodies(); $requests = $this->getRequestBodies(); ``` That allows you to capture the outgoing & incoming responses that form the expectations. Obviously some credential wrangling is needed to get an OK response but it's also possible with some processors (e.g ones that are basically done for) to just worry about ensuring the outgoing is captured --- CRM/Core/Payment/AuthorizeNet.php | 29 ++---- .../CRM/Core/Payment/AuthorizeNetTest.php | 93 ++++++++++++++----- 2 files changed, 80 insertions(+), 42 deletions(-) diff --git a/CRM/Core/Payment/AuthorizeNet.php b/CRM/Core/Payment/AuthorizeNet.php index abf5e4aaa7..2f85ec7c62 100644 --- a/CRM/Core/Payment/AuthorizeNet.php +++ b/CRM/Core/Payment/AuthorizeNet.php @@ -113,7 +113,9 @@ class CRM_Core_Payment_AuthorizeNet extends CRM_Core_Payment { */ public function doDirectPayment(&$params) { if (!defined('CURLOPT_SSLCERT')) { - return self::error(9001, 'Authorize.Net requires curl with SSL support'); + // Note that guzzle doesn't necessarily require CURL, although it prefers it. But we should leave this error + // here unless someone suggests it is not required since it's likely helpful. + throw new PaymentProcessorException('Authorize.Net requires curl with SSL support', 9001); } /* @@ -161,24 +163,13 @@ class CRM_Core_Payment_AuthorizeNet extends CRM_Core_Payment { return self::error(9004, 'It appears that this transaction is a duplicate. Have you already submitted the form once? If so there may have been a connection problem. Check your email for a receipt from Authorize.net. If you do not receive a receipt within 2 hours you can try your transaction again. If you continue to have problems please contact the site administrator.'); } - $submit = curl_init($this->_paymentProcessor['url_site']); - - if (!$submit) { - return self::error(9002, 'Could not initiate connection to payment gateway'); - } - - curl_setopt($submit, CURLOPT_POST, TRUE); - curl_setopt($submit, CURLOPT_RETURNTRANSFER, TRUE); - curl_setopt($submit, CURLOPT_POSTFIELDS, implode('&', $postFields)); - curl_setopt($submit, CURLOPT_SSL_VERIFYPEER, Civi::settings()->get('verifySSL')); - - $response = curl_exec($submit); - - if (!$response) { - return self::error(curl_errno($submit), curl_error($submit)); - } - - curl_close($submit); + $response = (string) $this->getGuzzleClient()->post($this->_paymentProcessor['url_site'], [ + 'body' => implode('&', $postFields), + 'curl' => [ + CURLOPT_RETURNTRANSFER => TRUE, + CURLOPT_SSL_VERIFYPEER => Civi::settings()->get('verifySSL'), + ], + ])->getBody(); $response_fields = $this->explode_csv($response); diff --git a/tests/phpunit/CRM/Core/Payment/AuthorizeNetTest.php b/tests/phpunit/CRM/Core/Payment/AuthorizeNetTest.php index 63b4c4bd2c..8d6d21d6e1 100644 --- a/tests/phpunit/CRM/Core/Payment/AuthorizeNetTest.php +++ b/tests/phpunit/CRM/Core/Payment/AuthorizeNetTest.php @@ -38,6 +38,39 @@ class CRM_Core_Payment_AuthorizeNetTest extends CiviUnitTestCase { $this->quickCleanUpFinancialEntities(); } + /** + * Test doing a one-off payment. + * + * @throws \Civi\Payment\Exception\PaymentProcessorException + */ + public function testSinglePayment() { + $this->createMockHandler([$this->getExpectedSinglePaymentResponse()]); + $this->setUpClientWithHistoryContainer(); + $this->processor->setGuzzleClient($this->getGuzzleClient()); + $params = $this->getBillingParams(); + $params['amount'] = 5.24; + $this->processor->doPayment($params); + $this->assertEquals($this->getExpectedSinglePaymentRequest(), $this->getRequestBodies()[0]); + } + + /** + * Get the expected response from Authorize.net. + * + * @return string + */ + public function getExpectedSinglePaymentResponse() { + return '"1","1","1","(TESTMODE) This transaction has been approved.","000000","P","0","","","5.24","CC","auth_capture","","John","O'Connor","","","","","","","","","","","","","","","","","","","","","","","",""'; + } + + /** + * Get the expected request from Authorize.net. + * + * @return string + */ + public function getExpectedSinglePaymentRequest() { + return 'x_login=4y5BfuW7jm&x_tran_key=4cAmW927n8uLf5J8&x_email_customer=&x_first_name=John&x_last_name=O%27Connor&x_address=&x_city=&x_state=&x_zip=&x_country=&x_customer_ip=&x_email=&x_invoice_num=&x_amount=5.24&x_currency_code=&x_description=&x_cust_id=&x_relay_response=FALSE&x_delim_data=TRUE&x_delim_char=%2C&x_encap_char=%22&x_card_num=4444333322221111&x_card_code=123&x_exp_date=10%2F2022&x_test_request=TRUE'; + } + /** * Create a single post dated payment as a recurring transaction. * @@ -48,7 +81,7 @@ class CRM_Core_Payment_AuthorizeNetTest extends CiviUnitTestCase { $this->setUpClientWithHistoryContainer(); $this->processor->setGuzzleClient($this->getGuzzleClient()); $firstName = 'John'; - $lastName = 'Smith'; + $lastName = "O\'Connor"; $nameParams = ['first_name' => 'John', 'last_name' => $lastName]; $contactId = $this->individualCreate($nameParams); @@ -82,24 +115,11 @@ class CRM_Core_Payment_AuthorizeNetTest extends CiviUnitTestCase { 'contribution_status_id' => 2, ]); - $params = [ + $billingParams = $this->getBillingParams(); + + $params = array_merge($billingParams, [ 'qfKey' => '08ed21c7ca00a1f7d32fff2488596ef7_4454', 'hidden_CreditCard' => 1, - 'billing_first_name' => $firstName, - 'billing_middle_name' => '', - 'billing_last_name' => $lastName, - 'billing_street_address-5' => '8 Hobbitton Road', - 'billing_city-5' => 'The Shire', - 'billing_state_province_id-5' => 1012, - 'billing_postal_code-5' => 5010, - 'billing_country_id-5' => 1228, - 'credit_card_number' => '4444333322221111', - 'cvv2' => 123, - 'credit_card_exp_date' => [ - 'M' => 9, - 'Y' => 2025, - ], - 'credit_card_type' => 'Visa', 'is_recur' => 1, 'frequency_interval' => 1, 'frequency_unit' => 'month', @@ -147,12 +167,12 @@ class CRM_Core_Payment_AuthorizeNetTest extends CiviUnitTestCase { 'contributionType_name' => 'My precious', 'contributionType_accounting_code' => '', 'contributionPageID' => '', - 'email' => "{$firstName}.{$lastName}@example.com", + 'email' => 'john.smith@example.com', 'contactID' => $contactId, 'contributionID' => $contribution['id'], 'contributionTypeID' => $this->_financialTypeId, 'contributionRecurID' => $recur['id'], - ]; + ]); // turn verifySSL off Civi::settings()->set('verifySSL', '0'); @@ -193,7 +213,7 @@ class CRM_Core_Payment_AuthorizeNetTest extends CiviUnitTestCase { $start_date = date('Ymd', strtotime('+ 1 week')); $firstName = 'John'; - $lastName = 'Smith'; + $lastName = "O'Connor"; $nameParams = ['first_name' => $firstName, 'last_name' => $lastName]; $contactId = $this->individualCreate($nameParams); @@ -294,7 +314,7 @@ class CRM_Core_Payment_AuthorizeNetTest extends CiviUnitTestCase { 'postal_code' => 5010, 'country' => 'US', 'contributionPageID' => '', - 'email' => "{$firstName}.{$lastName}@example.com", + 'email' => 'john.smith@example.com', 'contactID' => $contactId, 'contributionID' => $contribution['id'], 'contributionRecurID' => $recur['id'], @@ -366,11 +386,11 @@ class CRM_Core_Payment_AuthorizeNetTest extends CiviUnitTestCase { ' . $contactID . ' - John.Smith@example.com + john.smith@example.com John - Smith + O\'Connor
8 Hobbiton Road
The Shire IL @@ -391,4 +411,31 @@ class CRM_Core_Payment_AuthorizeNetTest extends CiviUnitTestCase { return '8d468ca1b1dd5c2b56c7OkI00001Successful.663205215120232801512027350'; } + /** + * Get some basic billing parameters. + * + * @return array + */ + protected function getBillingParams(): array { + return [ + 'billing_first_name' => 'John', + 'billing_middle_name' => '', + 'billing_last_name' => "O'Connor", + 'billing_street_address-5' => '8 Hobbitton Road', + 'billing_city-5' => 'The Shire', + 'billing_state_province_id-5' => 1012, + 'billing_postal_code-5' => 5010, + 'billing_country_id-5' => 1228, + 'credit_card_number' => '4444333322221111', + 'cvv2' => 123, + 'credit_card_exp_date' => [ + 'M' => 9, + 'Y' => 2025, + ], + 'credit_card_type' => 'Visa', + 'year' => 2022, + 'month' => 10, + ]; + } + } -- 2.25.1