diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml
new file mode 100644
index 0000000..ba58495
--- /dev/null
+++ b/.github/workflows/phpunit.yml
@@ -0,0 +1,75 @@
+name: "PHPUnit tests"
+
+on:
+ pull_request:
+ push:
+ branches:
+ - "master"
+
+jobs:
+ phpunit:
+ name: "PHPUnit tests"
+
+ runs-on: "ubuntu-latest"
+
+ strategy:
+ matrix:
+ dependencies:
+ - "highest"
+ php-version:
+ - "7.2"
+ - "7.3"
+ - "7.4"
+ - "8.0"
+ - "8.1"
+ - "8.2"
+ - "8.3"
+ - "8.4"
+
+ include:
+ - php-version: '7.2'
+ dependencies: "lowest"
+
+ steps:
+ - name: "Checkout"
+ uses: "actions/checkout@v4"
+ with:
+ # Fetch arbitrary more-than-one commit or Scrutinizer will error
+ fetch-depth: 10
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "pcov"
+ php-version: "${{ matrix.php-version }}"
+ ini-values: memory_limit=-1
+ tools: composer:v2
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ # Fixes any pubkeys failure (add a `composer diagnose` step to debug if necessary)
+ - name: "Composer force self-update"
+ run: "composer self-update"
+
+ - name: "Install lowest dependencies"
+ if: ${{ matrix.dependencies == 'lowest' }}
+ run: "composer update --prefer-lowest --no-interaction --no-progress"
+
+ - name: "Install highest dependencies"
+ if: ${{ matrix.dependencies == 'highest' }}
+ run: "composer update --no-interaction --no-progress"
+
+ - name: "Tests (PHPUnit 9)"
+ if: ${{ matrix.php-version <= '8.0' }}
+ run: "vendor/bin/phpunit --configuration phpunit9.xml.dist"
+
+ - name: "Tests (PHPUnit 10+)"
+ if: ${{ matrix.php-version >= '8.1' }}
+ run: "vendor/bin/phpunit"
+
+ - name: Upload Scrutinizer coverage
+ continue-on-error: true
+ uses: sudo-bot/action-scrutinizer@latest
+ # Do not run this step on forked versions of the main repository (example: contributor forks)
+ if: github.repository == 'patronbase/omnipay-bpoint'
+ with:
+ cli-args: "--format=php-clover build/logs/clover.xml --revision=${{ github.event.pull_request.head.sha || github.sha }}"
diff --git a/.gitignore b/.gitignore
index 8a282a5..472a3b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
composer.lock
composer.phar
phpunit.xml
+/.phpunit.result.cache
+/build
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 22814ad..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-language: php
-
-dist: trusty
-php:
- - "7.2"
- - "7.1"
- - "7.0"
- - "5.6"
- - "5.5"
- - "5.4"
-
-matrix:
- include:
- - php: "5.3"
- dist: precise
-
-before_script:
- - composer install -n --dev --prefer-source
-
-script:
- - vendor/bin/phpcs --standard=PSR2 src
- - vendor/bin/phpunit --verbose --coverage-clover coverage.clover
-
-after_success:
- - wget https://scrutinizer-ci.com/ocular.phar
- - php ocular.phar code-coverage:upload --format=php-clover coverage.clover
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d67ed30..ea026a7 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,4 +7,4 @@
* Ensure your code is nicely formatted in the [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
style and that all tests pass.
* Send the pull request.
-* Check that the Travis CI build passed. If not, rinse and repeat.
+* Check that the CI build passed. If not, rinse and repeat.
diff --git a/LICENSE b/LICENSE
index 758bb84..af5cb67 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2019 Leith Caldwell
+Copyright (c) 2020 Leith Caldwell
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/README.md b/README.md
index 64a5562..c25ef42 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
**BPOINT driver for the Omnipay PHP payment processing library**
-[](https://travis-ci.org/PatronBase/omnipay-bpoint)
+[](https://github.com/PatronBase/omnipay-bpoint/actions)
[](https://scrutinizer-ci.com/g/PatronBase/omnipay-bpoint/code-structure)
[](https://scrutinizer-ci.com/g/PatronBase/omnipay-bpoint/?branch=master)
[](LICENSE.md)
@@ -11,8 +11,8 @@
[Omnipay](https://github.com/thephpleague/omnipay) is a framework agnostic, multi-gateway payment
-processing library for PHP 5.3+. This package implements BPOINT support for Omnipay. It includes
-support for both redirect (3-party) and webservice (2-party) versions of the gateway.
+processing library for PHP 7.2+. This package implements BPOINT support for Omnipay. It includes
+support for only the redirect (3-party) version of the gateway.
## Installation
@@ -22,7 +22,7 @@ to your `composer.json` file:
```json
{
"require": {
- "PatronBase/omnipay-bpoint": "~2.0"
+ "PatronBase/omnipay-bpoint": "~3.0"
}
}
```
diff --git a/composer.json b/composer.json
index 366a236..35e313b 100644
--- a/composer.json
+++ b/composer.json
@@ -30,14 +30,29 @@
"psr-4": { "Omnipay\\BPOINT\\" : "tests/" }
},
"require": {
- "omnipay/common": "~2.0"
+ "php": "^7.2|^8.0",
+ "omnipay/common": "^3.1"
},
"require-dev": {
- "omnipay/tests": "~2.0"
+ "omnipay/tests": "^4.2",
+ "squizlabs/php_codesniffer": "^3.5",
+ "symfony/psr-http-message-bridge": "~1.1.1|^2.0",
+ "guzzlehttp/psr7": "^2.0"
+ },
+ "scripts": {
+ "test": "phpunit",
+ "check-style": "phpcs -p --standard=PSR2 src/",
+ "fix-style": "phpcbf -p --standard=PSR2 src/"
},
"extra": {
"branch-alias": {
- "dev-master": "2.0.x-dev"
+ "dev-master": "3.2.x-dev"
+ }
+ },
+ "prefer-stable": true,
+ "config": {
+ "allow-plugins": {
+ "php-http/discovery": true
}
}
}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 913e932..21263b8 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,28 +1,30 @@
-
-
-
-
-
-
- ./tests/
-
-
-
-
-
-
-
- ./src
-
-
+ stopOnFailure="false">
+
+
+
+
+
+
+
+
+
+ src/
+
+
+
+
+ tests
+
+
+
+
+
diff --git a/phpunit9.xml.dist b/phpunit9.xml.dist
new file mode 100644
index 0000000..5ab568e
--- /dev/null
+++ b/phpunit9.xml.dist
@@ -0,0 +1,32 @@
+
+
+
+
+ src/
+
+
+
+
+
+
+
+
+
+ tests
+
+
+
+
+
+
diff --git a/src/Message/CompletePurchaseRequest.php b/src/Message/CompletePurchaseRequest.php
index ffb5622..325aa9a 100644
--- a/src/Message/CompletePurchaseRequest.php
+++ b/src/Message/CompletePurchaseRequest.php
@@ -15,7 +15,7 @@ class CompletePurchaseRequest extends PurchaseRequest
public function getData()
{
- return $this->httpRequest->request->all();
+ return $this->httpRequest->request->all() + $this->httpRequest->query->all();
}
/**
@@ -26,14 +26,13 @@ public function sendData($data)
// if we have a valid API response and a result key, let's get more detail about the transaction
if (isset($data['ResponseCode']) && $data['ResponseCode'] == 0 && isset($data['ResultKey'])) {
// submit request with the returned result key
- $httpRequest = $this->httpClient->createRequest(
+ $httpResponse = $this->httpClient->request(
'GET',
$this->getEndpoint()."/".$data['ResultKey'],
- null
+ ['Authorization' => $this->getAuthHeader()]
);
- $httpResponse = $httpRequest->setHeader('Authorization', $this->getAuthHeader())->send();
// get response data
- $responseData = $httpResponse->json();
+ $responseData = json_decode($httpResponse->getBody()->getContents(), true);
$data = array_merge($data, $responseData['TxnResp']);
}
return $this->response = new CompletePurchaseResponse($this, $data);
diff --git a/src/Message/CompletePurchaseResponse.php b/src/Message/CompletePurchaseResponse.php
index 74d6e18..47e339b 100644
--- a/src/Message/CompletePurchaseResponse.php
+++ b/src/Message/CompletePurchaseResponse.php
@@ -27,7 +27,7 @@ public function isSuccessful()
*/
public function getTransactionReference()
{
- return isset($this->data['AuthoriseId']) ? $this->data['AuthoriseId'] : null;
+ return $this->data['AuthoriseId'] ?? null;
}
/**
@@ -37,7 +37,7 @@ public function getTransactionReference()
*/
public function getMessage()
{
- return isset($this->data['ResponseText']) ? $this->data['ResponseText'] : null;
+ return $this->data['ResponseText'] ?? null;
}
/**
@@ -47,6 +47,16 @@ public function getMessage()
*/
public function getCardType()
{
- return isset($this->data['CardType']) ? $this->data['CardType'] : null;
+ return $this->data['CardType'] ?? null;
+ }
+
+ /**
+ * Get the card reference (payment token) if available
+ *
+ * @return null|string
+ */
+ public function getCardReference()
+ {
+ return $this->data['DVToken'] ?? null;
}
}
diff --git a/src/Message/PurchaseRequest.php b/src/Message/PurchaseRequest.php
index 30a4828..0ca8948 100644
--- a/src/Message/PurchaseRequest.php
+++ b/src/Message/PurchaseRequest.php
@@ -45,6 +45,16 @@ public function setMerchantNumber($value)
return $this->setParameter('merchantNumber', $value);
}
+ public function getCreateToken()
+ {
+ return $this->getParameter('createToken');
+ }
+
+ public function setCreateToken($value)
+ {
+ return $this->setParameter('createToken', $value);
+ }
+
public function getMerchantShortName()
{
return $this->getParameter('merchantShortName');
@@ -55,6 +65,21 @@ public function setMerchantShortName($value)
return $this->setParameter('merchantShortName', $value);
}
+ public function getBillerCode()
+ {
+ return $this->getParameter('billerCode');
+ }
+
+ /**
+ * Set biller code for the transaction (differentiate income streams); required for using stored card tokens
+ *
+ * @param string $value String, max 50 characters
+ */
+ public function setBillerCode($value)
+ {
+ return $this->setParameter('billerCode', $value);
+ }
+
public function getCustomerReferenceNumber1()
{
return $this->getParameter('customerReferenceNumber1');
@@ -63,7 +88,7 @@ public function getCustomerReferenceNumber1()
/**
* Set customer configurable reference #1
*
- * @param bool $value String, max 50 characters
+ * @param string $value String, max 50 characters
*/
public function setCustomerReferenceNumber1($value)
{
@@ -78,7 +103,7 @@ public function getCustomerReferenceNumber2()
/**
* Set customer configurable reference #2
*
- * @param bool $value String, max 50 characters
+ * @param string $value String, max 50 characters
*/
public function setCustomerReferenceNumber2($value)
{
@@ -93,26 +118,91 @@ public function getCustomerReferenceNumber3()
/**
* Set customer configurable reference #3
*
- * @param bool $value String, max 50 characters
+ * @param string $value String, max 50 characters
*/
public function setCustomerReferenceNumber3($value)
{
return $this->setParameter('customerReferenceNumber3', $value);
}
+ public function getHideBillerCode()
+ {
+ return $this->getParameter('hideBillerCode');
+ }
+
+ /**
+ * Whether to hide the biller code from the end-user on the hosted checkout page
+ *
+ * @param bool $value
+ */
+ public function setHideBillerCode($value)
+ {
+ return $this->setParameter('hideBillerCode', $value);
+ }
+
+ public function getHideCustomerReferenceNumber1()
+ {
+ return $this->getParameter('hideCustomerReferenceNumber1');
+ }
+
+ /**
+ * Whether to hide the customer configurable reference #1 from the end-user on the hosted checkout page
+ *
+ * @param bool $value
+ */
+ public function setHideCustomerReferenceNumber1($value)
+ {
+ return $this->setParameter('hideCustomerReferenceNumber1', $value);
+ }
+
+ public function getHideCustomerReferenceNumber2()
+ {
+ return $this->getParameter('hideCustomerReferenceNumber2');
+ }
+
+ /**
+ * Whether to hide the customer configurable reference #2 from the end-user on the hosted checkout page
+ *
+ * @param bool $value
+ */
+ public function setHideCustomerReferenceNumber2($value)
+ {
+ return $this->setParameter('hideCustomerReferenceNumber2', $value);
+ }
+
+ public function getHideCustomerReferenceNumber3()
+ {
+ return $this->getParameter('hideCustomerReferenceNumber3');
+ }
+
+ /**
+ * Whether to hide the customer configurable reference #3 from the end-user on the hosted checkout page
+ *
+ * @param bool $value
+ */
+ public function setHideCustomerReferenceNumber3($value)
+ {
+ return $this->setParameter('hideCustomerReferenceNumber3', $value);
+ }
+
+ /**
+ * @deprecated Alias. Use standard `getCreateToken()` instead.
+ */
public function getGenerateToken()
{
- return $this->getParameter('generateToken');
+ return $this->getCreateToken();
}
/**
* Indicate whether or not to generate a token for the card used in the transaction
*
+ * @deprecated Alias. Use standard `setCreateToken()` instead.
+ *
* @param bool $value Generate a token or not
*/
public function setGenerateToken($value)
{
- return $this->setParameter('generateToken', $value);
+ return $this->setCreateToken($value);
}
public function getCustomerNumber()
@@ -123,7 +213,7 @@ public function getCustomerNumber()
/**
* Set the unique customer ID in the merchant system
*
- * @param bool $value Customer number to set
+ * @param string $value Customer number to set
*/
public function setCustomerNumber($value)
{
@@ -136,16 +226,23 @@ public function getData()
$amount = $this->getAmountInteger();
$data = array(
+ 'HppParameters' => array(
+ 'HideBillerCode' => (bool) $this->getHideBillerCode(),
+ 'HideCrn1' => (bool) $this->getHideCustomerReferenceNumber1(),
+ 'HideCrn2' => (bool) $this->getHideCustomerReferenceNumber2(),
+ 'HideCrn3' => (bool) $this->getHideCustomerReferenceNumber3(),
+ ),
'ProcessTxnData' => array(
'Action' => $amount > 0 ? 'payment' : 'verify_only',
'TestMode' => $this->getTestMode(),
'Amount' => $this->getAmountInteger(),
+ 'BillerCode' => $this->getBillerCode(),
'Crn1' => $this->getCustomerReferenceNumber1(),
'Crn2' => $this->getCustomerReferenceNumber2(),
'Crn3' => $this->getCustomerReferenceNumber3(),
'Currency' => $this->getCurrency(),
// 1 - no; 3 - always (don't leave it up to system or customer)
- 'TokenisationMode' => $this->getGenerateToken() ? 3 : 1,
+ 'TokenisationMode' => $this->getCreateToken() ? 3 : 1,
'MerchantReference' => $this->getTransactionId(),
'SubType' => 'single',
'Type' => 'internet',
@@ -153,6 +250,10 @@ public function getData()
'RedirectionUrl' => $this->getReturnUrl(),
'WebHookUrl' => $this->getNotifyUrl(),
);
+ if ($this->getCancelUrl()) {
+ $data['HppParameters']['ReturnBarLabel'] = 'Cancel';
+ $data['HppParameters']['ReturnBarUrl'] = $this->getCancelUrl();
+ }
// add item details if available
$items = $this->getItems();
if ($items) {
@@ -202,17 +303,32 @@ public function getData()
}
}
+ // add stored card token if available
+ if ($this->getToken() || $this->getCardReference()) {
+ $data['ProcessTxnData']['DVTokenData'] = array(
+ 'DVToken' => $this->getToken() ?? $this->getCardReference(),
+ 'UpdateDVTokenExpiryDate' => false,
+ );
+ }
+
return $data;
}
public function sendData($data)
{
// submit data as request to Authkey endpoint
- $httpRequest = $this->httpClient->createRequest('POST', $this->getEndpoint(), null);
- $httpRequest->setBody(json_encode($data), 'application/json');
- $httpResponse = $httpRequest->setHeader('Authorization', $this->getAuthHeader())->send();
+ $httpResponse = $this->httpClient->request(
+ 'POST',
+ $this->getEndpoint(),
+ [
+ 'Accept' => 'application/json',
+ 'Content-Type' => 'application/json',
+ 'Authorization' => $this->getAuthHeader(),
+ ],
+ json_encode($data)
+ );
// get response data
- $responseData = $httpResponse->json();
+ $responseData = json_decode($httpResponse->getBody()->getContents(), true);
return $this->response = new PurchaseResponse($this, $responseData);
}
diff --git a/src/Message/PurchaseResponse.php b/src/Message/PurchaseResponse.php
index b9bd8b2..484f3fa 100644
--- a/src/Message/PurchaseResponse.php
+++ b/src/Message/PurchaseResponse.php
@@ -3,11 +3,12 @@
namespace Omnipay\BPOINT\Message;
use Omnipay\Common\Message\AbstractResponse;
+use Omnipay\Common\Message\RedirectResponseInterface;
/**
* BPOINT Purchase Response
*/
-class PurchaseResponse extends AbstractResponse
+class PurchaseResponse extends AbstractResponse implements RedirectResponseInterface
{
public function isSuccessful()
{
diff --git a/src/Message/WebhookNotification.php b/src/Message/WebhookNotification.php
new file mode 100644
index 0000000..4b47377
--- /dev/null
+++ b/src/Message/WebhookNotification.php
@@ -0,0 +1,150 @@
+data = json_decode($httpRequest->getContent(), true);
+ }
+
+ /**
+ * ResponseInterface implemented so that we can return self here for any legacy support that uses send()
+ */
+ public function sendData($data)
+ {
+ return $this;
+ }
+
+ /**
+ * Get the authorisation code if available.
+ *
+ * @return null|string
+ */
+ public function getTransactionReference()
+ {
+ return isset($this->data['AuthoriseId']) ? $this->data['AuthoriseId'] : null;
+ }
+
+ /**
+ * Was the transaction successful?
+ *
+ * @return string Transaction status, one of {@link NotificationInterface::STATUS_COMPLETED},
+ * {@link NotificationInterface::STATUS_PENDING}, or {@link NotificationInterface::STATUS_FAILED}.
+ */
+ public function getTransactionStatus()
+ {
+ if (!isset($this->data['ResponseCode'])) {
+ return NotificationInterface::STATUS_FAILED;
+ }
+ if ($this->data['ResponseCode'] == '0') {
+ return NotificationInterface::STATUS_COMPLETED;
+ }
+ if ($this->data['ResponseCode'] == 'P') {
+ return NotificationInterface::STATUS_PENDING;
+ }
+
+ // last resort, assume failure
+ return NotificationInterface::STATUS_FAILED;
+ }
+
+ /**
+ * Get the merchant response message if available.
+ *
+ * @return null|string
+ */
+ public function getMessage()
+ {
+ return isset($this->data['ResponseText']) ? $this->data['ResponseText'] : null;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Get the original request which generated this response
+ *
+ * @return RequestInterface
+ */
+ public function getRequest()
+ {
+ return $this;
+ }
+
+ /**
+ * Is the response successful?
+ *
+ * @return boolean
+ */
+ public function isSuccessful()
+ {
+ return $this->getTransactionStatus() == NotificationInterface::STATUS_COMPLETED;
+ }
+
+ /**
+ * Does the response require a redirect?
+ *
+ * @return boolean
+ */
+ public function isRedirect()
+ {
+ return false;
+ }
+
+ /**
+ * Is the transaction cancelled by the user?
+ *
+ * @return boolean
+ */
+ public function isCancelled()
+ {
+ return isset($this->data['ResponseCode']) && $this->data['ResponseCode'] == 'C';
+ }
+
+ /**
+ * Response code
+ *
+ * @return null|string A response code from the payment gateway
+ */
+ public function getCode()
+ {
+ return isset($this->data['ResponseCode']) ? $this->data['ResponseCode'] : null;
+ }
+
+ /**
+ * Get the card type if available.
+ *
+ * @return null|string
+ */
+ public function getCardType()
+ {
+ return isset($this->data['CardType']) ? $this->data['CardType'] : null;
+ }
+}
diff --git a/src/RedirectGateway.php b/src/RedirectGateway.php
index 2fb2f70..db3aacb 100644
--- a/src/RedirectGateway.php
+++ b/src/RedirectGateway.php
@@ -5,6 +5,7 @@
use Omnipay\Common\AbstractGateway;
use Omnipay\BPOINT\Message\CompletePurchaseRequest;
use Omnipay\BPOINT\Message\PurchaseRequest;
+use Omnipay\BPOINT\Message\WebhookNotification;
/**
* BPOINT Redirect Gateway
@@ -78,4 +79,9 @@ public function completePurchase(array $parameters = array())
{
return $this->createRequest('\Omnipay\BPOINT\Message\CompletePurchaseRequest', $parameters);
}
+
+ public function acceptNotification()
+ {
+ return $this->createRequest('\Omnipay\BPOINT\Message\WebhookNotification', array());
+ }
}
diff --git a/tests/Message/CompletePurchaseResponseTest.php b/tests/Message/CompletePurchaseResponseTest.php
index 4f07709..30a0acd 100644
--- a/tests/Message/CompletePurchaseResponseTest.php
+++ b/tests/Message/CompletePurchaseResponseTest.php
@@ -12,7 +12,7 @@ class CompletePurchaseResponseTest extends TestCase
/** @var mixed[] Parsed TxnResp data from a transaction result */
private $responseData;
- public function setUp()
+ public function setUp(): void
{
// demo data from the BPOINT API documentation
$this->responseData = array(
@@ -62,7 +62,7 @@ public function setUp()
'DVToken' => null,
'Type' => 'internet',
'FraudScreeningResponse' => array(
- 'ReDResponse' => array(
+ 'ReDResponse' => array(
'FRAUD_REC_ID' => '123412341234SAX20150101100000000',
'FRAUD_RSP_CD' => '0100',
'FRAUD_STAT_CD' => 'ACCEPT',
@@ -104,6 +104,7 @@ public function testCompletePurchaseSuccess()
$this->assertSame('Approved', $this->response->getMessage());
$this->assertSame('372626', $this->response->getTransactionReference());
$this->assertSame('MC', $this->response->getCardType());
+ $this->assertNull($this->response->getCardReference());
// confirm the request format was valid
$requestData = $this->response->getRequest()->getData();
@@ -138,6 +139,7 @@ public function testCompletePurchaseFailure()
$this->assertSame('Invalid card number', $this->response->getMessage());
$this->assertNull($this->response->getTransactionReference());
$this->assertNull($this->response->getCardType());
+ $this->assertNull($this->response->getCardReference());
// confirm the request format was valid
$requestData = $this->response->getRequest()->getData();
@@ -165,9 +167,22 @@ public function testCompletePurchaseError()
$this->assertSame('Invalid credentials', $this->response->getMessage());
$this->assertNull($this->response->getTransactionReference());
$this->assertNull($this->response->getCardType());
+ $this->assertNull($this->response->getCardReference());
$data = $this->response->getData();
$this->assertSame(1, $data['ResponseCode']);
}
+
+ public function testCompletePurchaseReturnsCardReference()
+ {
+ // adjust to have a token
+ $this->responseData['DVToken'] = '1234567890123456';
+
+ $this->response = new CompletePurchaseResponse($this->getMockRequest(), $this->responseData);
+
+ $this->assertTrue($this->response->isSuccessful());
+ $this->assertFalse($this->response->isRedirect());
+ $this->assertSame('1234567890123456', $this->response->getCardReference());
+ }
}
diff --git a/tests/Message/PurchaseRequestTest.php b/tests/Message/PurchaseRequestTest.php
index d2a1afd..96d7436 100644
--- a/tests/Message/PurchaseRequestTest.php
+++ b/tests/Message/PurchaseRequestTest.php
@@ -12,7 +12,7 @@ class PurchaseRequestTest extends TestCase
/** @var mixed[] Data to initialize the request with */
private $options;
- public function setUp()
+ public function setUp(): void
{
$this->request = new PurchaseRequest($this->getHttpClient(), $this->getHttpRequest());
$this->options = array(
@@ -22,10 +22,11 @@ public function setUp()
'password' => 'DemoPassword!',
'merchantNumber' => '5353109000000000',
'merchantShortName' => 'DEMO123',
+ 'billerCode' => '1234567',
'customerReferenceNumber1' => 'cr1',
'customerReferenceNumber2' => 'cr2',
'customerReferenceNumber3' => 'cr3',
- 'generateToken' => false,
+ 'createToken' => false,
'customerNumber' => 'cust456',
'notifyUrl' => 'https://www.example.com/notify',
'returnUrl' => 'https://www.example.com/return',
@@ -39,9 +40,16 @@ public function testGetData()
{
$data = $this->request->getData();
+ $this->assertArrayHasKey('HppParameters', $data);
+ $this->assertFalse($data['HppParameters']['HideBillerCode']);
+ $this->assertFalse($data['HppParameters']['HideCrn1']);
+ $this->assertFalse($data['HppParameters']['HideCrn2']);
+ $this->assertFalse($data['HppParameters']['HideCrn3']);
+
$this->assertSame('payment', $data['ProcessTxnData']['Action']);
$this->assertTrue($data['ProcessTxnData']['TestMode']);
$this->assertSame(145, $data['ProcessTxnData']['Amount']);
+ $this->assertSame('1234567', $data['ProcessTxnData']['BillerCode']);
$this->assertSame('cr1', $data['ProcessTxnData']['Crn1']);
$this->assertSame('cr2', $data['ProcessTxnData']['Crn2']);
$this->assertSame('cr3', $data['ProcessTxnData']['Crn3']);
@@ -54,13 +62,90 @@ public function testGetData()
$this->assertSame('https://www.example.com/notify', $data['WebHookUrl']);
}
+ public function testHideFlags()
+ {
+ $this->options = array_merge($this->options, array(
+ 'hideBillerCode' => true,
+ 'hideCustomerReferenceNumber1' => true,
+ 'hideCustomerReferenceNumber2' => true,
+ 'hideCustomerReferenceNumber3' => true,
+ ));
+ $this->request->initialize($this->options);
+
+ $data = $this->request->getData();
+
+ $this->assertArrayHasKey('HppParameters', $data);
+ $this->assertTrue($data['HppParameters']['HideBillerCode']);
+ $this->assertTrue($data['HppParameters']['HideCrn1']);
+ $this->assertTrue($data['HppParameters']['HideCrn2']);
+ $this->assertTrue($data['HppParameters']['HideCrn3']);
+ }
+
+ public function testCancelUrl()
+ {
+ $this->options = array_merge($this->options, array('cancelUrl' => 'https://www.example.com/cancel'));
+ $this->request->initialize($this->options);
+
+ $data = $this->request->getData();
+
+ $this->assertArrayHasKey('HppParameters', $data);
+ $this->assertArrayHasKey('ReturnBarLabel', $data['HppParameters']);
+ $this->assertSame('Cancel', $data['HppParameters']['ReturnBarLabel']);
+ $this->assertArrayHasKey('ReturnBarUrl', $data['HppParameters']);
+ $this->assertSame('https://www.example.com/cancel', $data['HppParameters']['ReturnBarUrl']);
+ }
+
+ public function testCardReference()
+ {
+ $this->options = array_merge($this->options, array('cardReference' => '1234567890123456'));
+ $this->request->initialize($this->options);
+
+ $data = $this->request->getData();
+
+ $this->assertArrayHasKey('ProcessTxnData', $data);
+ $this->assertArrayHasKey('DVTokenData', $data['ProcessTxnData']);
+ $this->assertArrayHasKey('DVToken', $data['ProcessTxnData']['DVTokenData']);
+ $this->assertSame('1234567890123456', $data['ProcessTxnData']['DVTokenData']['DVToken']);
+ $this->assertArrayHasKey('UpdateDVTokenExpiryDate', $data['ProcessTxnData']['DVTokenData']);
+ $this->assertFalse($data['ProcessTxnData']['DVTokenData']['UpdateDVTokenExpiryDate']);
+ }
+
+ public function testToken()
+ {
+ $this->options = array_merge($this->options, array('token' => '1234567890123456'));
+ $this->request->initialize($this->options);
+
+ $data = $this->request->getData();
+
+ $this->assertArrayHasKey('ProcessTxnData', $data);
+ $this->assertArrayHasKey('DVTokenData', $data['ProcessTxnData']);
+ $this->assertArrayHasKey('DVToken', $data['ProcessTxnData']['DVTokenData']);
+ $this->assertSame('1234567890123456', $data['ProcessTxnData']['DVTokenData']['DVToken']);
+ $this->assertArrayHasKey('UpdateDVTokenExpiryDate', $data['ProcessTxnData']['DVTokenData']);
+ $this->assertFalse($data['ProcessTxnData']['DVTokenData']['UpdateDVTokenExpiryDate']);
+ }
+
public function testGetDataOnlyGetToken()
{
// override some data
- $this->options = array_merge($this->options, array('generateToken' => true, 'amount' => '0.00'));
+ $this->options = array_merge($this->options, array('createToken' => true, 'amount' => '0.00'));
$this->request->initialize($this->options);
+ $this->assertTrue($this->request->getCreateToken());
+
$data = $this->request->getData();
+ $this->assertSame('verify_only', $data['ProcessTxnData']['Action']);
+ $this->assertSame(0, $data['ProcessTxnData']['Amount']);
+ $this->assertSame(3, $data['ProcessTxnData']['TokenisationMode']);
+ }
+ public function testGetDataDeprecatedGetToken()
+ {
+ // override some data
+ $this->options = array_merge($this->options, array('generateToken' => true, 'amount' => '0.00'));
+ $this->request->initialize($this->options);
+ $this->assertTrue($this->request->getGenerateToken());
+
+ $data = $this->request->getData();
$this->assertSame('verify_only', $data['ProcessTxnData']['Action']);
$this->assertSame(0, $data['ProcessTxnData']['Amount']);
$this->assertSame(3, $data['ProcessTxnData']['TokenisationMode']);
diff --git a/tests/Message/PurchaseResponseTest.php b/tests/Message/PurchaseResponseTest.php
index 1c2bdf0..4092c63 100644
--- a/tests/Message/PurchaseResponseTest.php
+++ b/tests/Message/PurchaseResponseTest.php
@@ -12,7 +12,7 @@ class PurchaseResponseTest extends TestCase
/**
* Set up for the tests in this class
*/
- public function setUp()
+ public function setUp(): void
{
$this->response = new PurchaseResponse($this->getMockRequest(), array(
'APIResponse' => array(
diff --git a/tests/Mock/AcceptNotificationError.txt b/tests/Mock/AcceptNotificationError.txt
new file mode 100644
index 0000000..f953b1d
--- /dev/null
+++ b/tests/Mock/AcceptNotificationError.txt
@@ -0,0 +1,93 @@
+POST http://yourmerchantwebsite.com/txnWebHook HTTP/1.1
+Content-Type: application/json; charset=utf-8
+Host: merchant.com
+Content-Length: 827
+Expect: 100-continue
+Proxy-Connection: Keep-Alive
+
+{
+ "Action" : "payment",
+ "Amount" : 19900,
+ "AmountOriginal" : 19800,
+ "AmountSurcharge" : 100,
+ "AuthoriseId" : "",
+ "BankAccountDetails" : {
+ "AccountName" : "",
+ "AccountNumber" : "",
+ "BSBNumber" : "",
+ "TruncatedAccountNumber" : ""
+ },
+ "BankResponseCode" : "",
+ "BillerCode" : "",
+ "CardDetails" : {
+ "CardHolderName" : "John Smith",
+ "Category" : "",
+ "ExpiryDate" : "0521",
+ "Issuer" : "",
+ "IssuerCountryCode" : "",
+ "Localisation" : "",
+ "MaskedCardNumber" : "512345...346",
+ "SubType" : "extra comma here causes a malformed JSON error",
+ },
+ "CardType" : "",
+ "Crn1" : "Test reference 1",
+ "Crn2" : "",
+ "Crn3" : "",
+ "Currency" : "AUD",
+ "CVNResult" : {
+ "CVNResultCode" : ""
+ },
+ "DVToken" : "",
+ "EmailAddress" : "john.smith@email.com",
+ "FraudScreeningResponse" : {
+ "ReDResponse" : {
+ "FRAUD_REC_ID" : "",
+ "FRAUD_RSP_CD" : "",
+ "FRAUD_STAT_CD" : "",
+ "ORD_ID" : "",
+ "REQ_ID" : "",
+ "STAT_CD" : ""
+ },
+ "ResponseCode" : "",
+ "ResponseMessage" : "",
+ "TxnRejected" : false
+ },
+ "IsCVNPresent" : false,
+ "IsTestTxn" : false,
+ "IsThreeDS" : false,
+ "MerchantNumber" : "",
+ "MerchantReference" : "Test merchant reference",
+ "OriginalTxnNumber" : "",
+ "ProcessedDateTime" : "",
+ "ReceiptNumber" : "",
+ "ResponseCode" : "",
+ "ResponseText" : "",
+ "RRN" : "",
+ "SettlementDate" : "",
+ "Source" : "",
+ "StoreCard" : false,
+ "SubType" : "single",
+ "ThreeDSResponse" : {
+ "Eci" : "",
+ "Enrolled" : "",
+ "Status" : "",
+ "VerifySecurityLevel" : "",
+ "VerifyStatus" : "",
+ "VerifyToken" : "",
+ "VerifyType" : "",
+ "XID" : ""
+ },
+ "TxnNumber" : "",
+ "Type" : "internet",
+ "StatementDescriptor" : {
+ "AddressLine1" : "",
+ "AddressLine2" : "",
+ "City" : "",
+ "CompanyName" : "",
+ "CountryCode" : "",
+ "Postcode" : "",
+ "State" : "",
+ "MerchantName" : "",
+ "PhoneNumber" : ""
+ }
+}
diff --git a/tests/Mock/AcceptNotificationFailure.txt b/tests/Mock/AcceptNotificationFailure.txt
new file mode 100644
index 0000000..39c6d92
--- /dev/null
+++ b/tests/Mock/AcceptNotificationFailure.txt
@@ -0,0 +1,71 @@
+POST http://yourmerchantwebsite.com/txnWebHook HTTP/1.1
+Content-Type: application/json; charset=utf-8
+Content-Length: 827
+Expect: 100-continue
+Proxy-Connection: Keep-Alive
+
+{
+ "Action" : "payment",
+ "Amount" : 19900,
+ "AmountOriginal" : 19800,
+ "AmountSurcharge" : 100,
+ "AuthoriseId" : "372627",
+ "BankAccountDetails" : null,
+ "BankResponseCode" : "00",
+ "BillerCode" : null,
+ "CardDetails" : {
+ "CardHolderName" : "John Smith",
+ "Category" : "STANDARD",
+ "ExpiryDate" : "0521",
+ "Issuer" : "BANCO DEL PICHINCHA, C.A.",
+ "IssuerCountryCode" : "ECU",
+ "Localisation" : "international",
+ "MaskedCardNumber" : "512345...346",
+ "SubType" : "credit"
+ },
+ "CardType" : "MC",
+ "Crn1" : "test crn1",
+ "Crn2" : "test crn2",
+ "Crn3" : "test crn3",
+ "Currency" : "AUD",
+ "CVNResult" : {
+ "CVNResultCode" : "M"
+ },
+ "DVToken" : null,
+ "EmailAddress" : null,
+ "FraudScreeningResponse" : {
+ "ReDResponse" : null,
+ "ResponseCode" : "",
+ "ResponseMessage" : "",
+ "TxnRejected" : false
+ },
+ "IsCVNPresent" : true,
+ "IsTestTxn" : true,
+ "IsThreeDS" : false,
+ "MerchantNumber" : "5353109000000000",
+ "MerchantReference" : "test merchant ref",
+ "OriginalTxnNumber" : null,
+ "ProcessedDateTime" : "2014-12-12T12:15:19.6370000",
+ "ReceiptNumber" : "49316411177",
+ "ResponseCode" : "6",
+ "ResponseText" : "Transaction Declined",
+ "RRN" : "434612372626",
+ "SettlementDate" : "20141212",
+ "Source" : "internet",
+ "StoreCard" : false,
+ "SubType" : "single",
+ "ThreeDSResponse" : null,
+ "TxnNumber" : "1177",
+ "Type" : "internet",
+ "StatementDescriptor" : {
+ "AddressLine1" : "123 Drive Street",
+ "AddressLine2" : "",
+ "City" : "Melbourne",
+ "CompanyName" : "A Company Name",
+ "CountryCode" : "AUS",
+ "Postcode" : "3000",
+ "State" : "Victoria",
+ "MerchantName" : "A Merchant Name",
+ "PhoneNumber" : "0123456789"
+ }
+}
diff --git a/tests/Mock/AcceptNotificationPending.txt b/tests/Mock/AcceptNotificationPending.txt
new file mode 100644
index 0000000..350ca37
--- /dev/null
+++ b/tests/Mock/AcceptNotificationPending.txt
@@ -0,0 +1,71 @@
+POST http://yourmerchantwebsite.com/txnWebHook HTTP/1.1
+Content-Type: application/json; charset=utf-8
+Content-Length: 827
+Expect: 100-continue
+Proxy-Connection: Keep-Alive
+
+{
+ "Action" : "payment",
+ "Amount" : 19900,
+ "AmountOriginal" : 19800,
+ "AmountSurcharge" : 100,
+ "AuthoriseId" : "372628",
+ "BankAccountDetails" : null,
+ "BankResponseCode" : "00",
+ "BillerCode" : null,
+ "CardDetails" : {
+ "CardHolderName" : "John Smith",
+ "Category" : "STANDARD",
+ "ExpiryDate" : "0521",
+ "Issuer" : "BANCO DEL PICHINCHA, C.A.",
+ "IssuerCountryCode" : "ECU",
+ "Localisation" : "international",
+ "MaskedCardNumber" : "512345...346",
+ "SubType" : "credit"
+ },
+ "CardType" : "MC",
+ "Crn1" : "test crn1",
+ "Crn2" : "test crn2",
+ "Crn3" : "test crn3",
+ "Currency" : "AUD",
+ "CVNResult" : {
+ "CVNResultCode" : "M"
+ },
+ "DVToken" : null,
+ "EmailAddress" : null,
+ "FraudScreeningResponse" : {
+ "ReDResponse" : null,
+ "ResponseCode" : "",
+ "ResponseMessage" : "",
+ "TxnRejected" : false
+ },
+ "IsCVNPresent" : true,
+ "IsTestTxn" : true,
+ "IsThreeDS" : false,
+ "MerchantNumber" : "5353109000000000",
+ "MerchantReference" : "test merchant ref",
+ "OriginalTxnNumber" : null,
+ "ProcessedDateTime" : "2014-12-12T12:15:19.6370000",
+ "ReceiptNumber" : "49316411177",
+ "ResponseCode" : "P",
+ "ResponseText" : "Transaction is Pending",
+ "RRN" : "434612372626",
+ "SettlementDate" : "20141212",
+ "Source" : "internet",
+ "StoreCard" : false,
+ "SubType" : "single",
+ "ThreeDSResponse" : null,
+ "TxnNumber" : "1177",
+ "Type" : "internet",
+ "StatementDescriptor" : {
+ "AddressLine1" : "123 Drive Street",
+ "AddressLine2" : "",
+ "City" : "Melbourne",
+ "CompanyName" : "A Company Name",
+ "CountryCode" : "AUS",
+ "Postcode" : "3000",
+ "State" : "Victoria",
+ "MerchantName" : "A Merchant Name",
+ "PhoneNumber" : "0123456789"
+ }
+}
diff --git a/tests/Mock/AcceptNotificationSuccess.txt b/tests/Mock/AcceptNotificationSuccess.txt
new file mode 100644
index 0000000..5152c40
--- /dev/null
+++ b/tests/Mock/AcceptNotificationSuccess.txt
@@ -0,0 +1,71 @@
+POST http://yourmerchantwebsite.com/txnWebHook HTTP/1.1
+Content-Type: application/json; charset=utf-8
+Content-Length: 827
+Expect: 100-continue
+Proxy-Connection: Keep-Alive
+
+{
+ "Action" : "payment",
+ "Amount" : 19900,
+ "AmountOriginal" : 19800,
+ "AmountSurcharge" : 100,
+ "AuthoriseId" : "372626",
+ "BankAccountDetails" : null,
+ "BankResponseCode" : "00",
+ "BillerCode" : null,
+ "CardDetails" : {
+ "CardHolderName" : "John Smith",
+ "Category" : "STANDARD",
+ "ExpiryDate" : "0521",
+ "Issuer" : "BANCO DEL PICHINCHA, C.A.",
+ "IssuerCountryCode" : "ECU",
+ "Localisation" : "international",
+ "MaskedCardNumber" : "512345...346",
+ "SubType" : "credit"
+ },
+ "CardType" : "MC",
+ "Crn1" : "test crn1",
+ "Crn2" : "test crn2",
+ "Crn3" : "test crn3",
+ "Currency" : "AUD",
+ "CVNResult" : {
+ "CVNResultCode" : "M"
+ },
+ "DVToken" : null,
+ "EmailAddress" : null,
+ "FraudScreeningResponse" : {
+ "ReDResponse" : null,
+ "ResponseCode" : "",
+ "ResponseMessage" : "",
+ "TxnRejected" : false
+ },
+ "IsCVNPresent" : true,
+ "IsTestTxn" : true,
+ "IsThreeDS" : false,
+ "MerchantNumber" : "5353109000000000",
+ "MerchantReference" : "test merchant ref",
+ "OriginalTxnNumber" : null,
+ "ProcessedDateTime" : "2014-12-12T12:15:19.6370000",
+ "ReceiptNumber" : "49316411177",
+ "ResponseCode" : "0",
+ "ResponseText" : "Approved",
+ "RRN" : "434612372626",
+ "SettlementDate" : "20141212",
+ "Source" : "internet",
+ "StoreCard" : false,
+ "SubType" : "single",
+ "ThreeDSResponse" : null,
+ "TxnNumber" : "1177",
+ "Type" : "internet",
+ "StatementDescriptor" : {
+ "AddressLine1" : "123 Drive Street",
+ "AddressLine2" : "",
+ "City" : "Melbourne",
+ "CompanyName" : "A Company Name",
+ "CountryCode" : "AUS",
+ "Postcode" : "3000",
+ "State" : "Victoria",
+ "MerchantName" : "A Merchant Name",
+ "PhoneNumber" : "0123456789"
+ }
+}
diff --git a/tests/RedirectGatewayTest.php b/tests/RedirectGatewayTest.php
index a72260c..b342b23 100644
--- a/tests/RedirectGatewayTest.php
+++ b/tests/RedirectGatewayTest.php
@@ -2,14 +2,21 @@
namespace Omnipay\BPOINT;
+use Exception;
+use GuzzleHttp\Psr7\Message;
+use GuzzleHttp\Psr7\ServerRequest;
+use Omnipay\Common\Message\NotificationInterface;
+use ReflectionObject;
use Omnipay\Tests\GatewayTestCase;
+use Symfony\Component\HttpFoundation\Request as HttpRequest;
+use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
class RedirectGatewayTest extends GatewayTestCase
{
/** @var array */
protected $options;
- public function setUp()
+ public function setUp(): void
{
parent::setUp();
@@ -22,10 +29,11 @@ public function setUp()
'password' => 'DemoPassword!',
'merchantNumber' => '5353109000000000',
'merchantShortName' => 'DEMO123',
+ 'billerCode' => '1234567',
'customerReferenceNumber1' => 'cr1',
'customerReferenceNumber2' => 'cr2',
'customerReferenceNumber3' => 'cr3',
- 'generateToken' => true,
+ 'createToken' => true,
'customerNumber' => 'cust456',
'notifyUrl' => 'https://www.example.com/notify',
'returnUrl' => 'https://www.example.com/return',
@@ -115,4 +123,107 @@ public function testCompletePurchaseError()
$this->assertNull($response->getTransactionReference());
$this->assertNull($response->getCardType());
}
+
+ public function testAcceptNotificationSuccess()
+ {
+ $httpRequest = $this->setMockHttpRequest('AcceptNotificationSuccess.txt');
+ $gateway = new RedirectGateway($this->getHttpClient(), $httpRequest);
+ $notification = $gateway->acceptNotification();
+
+ // NotificationInterface methods
+ $this->assertSame('372626', $notification->getTransactionReference());
+ $this->assertSame(NotificationInterface::STATUS_COMPLETED, $notification->getTransactionStatus());
+ $this->assertSame('Approved', $notification->getMessage());
+
+ // ResponseInterface methods
+ $response = $notification->send();
+ $this->assertSame($notification, $response);
+ $this->assertSame($notification, $response->getRequest());
+ $this->assertTrue($response->isSuccessful());
+ $this->assertFalse($response->isRedirect());
+ $this->assertFalse($response->isCancelled());
+ $this->assertSame('0', $response->getCode());
+
+ $this->assertSame('MC', $notification->getCardType());
+ }
+
+ public function testAcceptNotificationFailure()
+ {
+ $httpRequest = $this->setMockHttpRequest('AcceptNotificationFailure.txt');
+ $gateway = new RedirectGateway($this->getHttpClient(), $httpRequest);
+ $notification = $gateway->acceptNotification();
+
+ // NotificationInterface methods
+ $this->assertSame('372627', $notification->getTransactionReference());
+ $this->assertSame(NotificationInterface::STATUS_FAILED, $notification->getTransactionStatus());
+ $this->assertSame('Transaction Declined', $notification->getMessage());
+
+ // ResponseInterface methods
+ $response = $notification->send();
+ $this->assertFalse($response->isSuccessful());
+ }
+
+ public function testAcceptNotificationPending()
+ {
+ $httpRequest = $this->setMockHttpRequest('AcceptNotificationPending.txt');
+ $gateway = new RedirectGateway($this->getHttpClient(), $httpRequest);
+ $notification = $gateway->acceptNotification();
+
+ // NotificationInterface methods
+ $this->assertSame('372628', $notification->getTransactionReference());
+ $this->assertSame(NotificationInterface::STATUS_PENDING, $notification->getTransactionStatus());
+ $this->assertSame('Transaction is Pending', $notification->getMessage());
+ }
+
+ public function testAcceptNotificationError()
+ {
+ $httpRequest = $this->setMockHttpRequest('AcceptNotificationError.txt');
+ $gateway = new RedirectGateway($this->getHttpClient(), $httpRequest);
+ $notification = $gateway->acceptNotification();
+
+ // NotificationInterface methods
+ $this->assertNull($notification->getTransactionReference());
+ $this->assertSame(NotificationInterface::STATUS_FAILED, $notification->getTransactionStatus());
+ $this->assertNull($notification->getMessage());
+
+ // bonus malformed JSON test
+ $this->assertTrue(json_last_error() == JSON_ERROR_SYNTAX);
+ }
+
+ /**
+ * Parses a saved raw request file into a new HTTP request object
+ *
+ * Initial file parsing adapted from TestCase::getMockHttpResponse()
+ *
+ * @param string $path The request file
+ *
+ * @return HttpRequest The new request
+ */
+ protected function setMockHttpRequest($path)
+ {
+ $ref = new ReflectionObject($this);
+ $dir = dirname($ref->getFileName());
+ // if mock file doesn't exist, check parent directory
+ if (file_exists($dir.'/Mock/'.$path)) {
+ $raw = file_get_contents($dir.'/Mock/'.$path);
+ } elseif (file_exists($dir.'/../Mock/'.$path)) {
+ $raw = file_get_contents($dir.'/../Mock/'.$path);
+ } else {
+ throw new Exception("Cannot open '{$path}'");
+ }
+
+ $guzzleRequest = Message::parseRequest($raw);
+ // PSR-bridge requires a ServerRequestInterface
+ $guzzleServerRequest = new ServerRequest(
+ $guzzleRequest->getMethod(),
+ $guzzleRequest->getUri(),
+ $guzzleRequest->getHeaders(),
+ $guzzleRequest->getBody(),
+ $guzzleRequest->getProtocolVersion(),
+ $_SERVER
+ );
+
+ $httpFoundationFactory = new HttpFoundationFactory();
+ return $httpFoundationFactory->createRequest($guzzleServerRequest);
+ }
}