diff --git a/.gitignore b/.gitignore index 8a282a5..9af5c19 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ composer.lock composer.phar phpunit.xml +/tests/Mock/myCredentials.json \ No newline at end of file diff --git a/src/Gateway.php b/src/Gateway.php index 9b345f3..1a6ca10 100644 --- a/src/Gateway.php +++ b/src/Gateway.php @@ -82,6 +82,36 @@ public function purchase(array $parameters = array()) return $this->createRequest('\Omnipay\Beanstream\Message\PurchaseRequest', $parameters); } + /** + * @param array $parameters + * + * @return \Omnipay\Beanstream\Message\RefundRequest + */ + public function refund(array $parameters = array()) + { + return $this->createRequest('\Omnipay\Beanstream\Message\RefundRequest', $parameters); + } + + /** + * @param array $parameters + * + * @return \Omnipay\Beanstream\Message\VoidReqeust + */ + public function void(array $parameters = array()) + { + return $this->createRequest('\Omnipay\Beanstream\Message\VoidRequest', $parameters); + } + + /** + * @param array $parameters + * + * @return \Omnipay\Beanstream\Message\CaptureRequest + */ + public function capture(array $parameters = array()) + { + return $this->createRequest('\Omnipay\Beanstream\Message\CaptureRequest', $parameters); + } + /** * @param array $parameters * @return \Omnipay\Beanstream\Message\CreateProfileRequest diff --git a/src/Message/CaptureRequest.php b/src/Message/CaptureRequest.php new file mode 100644 index 0000000..53db11e --- /dev/null +++ b/src/Message/CaptureRequest.php @@ -0,0 +1,27 @@ +endpoint . '/payments/' . $this->getTransactionReference() . '/completions'; + } +} diff --git a/src/Message/RefundRequest.php b/src/Message/RefundRequest.php new file mode 100644 index 0000000..4bcaf70 --- /dev/null +++ b/src/Message/RefundRequest.php @@ -0,0 +1,35 @@ +validate('amount', 'transactionReference'); + + return array( + 'amount'=>$this->getAmount(), + 'order_number'=>$this->getOrderNumber() + ); + } + + /** + * Get the endpoint for a Refund. This is overwriting the method so we can add the transaction reference dynamically + * + * @return string + */ + public function getEndpoint() + { + return $this->endpoint . '/payments/' . $this->getTransactionReference() . '/returns'; + } +} diff --git a/src/Message/VoidRequest.php b/src/Message/VoidRequest.php new file mode 100644 index 0000000..3c87b8d --- /dev/null +++ b/src/Message/VoidRequest.php @@ -0,0 +1,36 @@ +validate('amount', 'transactionReference'); + + return array( + 'amount'=>$this->getAmount() + ); + } + + /** + * Get the endpoint for a Void. This is overwriting the method so we can add the transaction reference dynamically + * + * @return string + */ + public function getEndpoint() + { + return $this->endpoint . '/payments/' . $this->getTransactionReference() . '/void'; + } +} diff --git a/tests/GatewayTest.php b/tests/GatewayTest.php index d15aec3..f330efc 100644 --- a/tests/GatewayTest.php +++ b/tests/GatewayTest.php @@ -4,6 +4,9 @@ class GatewayTest extends GatewayTestCase { + /** @var Gateway */ + protected $gateway; + public function setUp() { parent::setUp(); @@ -181,4 +184,58 @@ public function testDeleteProfileCard() $this->assertSame(2, $request->getCardId()); $this->assertSame('DELETE', $request->getHttpMethod()); } + + /** + * Test the creation of a RefundRequest object + */ + public function testRefund() + { + $request = $this->gateway->refund( + array( + 'transactionReference'=>100, + 'amount'=> 10.00 + ) + ); + $this->assertInstanceOf('Omnipay\Beanstream\Message\RefundRequest', $request); + $this->assertSame(100, $request->getTransactionReference()); + $this->assertSame('10.00', $request->getAmount()); + $this->assertSame('POST', $request->getHttpMethod()); + $this->assertSame('https://www.beanstream.com/api/v1/payments/100/returns', $request->getEndpoint()); + } + + /** + * Test the creation of a VoidRequest object + */ + public function testVoid() + { + $request = $this->gateway->void( + array( + 'transactionReference'=>100, + 'amount'=> 10.00 + ) + ); + $this->assertInstanceOf('Omnipay\Beanstream\Message\VoidRequest', $request); + $this->assertSame(100, $request->getTransactionReference()); + $this->assertSame('10.00', $request->getAmount()); + $this->assertSame('POST', $request->getHttpMethod()); + $this->assertSame('https://www.beanstream.com/api/v1/payments/100/void', $request->getEndpoint()); + } + + /** + * Test the creation of a CaptureRequest object + */ + public function testCapture() + { + $request = $this->gateway->capture( + array( + 'transactionReference'=>100, + 'amount'=>10.00 + ) + ); + $this->assertInstanceOf('Omnipay\Beanstream\Message\CaptureRequest', $request); + $this->assertSame(100, $request->getTransactionReference()); + $this->assertSame('10.00', $request->getAmount()); + $this->assertSame('POST', $request->getHttpMethod()); + $this->assertSame('https://www.beanstream.com/api/v1/payments/100/completions', $request->getEndpoint()); + } } diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php new file mode 100644 index 0000000..fa09b64 --- /dev/null +++ b/tests/IntegrationTest.php @@ -0,0 +1,165 @@ +", + * "apiPasscode":"" + * } + * + * If that file does not exist or is not formatted in this way, all tests in this class will be skipped. + * + * @package Omnipay\Beanstream\tests + */ +class IntegrationTest extends GatewayTestCase +{ + /** @var Gateway */ + protected $gateway; + + /** + * Check for the credentials file. Skips the test if the credentials file is missing or not setup correctly. Otherwise, + * instantiates the gateway and sets up the credentials. + */ + public function setUp() + { + $merchantId = ''; + $apiPasscode = ''; + $credentialsFilePath = dirname(__FILE__) . '/Mock/myCredentials.json'; + + if(file_exists($credentialsFilePath)) { + $credentialsJson = file_get_contents($credentialsFilePath); + if($credentialsJson) { + $credentials = json_decode($credentialsJson); + $merchantId = $credentials->merchantId; + $apiPasscode = $credentials->apiPasscode; + } + } + + if(empty($merchantId) || empty($apiPasscode)) { + $this->markTestSkipped(); + } else { + $this->gateway = new Gateway(); + $this->gateway->setMerchantId($merchantId); + $this->gateway->setApiPasscode($apiPasscode); + } + } + + /** + * Test an Authorize call followed by a capture call for that transaction + */ + public function testAuthCapture() + { + $card = $this->getValidCard(); + $card['number'] = '4030000010001234'; + $card['cvv'] = '123'; + $authResponse = $this->gateway->authorize( + array( + 'amount'=>10.00, + 'card'=>$card, + 'payment_method'=>'card' + ) + )->send(); + $this->assertTrue($authResponse->isSuccessful()); + $this->assertSame('Approved', $authResponse->getMessage()); + + $captureResponse = $this->gateway->capture( + array( + 'transactionReference'=>$authResponse->getTransactionReference(), + 'amount'=>10.00 + ) + )->send(); + + $this->assertTrue($captureResponse->isSuccessful()); + $this->assertSame('Approved', $captureResponse->getMessage()); + } + + /** + * Test a failed purchase transaction. The card number used below is a special one for Beanstream that always declines. + */ + public function testFailedPurchase() + { + $card = $this->getValidCard(); + $card['number'] = '4003050500040005'; + $card['cvv'] = '123'; + $purchaseResponse = $this->gateway->purchase( + array( + 'amount'=>10.00, + 'card'=>$card, + 'payment_method'=>'card' + ) + )->send(); + + $this->assertFalse($purchaseResponse->isSuccessful()); + $this->assertSame('DECLINE', $purchaseResponse->getMessage()); + } + + /** + * Test a purchase call followed by a refund call for that purchase + */ + public function testPurchaseRefund() + { + $card = $this->getValidCard(); + $card['number'] = '4030000010001234'; + $card['cvv'] = '123'; + $purchaseResponse = $this->gateway->purchase( + array( + 'amount'=>20.00, + 'card'=>$card, + 'payment_method'=>'card' + ) + )->send(); + + $this->assertTrue($purchaseResponse->isSuccessful()); + $this->assertSame('Approved', $purchaseResponse->getMessage()); + + $refundResponse = $this->gateway->refund( + array( + 'amount'=>20.00, + 'transactionReference'=>$purchaseResponse->getTransactionReference() + ) + )->send(); + + $this->assertTrue($refundResponse->isSuccessful()); + $this->assertSame('Approved', $refundResponse->getMessage()); + } + + /** + * Test a purchase call followed by a void call for that purchase + */ + public function testPurchaseVoid() + { + $card = $this->getValidCard(); + $card['number'] = '4030000010001234'; + $card['cvv'] = '123'; + $purchaseResponse = $this->gateway->purchase( + array( + 'amount'=>20.00, + 'card'=>$card, + 'payment_method'=>'card' + ) + )->send(); + + $this->assertTrue($purchaseResponse->isSuccessful()); + $this->assertSame('Approved', $purchaseResponse->getMessage()); + + $voidResponse = $this->gateway->void( + array( + 'amount'=>20.00, + 'transactionReference'=>$purchaseResponse->getTransactionReference() + ) + )->send(); + + $this->assertTrue($voidResponse->isSuccessful()); + $this->assertSame('Approved', $voidResponse->getMessage()); + } +} \ No newline at end of file diff --git a/tests/Message/CaptureRequestTest.php b/tests/Message/CaptureRequestTest.php new file mode 100644 index 0000000..653f271 --- /dev/null +++ b/tests/Message/CaptureRequestTest.php @@ -0,0 +1,84 @@ +request = new CaptureRequest($this->getHttpClient(), $this->getHttpRequest()); + $this->request->initialize( + array( + 'amount'=>10.00, + 'transactionReference'=>1000001 + ) + ); + } + + /** + * Test the correct endpoint is being generated + */ + public function testEndpoint() + { + $this->assertSame('https://www.beanstream.com/api/v1/payments/1000001/completions', $this->request->getEndpoint()); + } + + /** + * Test that the send and response object works correctly on success + */ + public function testSendSuccess() + { + $this->setMockHttpResponse('CaptureSuccess.txt'); + $response = $this->request->send(); + $this->assertTrue($response->isSuccessful()); + $this->assertSame('1000001', $response->getTransactionReference()); + $this->assertSame('1', $response->getOrderNumber()); + $this->assertSame('Approved', $response->getMessage()); + $this->assertSame('1', $response->getMessageId()); + $this->assertSame('TEST', $response->getAuthCode()); + $this->assertSame('PAC', $response->getType()); + $this->assertNull($response->getCode()); + $responseCard = $response->getCard(); + $this->assertNotEmpty($responseCard); + $this->assertSame('VI', $responseCard['card_type']); + $this->assertSame('1234', $responseCard['last_four']); + $this->assertSame(0, $responseCard['cvd_match']); + $this->assertSame(0, $responseCard['address_match']); + $this->assertSame(0, $responseCard['postal_result']); + } + + /** + * Test that the send and response object works correctly on failure + */ + public function testSendError() + { + $this->setMockHttpResponse('CaptureFailure.txt'); + $response = $this->request->send(); + $this->assertSame(49, $response->getCode()); + $this->assertSame(3, $response->getCategory()); + $this->assertSame('Invalid transaction request string', $response->getMessage()); + $this->assertSame('https://www.beanstream.com/docs/errors#49', $response->getReference()); + $this->assertFalse($response->isSuccessful()); + $this->assertNull($response->getTransactionReference()); + $this->assertNull($response->getOrderNumber()); + $this->assertNull($response->getType()); + $this->assertNull($response->getMessageId()); + $this->assertNull($response->getAuthCode()); + $responseCard = $response->getCard(); + $this->assertEmpty($responseCard); + } +} \ No newline at end of file diff --git a/tests/Message/RefundRequestTest.php b/tests/Message/RefundRequestTest.php new file mode 100644 index 0000000..def92e2 --- /dev/null +++ b/tests/Message/RefundRequestTest.php @@ -0,0 +1,72 @@ +request = new RefundRequest($this->getHttpClient(), $this->getHttpRequest()); + $this->request->initialize( + array( + 'amount'=>10.00, + 'transactionReference'=>1000001 + ) + ); + } + + public function testEndpoint() + { + $this->assertSame('https://www.beanstream.com/api/v1/payments/1000001/returns', $this->request->getEndpoint()); + } + + public function testSendSuccess() + { + $this->setMockHttpResponse('RefundSuccess.txt'); + $response = $this->request->send(); + $this->assertTrue($response->isSuccessful()); + $this->assertSame('1000001', $response->getTransactionReference()); + $this->assertSame('1', $response->getOrderNumber()); + $this->assertSame('Approved', $response->getMessage()); + $this->assertSame('1', $response->getMessageId()); + $this->assertSame('TEST', $response->getAuthCode()); + $this->assertSame('R', $response->getType()); + $this->assertNull($response->getCode()); + $responseCard = $response->getCard(); + $this->assertNotEmpty($responseCard); + $this->assertSame('VI', $responseCard['card_type']); + $this->assertSame('1234', $responseCard['last_four']); + $this->assertSame(0, $responseCard['cvd_match']); + $this->assertSame(0, $responseCard['address_match']); + $this->assertSame(0, $responseCard['postal_result']); + } + + public function testSendError() + { + $this->setMockHttpResponse('RefundFailure.txt'); + $response = $this->request->send(); + $this->assertSame(49, $response->getCode()); + $this->assertSame(3, $response->getCategory()); + $this->assertSame('Invalid transaction request string', $response->getMessage()); + $this->assertSame('https://www.beanstream.com/docs/errors#49', $response->getReference()); + $this->assertFalse($response->isSuccessful()); + $this->assertNull($response->getTransactionReference()); + $this->assertNull($response->getOrderNumber()); + $this->assertNull($response->getType()); + $this->assertNull($response->getMessageId()); + $this->assertNull($response->getAuthCode()); + $responseCard = $response->getCard(); + $this->assertEmpty($responseCard); + } +} \ No newline at end of file diff --git a/tests/Message/VoidRequestTest.php b/tests/Message/VoidRequestTest.php new file mode 100644 index 0000000..27f1b49 --- /dev/null +++ b/tests/Message/VoidRequestTest.php @@ -0,0 +1,84 @@ +request = new VoidRequest($this->getHttpClient(), $this->getHttpRequest()); + $this->request->initialize( + array( + 'amount'=>10.00, + 'transactionReference'=>1000001 + ) + ); + } + + /** + * Test that the endpoint is being generated correctly + */ + public function testEndpoint() + { + $this->assertSame('https://www.beanstream.com/api/v1/payments/1000001/void', $this->request->getEndpoint()); + } + + /** + * Test that the send function and response object are working correctly on success + */ + public function testSendSuccess() + { + $this->setMockHttpResponse('VoidSuccess.txt'); + $response = $this->request->send(); + $this->assertTrue($response->isSuccessful()); + $this->assertSame('1000001', $response->getTransactionReference()); + $this->assertSame('1', $response->getOrderNumber()); + $this->assertSame('Approved', $response->getMessage()); + $this->assertSame('1', $response->getMessageId()); + $this->assertSame('TEST', $response->getAuthCode()); + $this->assertSame('VP', $response->getType()); + $this->assertNull($response->getCode()); + $responseCard = $response->getCard(); + $this->assertNotEmpty($responseCard); + $this->assertSame('VI', $responseCard['card_type']); + $this->assertSame('1234', $responseCard['last_four']); + $this->assertSame(0, $responseCard['cvd_match']); + $this->assertSame(0, $responseCard['address_match']); + $this->assertSame(0, $responseCard['postal_result']); + } + + /** + * Test that the send function and response object are working correctly on failure + */ + public function testSendError() + { + $this->setMockHttpResponse('VoidFailure.txt'); + $response = $this->request->send(); + $this->assertSame(49, $response->getCode()); + $this->assertSame(3, $response->getCategory()); + $this->assertSame('Invalid transaction request string', $response->getMessage()); + $this->assertSame('https://www.beanstream.com/docs/errors#49', $response->getReference()); + $this->assertFalse($response->isSuccessful()); + $this->assertNull($response->getTransactionReference()); + $this->assertNull($response->getOrderNumber()); + $this->assertNull($response->getType()); + $this->assertNull($response->getMessageId()); + $this->assertNull($response->getAuthCode()); + $responseCard = $response->getCard(); + $this->assertEmpty($responseCard); + } +} \ No newline at end of file diff --git a/tests/Mock/CaptureFailure.txt b/tests/Mock/CaptureFailure.txt new file mode 100644 index 0000000..6bf36ce --- /dev/null +++ b/tests/Mock/CaptureFailure.txt @@ -0,0 +1,9 @@ +HTTP/1.1 400 Bad Request +Content-Type: application/json + +{ + "code":49, + "category":3, + "message":"Invalid transaction request string", + "reference":"https://www.beanstream.com/docs/errors#49" +} diff --git a/tests/Mock/CaptureSuccess.txt b/tests/Mock/CaptureSuccess.txt new file mode 100644 index 0000000..f0d827f --- /dev/null +++ b/tests/Mock/CaptureSuccess.txt @@ -0,0 +1,38 @@ +HTTP/1.1 200 OK +Access-Control-Allow-Headers: accept, origin, content-type +Access-Control-Allow-Origin: * +Cache-Control: no-cache +Content-Length: 477 +Content-Type: application/json; charset=utf-8 +Date: Thu, 24 Mar 2016 18:04:30 GMT +Expires: -1 +Pragma: no-cache +Server: Microsoft-IIS/8.5 +X-AspNet-Version: 4.0.30319 +X-Powered-By: ASP.NET + +{ + "id": "1000001", + "approved": "1", + "message_id": "1", + "message": "Approved", + "auth_code": "TEST", + "created": "2016-03-24T11:04:30", + "order_number": "1", + "type": "PAC", + "payment_method": "CC", + "card": { + "card_type": "VI", + "last_four": "1234", + "cvd_match": 0, + "address_match": 0, + "postal_result": 0 + }, + "links": [ + { + "rel": "complete", + "href": "https://www.beanstream.com/api/v1/payments/1000001/completions", + "method": "POST" + } + ] +} diff --git a/tests/Mock/RefundFailure.txt b/tests/Mock/RefundFailure.txt new file mode 100644 index 0000000..6bf36ce --- /dev/null +++ b/tests/Mock/RefundFailure.txt @@ -0,0 +1,9 @@ +HTTP/1.1 400 Bad Request +Content-Type: application/json + +{ + "code":49, + "category":3, + "message":"Invalid transaction request string", + "reference":"https://www.beanstream.com/docs/errors#49" +} diff --git a/tests/Mock/RefundSuccess.txt b/tests/Mock/RefundSuccess.txt new file mode 100644 index 0000000..9fe8da5 --- /dev/null +++ b/tests/Mock/RefundSuccess.txt @@ -0,0 +1,38 @@ +HTTP/1.1 200 OK +Access-Control-Allow-Headers: accept, origin, content-type +Access-Control-Allow-Origin: * +Cache-Control: no-cache +Content-Length: 477 +Content-Type: application/json; charset=utf-8 +Date: Thu, 24 Mar 2016 18:04:30 GMT +Expires: -1 +Pragma: no-cache +Server: Microsoft-IIS/8.5 +X-AspNet-Version: 4.0.30319 +X-Powered-By: ASP.NET + +{ + "id": "1000001", + "approved": "1", + "message_id": "1", + "message": "Approved", + "auth_code": "TEST", + "created": "2016-03-24T11:04:30", + "order_number": "1", + "type": "R", + "payment_method": "CC", + "card": { + "card_type": "VI", + "last_four": "1234", + "cvd_match": 0, + "address_match": 0, + "postal_result": 0 + }, + "links": [ + { + "rel": "complete", + "href": "https://www.beanstream.com/api/v1/payments/1000001/completions", + "method": "POST" + } + ] +} diff --git a/tests/Mock/VoidFailure.txt b/tests/Mock/VoidFailure.txt new file mode 100644 index 0000000..6bf36ce --- /dev/null +++ b/tests/Mock/VoidFailure.txt @@ -0,0 +1,9 @@ +HTTP/1.1 400 Bad Request +Content-Type: application/json + +{ + "code":49, + "category":3, + "message":"Invalid transaction request string", + "reference":"https://www.beanstream.com/docs/errors#49" +} diff --git a/tests/Mock/VoidSuccess.txt b/tests/Mock/VoidSuccess.txt new file mode 100644 index 0000000..1de849a --- /dev/null +++ b/tests/Mock/VoidSuccess.txt @@ -0,0 +1,38 @@ +HTTP/1.1 200 OK +Access-Control-Allow-Headers: accept, origin, content-type +Access-Control-Allow-Origin: * +Cache-Control: no-cache +Content-Length: 477 +Content-Type: application/json; charset=utf-8 +Date: Thu, 24 Mar 2016 18:04:30 GMT +Expires: -1 +Pragma: no-cache +Server: Microsoft-IIS/8.5 +X-AspNet-Version: 4.0.30319 +X-Powered-By: ASP.NET + +{ + "id": "1000001", + "approved": "1", + "message_id": "1", + "message": "Approved", + "auth_code": "TEST", + "created": "2016-03-24T11:04:30", + "order_number": "1", + "type": "VP", + "payment_method": "CC", + "card": { + "card_type": "VI", + "last_four": "1234", + "cvd_match": 0, + "address_match": 0, + "postal_result": 0 + }, + "links": [ + { + "rel": "complete", + "href": "https://www.beanstream.com/api/v1/payments/1000001/completions", + "method": "POST" + } + ] +}