From 9cce2c535e2fb99b487a9a3a3d04b5098675b0bb Mon Sep 17 00:00:00 2001 From: Sujip Thapa Date: Sun, 22 Feb 2026 14:01:13 +0545 Subject: [PATCH 1/9] refactor: modernize architecture with transport abstraction and CI tooling --- .github/workflows/ci.yml | 47 ++++++++++ .travis.yml | 29 ------ ARCHITECTURE.md | 82 +++++++++++++++++ composer.json | 93 +++++++++++--------- grumphp.yml | 15 ---- phpstan.neon.dist | 9 ++ src/Message/AbstractRequest.php | 69 ++++++++++----- src/Message/SecureXMLAbstractRequest.php | 30 +++++-- src/SecureXMLGateway.php | 47 ++++++++++ src/Transport/CurlTransport.php | 59 +++++++++++++ src/Transport/OmnipayHttpClientTransport.php | 52 +++++++++++ src/Transport/TransportInterface.php | 17 ++++ src/Transport/TransportResponse.php | 57 ++++++++++++ 13 files changed, 492 insertions(+), 114 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml create mode 100644 ARCHITECTURE.md delete mode 100644 grumphp.yml create mode 100644 phpstan.neon.dist create mode 100644 src/Transport/CurlTransport.php create mode 100644 src/Transport/OmnipayHttpClientTransport.php create mode 100644 src/Transport/TransportInterface.php create mode 100644 src/Transport/TransportResponse.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9a2360c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,47 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: [ '8.1', '8.2', '8.3', '8.4', '8.5' ] + deps: [ 'stable' ] + include: + - php: '8.1' + deps: 'lowest' + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + + - name: Validate composer.json + run: composer validate --strict + + - name: Install dependencies + run: | + if [ "${{ matrix.deps }}" = "lowest" ]; then + composer update --prefer-lowest --prefer-stable --no-interaction --no-progress + else + composer update --no-interaction --no-progress + fi + + - name: Run lint + run: composer lint + + - name: Run tests + run: composer test + + - name: Run PHPStan + run: composer stan diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6a9d6f8..0000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: php - -php: - - 5.6 - - 7.0 - - 7.1 - - 7.2 - - 7.3 - - 7.4 - -env: - global: - - setup=basic - -matrix: - include: - - php: 5.6 - env: setup=lowest - -sudo: false - -before_install: - - travis_retry composer self-update - -install: - - if [[ $setup = 'basic' ]]; then travis_retry composer install --no-interaction --prefer-dist; fi - - if [[ $setup = 'lowest' ]]; then travis_retry composer update --prefer-dist --no-interaction --prefer-lowest --prefer-stable; fi - -script: vendor/bin/phpcs --exclude=Generic.Files.LineLength src/ --standard=PSR2 src && vendor/bin/phpunit --coverage-text diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..48efa81 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,82 @@ +# Architecture + +## Overview + +`omnipay-nabtransact` remains an Omnipay gateway package and keeps full Omnipay request/response compatibility. + +This modernization adds a transport layer so the package can run with: + +- Omnipay HTTP client (default, backward compatible) +- Native cURL transport (framework-agnostic alternative) +- Custom user transport via contract + +## Design Goals + +- Preserve Omnipay API surface and existing integrations. +- Keep non-breaking behavior for request classes and gateway methods. +- Improve reliability and testability through explicit transport contracts. +- Keep dependencies minimal. + +## Project Map + +```text +src/ + DirectPostGateway.php + SecureXMLGateway.php + HostedPaymentGateway.php + UnionPayGateway.php + + Message/ + *Request.php + *Response.php + + Transport/ + TransportInterface.php + TransportResponse.php + OmnipayHttpClientTransport.php + CurlTransport.php + + Enums/ + TransactionType.php +``` + +## Runtime Flows + +### 1) SecureXML requests + +1. Gateway creates request through Omnipay (`authorize/purchase/capture/refund/echoTest`). +2. Request builds XML payload. +3. Request resolves transport in this order: + - explicit request transport (`setTransport()`) + - Omnipay adapter transport (default) +4. Response XML is mapped to `SecureXMLResponse`. + +### 2) DirectPost / UnionPay + +- Redirect-based requests remain unchanged. +- Fingerprint verification stays compatible. +- Backward-compat typo alias (`vefiyFingerPrint`) remains supported. +- DirectPost supports purchase, authorize, complete callbacks, store-only flow, and additive server-to-server operations (`capture`, `refund`, `void`). +- EMV 3DS order management is exposed via `createEMV3DSOrder()`. + +### 3) Hosted Payment + +- Redirect form flow remains unchanged. +- Environment endpoint selection now aligns correctly with `testMode`. + +## Extension Points + +- Implement `TransportInterface` for custom HTTP stacks. +- Inject custom transport per request via `setTransport($transport)`. + +## Backward Compatibility + +- Omnipay gateway class names and behavior remain intact. +- Existing request/response classes remain available. +- Deprecated typo method remains callable. + +## Testing Strategy + +- Request/response behavior tests for existing Omnipay flows. +- Dedicated tests for transport injection and endpoint correctness. +- Fingerprint verification coverage (success/failure). diff --git a/composer.json b/composer.json index 111d3ea..9278d04 100644 --- a/composer.json +++ b/composer.json @@ -1,45 +1,52 @@ { - "name":"sudiptpa/omnipay-nabtransact", - "type":"library", - "description":"National Australia Bank (NAB) Transact driver for the Omnipay payment processing library.", - "keywords":[ - "gateway", - "merchant", - "omnipay", - "pay", - "payment", - "nabtransact" - ], - "homepage":"https://github.com/sudiptpa/nabtransact", - "license":"MIT", - "authors":[ - { - "name":"Sujip Thapa", - "email":"sudiptpa@gmail.com" - } - ], - "autoload":{ - "psr-4":{ - "Omnipay\\NABTransact\\":"src/" - } - }, - "require":{ - "omnipay/common":"^3" - }, - "require-dev":{ - "omnipay/tests":"^3", - "squizlabs/php_codesniffer":"^3", - "phpro/grumphp":"^0.14.0" - }, - "extra":{ - "branch-alias":{ - "dev-master":"3.0.x-dev" - } - }, - "scripts":{ - "test":"vendor/bin/phpunit", - "check-style":"phpcs -p --standard=PSR2 src/", - "fix-style":"phpcbf -p --standard=PSR2 src/" - }, - "prefer-stable":true + "name": "sudiptpa/omnipay-nabtransact", + "type": "library", + "description": "National Australia Bank (NAB) Transact driver for the Omnipay payment processing library.", + "keywords": [ + "gateway", + "merchant", + "omnipay", + "pay", + "payment", + "nabtransact" + ], + "homepage": "https://github.com/sudiptpa/omnipay-nabtransact", + "license": "MIT", + "authors": [ + { + "name": "Sujip Thapa", + "email": "sudiptpa@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Omnipay\\NABTransact\\": "src/" + } + }, + "require": { + "php": "^7.2 || ^8.0", + "omnipay/common": "^3" + }, + "require-dev": { + "omnipay/tests": "^3", + "phpstan/phpstan": "^1.12", + "phpunit/phpunit": "^8.5 || ^9.6", + "squizlabs/php_codesniffer": "^3" + }, + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "scripts": { + "test": "phpunit -c phpunit.xml.dist", + "stan": "phpstan analyse --configuration=phpstan.neon.dist --no-progress", + "lint": "find src tests -type f -name '*.php' -print0 | xargs -0 -n1 php -l", + "check-style": "phpcs -p --standard=PSR12 src tests", + "fix-style": "phpcbf -p --standard=PSR12 src tests" + }, + "config": { + "sort-packages": true + }, + "prefer-stable": true } diff --git a/grumphp.yml b/grumphp.yml deleted file mode 100644 index bf81fa5..0000000 --- a/grumphp.yml +++ /dev/null @@ -1,15 +0,0 @@ -parameters: - git_dir: . - bin_dir: vendor/bin - tasks: - phpunit: - config_file: ~ - testsuite: ~ - group: [] - always_execute: false - phpcs: - standard: PSR2 - warning_severity: ~ - ignore_patterns: - - tests/ - triggered_by: [php] diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..e990136 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,9 @@ +parameters: + level: 5 + paths: + - src + - tests + inferPrivatePropertyTypeFromConstructor: true + checkMissingIterableValueType: false + ignoreErrors: + - '#Call to deprecated method vefiyFingerPrint\(\)#' diff --git a/src/Message/AbstractRequest.php b/src/Message/AbstractRequest.php index 862054f..89c48f7 100644 --- a/src/Message/AbstractRequest.php +++ b/src/Message/AbstractRequest.php @@ -2,48 +2,32 @@ namespace Omnipay\NABTransact\Message; +use Omnipay\NABTransact\Transport\TransportInterface; + /** * NABTransact Abstract Request. */ abstract class AbstractRequest extends \Omnipay\Common\Message\AbstractRequest { - /** - * @var string - */ public $testEndpoint; - /** - * @var string - */ public $liveEndpoint; - /** - * @return string - */ public function getMerchantId() { return $this->getParameter('merchantId'); } - /** - * @param $value - */ public function setMerchantId($value) { return $this->setParameter('merchantId', $value); } - /** - * @return string - */ public function getTransactionPassword() { return $this->getParameter('transactionPassword'); } - /** - * @param $value - */ public function setTransactionPassword($value) { return $this->setParameter('transactionPassword', $value); @@ -54,17 +38,58 @@ public function getHasEMV3DSEnabled() return $this->getParameter('hasEMV3DSEnabled'); } - /** - * @param $value - */ public function setHasEMV3DSEnabled($value) { return $this->setParameter('hasEMV3DSEnabled', $value); } + public function getHasRiskManagementEnabled() + { + return $this->getParameter('hasRiskManagementEnabled'); + } + + public function setHasRiskManagementEnabled($value) + { + return $this->setParameter('hasRiskManagementEnabled', $value); + } + + /** + * @param TransportInterface $transport + * + * @return $this + */ + public function setTransport(TransportInterface $transport) + { + return $this->setParameter('transport', $transport); + } + /** - * @return string + * @return TransportInterface|null */ + public function getTransport() + { + return $this->getParameter('transport'); + } + + /** + * @return int + */ + public function getTimeoutSeconds() + { + $timeout = $this->getParameter('timeoutSeconds'); + + if ($timeout === null) { + return 60; + } + + return max(1, (int) $timeout); + } + + public function setTimeoutSeconds($value) + { + return $this->setParameter('timeoutSeconds', (int) $value); + } + public function getEndpoint() { return $this->getTestMode() ? $this->testEndpoint : $this->liveEndpoint; diff --git a/src/Message/SecureXMLAbstractRequest.php b/src/Message/SecureXMLAbstractRequest.php index 0dc3330..7c6c84f 100644 --- a/src/Message/SecureXMLAbstractRequest.php +++ b/src/Message/SecureXMLAbstractRequest.php @@ -2,6 +2,8 @@ namespace Omnipay\NABTransact\Message; +use Omnipay\NABTransact\Transport\OmnipayHttpClientTransport; +use Omnipay\NABTransact\Transport\TransportInterface; use SimpleXMLElement; /** @@ -64,8 +66,6 @@ public function generateMessageId() */ public function getMessageId() { - $messageId = $this->getParameter('messageId'); - if (!$this->getParameter('messageId')) { $this->setMessageId($this->generateMessageId()); } @@ -73,11 +73,31 @@ public function getMessageId() return $this->getParameter('messageId'); } - public function sendData($data) + /** + * @return TransportInterface + */ + protected function resolveTransport() { - $httpResponse = $this->httpClient->request('POST', $this->getEndpoint(), [], $data->asXML()); + $transport = $this->getTransport(); + + if ($transport instanceof TransportInterface) { + return $transport; + } + + return new OmnipayHttpClientTransport($this->httpClient); + } - $xml = new SimpleXMLElement($httpResponse->getBody()->getContents()); + public function sendData($data) + { + $response = $this->resolveTransport()->send( + 'POST', + $this->getEndpoint(), + [], + $data->asXML(), + $this->getTimeoutSeconds() + ); + + $xml = new SimpleXMLElement($response->getBody()); return $this->response = new SecureXMLResponse($this, $xml); } diff --git a/src/SecureXMLGateway.php b/src/SecureXMLGateway.php index 53e7499..f620ab5 100644 --- a/src/SecureXMLGateway.php +++ b/src/SecureXMLGateway.php @@ -3,6 +3,7 @@ namespace Omnipay\NABTransact; use Omnipay\Common\AbstractGateway; +use Omnipay\NABTransact\Transport\TransportInterface; /** * NABTransact Secure XML Gateway. @@ -71,6 +72,52 @@ public function setTransactionPassword($value) return $this->setParameter('transactionPassword', $value); } + /** + * Optional custom transport for SecureXML requests. + * + * @param TransportInterface $value + * + * @return mixed + */ + public function setTransport(TransportInterface $value) + { + return $this->setParameter('transport', $value); + } + + /** + * @return TransportInterface|null + */ + public function getTransport() + { + return $this->getParameter('transport'); + } + + /** + * Optional timeout in seconds for outbound verification calls. + * + * @param int $value + * + * @return mixed + */ + public function setTimeoutSeconds($value) + { + return $this->setParameter('timeoutSeconds', (int) $value); + } + + /** + * @return int|null + */ + public function getTimeoutSeconds() + { + $timeout = $this->getParameter('timeoutSeconds'); + + if ($timeout === null) { + return null; + } + + return (int) $timeout; + } + /** * @param array $parameters * diff --git a/src/Transport/CurlTransport.php b/src/Transport/CurlTransport.php new file mode 100644 index 0000000..66d908a --- /dev/null +++ b/src/Transport/CurlTransport.php @@ -0,0 +1,59 @@ + $headers + * @param string $body + * @param int $timeoutSeconds + * + * @return TransportResponse + */ + public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) + { + if (!function_exists('curl_init')) { + throw new RuntimeException('cURL extension is required for CurlTransport.'); + } + + $handle = curl_init(); + + if ($handle === false) { + throw new RuntimeException('Unable to initialize cURL transport.'); + } + + $formattedHeaders = []; + foreach ($headers as $name => $value) { + $formattedHeaders[] = $name.': '.$value; + } + + $options = [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CUSTOMREQUEST => strtoupper($method), + CURLOPT_TIMEOUT => max(1, (int) $timeoutSeconds), + CURLOPT_HTTPHEADER => $formattedHeaders, + CURLOPT_POSTFIELDS => $body, + ]; + + curl_setopt_array($handle, $options); + + $responseBody = curl_exec($handle); + + if ($responseBody === false) { + $message = curl_error($handle); + curl_close($handle); + throw new RuntimeException('cURL transport request failed: '.$message); + } + + $statusCode = (int) curl_getinfo($handle, CURLINFO_RESPONSE_CODE); + curl_close($handle); + + return new TransportResponse($statusCode, (string) $responseBody); + } +} diff --git a/src/Transport/OmnipayHttpClientTransport.php b/src/Transport/OmnipayHttpClientTransport.php new file mode 100644 index 0000000..c2f204c --- /dev/null +++ b/src/Transport/OmnipayHttpClientTransport.php @@ -0,0 +1,52 @@ +httpClient = $httpClient; + } + + /** + * @param string $method + * @param string $url + * @param array $headers + * @param string $body + * @param int $timeoutSeconds + * + * @return TransportResponse + */ + public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) + { + $response = $this->httpClient->request(strtoupper($method), $url, $headers, $body); + + $statusCode = method_exists($response, 'getStatusCode') ? (int) $response->getStatusCode() : 200; + $responseBody = ''; + + if (method_exists($response, 'getBody')) { + $stream = $response->getBody(); + + if (is_string($stream)) { + $responseBody = $stream; + } elseif (method_exists($stream, '__toString')) { + $responseBody = (string) $stream; + } elseif (method_exists($stream, 'getContents')) { + $responseBody = (string) $stream->getContents(); + } + } + + return new TransportResponse($statusCode, $responseBody); + } +} diff --git a/src/Transport/TransportInterface.php b/src/Transport/TransportInterface.php new file mode 100644 index 0000000..16ba921 --- /dev/null +++ b/src/Transport/TransportInterface.php @@ -0,0 +1,17 @@ + $headers + * @param string $body + * @param int $timeoutSeconds + * + * @return TransportResponse + */ + public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60); +} diff --git a/src/Transport/TransportResponse.php b/src/Transport/TransportResponse.php new file mode 100644 index 0000000..7f7af32 --- /dev/null +++ b/src/Transport/TransportResponse.php @@ -0,0 +1,57 @@ + + */ + private $headers; + + /** + * @param int $statusCode + * @param string $body + * @param array $headers + */ + public function __construct($statusCode, $body, array $headers = []) + { + $this->statusCode = (int) $statusCode; + $this->body = (string) $body; + $this->headers = $headers; + } + + /** + * @return int + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * @return string + */ + public function getBody() + { + return $this->body; + } + + /** + * @return array + */ + public function getHeaders() + { + return $this->headers; + } +} From 34a71076fb753afcc0f89e2b6aae80489d7cdd06 Mon Sep 17 00:00:00 2001 From: Sujip Thapa Date: Sun, 22 Feb 2026 14:01:42 +0545 Subject: [PATCH 2/9] feat: add directpost operations and emv3ds order support --- src/DirectPostGateway.php | 149 +++++++++++++++++ src/Enums/TransactionType.php | 6 + src/HostedPaymentGateway.php | 46 ++++++ src/Message/DirectPostAbstractRequest.php | 66 ++++++-- src/Message/DirectPostApiResponse.php | 97 +++++++++++ src/Message/DirectPostCaptureRequest.php | 21 +++ .../DirectPostCompletePurchaseResponse.php | 10 +- src/Message/DirectPostOperationRequest.php | 151 ++++++++++++++++++ src/Message/DirectPostRefundRequest.php | 16 ++ src/Message/DirectPostReversalRequest.php | 16 ++ src/Message/DirectPostStoreRequest.php | 28 ++++ src/Message/DirectPostWebhookRequest.php | 26 ++- src/Message/EMV3DSOrderRequest.php | 124 ++++++++++++++ src/Message/EMV3DSOrderResponse.php | 123 ++++++++++++++ .../HostedPaymentCompletePurchaseRequest.php | 37 +++++ .../HostedPaymentCompletePurchaseResponse.php | 40 +++++ src/Message/HostedPaymentPurchaseRequest.php | 4 +- src/Message/SecureXMLRiskPurchaseRequest.php | 12 +- 18 files changed, 944 insertions(+), 28 deletions(-) create mode 100644 src/Message/DirectPostApiResponse.php create mode 100644 src/Message/DirectPostCaptureRequest.php create mode 100644 src/Message/DirectPostOperationRequest.php create mode 100644 src/Message/DirectPostRefundRequest.php create mode 100644 src/Message/DirectPostReversalRequest.php create mode 100644 src/Message/DirectPostStoreRequest.php create mode 100644 src/Message/EMV3DSOrderRequest.php create mode 100644 src/Message/EMV3DSOrderResponse.php create mode 100644 src/Message/HostedPaymentCompletePurchaseRequest.php diff --git a/src/DirectPostGateway.php b/src/DirectPostGateway.php index 1a8ce70..82ca3d5 100644 --- a/src/DirectPostGateway.php +++ b/src/DirectPostGateway.php @@ -3,6 +3,7 @@ namespace Omnipay\NABTransact; use Omnipay\Common\AbstractGateway; +use Omnipay\NABTransact\Transport\TransportInterface; /** * NABTransact Direct Post Gateway. @@ -70,6 +71,82 @@ public function setHasEMV3DSEnabled($value) return $this->setParameter('hasEMV3DSEnabled', $value); } + public function getHasRiskManagementEnabled() + { + return $this->getParameter('hasRiskManagementEnabled'); + } + + public function setHasRiskManagementEnabled($value) + { + return $this->setParameter('hasRiskManagementEnabled', $value); + } + + /** + * Optional custom transport for server-to-server DirectPost calls. + * + * @param TransportInterface $value + * + * @return mixed + */ + public function setTransport(TransportInterface $value) + { + return $this->setParameter('transport', $value); + } + + /** + * @return TransportInterface|null + */ + public function getTransport() + { + return $this->getParameter('transport'); + } + + /** + * Optional timeout in seconds for server-to-server DirectPost calls. + * + * @param int $value + * + * @return mixed + */ + public function setTimeoutSeconds($value) + { + return $this->setParameter('timeoutSeconds', (int) $value); + } + + /** + * @return int|null + */ + public function getTimeoutSeconds() + { + $timeout = $this->getParameter('timeoutSeconds'); + + if ($timeout === null) { + return null; + } + + return (int) $timeout; + } + + /** + * Backward-friendly alias. + */ + public function getRiskManagement() + { + return $this->getHasRiskManagementEnabled(); + } + + /** + * Backward-friendly alias. + * + * @param mixed $value + * + * @return mixed + */ + public function setRiskManagement($value) + { + return $this->setHasRiskManagementEnabled($value); + } + /** * @param array $parameters * @@ -109,4 +186,76 @@ public function completePurchase(array $parameters = []) { return $this->createRequest('\Omnipay\NABTransact\Message\DirectPostCompletePurchaseRequest', $parameters); } + + /** + * Complete preauth and capture funds using DirectPost server-to-server flow. + * + * @param array $parameters + * + * @return \Omnipay\NABTransact\Message\DirectPostCaptureRequest + */ + public function capture(array $parameters = []) + { + return $this->createRequest('\Omnipay\NABTransact\Message\DirectPostCaptureRequest', $parameters); + } + + /** + * Refund a DirectPost transaction using server-to-server flow. + * + * @param array $parameters + * + * @return \Omnipay\NABTransact\Message\DirectPostRefundRequest + */ + public function refund(array $parameters = []) + { + return $this->createRequest('\Omnipay\NABTransact\Message\DirectPostRefundRequest', $parameters); + } + + /** + * Void/reverse a DirectPost transaction using server-to-server flow. + * + * @param array $parameters + * + * @return \Omnipay\NABTransact\Message\DirectPostReversalRequest + */ + public function void(array $parameters = []) + { + return $this->createRequest('\Omnipay\NABTransact\Message\DirectPostReversalRequest', $parameters); + } + + /** + * Create an EMV 3DS order (order management API). + * + * @param array $parameters + * + * @return \Omnipay\NABTransact\Message\EMV3DSOrderRequest + */ + public function createEMV3DSOrder(array $parameters = []) + { + return $this->createRequest('\Omnipay\NABTransact\Message\EMV3DSOrderRequest', $parameters); + } + + /** + * Store card/token details without charging a real amount. + * + * @param array $parameters + * + * @return \Omnipay\NABTransact\Message\DirectPostStoreRequest + */ + public function store(array $parameters = []) + { + return $this->createRequest('\Omnipay\NABTransact\Message\DirectPostStoreRequest', $parameters); + } + + /** + * Convenience factory for webhook fingerprint verification payloads. + * + * @param array $data + * + * @return \Omnipay\NABTransact\Message\DirectPostWebhookRequest + */ + public function webhook(array $data = []) + { + return new \Omnipay\NABTransact\Message\DirectPostWebhookRequest($data); + } } diff --git a/src/Enums/TransactionType.php b/src/Enums/TransactionType.php index ee5aa1e..bb9bf83 100644 --- a/src/Enums/TransactionType.php +++ b/src/Enums/TransactionType.php @@ -17,4 +17,10 @@ class TransactionType const PREAUTH_RISK_MANAGEMENT_3DS_EMV3DS = 7; const STORE_ONLY = 8; + + // DirectPost transaction type string variants from NAB docs. + const COMPLETE_PREAUTH = 'COMPLETE'; + const REFUND = 'REFUND'; + const REVERSAL = 'REVERSAL'; + const ANTIFRAUD = 'ANTIFRAUD'; } diff --git a/src/HostedPaymentGateway.php b/src/HostedPaymentGateway.php index 567c95f..a7b28e5 100644 --- a/src/HostedPaymentGateway.php +++ b/src/HostedPaymentGateway.php @@ -9,6 +9,16 @@ */ class HostedPaymentGateway extends AbstractGateway { + public function getDefaultParameters() + { + return [ + 'merchantId' => '', + 'paymentAlertEmail' => '', + 'returnUrlText' => '', + 'testMode' => false, + ]; + } + /** * @param array $parameters * @@ -32,6 +42,42 @@ public function getName() return 'NAB Hosted Payment'; } + /** + * @return string + */ + public function getPaymentAlertEmail() + { + return $this->getParameter('paymentAlertEmail'); + } + + /** + * @param string $value + * + * @return mixed + */ + public function setPaymentAlertEmail($value) + { + return $this->setParameter('paymentAlertEmail', $value); + } + + /** + * @return string + */ + public function getReturnUrlText() + { + return $this->getParameter('returnUrlText'); + } + + /** + * @param string $value + * + * @return mixed + */ + public function setReturnUrlText($value) + { + return $this->setParameter('returnUrlText', $value); + } + /** * @param array $parameters * diff --git a/src/Message/DirectPostAbstractRequest.php b/src/Message/DirectPostAbstractRequest.php index 465a62b..2fab9c1 100644 --- a/src/Message/DirectPostAbstractRequest.php +++ b/src/Message/DirectPostAbstractRequest.php @@ -19,6 +19,30 @@ abstract class DirectPostAbstractRequest extends AbstractRequest */ public $liveEndpoint = 'https://transact.nab.com.au/live/directpostv2/authorise'; + /** + * @return string + */ + protected function resolveTxnType() + { + $isPreauth = (string) $this->txnType === (string) TransactionType::NORMAL_PREAUTH; + $risk = (bool) $this->getHasRiskManagementEnabled(); + $emv = (bool) $this->getHasEMV3DSEnabled(); + + if ($risk && $emv) { + return (string) ($isPreauth ? TransactionType::PREAUTH_RISK_MANAGEMENT_3DS_EMV3DS : TransactionType::PAYMENT_RISK_MANAGEMENT_3DS_EMV3DS); + } + + if ($risk) { + return (string) ($isPreauth ? TransactionType::PREAUTH_RISK_MANAGEMENT : TransactionType::PAYMENT_RISK_MANAGEMENT); + } + + if ($emv) { + return (string) ($isPreauth ? TransactionType::PREAUTH_3DS_EMV3DS : TransactionType::PAYMENT_3DS_EMV3DS); + } + + return (string) $this->txnType; + } + /** * @param array $data */ @@ -53,7 +77,7 @@ public function getBaseData() $data = []; $data['EPS_MERCHANT'] = $this->getMerchantId(); - $data['EPS_TXNTYPE'] = $this->txnType; + $data['EPS_TXNTYPE'] = $this->resolveTxnType(); $data['EPS_REFERENCEID'] = $this->getTransactionId(); $data['EPS_AMOUNT'] = $this->getAmount(); $data['EPS_TIMESTAMP'] = gmdate('YmdHis'); @@ -71,30 +95,38 @@ public function getBaseData() $card = $this->getCard(); - if ($billingPostcode = $card->getBillingPostcode()) { - $data['EPS_ZIPCODE'] = $billingPostcode; - } + if ($card) { + if ($billingFirstName = $card->getBillingFirstName()) { + $data['EPS_FIRSTNAME'] = $billingFirstName; + } - if ($billingCity = $card->getBillingCity()) { - $data['EPS_TOWN'] = $billingCity; - } + if ($billingLastName = $card->getBillingLastName()) { + $data['EPS_LASTNAME'] = $billingLastName; + } - if ($billingCountry = $card->getBillingCountry()) { - $data['EPS_BILLINGCOUNTRY'] = $billingCountry; - } + if ($billingPostcode = $card->getBillingPostcode()) { + $data['EPS_ZIPCODE'] = $billingPostcode; + } - if ($shippingCountry = $card->getShippingCountry()) { - $data['EPS_DELIVERYCOUNTRY'] = $shippingCountry; - } + if ($billingCity = $card->getBillingCity()) { + $data['EPS_TOWN'] = $billingCity; + } + + if ($billingCountry = $card->getBillingCountry()) { + $data['EPS_BILLINGCOUNTRY'] = $billingCountry; + } - if ($emailAddress = $card->getEmail()) { - $data['EPS_EMAILADDRESS'] = $emailAddress; + if ($shippingCountry = $card->getShippingCountry()) { + $data['EPS_DELIVERYCOUNTRY'] = $shippingCountry; + } + + if ($emailAddress = $card->getEmail()) { + $data['EPS_EMAILADDRESS'] = $emailAddress; + } } if ($this->getHasEMV3DSEnabled()) { $data['EPS_ORDERID'] = $this->getTransactionReference(); - - $data['EPS_TXNTYPE'] = TransactionType::PAYMENT_3DS_EMV3DS; } $data['EPS_FINGERPRINT'] = $this->generateFingerprint($data); diff --git a/src/Message/DirectPostApiResponse.php b/src/Message/DirectPostApiResponse.php new file mode 100644 index 0000000..a2ad6a6 --- /dev/null +++ b/src/Message/DirectPostApiResponse.php @@ -0,0 +1,97 @@ +getCode(); + + if ($code !== null) { + return in_array($code, ['00', '08', '11'], true); + } + + $statusCode = $this->getHttpStatusCode(); + + if ($statusCode === null) { + return false; + } + + return $statusCode >= 200 && $statusCode <= 299; + } + + /** + * @return int|null + */ + public function getHttpStatusCode() + { + if (isset($this->data['http_status_code'])) { + return (int) $this->data['http_status_code']; + } + + return null; + } + + /** + * @return string|null + */ + public function getCode() + { + foreach (['rescode', 'responsecode', 'statuscode', 'code'] as $key) { + if (isset($this->data[$key])) { + return (string) $this->data[$key]; + } + } + + return null; + } + + /** + * @return string|null + */ + public function getMessage() + { + foreach (['restext', 'responsetext', 'statusdescription', 'message', 'error'] as $key) { + if (isset($this->data[$key])) { + return (string) $this->data[$key]; + } + } + + return null; + } + + /** + * @return string|null + */ + public function getTransactionReference() + { + foreach (['txnid', 'transactionid', 'eps_txnid'] as $key) { + if (isset($this->data[$key])) { + return (string) $this->data[$key]; + } + } + + return null; + } + + /** + * @return string|null + */ + public function getRawResponse() + { + if (isset($this->data['raw'])) { + return (string) $this->data['raw']; + } + + return null; + } +} diff --git a/src/Message/DirectPostCaptureRequest.php b/src/Message/DirectPostCaptureRequest.php new file mode 100644 index 0000000..75288ce --- /dev/null +++ b/src/Message/DirectPostCaptureRequest.php @@ -0,0 +1,21 @@ +summaryCode() && in_array($this->getCode(), ['00', '08', '11']); + return $this->summaryCode() && in_array($this->getCode(), ['00', '08', '11'], true); } public function summaryCode() { - return isset($this->data['summarycode']) && (int) $this->data['summarycode'] == 1; + return isset($this->data['summarycode']) && (int) $this->data['summarycode'] === 1; } /** @@ -30,6 +30,8 @@ public function getMessage() if (isset($this->data['restext'])) { return $this->data['restext']; } + + return null; } /** @@ -40,6 +42,8 @@ public function getCode() if (isset($this->data['rescode'])) { return $this->data['rescode']; } + + return null; } /** @@ -50,5 +54,7 @@ public function getTransactionReference() if (isset($this->data['txnid'])) { return $this->data['txnid']; } + + return null; } } diff --git a/src/Message/DirectPostOperationRequest.php b/src/Message/DirectPostOperationRequest.php new file mode 100644 index 0000000..45e7226 --- /dev/null +++ b/src/Message/DirectPostOperationRequest.php @@ -0,0 +1,151 @@ +validate('merchantId', 'transactionPassword', 'amount', 'transactionId', 'transactionReference'); + + $data = [ + 'EPS_MERCHANT' => $this->getMerchantId(), + 'EPS_TXNTYPE' => $this->resolveTxnType(), + 'EPS_REFERENCEID' => $this->getTransactionId(), + 'EPS_AMOUNT' => $this->getAmount(), + $this->targetTransactionField => $this->getTransactionReference(), + 'EPS_TIMESTAMP' => gmdate('YmdHis'), + ]; + + if ($currency = $this->getCurrency()) { + $data['EPS_CURRENCY'] = $currency; + } + + if ($returnUrl = $this->getReturnUrl()) { + $data['EPS_RESULTURL'] = $returnUrl; + } + + $data['EPS_FINGERPRINT'] = $this->generateOperationFingerprint($data); + + return $data; + } + + /** + * @param array $data + * + * @return string + */ + protected function generateOperationFingerprint(array $data) + { + $hashable = [ + $data['EPS_MERCHANT'], + $this->getTransactionPassword(), + $data['EPS_TXNTYPE'], + $data['EPS_REFERENCEID'], + $data['EPS_AMOUNT'], + $data[$this->targetTransactionField], + $data['EPS_TIMESTAMP'], + ]; + + return hash_hmac('sha256', implode('|', $hashable), $this->getTransactionPassword()); + } + + /** + * @param array $data + * + * @return DirectPostApiResponse + */ + public function sendData($data) + { + $transport = $this->getTransport(); + if ($transport === null) { + $transport = new OmnipayHttpClientTransport($this->httpClient); + } + + $response = $transport->send( + 'POST', + $this->getEndpoint(), + ['Content-Type' => 'application/x-www-form-urlencoded'], + http_build_query($data, '', '&'), + $this->getTimeoutSeconds() + ); + + $parsed = $this->parseTransportResponse($response); + + return $this->response = new DirectPostApiResponse($this, $parsed); + } + + /** + * @return array + */ + protected function parseTransportResponse(TransportResponse $response) + { + $body = trim((string) $response->getBody()); + + $parsed = [ + 'http_status_code' => $response->getStatusCode(), + 'raw' => $body, + ]; + + if ($body === '') { + return $parsed; + } + + $json = json_decode($body, true); + if (is_array($json)) { + return array_merge($parsed, $this->normalizeKeys($json)); + } + + $query = []; + parse_str($body, $query); + if (!empty($query)) { + return array_merge($parsed, $this->normalizeKeys($query)); + } + + if (function_exists('simplexml_load_string')) { + $xml = @simplexml_load_string($body); + if ($xml !== false) { + $xmlData = []; + foreach ($xml->children() as $node) { + $xmlData[strtolower($node->getName())] = (string) $node; + } + + if (!empty($xmlData)) { + return array_merge($parsed, $xmlData); + } + } + } + + return $parsed; + } + + /** + * @param array $data + * + * @return array + */ + private function normalizeKeys(array $data) + { + $normalized = []; + + foreach ($data as $key => $value) { + $normalized[strtolower((string) $key)] = $value; + } + + return $normalized; + } +} diff --git a/src/Message/DirectPostRefundRequest.php b/src/Message/DirectPostRefundRequest.php new file mode 100644 index 0000000..7ebf3bd --- /dev/null +++ b/src/Message/DirectPostRefundRequest.php @@ -0,0 +1,16 @@ +validate('returnUrl', 'card'); + + if (!$this->getAmount()) { + $this->setAmount('0.00'); + } + + return parent::getData(); + } +} diff --git a/src/Message/DirectPostWebhookRequest.php b/src/Message/DirectPostWebhookRequest.php index f5f5a9e..934cbb1 100644 --- a/src/Message/DirectPostWebhookRequest.php +++ b/src/Message/DirectPostWebhookRequest.php @@ -4,8 +4,14 @@ class DirectPostWebhookRequest extends DirectPostAbstractRequest { + /** + * @var array + */ private $data = []; + /** + * @param array $data + */ public function __construct($data = []) { $this->data = $data; @@ -27,16 +33,27 @@ public function generateResponseFingerprint($data) return hash_hmac('sha256', $hash, $data['txn_password']); } - public function vefiyFingerPrint($fingerprint) + public function verifyFingerPrint($fingerprint) { $data = $this->data; if ($fingerprint !== $this->generateResponseFingerprint($data)) { - $data['restext'] = $data['restext'].', Invalid fingerprint.'; + $existing = isset($data['restext']) ? trim((string) $data['restext']) : ''; + $data['restext'] = $existing === '' ? 'Invalid fingerprint.' : $existing.', Invalid fingerprint.'; $data['summarycode'] = 3; } - return new DirectPostCompletePurchaseResponse($this, $data); + return $this->response = new DirectPostCompletePurchaseResponse($this, $data); + } + + /** + * Backward-compatible typo alias. + * + * @deprecated Use verifyFingerPrint(). + */ + public function vefiyFingerPrint($fingerprint) + { + return $this->verifyFingerPrint($fingerprint); } public function getData() @@ -46,5 +63,8 @@ public function getData() public function sendData($data) { + $fingerprint = isset($data['fingerprint']) ? $data['fingerprint'] : ''; + + return $this->verifyFingerPrint($fingerprint); } } diff --git a/src/Message/EMV3DSOrderRequest.php b/src/Message/EMV3DSOrderRequest.php new file mode 100644 index 0000000..2b0d786 --- /dev/null +++ b/src/Message/EMV3DSOrderRequest.php @@ -0,0 +1,124 @@ +validate('merchantId', 'transactionPassword', 'amount', 'currency', 'clientIp', 'transactionReference'); + + return [ + 'amount' => $this->getAmountInteger(), + 'currency' => $this->getCurrency(), + 'ip' => $this->getClientIp(), + 'merchantId' => $this->getMerchantId(), + 'merchantOrderReference' => $this->getTransactionReference(), + 'orderType' => $this->getOrderType(), + 'intents' => $this->getIntents(), + ]; + } + + /** + * @return string + */ + public function getOrderType() + { + return $this->getParameter('orderType') ?: 'PAYMENT'; + } + + /** + * @param string $value + * + * @return mixed + */ + public function setOrderType($value) + { + return $this->setParameter('orderType', $value); + } + + /** + * @return array + */ + public function getIntents() + { + $intents = $this->getParameter('intents'); + + if (!is_array($intents) || empty($intents)) { + return ['THREED_SECURE']; + } + + return array_values($intents); + } + + /** + * @param array $value + * + * @return mixed + */ + public function setIntents(array $value) + { + return $this->setParameter('intents', $value); + } + + /** + * @param array $data + * + * @return EMV3DSOrderResponse + */ + public function sendData($data) + { + $transport = $this->getTransport(); + if ($transport === null) { + $transport = new OmnipayHttpClientTransport($this->httpClient); + } + + $payload = json_encode($data); + + if ($payload === false) { + throw new InvalidRequestException('Unable to encode EMV 3DS order payload.'); + } + + $authorization = base64_encode($this->getMerchantId().':'.$this->getTransactionPassword()); + + $response = $transport->send( + 'POST', + $this->getEndpoint(), + [ + 'Content-Type' => 'application/json; charset=UTF-8', + 'Authorization' => 'Basic '.$authorization, + ], + $payload, + $this->getTimeoutSeconds() + ); + + $parsed = json_decode((string) $response->getBody(), true); + if (!is_array($parsed)) { + $parsed = []; + } + + $parsed['http_status_code'] = $response->getStatusCode(); + $parsed['raw'] = (string) $response->getBody(); + + return $this->response = new EMV3DSOrderResponse($this, $parsed); + } +} diff --git a/src/Message/EMV3DSOrderResponse.php b/src/Message/EMV3DSOrderResponse.php new file mode 100644 index 0000000..5666c54 --- /dev/null +++ b/src/Message/EMV3DSOrderResponse.php @@ -0,0 +1,123 @@ +getHttpStatusCode(); + + if ($statusCode === null) { + return false; + } + + return $statusCode >= 200 && $statusCode <= 399; + } + + /** + * @return int|null + */ + public function getHttpStatusCode() + { + if (isset($this->data['http_status_code'])) { + return (int) $this->data['http_status_code']; + } + + return null; + } + + /** + * @return string|null + */ + public function getOrderId() + { + if (isset($this->data['orderId'])) { + return $this->data['orderId']; + } + + return null; + } + + /** + * @return string|null + */ + public function getSimpleToken() + { + if (isset($this->data['simpleToken'])) { + return $this->data['simpleToken']; + } + + return null; + } + + /** + * @return string|null + */ + public function getOrderToken() + { + if (isset($this->data['orderToken'])) { + return $this->data['orderToken']; + } + + return null; + } + + /** + * @return string|null + */ + public function getProviderClientId() + { + if (isset($this->data['threedSecure']['providerClientId'])) { + return $this->data['threedSecure']['providerClientId']; + } + + return null; + } + + /** + * @return string|null + */ + public function getSessionId() + { + if (isset($this->data['threedSecure']['sessionId'])) { + return $this->data['threedSecure']['sessionId']; + } + + return null; + } + + /** + * @return string|null + */ + public function getMessage() + { + foreach (['message', 'error', 'description'] as $key) { + if (isset($this->data[$key])) { + return (string) $this->data[$key]; + } + } + + return null; + } + + /** + * @return string|null + */ + public function getRawResponse() + { + if (isset($this->data['raw'])) { + return (string) $this->data['raw']; + } + + return null; + } +} diff --git a/src/Message/HostedPaymentCompletePurchaseRequest.php b/src/Message/HostedPaymentCompletePurchaseRequest.php new file mode 100644 index 0000000..baf0f52 --- /dev/null +++ b/src/Message/HostedPaymentCompletePurchaseRequest.php @@ -0,0 +1,37 @@ +httpRequest->query->all(); + if (!empty($query)) { + return $query; + } + + $request = $this->httpRequest->request->all(); + if (!empty($request)) { + return $request; + } + + return []; + } + + /** + * @param array $data + * + * @return HostedPaymentCompletePurchaseResponse + */ + public function sendData($data) + { + return $this->response = new HostedPaymentCompletePurchaseResponse($this, $data); + } +} diff --git a/src/Message/HostedPaymentCompletePurchaseResponse.php b/src/Message/HostedPaymentCompletePurchaseResponse.php index 8eb1a91..90cd007 100644 --- a/src/Message/HostedPaymentCompletePurchaseResponse.php +++ b/src/Message/HostedPaymentCompletePurchaseResponse.php @@ -22,4 +22,44 @@ public function __construct(RequestInterface $request, $data) parent::__construct($request, $data); } + + /** + * @return bool + */ + public function isSuccessful() + { + return $this->summaryCode() && in_array($this->getCode(), ['00', '08', '11'], true); + } + + /** + * @return bool + */ + public function summaryCode() + { + return isset($this->data['summarycode']) && (int) $this->data['summarycode'] === 1; + } + + /** + * @return string|null + */ + public function getMessage() + { + return isset($this->data['restext']) ? $this->data['restext'] : null; + } + + /** + * @return string|null + */ + public function getCode() + { + return isset($this->data['rescode']) ? $this->data['rescode'] : null; + } + + /** + * @return string|null + */ + public function getTransactionReference() + { + return isset($this->data['txnid']) ? $this->data['txnid'] : null; + } } diff --git a/src/Message/HostedPaymentPurchaseRequest.php b/src/Message/HostedPaymentPurchaseRequest.php index 951bcad..2bbbf2a 100644 --- a/src/Message/HostedPaymentPurchaseRequest.php +++ b/src/Message/HostedPaymentPurchaseRequest.php @@ -10,12 +10,12 @@ class HostedPaymentPurchaseRequest extends AbstractRequest /** * @var string */ - public $liveEndpoint = 'https://transact.nab.com.au/test/hpp/payment'; + public $liveEndpoint = 'https://transact.nab.com.au/live/hpp/payment'; /** * @var string */ - public $testEndpoint = 'https://transact.nab.com.au/live/hpp/payment'; + public $testEndpoint = 'https://transact.nab.com.au/test/hpp/payment'; /** * @return array diff --git a/src/Message/SecureXMLRiskPurchaseRequest.php b/src/Message/SecureXMLRiskPurchaseRequest.php index bf278f3..2e04edf 100644 --- a/src/Message/SecureXMLRiskPurchaseRequest.php +++ b/src/Message/SecureXMLRiskPurchaseRequest.php @@ -29,7 +29,7 @@ class SecureXMLRiskPurchaseRequest extends SecureXMLAbstractRequest public function setIp($value) { - $this->setParameter('ip', $value); + return $this->setParameter('ip', $value); } public function getIp() @@ -45,26 +45,30 @@ public function getData() $xml = $this->getBasePaymentXMLWithCard(); $buyer = $xml->addChild('BuyerInfo'); - - $buyer->addChild('ip', $this->getIp('ip')); + $buyer->addChild('ip', $this->getIp()); $card = $this->getCard(); if ($firstName = $card->getFirstName()) { $buyer->addChild('firstName', $firstName); } + if ($lastName = $card->getLastName()) { - $buyer->addChild('firstName', $lastName); + $buyer->addChild('lastName', $lastName); } + if ($postCode = $card->getBillingPostcode()) { $buyer->addChild('zipcode', $postCode); } + if ($city = $card->getBillingCity()) { $buyer->addChild('town', $city); } + if ($country = $card->getBillingCountry()) { $buyer->addChild('billingCountry', $country); } + if ($email = $card->getEmail()) { $buyer->addChild('emailAddress', $email); } From 9477a627e52b9db42396bb75bfe0afdd5afd9247 Mon Sep 17 00:00:00 2001 From: Sujip Thapa Date: Sun, 22 Feb 2026 14:01:57 +0545 Subject: [PATCH 3/9] test: expand coverage and docs for new NAB payment flows --- README.md | 115 +++++++++++++++++- docs/feature-matrix.md | 72 +++++++++++ tests/DirectPostGatewayTest.php | 73 +++++++++++ tests/HostedPaymentGatewayTest.php | 31 +++++ tests/Message/DirectPostApiResponseTest.php | 50 ++++++++ .../Message/DirectPostCaptureRequestTest.php | 55 +++++++++ tests/Message/DirectPostRefundRequestTest.php | 51 ++++++++ .../Message/DirectPostReversalRequestTest.php | 51 ++++++++ tests/Message/DirectPostStoreRequestTest.php | 35 ++++++ .../DirectPostTransactionTypeRequestTest.php | 87 +++++++++++++ .../Message/DirectPostWebhookRequestTest.php | 53 ++++++++ tests/Message/EMV3DSOrderRequestTest.php | 95 +++++++++++++++ ...stedPaymentCompletePurchaseRequestTest.php | 45 +++++++ .../HostedPaymentPurchaseRequestTest.php | 38 ++++++ .../SecureXMLRiskPurchaseRequestTest.php | 44 +++++++ .../Message/SecureXMLTransportRequestTest.php | 80 ++++++++++++ tests/SecureXMLGatewayTest.php | 23 +++- 17 files changed, 993 insertions(+), 5 deletions(-) create mode 100644 docs/feature-matrix.md create mode 100644 tests/HostedPaymentGatewayTest.php create mode 100644 tests/Message/DirectPostApiResponseTest.php create mode 100644 tests/Message/DirectPostCaptureRequestTest.php create mode 100644 tests/Message/DirectPostRefundRequestTest.php create mode 100644 tests/Message/DirectPostReversalRequestTest.php create mode 100644 tests/Message/DirectPostStoreRequestTest.php create mode 100644 tests/Message/DirectPostTransactionTypeRequestTest.php create mode 100644 tests/Message/DirectPostWebhookRequestTest.php create mode 100644 tests/Message/EMV3DSOrderRequestTest.php create mode 100644 tests/Message/HostedPaymentCompletePurchaseRequestTest.php create mode 100644 tests/Message/HostedPaymentPurchaseRequestTest.php create mode 100644 tests/Message/SecureXMLRiskPurchaseRequestTest.php create mode 100644 tests/Message/SecureXMLTransportRequestTest.php diff --git a/README.md b/README.md index 10b1a1f..396e484 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,10 @@ **NAB Transact driver for the Omnipay PHP payment processing library** +[![CI](https://github.com/sudiptpa/omnipay-nabtransact/actions/workflows/ci.yml/badge.svg)](https://github.com/sudiptpa/omnipay-nabtransact/actions/workflows/ci.yml) [Omnipay](https://github.com/thephpleague/omnipay) is a framework agnostic, multi-gateway payment processing library for PHP. This package implements NAB Transact support for Omnipay. -[![StyleCI](https://styleci.io/repos/74269379/shield?style=flat&branch=master)](https://styleci.io/repos/74269379) -[![Build Status](https://travis-ci.org/sudiptpa/omnipay-nabtransact.svg?branch=master&style=flat-square)](https://travis-ci.org/sudiptpa/omnipay-nabtransact) [![Latest Stable Version](https://poser.pugx.org/sudiptpa/omnipay-nabtransact/v/stable?style=flat-square)](https://packagist.org/packages/sudiptpa/omnipay-nabtransact) [![Total Downloads](https://poser.pugx.org/sudiptpa/omnipay-nabtransact/downloads?style=flat-square)](https://packagist.org/packages/sudiptpa/omnipay-nabtransact) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://raw.githubusercontent.com/sudiptpa/omnipay-nabtransact/master/LICENSE) @@ -25,6 +24,7 @@ The following gateways are provided by this package: * NABTransact_DirectPost (NAB Transact Direct Post v2) * NABTransact_SecureXML (NAB Transact SecurePay XML) +* NABTransact_HostedPayment (NAB Hosted Payment Page) * NABTransact_UnionPay (UnionPay via NAB Transact) ### NAB Transact SecureXML API @@ -129,7 +129,7 @@ The following gateways are provided by this package: $response = $gateway->purchase(array( 'amount' => '12.00', 'transactionId' => 'ORDER-ZYX8', - 'transactionReference' => '11fc42b0-bb7a-41a4-8b3c-096b3fd4d402' + 'transactionReference' => '11fc42b0-bb7a-41a4-8b3c-096b3fd4d402', 'currency' => 'AUD', 'card' => $card, 'clientIp' => '192.168.1.1' @@ -148,6 +148,82 @@ The following gateways are provided by this package: ``` +#### DirectPost Store Only + +```php + $gateway = Omnipay::create('NABTransact_DirectPost'); + $gateway->setMerchantId('XYZ0010'); + $gateway->setTransactionPassword('abcd1234'); + $gateway->setTestMode(true); + + $response = $gateway->store(array( + 'transactionId' => 'STORE-ORDER-100', + 'returnUrl' => 'http://example.com/payment/response', + 'card' => $card, + ))->send(); + + if ($response->isRedirect()) { + $response->redirect(); + } +``` + +#### DirectPost Capture (Complete Preauth, Server-to-Server) + +```php + $gateway = Omnipay::create('NABTransact_DirectPost'); + $gateway->setMerchantId('XYZ0010'); + $gateway->setTransactionPassword('abcd1234'); + $gateway->setTestMode(true); + + $response = $gateway->capture(array( + 'transactionId' => 'CAPTURE-ORDER-100', + 'transactionReference' => 'NAB-ORIGINAL-TXN-ID', + 'amount' => '12.00', + 'currency' => 'AUD', + ))->send(); + + if ($response->isSuccessful()) { + echo 'Capture successful: '.$response->getTransactionReference(); + } +``` + +#### DirectPost Refund (Server-to-Server) + +```php + $response = $gateway->refund(array( + 'transactionId' => 'REFUND-ORDER-100', + 'transactionReference' => 'NAB-SETTLED-TXN-ID', + 'amount' => '5.00', + 'currency' => 'AUD', + ))->send(); +``` + +#### DirectPost Reversal/Void (Server-to-Server) + +```php + $response = $gateway->void(array( + 'transactionId' => 'VOID-ORDER-100', + 'transactionReference' => 'NAB-AUTH-TXN-ID', + 'amount' => '12.00', + ))->send(); +``` + +#### EMV 3DS Order Creation API + +```php + $response = $gateway->createEMV3DSOrder(array( + 'amount' => '12.00', + 'currency' => 'AUD', + 'clientIp' => '203.0.113.10', + 'transactionReference' => 'ORDER-REF-100', + ))->send(); + + if ($response->isSuccessful()) { + echo 'Order ID: '.$response->getOrderId(); + echo 'Simple Token: '.$response->getSimpleToken(); + } +``` + ### NAB Transact DirectPost v2 UnionPay Online Payment ```php @@ -188,7 +264,7 @@ The following gateways are provided by this package: $response = $gateway->completePurchase(array( 'amount' => '12.00', 'transactionId' => '1234566789205067', - 'transactionReference' => '11fc42b0-bb7a-41a4-8b3c-096b3fd4d402' + 'transactionReference' => '11fc42b0-bb7a-41a4-8b3c-096b3fd4d402', 'currency' => 'AUD', 'returnUrl' => 'http://example.com/payment/response', )) @@ -205,6 +281,32 @@ The following gateways are provided by this package: For general usage instructions, please see the main [Omnipay](https://github.com/thephpleague/omnipay) repository. +## Framework-Agnostic Transport Option + +Omnipay support remains the default behavior. SecureXML requests can now also use a framework-agnostic cURL transport: + +```php +use Omnipay\NABTransact\Transport\CurlTransport; + +$request = $gateway->purchase([ + 'amount' => '10.00', + 'transactionId' => 'ORDER-1000', + 'card' => $card, +]); + +$request->setTransport(new CurlTransport()); +$response = $request->send(); +``` + +DirectPost server-to-server operations (`capture`, `refund`, `void`, `createEMV3DSOrder`) can use the same transport injection pattern. + +See `ARCHITECTURE.md` for design details and extension points. + +## NAB Feature Coverage + +Core payment features are mapped in `docs/feature-matrix.md` with request/response classes and test coverage. +This package targets payment-processing API coverage and does not include NAB admin/reporting portal features. + ## Contributing Contributions are **welcome** and will be fully **credited**. @@ -223,3 +325,8 @@ you can subscribe to. If you believe you have found a bug, please report it using the [GitHub issue tracker](https://github.com/sudiptpa/nabtransact/issues), or better yet, fork the library and submit a pull request. + + +## Architecture + +See `ARCHITECTURE.md` for package structure, flow, and extension points. diff --git a/docs/feature-matrix.md b/docs/feature-matrix.md new file mode 100644 index 0000000..573b8db --- /dev/null +++ b/docs/feature-matrix.md @@ -0,0 +1,72 @@ +# NAB Transact Feature Matrix + +This matrix documents payment API feature coverage in this package. + +## Scope + +In scope: +- NAB Transact payment-processing APIs used in Omnipay flows. + +Out of scope: +- NAB admin/reporting portal operations. +- Merchant account management workflows. + +## SecureXML + +| Feature | Omnipay Method | Class | Status | +| --- | --- | --- | --- | +| Echo/Test | `echoTest()` | `SecureXMLEchoTestRequest` | Implemented | +| Purchase | `purchase()` | `SecureXMLPurchaseRequest` | Implemented | +| Risk-managed purchase | `purchase()` with `riskManagement=true` | `SecureXMLRiskPurchaseRequest` | Implemented | +| Authorize | `authorize()` | `SecureXMLAuthorizeRequest` | Implemented | +| Capture | `capture()` | `SecureXMLCaptureRequest` | Implemented | +| Refund | `refund()` | `SecureXMLRefundRequest` | Implemented | +| XML response mapping | n/a | `SecureXMLResponse` | Implemented | + +## DirectPost v2 + +| Feature | Omnipay Method | Class | Status | +| --- | --- | --- | --- | +| Purchase redirect | `purchase()` | `DirectPostPurchaseRequest` | Implemented | +| Authorize redirect | `authorize()` | `DirectPostAuthorizeRequest` | Implemented | +| Store-only redirect | `store()` | `DirectPostStoreRequest` | Implemented | +| Complete purchase callback | `completePurchase()` | `DirectPostCompletePurchaseRequest` | Implemented | +| Complete authorize callback | `completeAuthorize()` | `DirectPostCompletePurchaseRequest` | Implemented | +| Complete preauth (capture) | `capture()` | `DirectPostCaptureRequest` | Implemented | +| Refund (server-to-server) | `refund()` | `DirectPostRefundRequest` | Implemented | +| Reversal/void (server-to-server) | `void()` | `DirectPostReversalRequest` | Implemented | +| DirectPost API response mapper | n/a | `DirectPostApiResponse` | Implemented | +| Fingerprint verification helper | `webhook()` | `DirectPostWebhookRequest` | Implemented | +| EMV 3DS txnType mapping | `setHasEMV3DSEnabled(true)` | `DirectPostAbstractRequest` | Implemented | +| Risk-managed txnType mapping | `setHasRiskManagementEnabled(true)` | `DirectPostAbstractRequest` | Implemented | +| Risk + EMV txnType mapping | both flags enabled | `DirectPostAbstractRequest` | Implemented | +| EMV 3DS order creation API | `createEMV3DSOrder()` | `EMV3DSOrderRequest` | Implemented | + +## Hosted Payment + +| Feature | Omnipay Method | Class | Status | +| --- | --- | --- | --- | +| Hosted purchase redirect | `purchase()` | `HostedPaymentPurchaseRequest` | Implemented | +| Hosted callback completion | `completePurchase()` | `HostedPaymentCompletePurchaseRequest` | Implemented | +| Hosted callback response mapping | n/a | `HostedPaymentCompletePurchaseResponse` | Implemented | + +## UnionPay + +| Feature | Omnipay Method | Class | Status | +| --- | --- | --- | --- | +| UnionPay purchase redirect | `purchase()` | `UnionPayPurchaseRequest` | Implemented | +| UnionPay callback completion | `completePurchase()` | `UnionPayCompletePurchaseRequest` | Implemented | +| UnionPay completion response mapping | n/a | `UnionPayCompletePurchaseResponse` | Implemented | + +## Transport Layer + +| Feature | Class | Status | +| --- | --- | --- | +| Omnipay HTTP client adapter (default path) | `OmnipayHttpClientTransport` | Implemented | +| Native cURL transport | `CurlTransport` | Implemented | +| Custom transport contract | `TransportInterface` | Implemented | + +## Notes + +- Existing Omnipay request/response APIs are preserved for backward compatibility. +- Additional methods were added additively (no removal of existing methods). diff --git a/tests/DirectPostGatewayTest.php b/tests/DirectPostGatewayTest.php index 8bd1002..89847ee 100644 --- a/tests/DirectPostGatewayTest.php +++ b/tests/DirectPostGatewayTest.php @@ -2,6 +2,8 @@ namespace Omnipay\NABTransact; +use Omnipay\NABTransact\Transport\TransportInterface; +use Omnipay\NABTransact\Transport\TransportResponse; use Omnipay\Tests\GatewayTestCase; class DirectPostGatewayTest extends GatewayTestCase @@ -45,4 +47,75 @@ public function testCompletePurchase() $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostCompletePurchaseRequest', $request); $this->assertSame('10.00', $request->getAmount()); } + + public function testRiskManagementAliases() + { + $this->gateway->setRiskManagement(true); + + $this->assertTrue((bool) $this->gateway->getRiskManagement()); + $this->assertTrue((bool) $this->gateway->getHasRiskManagementEnabled()); + } + + public function testWebhookFactory() + { + $request = $this->gateway->webhook(['merchant' => 'XYZ0010']); + + $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostWebhookRequest', $request); + } + + public function testStore() + { + $request = $this->gateway->store(['amount' => '0.00', 'returnUrl' => 'https://example.com/return']); + + $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostStoreRequest', $request); + $this->assertSame('0.00', $request->getAmount()); + } + + public function testCapture() + { + $request = $this->gateway->capture(['amount' => '10.00']); + + $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostCaptureRequest', $request); + $this->assertSame('10.00', $request->getAmount()); + } + + public function testRefund() + { + $request = $this->gateway->refund(['amount' => '10.00']); + + $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostRefundRequest', $request); + $this->assertSame('10.00', $request->getAmount()); + } + + public function testVoid() + { + $request = $this->gateway->void(['amount' => '10.00']); + + $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostReversalRequest', $request); + $this->assertSame('10.00', $request->getAmount()); + } + + public function testCreateEmv3dsOrder() + { + $request = $this->gateway->createEMV3DSOrder(['amount' => '10.00']); + + $this->assertInstanceOf('\Omnipay\NABTransact\Message\EMV3DSOrderRequest', $request); + $this->assertSame('10.00', $request->getAmount()); + } + + public function testSetTransportAndTimeout() + { + $transport = new class implements TransportInterface { + public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) + { + return new TransportResponse(200, '{}'); + } + }; + + $this->gateway->setTransport($transport); + $this->gateway->setTimeoutSeconds(12); + + $this->assertSame($transport, $this->gateway->getTransport()); + $this->assertSame(12, $this->gateway->getTimeoutSeconds()); + } } diff --git a/tests/HostedPaymentGatewayTest.php b/tests/HostedPaymentGatewayTest.php new file mode 100644 index 0000000..45313cf --- /dev/null +++ b/tests/HostedPaymentGatewayTest.php @@ -0,0 +1,31 @@ +gateway = new HostedPaymentGateway($this->getHttpClient(), $this->getHttpRequest()); + $this->gateway->setMerchantId('XYZ0010'); + } + + public function testPurchase() + { + $request = $this->gateway->purchase(['amount' => '10.00', 'transactionId' => 'ORDER-100']); + + $this->assertInstanceOf('\Omnipay\NABTransact\Message\HostedPaymentPurchaseRequest', $request); + $this->assertSame('10.00', $request->getAmount()); + } + + public function testCompletePurchase() + { + $request = $this->gateway->completePurchase(); + + $this->assertInstanceOf('\Omnipay\NABTransact\Message\HostedPaymentCompletePurchaseRequest', $request); + } +} diff --git a/tests/Message/DirectPostApiResponseTest.php b/tests/Message/DirectPostApiResponseTest.php new file mode 100644 index 0000000..a92e86a --- /dev/null +++ b/tests/Message/DirectPostApiResponseTest.php @@ -0,0 +1,50 @@ +getHttpClient(), $this->getHttpRequest()); + + $response = new DirectPostApiResponse($request, [ + 'rescode' => '00', + 'restext' => 'Approved', + 'txnid' => 'TXN-100', + 'http_status_code' => 500, + 'raw' => 'rescode=00', + ]); + + $this->assertTrue($response->isSuccessful()); + $this->assertSame('00', $response->getCode()); + $this->assertSame('Approved', $response->getMessage()); + $this->assertSame('TXN-100', $response->getTransactionReference()); + $this->assertSame(500, $response->getHttpStatusCode()); + $this->assertSame('rescode=00', $response->getRawResponse()); + } + + public function testSuccessfulByHttpStatusWhenNoResultCode() + { + $request = new DirectPostRefundRequest($this->getHttpClient(), $this->getHttpRequest()); + + $response = new DirectPostApiResponse($request, [ + 'http_status_code' => 200, + 'message' => 'OK', + ]); + + $this->assertTrue($response->isSuccessful()); + $this->assertNull($response->getCode()); + $this->assertSame('OK', $response->getMessage()); + } + + public function testFailureWhenNoSignals() + { + $request = new DirectPostRefundRequest($this->getHttpClient(), $this->getHttpRequest()); + $response = new DirectPostApiResponse($request, []); + + $this->assertFalse($response->isSuccessful()); + } +} diff --git a/tests/Message/DirectPostCaptureRequestTest.php b/tests/Message/DirectPostCaptureRequestTest.php new file mode 100644 index 0000000..4937e5e --- /dev/null +++ b/tests/Message/DirectPostCaptureRequestTest.php @@ -0,0 +1,55 @@ +request = new DirectPostCaptureRequest($this->getHttpClient(), $this->getHttpRequest()); + + $this->request->initialize([ + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'amount' => '12.00', + 'currency' => 'AUD', + 'transactionId' => 'CAPTURE-ORDER-100', + 'transactionReference' => 'NAB-ORIG-100', + ]); + } + + public function testGetDataBuildsCapturePayload() + { + $data = $this->request->getData(); + + $this->assertSame('COMPLETE', $data['EPS_TXNTYPE']); + $this->assertSame('NAB-ORIG-100', $data['EPS_TXNID']); + $this->assertSame('12.00', $data['EPS_AMOUNT']); + $this->assertSame('AUD', $data['EPS_CURRENCY']); + $this->assertArrayHasKey('EPS_FINGERPRINT', $data); + } + + public function testSendParsesQueryStringResponse() + { + $request = $this->request; + + $request->setTransport(new class implements TransportInterface { + public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) + { + return new TransportResponse(200, 'rescode=00&restext=Approved&txnid=CAPTURED-1'); + } + }); + + $response = $request->send(); + + $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostApiResponse', $response); + $this->assertTrue($response->isSuccessful()); + $this->assertSame('00', $response->getCode()); + $this->assertSame('Approved', $response->getMessage()); + $this->assertSame('CAPTURED-1', $response->getTransactionReference()); + } +} diff --git a/tests/Message/DirectPostRefundRequestTest.php b/tests/Message/DirectPostRefundRequestTest.php new file mode 100644 index 0000000..02138b9 --- /dev/null +++ b/tests/Message/DirectPostRefundRequestTest.php @@ -0,0 +1,51 @@ +request = new DirectPostRefundRequest($this->getHttpClient(), $this->getHttpRequest()); + + $this->request->initialize([ + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'amount' => '5.00', + 'transactionId' => 'REFUND-ORDER-100', + 'transactionReference' => 'NAB-TXN-100', + ]); + } + + public function testGetDataBuildsRefundPayload() + { + $data = $this->request->getData(); + + $this->assertSame('REFUND', $data['EPS_TXNTYPE']); + $this->assertSame('NAB-TXN-100', $data['EPS_ORIGINALTXNID']); + $this->assertArrayHasKey('EPS_FINGERPRINT', $data); + } + + public function testSendParsesJsonResponse() + { + $request = $this->request; + + $request->setTransport(new class implements TransportInterface { + public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) + { + return new TransportResponse(200, '{"rescode":"00","restext":"Refunded","txnid":"REF-1"}'); + } + }); + + $response = $request->send(); + + $this->assertTrue($response->isSuccessful()); + $this->assertSame('00', $response->getCode()); + $this->assertSame('Refunded', $response->getMessage()); + $this->assertSame('REF-1', $response->getTransactionReference()); + } +} diff --git a/tests/Message/DirectPostReversalRequestTest.php b/tests/Message/DirectPostReversalRequestTest.php new file mode 100644 index 0000000..7d62171 --- /dev/null +++ b/tests/Message/DirectPostReversalRequestTest.php @@ -0,0 +1,51 @@ +request = new DirectPostReversalRequest($this->getHttpClient(), $this->getHttpRequest()); + + $this->request->initialize([ + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'amount' => '12.00', + 'transactionId' => 'VOID-ORDER-100', + 'transactionReference' => 'NAB-TXN-VOID-100', + ]); + } + + public function testGetDataBuildsReversalPayload() + { + $data = $this->request->getData(); + + $this->assertSame('REVERSAL', $data['EPS_TXNTYPE']); + $this->assertSame('NAB-TXN-VOID-100', $data['EPS_ORIGINALTXNID']); + $this->assertArrayHasKey('EPS_FINGERPRINT', $data); + } + + public function testSendParsesXmlResponse() + { + $request = $this->request; + + $request->setTransport(new class implements TransportInterface { + public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) + { + return new TransportResponse(200, '00ReversedVOID-1'); + } + }); + + $response = $request->send(); + + $this->assertTrue($response->isSuccessful()); + $this->assertSame('00', $response->getCode()); + $this->assertSame('Reversed', $response->getMessage()); + $this->assertSame('VOID-1', $response->getTransactionReference()); + } +} diff --git a/tests/Message/DirectPostStoreRequestTest.php b/tests/Message/DirectPostStoreRequestTest.php new file mode 100644 index 0000000..1ef4845 --- /dev/null +++ b/tests/Message/DirectPostStoreRequestTest.php @@ -0,0 +1,35 @@ +request = new DirectPostStoreRequest($this->getHttpClient(), $this->getHttpRequest()); + + $this->request->initialize([ + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'returnUrl' => 'https://www.example.com/return', + 'transactionId' => 'STORE-ORDER-100', + 'card' => [ + 'number' => '4444333322221111', + 'expiryMonth' => '12', + 'expiryYear' => '2030', + 'cvv' => '123', + ], + ]); + } + + public function testStoreDefaultsAmountAndUsesStoreTxnType() + { + $data = $this->request->getData(); + + $this->assertSame('8', $data['EPS_TXNTYPE']); + $this->assertSame('0.00', $data['EPS_AMOUNT']); + $this->assertArrayHasKey('EPS_FINGERPRINT', $data); + } +} diff --git a/tests/Message/DirectPostTransactionTypeRequestTest.php b/tests/Message/DirectPostTransactionTypeRequestTest.php new file mode 100644 index 0000000..7092414 --- /dev/null +++ b/tests/Message/DirectPostTransactionTypeRequestTest.php @@ -0,0 +1,87 @@ +purchaseRequest = new DirectPostPurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); + $this->purchaseRequest->initialize([ + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'amount' => '12.00', + 'returnUrl' => 'https://www.example.com/return', + 'transactionId' => 'ORDER-100', + 'transactionReference' => 'ORDER-REF-100', + 'card' => [ + 'firstName' => 'Example', + 'lastName' => 'User', + 'number' => '4444333322221111', + 'expiryMonth' => '12', + 'expiryYear' => '2030', + 'cvv' => '123', + ], + ]); + + $this->authorizeRequest = new DirectPostAuthorizeRequest($this->getHttpClient(), $this->getHttpRequest()); + $this->authorizeRequest->initialize([ + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'amount' => '12.00', + 'returnUrl' => 'https://www.example.com/return', + 'transactionId' => 'ORDER-101', + 'transactionReference' => 'ORDER-REF-101', + 'card' => [ + 'firstName' => 'Example', + 'lastName' => 'User', + 'number' => '4444333322221111', + 'expiryMonth' => '12', + 'expiryYear' => '2030', + 'cvv' => '123', + ], + ]); + } + + public function testRiskManagementPurchaseTransactionType() + { + $this->purchaseRequest->setHasRiskManagementEnabled(true); + + $data = $this->purchaseRequest->getData(); + + $this->assertSame('2', $data['EPS_TXNTYPE']); + } + + public function testRiskManagementAuthorizeTransactionType() + { + $this->authorizeRequest->setHasRiskManagementEnabled(true); + + $data = $this->authorizeRequest->getData(); + + $this->assertSame('3', $data['EPS_TXNTYPE']); + } + + public function testRiskManagementWithEmv3dsPurchaseTransactionType() + { + $this->purchaseRequest->setHasRiskManagementEnabled(true); + $this->purchaseRequest->setHasEMV3DSEnabled(true); + + $data = $this->purchaseRequest->getData(); + + $this->assertSame('6', $data['EPS_TXNTYPE']); + $this->assertArrayHasKey('EPS_ORDERID', $data); + } + + public function testRiskManagementWithEmv3dsAuthorizeTransactionType() + { + $this->authorizeRequest->setHasRiskManagementEnabled(true); + $this->authorizeRequest->setHasEMV3DSEnabled(true); + + $data = $this->authorizeRequest->getData(); + + $this->assertSame('7', $data['EPS_TXNTYPE']); + $this->assertArrayHasKey('EPS_ORDERID', $data); + } +} diff --git a/tests/Message/DirectPostWebhookRequestTest.php b/tests/Message/DirectPostWebhookRequestTest.php new file mode 100644 index 0000000..4d36f71 --- /dev/null +++ b/tests/Message/DirectPostWebhookRequestTest.php @@ -0,0 +1,53 @@ + 'XYZ0010', + 'txn_password' => 'abcd1234', + 'refid' => 'ORDER-123', + 'amount' => '10.00', + 'timestamp' => '20260222000000', + 'summarycode' => '1', + 'restext' => 'Approved', + 'rescode' => '00', + 'txnid' => '10001', + ]; + + $request = new DirectPostWebhookRequest($payload); + $fingerprint = $request->generateResponseFingerprint($payload); + + $responseFromCorrectMethod = $request->verifyFingerPrint($fingerprint); + $responseFromTypoAlias = $request->vefiyFingerPrint($fingerprint); + + $this->assertTrue($responseFromCorrectMethod->isSuccessful()); + $this->assertTrue($responseFromTypoAlias->isSuccessful()); + } + + public function testInvalidFingerprintMarksFailure() + { + $payload = [ + 'merchant' => 'XYZ0010', + 'txn_password' => 'abcd1234', + 'refid' => 'ORDER-123', + 'amount' => '10.00', + 'timestamp' => '20260222000000', + 'summarycode' => '1', + 'restext' => 'Approved', + 'rescode' => '00', + 'txnid' => '10001', + ]; + + $request = new DirectPostWebhookRequest($payload); + $response = $request->sendData(['fingerprint' => 'invalid']); + + $this->assertFalse($response->isSuccessful()); + $this->assertStringContainsString('Invalid fingerprint', (string) $response->getMessage()); + } +} diff --git a/tests/Message/EMV3DSOrderRequestTest.php b/tests/Message/EMV3DSOrderRequestTest.php new file mode 100644 index 0000000..e2d9a58 --- /dev/null +++ b/tests/Message/EMV3DSOrderRequestTest.php @@ -0,0 +1,95 @@ +request = new EMV3DSOrderRequest($this->getHttpClient(), $this->getHttpRequest()); + + $this->request->initialize([ + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'amount' => '12.00', + 'currency' => 'AUD', + 'clientIp' => '1.2.3.4', + 'transactionReference' => 'EMV-ORDER-100', + ]); + } + + public function testGetDataBuildsDefaultPayload() + { + $data = $this->request->getData(); + + $this->assertSame(1200, $data['amount']); + $this->assertSame('AUD', $data['currency']); + $this->assertSame('1.2.3.4', $data['ip']); + $this->assertSame('XYZ0010', $data['merchantId']); + $this->assertSame('EMV-ORDER-100', $data['merchantOrderReference']); + $this->assertSame('PAYMENT', $data['orderType']); + $this->assertSame(['THREED_SECURE'], $data['intents']); + } + + public function testSendUsesTransportAndParsesResponse() + { + $capture = (object) ['method' => null, 'url' => null, 'headers' => [], 'body' => null, 'timeout' => null]; + + $request = $this->request; + $request->setTimeoutSeconds(15); + $request->setTransport(new class($capture) implements TransportInterface { + private $capture; + + public function __construct($capture) + { + $this->capture = $capture; + } + + public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) + { + $this->capture->method = $method; + $this->capture->url = $url; + $this->capture->headers = $headers; + $this->capture->body = $body; + $this->capture->timeout = $timeoutSeconds; + + return new TransportResponse(201, '{"orderId":"ORDER-1","simpleToken":"S-1","orderToken":"O-1","threedSecure":{"providerClientId":"P-1","sessionId":"SESS-1"}}'); + } + }); + + $response = $request->send(); + + $this->assertSame('POST', $capture->method); + $this->assertSame('https://transact.nab.com.au/services/order-management/v2/payments/orders', $capture->url); + $this->assertArrayHasKey('Authorization', $capture->headers); + $this->assertStringContainsString('"merchantOrderReference":"EMV-ORDER-100"', $capture->body); + $this->assertSame(15, $capture->timeout); + + $this->assertTrue($response->isSuccessful()); + $this->assertSame('ORDER-1', $response->getOrderId()); + $this->assertSame('S-1', $response->getSimpleToken()); + $this->assertSame('O-1', $response->getOrderToken()); + $this->assertSame('P-1', $response->getProviderClientId()); + $this->assertSame('SESS-1', $response->getSessionId()); + } + + public function testSendHandlesInvalidJsonResponse() + { + $request = $this->request; + $request->setTransport(new class implements TransportInterface { + public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) + { + return new TransportResponse(500, 'Internal Error'); + } + }); + + $response = $request->send(); + + $this->assertFalse($response->isSuccessful()); + $this->assertSame('Internal Error', $response->getRawResponse()); + } +} diff --git a/tests/Message/HostedPaymentCompletePurchaseRequestTest.php b/tests/Message/HostedPaymentCompletePurchaseRequestTest.php new file mode 100644 index 0000000..b60019d --- /dev/null +++ b/tests/Message/HostedPaymentCompletePurchaseRequestTest.php @@ -0,0 +1,45 @@ +request = new HostedPaymentCompletePurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); + } + + public function testReadDataFromQuery() + { + $this->getHttpRequest()->query->replace([ + 'summarycode' => '1', + 'rescode' => '00', + 'restext' => 'Approved', + 'txnid' => 'TXN-100', + ]); + + $response = $this->request->send(); + + $this->assertInstanceOf('\Omnipay\NABTransact\Message\HostedPaymentCompletePurchaseResponse', $response); + $this->assertTrue($response->isSuccessful()); + $this->assertSame('00', $response->getCode()); + $this->assertSame('TXN-100', $response->getTransactionReference()); + } + + public function testReadDataFromRequestBodyWhenQueryMissing() + { + $this->getHttpRequest()->request->replace([ + 'summarycode' => '3', + 'rescode' => '06', + 'restext' => 'Declined', + 'txnid' => 'TXN-101', + ]); + + $response = $this->request->send(); + + $this->assertFalse($response->isSuccessful()); + $this->assertSame('Declined', $response->getMessage()); + } +} diff --git a/tests/Message/HostedPaymentPurchaseRequestTest.php b/tests/Message/HostedPaymentPurchaseRequestTest.php new file mode 100644 index 0000000..0cfe71b --- /dev/null +++ b/tests/Message/HostedPaymentPurchaseRequestTest.php @@ -0,0 +1,38 @@ +request = new HostedPaymentPurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); + + $this->request->initialize([ + 'merchantId' => 'XYZ0010', + 'paymentAlertEmail' => 'merchant@example.com', + 'amount' => '10.00', + 'currency' => 'AUD', + 'transactionId' => 'ORDER-123', + 'returnUrl' => 'https://example.com/return', + 'notifyUrl' => 'https://example.com/notify', + 'returnUrlText' => 'Return', + ]); + } + + public function testUsesTestEndpointInTestMode() + { + $this->request->setTestMode(true); + + $this->assertSame('https://transact.nab.com.au/test/hpp/payment', $this->request->getEndpoint()); + } + + public function testUsesLiveEndpointInLiveMode() + { + $this->request->setTestMode(false); + + $this->assertSame('https://transact.nab.com.au/live/hpp/payment', $this->request->getEndpoint()); + } +} diff --git a/tests/Message/SecureXMLRiskPurchaseRequestTest.php b/tests/Message/SecureXMLRiskPurchaseRequestTest.php new file mode 100644 index 0000000..530d821 --- /dev/null +++ b/tests/Message/SecureXMLRiskPurchaseRequestTest.php @@ -0,0 +1,44 @@ +request = new SecureXMLRiskPurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); + + $this->request->initialize([ + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'testMode' => true, + 'amount' => '12.00', + 'transactionId' => '1234', + 'ip' => '1.1.1.1', + 'card' => [ + 'firstName' => 'Example', + 'lastName' => 'User', + 'number' => '4444333322221111', + 'expiryMonth' => '12', + 'expiryYear' => '2030', + 'cvv' => '123', + 'email' => 'example@example.com', + 'billingPostcode' => '12345', + 'billingCity' => 'Billstown', + 'billingCountry' => 'US', + ], + ]); + } + + public function testBuildsBuyerInfoWithLastNameField() + { + $xml = (string) $this->request->getData()->asXML(); + + $this->assertStringContainsString('Example', $xml); + $this->assertStringContainsString('User', $xml); + $this->assertStringNotContainsString('User', $xml); + $this->assertStringContainsString('1.1.1.1', $xml); + } +} diff --git a/tests/Message/SecureXMLTransportRequestTest.php b/tests/Message/SecureXMLTransportRequestTest.php new file mode 100644 index 0000000..906564c --- /dev/null +++ b/tests/Message/SecureXMLTransportRequestTest.php @@ -0,0 +1,80 @@ +getHttpClient(), $this->getHttpRequest()); + + $request->initialize([ + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'testMode' => true, + 'amount' => '12.00', + 'transactionId' => '1234', + 'card' => [ + 'number' => '4444333322221111', + 'expiryMonth' => '12', + 'expiryYear' => '2030', + 'cvv' => '123', + 'cardHolderName' => 'Sujip Thapa', + ], + ]); + + $capture = (object) ['called' => false, 'method' => null, 'url' => null]; + + $transport = new class($capture) implements TransportInterface { + private $capture; + + public function __construct($capture) + { + $this->capture = $capture; + } + + public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) + { + $this->capture->called = true; + $this->capture->method = $method; + $this->capture->url = $url; + + return new TransportResponse(200, << + 20260222000000000+0000 + 000Normal + Payment + + + + Yes + 00 + Approved + 1234 + 1000 + 1200 + AUD + 23 + + + + +XML + ); + } + }; + + $request->setTransport($transport); + + $response = $request->send(); + + $this->assertTrue($capture->called); + $this->assertSame('POST', $capture->method); + $this->assertTrue($response->isSuccessful()); + $this->assertSame('00', $response->getCode()); + } +} diff --git a/tests/SecureXMLGatewayTest.php b/tests/SecureXMLGatewayTest.php index 17a4bab..c49cab2 100644 --- a/tests/SecureXMLGatewayTest.php +++ b/tests/SecureXMLGatewayTest.php @@ -2,6 +2,8 @@ namespace Omnipay\NABTransact; +use Omnipay\NABTransact\Transport\TransportInterface; +use Omnipay\NABTransact\Transport\TransportResponse; use Omnipay\Tests\GatewayTestCase; class SecureXMLGatewayTest extends GatewayTestCase @@ -56,7 +58,7 @@ public function testPurchaseRiskManaged() $this->assertInstanceOf('\Omnipay\NABTransact\Message\SecureXMLRiskPurchaseRequest', $request); $this->assertSame('25.00', $request->getAmount()); $this->assertContains( - '1.1.1.1ExampleUser12345BillstownUS', + '1.1.1.1ExampleUser12345BillstownUS', (string) $request->getData()->asXml() ); } @@ -69,4 +71,23 @@ public function testRefund() $this->assertSame('10.00', $request->getAmount()); $this->assertSame('Order-YKHU67', $request->getTransactionId()); } + + public function testTransportAndTimeoutArePassedToRequest() + { + $transport = new class implements TransportInterface { + public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) + { + return new TransportResponse(200, '000NormalEcho'); + } + }; + + $gateway = clone $this->gateway; + $gateway->setTransport($transport); + $gateway->setTimeoutSeconds(25); + + $request = $gateway->echoTest(); + + $this->assertSame($transport, $request->getTransport()); + $this->assertSame(25, $request->getTimeoutSeconds()); + } } From 636f755f44471628c9b11bcaa842c11e74cc5118 Mon Sep 17 00:00:00 2001 From: Sujip Thapa Date: Sun, 22 Feb 2026 14:07:15 +0545 Subject: [PATCH 4/9] test: modernize legacy tests and remove dead setup code --- tests/DirectPostGatewayTest.php | 22 ++++++------- tests/HostedPaymentGatewayTest.php | 6 ++-- .../DirectPostAuthorizeRequestTest.php | 5 +-- .../Message/DirectPostCaptureRequestTest.php | 5 +-- .../DirectPostCompletePurchaseRequestTest.php | 7 ++-- .../Message/DirectPostPurchaseRequestTest.php | 5 +-- tests/Message/DirectPostRefundRequestTest.php | 3 +- .../Message/DirectPostReversalRequestTest.php | 3 +- tests/Message/DirectPostStoreRequestTest.php | 3 +- .../DirectPostTransactionTypeRequestTest.php | 3 +- tests/Message/EMV3DSOrderRequestTest.php | 3 +- ...stedPaymentCompletePurchaseRequestTest.php | 5 +-- .../HostedPaymentPurchaseRequestTest.php | 3 +- .../Message/SecureXMLAuthorizeRequestTest.php | 13 ++++---- .../Message/SecureXMLEchoTestRequestTest.php | 6 ++-- .../Message/SecureXMLPurchaseRequestTest.php | 5 +-- .../SecureXMLRiskPurchaseRequestTest.php | 3 +- .../UnionPayCompletePurchaseRequestTest.php | 33 ++++++++----------- tests/Message/UnionPayPurchaseRequestTest.php | 5 +-- tests/SecureXMLGatewayTest.php | 16 ++++----- 20 files changed, 82 insertions(+), 72 deletions(-) diff --git a/tests/DirectPostGatewayTest.php b/tests/DirectPostGatewayTest.php index 89847ee..cf131a5 100644 --- a/tests/DirectPostGatewayTest.php +++ b/tests/DirectPostGatewayTest.php @@ -8,7 +8,7 @@ class DirectPostGatewayTest extends GatewayTestCase { - public function setUp() + protected function setUp(): void { parent::setUp(); @@ -20,7 +20,7 @@ public function testAuthorize() { $request = $this->gateway->authorize(['amount' => '10.00']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostAuthorizeRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostAuthorizeRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); } @@ -28,7 +28,7 @@ public function testCompleteAuthorize() { $request = $this->gateway->completeAuthorize(['amount' => '10.00']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostCompletePurchaseRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostCompletePurchaseRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); } @@ -36,7 +36,7 @@ public function testPurchase() { $request = $this->gateway->purchase(['amount' => '10.00']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostPurchaseRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostPurchaseRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); } @@ -44,7 +44,7 @@ public function testCompletePurchase() { $request = $this->gateway->completePurchase(['amount' => '10.00']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostCompletePurchaseRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostCompletePurchaseRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); } @@ -60,14 +60,14 @@ public function testWebhookFactory() { $request = $this->gateway->webhook(['merchant' => 'XYZ0010']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostWebhookRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostWebhookRequest::class, $request); } public function testStore() { $request = $this->gateway->store(['amount' => '0.00', 'returnUrl' => 'https://example.com/return']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostStoreRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostStoreRequest::class, $request); $this->assertSame('0.00', $request->getAmount()); } @@ -75,7 +75,7 @@ public function testCapture() { $request = $this->gateway->capture(['amount' => '10.00']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostCaptureRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostCaptureRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); } @@ -83,7 +83,7 @@ public function testRefund() { $request = $this->gateway->refund(['amount' => '10.00']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostRefundRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostRefundRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); } @@ -91,7 +91,7 @@ public function testVoid() { $request = $this->gateway->void(['amount' => '10.00']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostReversalRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostReversalRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); } @@ -99,7 +99,7 @@ public function testCreateEmv3dsOrder() { $request = $this->gateway->createEMV3DSOrder(['amount' => '10.00']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\EMV3DSOrderRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\EMV3DSOrderRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); } diff --git a/tests/HostedPaymentGatewayTest.php b/tests/HostedPaymentGatewayTest.php index 45313cf..ab22efe 100644 --- a/tests/HostedPaymentGatewayTest.php +++ b/tests/HostedPaymentGatewayTest.php @@ -6,7 +6,7 @@ class HostedPaymentGatewayTest extends GatewayTestCase { - public function setUp() + protected function setUp(): void { parent::setUp(); @@ -18,7 +18,7 @@ public function testPurchase() { $request = $this->gateway->purchase(['amount' => '10.00', 'transactionId' => 'ORDER-100']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\HostedPaymentPurchaseRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\HostedPaymentPurchaseRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); } @@ -26,6 +26,6 @@ public function testCompletePurchase() { $request = $this->gateway->completePurchase(); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\HostedPaymentCompletePurchaseRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\HostedPaymentCompletePurchaseRequest::class, $request); } } diff --git a/tests/Message/DirectPostAuthorizeRequestTest.php b/tests/Message/DirectPostAuthorizeRequestTest.php index c8f2670..53c65fe 100644 --- a/tests/Message/DirectPostAuthorizeRequestTest.php +++ b/tests/Message/DirectPostAuthorizeRequestTest.php @@ -6,8 +6,9 @@ class DirectPostAuthorizeRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new DirectPostAuthorizeRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ @@ -36,7 +37,7 @@ public function testSend() { $response = $this->request->send(); - $this->assertInstanceOf('Omnipay\NABTransact\Message\DirectPostAuthorizeResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\DirectPostAuthorizeResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertTrue($response->isRedirect()); $this->assertNull($response->getTransactionReference()); diff --git a/tests/Message/DirectPostCaptureRequestTest.php b/tests/Message/DirectPostCaptureRequestTest.php index 4937e5e..e466e79 100644 --- a/tests/Message/DirectPostCaptureRequestTest.php +++ b/tests/Message/DirectPostCaptureRequestTest.php @@ -8,8 +8,9 @@ class DirectPostCaptureRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new DirectPostCaptureRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ @@ -46,7 +47,7 @@ public function send($method, $url, array $headers = [], $body = '', $timeoutSec $response = $request->send(); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\DirectPostApiResponse', $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostApiResponse::class, $response); $this->assertTrue($response->isSuccessful()); $this->assertSame('00', $response->getCode()); $this->assertSame('Approved', $response->getMessage()); diff --git a/tests/Message/DirectPostCompletePurchaseRequestTest.php b/tests/Message/DirectPostCompletePurchaseRequestTest.php index 2601563..308a803 100644 --- a/tests/Message/DirectPostCompletePurchaseRequestTest.php +++ b/tests/Message/DirectPostCompletePurchaseRequestTest.php @@ -6,8 +6,9 @@ class DirectPostCompletePurchaseRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new DirectPostCompletePurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); } @@ -56,7 +57,7 @@ public function testSuccess() $response = $this->request->send(); - $this->assertInstanceOf('Omnipay\NABTransact\Message\DirectPostCompletePurchaseResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\DirectPostCompletePurchaseResponse::class, $response); $this->assertTrue($response->isSuccessful()); $this->assertFalse($response->isRedirect()); @@ -89,7 +90,7 @@ public function testFailure() $response = $this->request->send(); - $this->assertInstanceOf('Omnipay\NABTransact\Message\DirectPostCompletePurchaseResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\DirectPostCompletePurchaseResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertFalse($response->isRedirect()); diff --git a/tests/Message/DirectPostPurchaseRequestTest.php b/tests/Message/DirectPostPurchaseRequestTest.php index ecf0eaf..7a33ee8 100644 --- a/tests/Message/DirectPostPurchaseRequestTest.php +++ b/tests/Message/DirectPostPurchaseRequestTest.php @@ -6,8 +6,9 @@ class DirectPostPurchaseRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new DirectPostPurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ @@ -36,7 +37,7 @@ public function testSend() { $response = $this->request->send(); - $this->assertInstanceOf('Omnipay\NABTransact\Message\DirectPostAuthorizeResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\DirectPostAuthorizeResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertTrue($response->isRedirect()); $this->assertNull($response->getTransactionReference()); diff --git a/tests/Message/DirectPostRefundRequestTest.php b/tests/Message/DirectPostRefundRequestTest.php index 02138b9..791b0b7 100644 --- a/tests/Message/DirectPostRefundRequestTest.php +++ b/tests/Message/DirectPostRefundRequestTest.php @@ -8,8 +8,9 @@ class DirectPostRefundRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new DirectPostRefundRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ diff --git a/tests/Message/DirectPostReversalRequestTest.php b/tests/Message/DirectPostReversalRequestTest.php index 7d62171..90187ee 100644 --- a/tests/Message/DirectPostReversalRequestTest.php +++ b/tests/Message/DirectPostReversalRequestTest.php @@ -8,8 +8,9 @@ class DirectPostReversalRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new DirectPostReversalRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ diff --git a/tests/Message/DirectPostStoreRequestTest.php b/tests/Message/DirectPostStoreRequestTest.php index 1ef4845..8133668 100644 --- a/tests/Message/DirectPostStoreRequestTest.php +++ b/tests/Message/DirectPostStoreRequestTest.php @@ -6,8 +6,9 @@ class DirectPostStoreRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new DirectPostStoreRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ diff --git a/tests/Message/DirectPostTransactionTypeRequestTest.php b/tests/Message/DirectPostTransactionTypeRequestTest.php index 7092414..67112eb 100644 --- a/tests/Message/DirectPostTransactionTypeRequestTest.php +++ b/tests/Message/DirectPostTransactionTypeRequestTest.php @@ -6,8 +6,9 @@ class DirectPostTransactionTypeRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->purchaseRequest = new DirectPostPurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); $this->purchaseRequest->initialize([ 'merchantId' => 'XYZ0010', diff --git a/tests/Message/EMV3DSOrderRequestTest.php b/tests/Message/EMV3DSOrderRequestTest.php index e2d9a58..02383b1 100644 --- a/tests/Message/EMV3DSOrderRequestTest.php +++ b/tests/Message/EMV3DSOrderRequestTest.php @@ -8,8 +8,9 @@ class EMV3DSOrderRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new EMV3DSOrderRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ diff --git a/tests/Message/HostedPaymentCompletePurchaseRequestTest.php b/tests/Message/HostedPaymentCompletePurchaseRequestTest.php index b60019d..72fd863 100644 --- a/tests/Message/HostedPaymentCompletePurchaseRequestTest.php +++ b/tests/Message/HostedPaymentCompletePurchaseRequestTest.php @@ -6,8 +6,9 @@ class HostedPaymentCompletePurchaseRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new HostedPaymentCompletePurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); } @@ -22,7 +23,7 @@ public function testReadDataFromQuery() $response = $this->request->send(); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\HostedPaymentCompletePurchaseResponse', $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\HostedPaymentCompletePurchaseResponse::class, $response); $this->assertTrue($response->isSuccessful()); $this->assertSame('00', $response->getCode()); $this->assertSame('TXN-100', $response->getTransactionReference()); diff --git a/tests/Message/HostedPaymentPurchaseRequestTest.php b/tests/Message/HostedPaymentPurchaseRequestTest.php index 0cfe71b..7b96471 100644 --- a/tests/Message/HostedPaymentPurchaseRequestTest.php +++ b/tests/Message/HostedPaymentPurchaseRequestTest.php @@ -6,8 +6,9 @@ class HostedPaymentPurchaseRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new HostedPaymentPurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ diff --git a/tests/Message/SecureXMLAuthorizeRequestTest.php b/tests/Message/SecureXMLAuthorizeRequestTest.php index 6480259..f1c19ba 100644 --- a/tests/Message/SecureXMLAuthorizeRequestTest.php +++ b/tests/Message/SecureXMLAuthorizeRequestTest.php @@ -6,8 +6,9 @@ class SecureXMLAuthorizeRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new SecureXMLAuthorizeRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ @@ -34,7 +35,7 @@ public function testSendSuccess() $data = $response->getData(); - $this->assertInstanceOf('Omnipay\NABTransact\Message\SecureXMLResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertTrue($response->isSuccessful()); $this->assertFalse($response->isRedirect()); @@ -53,7 +54,7 @@ public function testSendFailure() $this->setMockHttpResponse('SecureXMLAuthorizeRequestFail.txt'); $response = $this->request->send(); - $this->assertInstanceOf('Omnipay\NABTransact\Message\SecureXMLResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertFalse($response->isRedirect()); @@ -67,7 +68,7 @@ public function testInsufficientFundsFailure() $this->setMockHttpResponse('SecureXMLAuthorizeRequestInsufficientFundsFail.txt'); $response = $this->request->send(); - $this->assertInstanceOf('Omnipay\NABTransact\Message\SecureXMLResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertFalse($response->isRedirect()); @@ -81,7 +82,7 @@ public function testInvalidMerchantFailure() $this->setMockHttpResponse('SecureXMLAuthorizeRequestInvalidMerchantFail.txt'); $response = $this->request->send(); - $this->assertInstanceOf('Omnipay\NABTransact\Message\SecureXMLResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertFalse($response->isRedirect()); @@ -95,7 +96,7 @@ public function testInvalidMerchantIDFailure() $this->setMockHttpResponse('SecureXMLAuthorizeRequestInvalidMerchantIDFail.txt'); $response = $this->request->send(); - $this->assertInstanceOf('Omnipay\NABTransact\Message\SecureXMLResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertFalse($response->isRedirect()); diff --git a/tests/Message/SecureXMLEchoTestRequestTest.php b/tests/Message/SecureXMLEchoTestRequestTest.php index ac61b7d..b03d02d 100644 --- a/tests/Message/SecureXMLEchoTestRequestTest.php +++ b/tests/Message/SecureXMLEchoTestRequestTest.php @@ -6,8 +6,9 @@ class SecureXMLEchoTestRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new SecureXMLEchoTestRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ @@ -22,9 +23,8 @@ public function testSuccess() $this->setMockHttpResponse('SecureXMLEchoTestRequestSuccess.txt'); $response = $this->request->send(); - $data = $response->getData(); - $this->assertInstanceOf('Omnipay\NABTransact\Message\SecureXMLResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertSame('Normal', $response->getMessage()); $this->assertFalse($response->isRedirect()); $this->assertSame('000', $response->getStatusCode()); diff --git a/tests/Message/SecureXMLPurchaseRequestTest.php b/tests/Message/SecureXMLPurchaseRequestTest.php index 6838f4b..aa13501 100644 --- a/tests/Message/SecureXMLPurchaseRequestTest.php +++ b/tests/Message/SecureXMLPurchaseRequestTest.php @@ -6,8 +6,9 @@ class SecureXMLPurchaseRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new SecureXMLPurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ @@ -33,7 +34,7 @@ public function testSendSuccess() $response = $this->request->send(); $data = $response->getData(); - $this->assertInstanceOf('Omnipay\NABTransact\Message\SecureXMLResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertTrue($response->isSuccessful()); $this->assertFalse($response->isRedirect()); diff --git a/tests/Message/SecureXMLRiskPurchaseRequestTest.php b/tests/Message/SecureXMLRiskPurchaseRequestTest.php index 530d821..0fe67af 100644 --- a/tests/Message/SecureXMLRiskPurchaseRequestTest.php +++ b/tests/Message/SecureXMLRiskPurchaseRequestTest.php @@ -6,8 +6,9 @@ class SecureXMLRiskPurchaseRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new SecureXMLRiskPurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ diff --git a/tests/Message/UnionPayCompletePurchaseRequestTest.php b/tests/Message/UnionPayCompletePurchaseRequestTest.php index 8c4a2ee..b2667f7 100644 --- a/tests/Message/UnionPayCompletePurchaseRequestTest.php +++ b/tests/Message/UnionPayCompletePurchaseRequestTest.php @@ -6,23 +6,18 @@ class UnionPayCompletePurchaseRequestTest extends TestCase { - public function setUp() - { - $this->request = new UnionPayCompletePurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); - } - public function testUnionPayCompletePurchaseSuccess() { - $data = []; - - $data['restext'] = 'Approved'; - $data['rescode'] = '00'; - $data['summarycode'] = '1'; - $data['txnid'] = '12345'; + $data = [ + 'restext' => 'Approved', + 'rescode' => '00', + 'summarycode' => '1', + 'txnid' => '12345', + ]; $response = new UnionPayCompletePurchaseResponse($this->getMockRequest(), $data); - $this->assertInstanceOf('Omnipay\NABTransact\Message\UnionPayCompletePurchaseResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\UnionPayCompletePurchaseResponse::class, $response); $this->assertTrue($response->isSuccessful()); $this->assertFalse($response->isRedirect()); $this->assertSame('12345', $response->getTransactionReference()); @@ -33,16 +28,16 @@ public function testUnionPayCompletePurchaseSuccess() public function testUnionPayCompletePurchaseFailure() { - $data = []; - - $data['restext'] = 'Error'; - $data['txnid'] = '12345'; - $data['summarycode'] = '3'; - $data['rescode'] = '06'; + $data = [ + 'restext' => 'Error', + 'txnid' => '12345', + 'summarycode' => '3', + 'rescode' => '06', + ]; $response = new UnionPayCompletePurchaseResponse($this->getMockRequest(), $data); - $this->assertInstanceOf('Omnipay\NABTransact\Message\UnionPayCompletePurchaseResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\UnionPayCompletePurchaseResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertFalse($response->isRedirect()); $this->assertSame('12345', $response->getTransactionReference()); diff --git a/tests/Message/UnionPayPurchaseRequestTest.php b/tests/Message/UnionPayPurchaseRequestTest.php index 0885661..4f44b1b 100644 --- a/tests/Message/UnionPayPurchaseRequestTest.php +++ b/tests/Message/UnionPayPurchaseRequestTest.php @@ -6,8 +6,9 @@ class UnionPayPurchaseRequestTest extends TestCase { - public function setUp() + protected function setUp(): void { + parent::setUp(); $this->request = new UnionPayPurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ @@ -31,7 +32,7 @@ public function testPurchase() { $response = $this->request->send(); - $this->assertInstanceOf('Omnipay\NABTransact\Message\UnionPayPurchaseResponse', $response); + $this->assertInstanceOf(Omnipay\NABTransact\Message\UnionPayPurchaseResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertTrue($response->isRedirect()); diff --git a/tests/SecureXMLGatewayTest.php b/tests/SecureXMLGatewayTest.php index c49cab2..9692e2f 100644 --- a/tests/SecureXMLGatewayTest.php +++ b/tests/SecureXMLGatewayTest.php @@ -8,7 +8,7 @@ class SecureXMLGatewayTest extends GatewayTestCase { - public function setUp() + protected function setUp(): void { parent::setUp(); @@ -19,7 +19,7 @@ public function setUp() public function testEcho() { $request = $this->gateway->echoTest(['amount' => '10.00', 'transactionId' => 'Order-YKHU67']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\SecureXMLEchoTestRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\SecureXMLEchoTestRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); $this->assertSame('Order-YKHU67', $request->getTransactionId()); } @@ -28,7 +28,7 @@ public function testAuthorize() { $request = $this->gateway->authorize(['amount' => '10.00']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\SecureXMLAuthorizeRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\SecureXMLAuthorizeRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); } @@ -36,7 +36,7 @@ public function testCapture() { $request = $this->gateway->capture(['amount' => '10.00', 'transactionId' => 'Order-YKHU67']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\SecureXMLCaptureRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\SecureXMLCaptureRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); $this->assertSame('Order-YKHU67', $request->getTransactionId()); } @@ -45,7 +45,7 @@ public function testPurchase() { $request = $this->gateway->purchase(['amount' => '10.00']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\SecureXMLPurchaseRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\SecureXMLPurchaseRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); } @@ -55,9 +55,9 @@ public function testPurchaseRiskManaged() $gateway->setRiskManagement(true); $request = $gateway->purchase(['card' => $this->getValidCard(), 'transactionId' => 'Test1234', 'ip' => '1.1.1.1', 'amount' => '25.00']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\SecureXMLRiskPurchaseRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\SecureXMLRiskPurchaseRequest::class, $request); $this->assertSame('25.00', $request->getAmount()); - $this->assertContains( + $this->assertStringContainsString( '1.1.1.1ExampleUser12345BillstownUS', (string) $request->getData()->asXml() ); @@ -67,7 +67,7 @@ public function testRefund() { $request = $this->gateway->refund(['amount' => '10.00', 'transactionId' => 'Order-YKHU67']); - $this->assertInstanceOf('\Omnipay\NABTransact\Message\SecureXMLRefundRequest', $request); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\SecureXMLRefundRequest::class, $request); $this->assertSame('10.00', $request->getAmount()); $this->assertSame('Order-YKHU67', $request->getTransactionId()); } From c1c9e36622a26eecb6dea416379708a5a9f53d99 Mon Sep 17 00:00:00 2001 From: Sujip Thapa Date: Sun, 22 Feb 2026 14:34:47 +0545 Subject: [PATCH 5/9] test: decouple suite from omnipay-tests with local mock harness --- README.md | 1 + composer.json | 6 +- docs/features-implemented-tree.md | 76 +++++++++ tests/DirectPostGatewayTest.php | 2 +- tests/HostedPaymentGatewayTest.php | 2 +- tests/Message/DirectPostApiResponseTest.php | 2 +- .../DirectPostAuthorizeRequestTest.php | 2 +- .../Message/DirectPostCaptureRequestTest.php | 2 +- .../DirectPostCompletePurchaseRequestTest.php | 2 +- .../Message/DirectPostPurchaseRequestTest.php | 2 +- tests/Message/DirectPostRefundRequestTest.php | 2 +- .../Message/DirectPostReversalRequestTest.php | 2 +- tests/Message/DirectPostStoreRequestTest.php | 2 +- .../DirectPostTransactionTypeRequestTest.php | 2 +- .../Message/DirectPostWebhookRequestTest.php | 2 +- tests/Message/EMV3DSOrderRequestTest.php | 2 +- ...stedPaymentCompletePurchaseRequestTest.php | 2 +- .../HostedPaymentPurchaseRequestTest.php | 2 +- .../Message/SecureXMLAuthorizeRequestTest.php | 12 +- .../Message/SecureXMLEchoTestRequestTest.php | 4 +- .../Message/SecureXMLPurchaseRequestTest.php | 4 +- .../SecureXMLRiskPurchaseRequestTest.php | 2 +- .../Message/SecureXMLTransportRequestTest.php | 2 +- .../UnionPayCompletePurchaseRequestTest.php | 2 +- tests/Message/UnionPayPurchaseRequestTest.php | 2 +- .../Message/UnionPayPurchaseResponseTest.php | 2 +- tests/SecureXMLGatewayTest.php | 2 +- tests/Support/GatewayTestCase.php | 9 + tests/Support/MockHttpClient.php | 58 +++++++ tests/Support/MockHttpResponse.php | 159 ++++++++++++++++++ tests/Support/MockStream.php | 141 ++++++++++++++++ tests/Support/TestCase.php | 79 +++++++++ 32 files changed, 559 insertions(+), 32 deletions(-) create mode 100644 docs/features-implemented-tree.md create mode 100644 tests/Support/GatewayTestCase.php create mode 100644 tests/Support/MockHttpClient.php create mode 100644 tests/Support/MockHttpResponse.php create mode 100644 tests/Support/MockStream.php create mode 100644 tests/Support/TestCase.php diff --git a/README.md b/README.md index 396e484..5aca3b3 100644 --- a/README.md +++ b/README.md @@ -305,6 +305,7 @@ See `ARCHITECTURE.md` for design details and extension points. ## NAB Feature Coverage Core payment features are mapped in `docs/feature-matrix.md` with request/response classes and test coverage. +For a contributor-friendly package map, see `docs/features-implemented-tree.md`. This package targets payment-processing API coverage and does not include NAB admin/reporting portal features. ## Contributing diff --git a/composer.json b/composer.json index 9278d04..baab2ab 100644 --- a/composer.json +++ b/composer.json @@ -23,12 +23,16 @@ "Omnipay\\NABTransact\\": "src/" } }, + "autoload-dev": { + "psr-4": { + "Omnipay\\NABTransact\\Tests\\Support\\": "tests/Support/" + } + }, "require": { "php": "^7.2 || ^8.0", "omnipay/common": "^3" }, "require-dev": { - "omnipay/tests": "^3", "phpstan/phpstan": "^1.12", "phpunit/phpunit": "^8.5 || ^9.6", "squizlabs/php_codesniffer": "^3" diff --git a/docs/features-implemented-tree.md b/docs/features-implemented-tree.md new file mode 100644 index 0000000..9600504 --- /dev/null +++ b/docs/features-implemented-tree.md @@ -0,0 +1,76 @@ +# NAB Transact Implemented Features Tree + +This tree is generated from the current package code (gateway methods + request/response classes). + +```text +omnipay-nabtransact +├── Gateways +│ ├── NABTransact_SecureXML (SecureXMLGateway) +│ │ ├── echoTest() -> SecureXMLEchoTestRequest +│ │ ├── authorize() -> SecureXMLAuthorizeRequest +│ │ ├── purchase() -> SecureXMLPurchaseRequest +│ │ ├── purchase() + riskManagement=true -> SecureXMLRiskPurchaseRequest +│ │ ├── capture() -> SecureXMLCaptureRequest +│ │ ├── refund() -> SecureXMLRefundRequest +│ │ ├── transport override (setTransport/getTransport) +│ │ └── timeout control (setTimeoutSeconds/getTimeoutSeconds) +│ │ +│ ├── NABTransact_DirectPost (DirectPostGateway) +│ │ ├── Redirect flows +│ │ │ ├── authorize() -> DirectPostAuthorizeRequest +│ │ │ ├── purchase() -> DirectPostPurchaseRequest +│ │ │ ├── completeAuthorize() -> DirectPostCompletePurchaseRequest +│ │ │ ├── completePurchase() -> DirectPostCompletePurchaseRequest +│ │ │ └── store() -> DirectPostStoreRequest +│ │ ├── Server-to-server operations +│ │ │ ├── capture() -> DirectPostCaptureRequest +│ │ │ ├── refund() -> DirectPostRefundRequest +│ │ │ └── void() -> DirectPostReversalRequest +│ │ ├── EMV 3DS order API +│ │ │ └── createEMV3DSOrder() -> EMV3DSOrderRequest +│ │ ├── Webhook/fingerprint helper +│ │ │ └── webhook() -> DirectPostWebhookRequest +│ │ ├── Security/txn behavior +│ │ │ ├── fingerprint generation + verification +│ │ │ ├── risk + EMV txnType resolution +│ │ │ └── legacy typo alias support: vefiyFingerPrint() +│ │ └── transport + timeout control for server-to-server calls +│ │ +│ ├── NABTransact_HostedPayment (HostedPaymentGateway) +│ │ ├── purchase() -> HostedPaymentPurchaseRequest +│ │ ├── completePurchase() -> HostedPaymentCompletePurchaseRequest +│ │ ├── payment alert configuration (paymentAlertEmail) +│ │ └── return link text configuration (returnUrlText) +│ │ +│ └── NABTransact_UnionPay (UnionPayGateway) +│ ├── purchase() -> UnionPayPurchaseRequest +│ └── completePurchase() -> UnionPayCompletePurchaseRequest +│ +├── Response Models +│ ├── SecureXMLResponse +│ ├── DirectPostAuthorizeResponse +│ ├── DirectPostCompletePurchaseResponse +│ ├── DirectPostApiResponse +│ ├── EMV3DSOrderResponse +│ ├── HostedPaymentPurchaseResponse +│ ├── HostedPaymentCompletePurchaseResponse +│ ├── UnionPayPurchaseResponse +│ └── UnionPayCompletePurchaseResponse +│ +├── Transport Layer +│ ├── TransportInterface (custom adapter contract) +│ ├── TransportResponse (status/body container) +│ ├── OmnipayHttpClientTransport (default compatibility path) +│ └── CurlTransport (framework-agnostic native cURL) +│ +└── Compatibility + ├── Existing Omnipay gateway names preserved + ├── Existing request/response classes preserved + ├── Additive APIs only (no removals in this modernization) + └── Legacy typo alias kept for webhook fingerprint method +``` + +## Related docs + +- Feature matrix: `docs/feature-matrix.md` +- Architecture: `ARCHITECTURE.md` diff --git a/tests/DirectPostGatewayTest.php b/tests/DirectPostGatewayTest.php index cf131a5..ce75e3e 100644 --- a/tests/DirectPostGatewayTest.php +++ b/tests/DirectPostGatewayTest.php @@ -4,7 +4,7 @@ use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\Tests\GatewayTestCase; +use Omnipay\NABTransact\Tests\Support\GatewayTestCase; class DirectPostGatewayTest extends GatewayTestCase { diff --git a/tests/HostedPaymentGatewayTest.php b/tests/HostedPaymentGatewayTest.php index ab22efe..9bdd242 100644 --- a/tests/HostedPaymentGatewayTest.php +++ b/tests/HostedPaymentGatewayTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact; -use Omnipay\Tests\GatewayTestCase; +use Omnipay\NABTransact\Tests\Support\GatewayTestCase; class HostedPaymentGatewayTest extends GatewayTestCase { diff --git a/tests/Message/DirectPostApiResponseTest.php b/tests/Message/DirectPostApiResponseTest.php index a92e86a..fdf5044 100644 --- a/tests/Message/DirectPostApiResponseTest.php +++ b/tests/Message/DirectPostApiResponseTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class DirectPostApiResponseTest extends TestCase { diff --git a/tests/Message/DirectPostAuthorizeRequestTest.php b/tests/Message/DirectPostAuthorizeRequestTest.php index 53c65fe..e2eb83d 100644 --- a/tests/Message/DirectPostAuthorizeRequestTest.php +++ b/tests/Message/DirectPostAuthorizeRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class DirectPostAuthorizeRequestTest extends TestCase { diff --git a/tests/Message/DirectPostCaptureRequestTest.php b/tests/Message/DirectPostCaptureRequestTest.php index e466e79..74c7484 100644 --- a/tests/Message/DirectPostCaptureRequestTest.php +++ b/tests/Message/DirectPostCaptureRequestTest.php @@ -4,7 +4,7 @@ use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class DirectPostCaptureRequestTest extends TestCase { diff --git a/tests/Message/DirectPostCompletePurchaseRequestTest.php b/tests/Message/DirectPostCompletePurchaseRequestTest.php index 308a803..0fb7a68 100644 --- a/tests/Message/DirectPostCompletePurchaseRequestTest.php +++ b/tests/Message/DirectPostCompletePurchaseRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class DirectPostCompletePurchaseRequestTest extends TestCase { diff --git a/tests/Message/DirectPostPurchaseRequestTest.php b/tests/Message/DirectPostPurchaseRequestTest.php index 7a33ee8..f4e62d4 100644 --- a/tests/Message/DirectPostPurchaseRequestTest.php +++ b/tests/Message/DirectPostPurchaseRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class DirectPostPurchaseRequestTest extends TestCase { diff --git a/tests/Message/DirectPostRefundRequestTest.php b/tests/Message/DirectPostRefundRequestTest.php index 791b0b7..ed7eb66 100644 --- a/tests/Message/DirectPostRefundRequestTest.php +++ b/tests/Message/DirectPostRefundRequestTest.php @@ -4,7 +4,7 @@ use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class DirectPostRefundRequestTest extends TestCase { diff --git a/tests/Message/DirectPostReversalRequestTest.php b/tests/Message/DirectPostReversalRequestTest.php index 90187ee..52a8c59 100644 --- a/tests/Message/DirectPostReversalRequestTest.php +++ b/tests/Message/DirectPostReversalRequestTest.php @@ -4,7 +4,7 @@ use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class DirectPostReversalRequestTest extends TestCase { diff --git a/tests/Message/DirectPostStoreRequestTest.php b/tests/Message/DirectPostStoreRequestTest.php index 8133668..9620057 100644 --- a/tests/Message/DirectPostStoreRequestTest.php +++ b/tests/Message/DirectPostStoreRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class DirectPostStoreRequestTest extends TestCase { diff --git a/tests/Message/DirectPostTransactionTypeRequestTest.php b/tests/Message/DirectPostTransactionTypeRequestTest.php index 67112eb..9cf21a1 100644 --- a/tests/Message/DirectPostTransactionTypeRequestTest.php +++ b/tests/Message/DirectPostTransactionTypeRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class DirectPostTransactionTypeRequestTest extends TestCase { diff --git a/tests/Message/DirectPostWebhookRequestTest.php b/tests/Message/DirectPostWebhookRequestTest.php index 4d36f71..9ca73f4 100644 --- a/tests/Message/DirectPostWebhookRequestTest.php +++ b/tests/Message/DirectPostWebhookRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class DirectPostWebhookRequestTest extends TestCase { diff --git a/tests/Message/EMV3DSOrderRequestTest.php b/tests/Message/EMV3DSOrderRequestTest.php index 02383b1..78b1ff0 100644 --- a/tests/Message/EMV3DSOrderRequestTest.php +++ b/tests/Message/EMV3DSOrderRequestTest.php @@ -4,7 +4,7 @@ use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class EMV3DSOrderRequestTest extends TestCase { diff --git a/tests/Message/HostedPaymentCompletePurchaseRequestTest.php b/tests/Message/HostedPaymentCompletePurchaseRequestTest.php index 72fd863..a85ac0c 100644 --- a/tests/Message/HostedPaymentCompletePurchaseRequestTest.php +++ b/tests/Message/HostedPaymentCompletePurchaseRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class HostedPaymentCompletePurchaseRequestTest extends TestCase { diff --git a/tests/Message/HostedPaymentPurchaseRequestTest.php b/tests/Message/HostedPaymentPurchaseRequestTest.php index 7b96471..0bf2365 100644 --- a/tests/Message/HostedPaymentPurchaseRequestTest.php +++ b/tests/Message/HostedPaymentPurchaseRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class HostedPaymentPurchaseRequestTest extends TestCase { diff --git a/tests/Message/SecureXMLAuthorizeRequestTest.php b/tests/Message/SecureXMLAuthorizeRequestTest.php index f1c19ba..a9794a2 100644 --- a/tests/Message/SecureXMLAuthorizeRequestTest.php +++ b/tests/Message/SecureXMLAuthorizeRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class SecureXMLAuthorizeRequestTest extends TestCase { @@ -29,7 +29,7 @@ protected function setUp(): void public function testSendSuccess() { - $this->setMockHttpResponse('SecureXMLAuthorizeRequestSuccess.txt'); + $this->queueFixtureResponse('SecureXMLAuthorizeRequestSuccess.txt'); $response = $this->request->send(); @@ -51,7 +51,7 @@ public function testSendSuccess() public function testSendFailure() { - $this->setMockHttpResponse('SecureXMLAuthorizeRequestFail.txt'); + $this->queueFixtureResponse('SecureXMLAuthorizeRequestFail.txt'); $response = $this->request->send(); $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); @@ -65,7 +65,7 @@ public function testSendFailure() public function testInsufficientFundsFailure() { - $this->setMockHttpResponse('SecureXMLAuthorizeRequestInsufficientFundsFail.txt'); + $this->queueFixtureResponse('SecureXMLAuthorizeRequestInsufficientFundsFail.txt'); $response = $this->request->send(); $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); @@ -79,7 +79,7 @@ public function testInsufficientFundsFailure() public function testInvalidMerchantFailure() { - $this->setMockHttpResponse('SecureXMLAuthorizeRequestInvalidMerchantFail.txt'); + $this->queueFixtureResponse('SecureXMLAuthorizeRequestInvalidMerchantFail.txt'); $response = $this->request->send(); $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); @@ -93,7 +93,7 @@ public function testInvalidMerchantFailure() public function testInvalidMerchantIDFailure() { - $this->setMockHttpResponse('SecureXMLAuthorizeRequestInvalidMerchantIDFail.txt'); + $this->queueFixtureResponse('SecureXMLAuthorizeRequestInvalidMerchantIDFail.txt'); $response = $this->request->send(); $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); diff --git a/tests/Message/SecureXMLEchoTestRequestTest.php b/tests/Message/SecureXMLEchoTestRequestTest.php index b03d02d..fc6e6f6 100644 --- a/tests/Message/SecureXMLEchoTestRequestTest.php +++ b/tests/Message/SecureXMLEchoTestRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class SecureXMLEchoTestRequestTest extends TestCase { @@ -20,7 +20,7 @@ protected function setUp(): void public function testSuccess() { - $this->setMockHttpResponse('SecureXMLEchoTestRequestSuccess.txt'); + $this->queueFixtureResponse('SecureXMLEchoTestRequestSuccess.txt'); $response = $this->request->send(); diff --git a/tests/Message/SecureXMLPurchaseRequestTest.php b/tests/Message/SecureXMLPurchaseRequestTest.php index aa13501..9a33489 100644 --- a/tests/Message/SecureXMLPurchaseRequestTest.php +++ b/tests/Message/SecureXMLPurchaseRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class SecureXMLPurchaseRequestTest extends TestCase { @@ -29,7 +29,7 @@ protected function setUp(): void public function testSendSuccess() { - $this->setMockHttpResponse('SecureXMLPurchaseRequestSendSuccess.txt'); + $this->queueFixtureResponse('SecureXMLPurchaseRequestSendSuccess.txt'); $response = $this->request->send(); $data = $response->getData(); diff --git a/tests/Message/SecureXMLRiskPurchaseRequestTest.php b/tests/Message/SecureXMLRiskPurchaseRequestTest.php index 0fe67af..6f10a6c 100644 --- a/tests/Message/SecureXMLRiskPurchaseRequestTest.php +++ b/tests/Message/SecureXMLRiskPurchaseRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class SecureXMLRiskPurchaseRequestTest extends TestCase { diff --git a/tests/Message/SecureXMLTransportRequestTest.php b/tests/Message/SecureXMLTransportRequestTest.php index 906564c..974d375 100644 --- a/tests/Message/SecureXMLTransportRequestTest.php +++ b/tests/Message/SecureXMLTransportRequestTest.php @@ -4,7 +4,7 @@ use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class SecureXMLTransportRequestTest extends TestCase { diff --git a/tests/Message/UnionPayCompletePurchaseRequestTest.php b/tests/Message/UnionPayCompletePurchaseRequestTest.php index b2667f7..a31e8ae 100644 --- a/tests/Message/UnionPayCompletePurchaseRequestTest.php +++ b/tests/Message/UnionPayCompletePurchaseRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class UnionPayCompletePurchaseRequestTest extends TestCase { diff --git a/tests/Message/UnionPayPurchaseRequestTest.php b/tests/Message/UnionPayPurchaseRequestTest.php index 4f44b1b..b8211c5 100644 --- a/tests/Message/UnionPayPurchaseRequestTest.php +++ b/tests/Message/UnionPayPurchaseRequestTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class UnionPayPurchaseRequestTest extends TestCase { diff --git a/tests/Message/UnionPayPurchaseResponseTest.php b/tests/Message/UnionPayPurchaseResponseTest.php index bbfe4a2..470f706 100644 --- a/tests/Message/UnionPayPurchaseResponseTest.php +++ b/tests/Message/UnionPayPurchaseResponseTest.php @@ -2,7 +2,7 @@ namespace Omnipay\NABTransact\Message; -use Omnipay\Tests\TestCase; +use Omnipay\NABTransact\Tests\Support\TestCase; class UnionPayPurchaseResponseTest extends TestCase { diff --git a/tests/SecureXMLGatewayTest.php b/tests/SecureXMLGatewayTest.php index 9692e2f..0ad5b7f 100644 --- a/tests/SecureXMLGatewayTest.php +++ b/tests/SecureXMLGatewayTest.php @@ -4,7 +4,7 @@ use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\Tests\GatewayTestCase; +use Omnipay\NABTransact\Tests\Support\GatewayTestCase; class SecureXMLGatewayTest extends GatewayTestCase { diff --git a/tests/Support/GatewayTestCase.php b/tests/Support/GatewayTestCase.php new file mode 100644 index 0000000..cb843b0 --- /dev/null +++ b/tests/Support/GatewayTestCase.php @@ -0,0 +1,9 @@ + */ + private array $responses = []; + + /** @var array> */ + private array $requests = []; + + /** + * @param int $statusCode + * @param array $headers + */ + public function queueResponse(string $body, int $statusCode = 200, array $headers = []): void + { + $this->responses[] = new MockHttpResponse($statusCode, $headers, $body); + } + + public function queueRawResponse(ResponseInterface $response): void + { + $this->responses[] = $response; + } + + public function getRequests(): array + { + return $this->requests; + } + + /** + * {@inheritDoc} + */ + public function request($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1'): ResponseInterface + { + $this->requests[] = [ + 'method' => (string) $method, + 'uri' => (string) $uri, + 'headers' => $headers, + 'body' => $body, + 'protocolVersion' => (string) $protocolVersion, + ]; + + if (empty($this->responses)) { + return new MockHttpResponse(200, [], ''); + } + + $response = array_shift($this->responses); + + return $response instanceof ResponseInterface ? $response : new MockHttpResponse(200, [], ''); + } +} diff --git a/tests/Support/MockHttpResponse.php b/tests/Support/MockHttpResponse.php new file mode 100644 index 0000000..a8c7fd2 --- /dev/null +++ b/tests/Support/MockHttpResponse.php @@ -0,0 +1,159 @@ +> */ + private array $headers; + + private StreamInterface $body; + + private int $statusCode; + + private string $reasonPhrase; + + /** @var array */ + private static $defaultPhrases = [ + 200 => 'OK', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 403 => 'Forbidden', + 404 => 'Not Found', + 500 => 'Internal Server Error', + ]; + + /** + * @param array|string> $headers + */ + public function __construct( + int $statusCode = 200, + array $headers = [], + $body = '', + string $protocolVersion = '1.1', + string $reasonPhrase = '' + ) + { + $this->statusCode = $statusCode; + $this->headers = []; + + foreach ($headers as $name => $value) { + $values = is_array($value) ? $value : [(string) $value]; + $this->headers[strtolower($name)] = array_values(array_map('strval', $values)); + } + + $this->body = $body instanceof StreamInterface ? $body : new MockStream((string) $body); + $this->protocolVersion = $protocolVersion; + $this->reasonPhrase = $reasonPhrase; + } + + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + public function withProtocolVersion($version) + { + $clone = clone $this; + $clone->protocolVersion = (string) $version; + + return $clone; + } + + public function getHeaders() + { + return $this->headers; + } + + public function hasHeader($name) + { + return array_key_exists(strtolower((string) $name), $this->headers); + } + + public function getHeader($name) + { + $key = strtolower((string) $name); + + return $this->headers[$key] ?? []; + } + + public function getHeaderLine($name) + { + return implode(', ', $this->getHeader($name)); + } + + public function withHeader($name, $value) + { + $clone = clone $this; + $values = is_array($value) ? $value : [(string) $value]; + $clone->headers[strtolower((string) $name)] = array_values(array_map('strval', $values)); + + return $clone; + } + + public function withAddedHeader($name, $value) + { + $clone = clone $this; + $key = strtolower((string) $name); + $values = is_array($value) ? $value : [(string) $value]; + + if (!isset($clone->headers[$key])) { + $clone->headers[$key] = []; + } + + $clone->headers[$key] = array_merge($clone->headers[$key], array_values(array_map('strval', $values))); + + return $clone; + } + + public function withoutHeader($name) + { + $clone = clone $this; + unset($clone->headers[strtolower((string) $name)]); + + return $clone; + } + + public function getBody() + { + return $this->body; + } + + public function withBody(StreamInterface $body) + { + $clone = clone $this; + $clone->body = $body; + + return $clone; + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function withStatus($code, $reasonPhrase = '') + { + $clone = clone $this; + $clone->statusCode = (int) $code; + $clone->reasonPhrase = (string) $reasonPhrase; + + return $clone; + } + + public function getReasonPhrase() + { + if ($this->reasonPhrase !== '') { + return $this->reasonPhrase; + } + + return self::$defaultPhrases[$this->statusCode] ?? ''; + } +} diff --git a/tests/Support/MockStream.php b/tests/Support/MockStream.php new file mode 100644 index 0000000..a4546d9 --- /dev/null +++ b/tests/Support/MockStream.php @@ -0,0 +1,141 @@ +content = $content; + } + + public function __toString() + { + return $this->content; + } + + public function close() + { + // no-op + } + + public function detach() + { + return null; + } + + public function getSize() + { + return strlen($this->content); + } + + public function tell() + { + return $this->position; + } + + public function eof() + { + return $this->position >= strlen($this->content); + } + + public function isSeekable() + { + return true; + } + + public function seek($offset, $whence = SEEK_SET) + { + if ($whence === SEEK_SET) { + $this->position = max(0, (int) $offset); + + return; + } + + if ($whence === SEEK_CUR) { + $this->position = max(0, $this->position + (int) $offset); + + return; + } + + if ($whence === SEEK_END) { + $this->position = max(0, strlen($this->content) + (int) $offset); + } + } + + public function rewind() + { + $this->position = 0; + } + + public function isWritable() + { + return true; + } + + public function write($string) + { + $string = (string) $string; + + $prefix = substr($this->content, 0, $this->position); + $suffixStart = $this->position + strlen($string); + $suffix = ''; + + if ($suffixStart < strlen($this->content)) { + $suffix = substr($this->content, $suffixStart); + } + + $this->content = $prefix.$string.$suffix; + $this->position += strlen($string); + + return strlen($string); + } + + public function isReadable() + { + return true; + } + + public function read($length) + { + $length = max(0, (int) $length); + + if ($length === 0 || $this->eof()) { + return ''; + } + + $chunk = substr($this->content, $this->position, $length); + $this->position += strlen($chunk); + + return $chunk; + } + + public function getContents() + { + if ($this->eof()) { + return ''; + } + + $chunk = substr($this->content, $this->position); + $this->position = strlen($this->content); + + return $chunk; + } + + public function getMetadata($key = null) + { + if ($key === null) { + return []; + } + + return null; + } +} diff --git a/tests/Support/TestCase.php b/tests/Support/TestCase.php new file mode 100644 index 0000000..232f473 --- /dev/null +++ b/tests/Support/TestCase.php @@ -0,0 +1,79 @@ +httpClient = new MockHttpClient(); + $this->httpRequest = Request::create('/'); + } + + protected function getHttpClient(): ClientInterface + { + return $this->httpClient; + } + + protected function getHttpRequest(): Request + { + return $this->httpRequest; + } + + protected function getMockRequest(): RequestInterface + { + return $this->createMock(RequestInterface::class); + } + + protected function queueFixtureResponse(string $filename, int $statusCode = 200, array $headers = []): void + { + $body = $this->fixtureContents($filename); + + $this->httpClient->queueResponse($body, $statusCode, $headers); + } + + protected function fixtureContents(string $filename): string + { + $path = dirname(__DIR__).'/Mock/'.$filename; + + if (!is_file($path)) { + $this->fail('Missing mock HTTP fixture: '.$filename); + } + + $body = file_get_contents($path); + if ($body === false) { + $this->fail('Unable to read mock HTTP fixture: '.$filename); + } + + return $body; + } + + protected function getValidCard(): array + { + return [ + 'firstName' => 'Example', + 'lastName' => 'User', + 'number' => '4444333322221111', + 'expiryMonth' => '12', + 'expiryYear' => '2030', + 'cvv' => '123', + 'address1' => '123 Test Street', + 'city' => 'Billstown', + 'postcode' => '12345', + 'country' => 'US', + ]; + } +} From 98a873dab0efc5493e0bafcf150131ba7018a379 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Sun, 22 Feb 2026 08:50:39 +0000 Subject: [PATCH 6/9] Apply fixes from StyleCI --- src/HostedPaymentGateway.php | 6 +-- src/Message/DirectPostAuthorizeResponse.php | 4 +- src/Message/DirectPostOperationRequest.php | 12 ++--- src/Message/EMV3DSOrderRequest.php | 14 +++--- .../HostedPaymentCompletePurchaseResponse.php | 2 +- src/Message/HostedPaymentPurchaseResponse.php | 4 +- .../UnionPayCompletePurchaseResponse.php | 2 +- src/Message/UnionPayPurchaseResponse.php | 4 +- src/Transport/CurlTransport.php | 11 ++--- tests/DirectPostGatewayTest.php | 4 +- tests/Message/DirectPostApiResponseTest.php | 10 ++--- .../Message/DirectPostCaptureRequestTest.php | 14 +++--- tests/Message/DirectPostRefundRequestTest.php | 12 ++--- .../Message/DirectPostReversalRequestTest.php | 12 ++--- tests/Message/DirectPostStoreRequestTest.php | 14 +++--- .../DirectPostTransactionTypeRequestTest.php | 44 +++++++++---------- .../Message/DirectPostWebhookRequestTest.php | 32 +++++++------- tests/Message/EMV3DSOrderRequestTest.php | 14 +++--- ...stedPaymentCompletePurchaseRequestTest.php | 12 ++--- .../HostedPaymentPurchaseRequestTest.php | 14 +++--- .../SecureXMLRiskPurchaseRequestTest.php | 30 ++++++------- .../Message/SecureXMLTransportRequestTest.php | 24 +++++----- .../UnionPayCompletePurchaseRequestTest.php | 12 ++--- tests/SecureXMLGatewayTest.php | 4 +- tests/Support/MockHttpClient.php | 8 ++-- tests/Support/MockHttpResponse.php | 3 +- tests/Support/TestCase.php | 18 ++++---- 27 files changed, 171 insertions(+), 169 deletions(-) diff --git a/src/HostedPaymentGateway.php b/src/HostedPaymentGateway.php index a7b28e5..6a3272c 100644 --- a/src/HostedPaymentGateway.php +++ b/src/HostedPaymentGateway.php @@ -12,10 +12,10 @@ class HostedPaymentGateway extends AbstractGateway public function getDefaultParameters() { return [ - 'merchantId' => '', + 'merchantId' => '', 'paymentAlertEmail' => '', - 'returnUrlText' => '', - 'testMode' => false, + 'returnUrlText' => '', + 'testMode' => false, ]; } diff --git a/src/Message/DirectPostAuthorizeResponse.php b/src/Message/DirectPostAuthorizeResponse.php index d95bba7..4914dc9 100644 --- a/src/Message/DirectPostAuthorizeResponse.php +++ b/src/Message/DirectPostAuthorizeResponse.php @@ -18,8 +18,8 @@ class DirectPostAuthorizeResponse extends AbstractResponse implements RedirectRe /** * @param RequestInterface $request - * @param $data - * @param $redirectUrl + * @param $data + * @param $redirectUrl */ public function __construct(RequestInterface $request, $data, $redirectUrl) { diff --git a/src/Message/DirectPostOperationRequest.php b/src/Message/DirectPostOperationRequest.php index 45e7226..7681e6b 100644 --- a/src/Message/DirectPostOperationRequest.php +++ b/src/Message/DirectPostOperationRequest.php @@ -23,12 +23,12 @@ public function getData() $this->validate('merchantId', 'transactionPassword', 'amount', 'transactionId', 'transactionReference'); $data = [ - 'EPS_MERCHANT' => $this->getMerchantId(), - 'EPS_TXNTYPE' => $this->resolveTxnType(), - 'EPS_REFERENCEID' => $this->getTransactionId(), - 'EPS_AMOUNT' => $this->getAmount(), + 'EPS_MERCHANT' => $this->getMerchantId(), + 'EPS_TXNTYPE' => $this->resolveTxnType(), + 'EPS_REFERENCEID' => $this->getTransactionId(), + 'EPS_AMOUNT' => $this->getAmount(), $this->targetTransactionField => $this->getTransactionReference(), - 'EPS_TIMESTAMP' => gmdate('YmdHis'), + 'EPS_TIMESTAMP' => gmdate('YmdHis'), ]; if ($currency = $this->getCurrency()) { @@ -98,7 +98,7 @@ protected function parseTransportResponse(TransportResponse $response) $parsed = [ 'http_status_code' => $response->getStatusCode(), - 'raw' => $body, + 'raw' => $body, ]; if ($body === '') { diff --git a/src/Message/EMV3DSOrderRequest.php b/src/Message/EMV3DSOrderRequest.php index 2b0d786..81ae20d 100644 --- a/src/Message/EMV3DSOrderRequest.php +++ b/src/Message/EMV3DSOrderRequest.php @@ -28,13 +28,13 @@ public function getData() $this->validate('merchantId', 'transactionPassword', 'amount', 'currency', 'clientIp', 'transactionReference'); return [ - 'amount' => $this->getAmountInteger(), - 'currency' => $this->getCurrency(), - 'ip' => $this->getClientIp(), - 'merchantId' => $this->getMerchantId(), + 'amount' => $this->getAmountInteger(), + 'currency' => $this->getCurrency(), + 'ip' => $this->getClientIp(), + 'merchantId' => $this->getMerchantId(), 'merchantOrderReference' => $this->getTransactionReference(), - 'orderType' => $this->getOrderType(), - 'intents' => $this->getIntents(), + 'orderType' => $this->getOrderType(), + 'intents' => $this->getIntents(), ]; } @@ -104,7 +104,7 @@ public function sendData($data) 'POST', $this->getEndpoint(), [ - 'Content-Type' => 'application/json; charset=UTF-8', + 'Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => 'Basic '.$authorization, ], $payload, diff --git a/src/Message/HostedPaymentCompletePurchaseResponse.php b/src/Message/HostedPaymentCompletePurchaseResponse.php index 90cd007..4f89742 100644 --- a/src/Message/HostedPaymentCompletePurchaseResponse.php +++ b/src/Message/HostedPaymentCompletePurchaseResponse.php @@ -12,7 +12,7 @@ class HostedPaymentCompletePurchaseResponse extends AbstractResponse { /** * @param RequestInterface $request - * @param $data + * @param $data */ public function __construct(RequestInterface $request, $data) { diff --git a/src/Message/HostedPaymentPurchaseResponse.php b/src/Message/HostedPaymentPurchaseResponse.php index 3e84a7a..11a8d41 100644 --- a/src/Message/HostedPaymentPurchaseResponse.php +++ b/src/Message/HostedPaymentPurchaseResponse.php @@ -18,8 +18,8 @@ class HostedPaymentPurchaseResponse extends AbstractResponse implements Redirect /** * @param RequestInterface $request - * @param $data - * @param $redirectUrl + * @param $data + * @param $redirectUrl */ public function __construct(RequestInterface $request, $data, $redirectUrl) { diff --git a/src/Message/UnionPayCompletePurchaseResponse.php b/src/Message/UnionPayCompletePurchaseResponse.php index 9011011..5aec4a4 100644 --- a/src/Message/UnionPayCompletePurchaseResponse.php +++ b/src/Message/UnionPayCompletePurchaseResponse.php @@ -11,7 +11,7 @@ class UnionPayCompletePurchaseResponse extends DirectPostCompletePurchaseRespons { /** * @param RequestInterface $request - * @param $data + * @param $data */ public function __construct(RequestInterface $request, $data) { diff --git a/src/Message/UnionPayPurchaseResponse.php b/src/Message/UnionPayPurchaseResponse.php index 482f70c..1aedf5d 100644 --- a/src/Message/UnionPayPurchaseResponse.php +++ b/src/Message/UnionPayPurchaseResponse.php @@ -18,8 +18,8 @@ class UnionPayPurchaseResponse extends AbstractResponse implements RedirectRespo /** * @param RequestInterface $request - * @param $data - * @param $redirectUrl + * @param $data + * @param $redirectUrl */ public function __construct(RequestInterface $request, $data, $redirectUrl) { diff --git a/src/Transport/CurlTransport.php b/src/Transport/CurlTransport.php index 66d908a..ad5f1d0 100644 --- a/src/Transport/CurlTransport.php +++ b/src/Transport/CurlTransport.php @@ -33,12 +33,12 @@ public function send($method, $url, array $headers = [], $body = '', $timeoutSec } $options = [ - CURLOPT_URL => $url, + CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, - CURLOPT_CUSTOMREQUEST => strtoupper($method), - CURLOPT_TIMEOUT => max(1, (int) $timeoutSeconds), - CURLOPT_HTTPHEADER => $formattedHeaders, - CURLOPT_POSTFIELDS => $body, + CURLOPT_CUSTOMREQUEST => strtoupper($method), + CURLOPT_TIMEOUT => max(1, (int) $timeoutSeconds), + CURLOPT_HTTPHEADER => $formattedHeaders, + CURLOPT_POSTFIELDS => $body, ]; curl_setopt_array($handle, $options); @@ -48,6 +48,7 @@ public function send($method, $url, array $headers = [], $body = '', $timeoutSec if ($responseBody === false) { $message = curl_error($handle); curl_close($handle); + throw new RuntimeException('cURL transport request failed: '.$message); } diff --git a/tests/DirectPostGatewayTest.php b/tests/DirectPostGatewayTest.php index ce75e3e..d0ec6db 100644 --- a/tests/DirectPostGatewayTest.php +++ b/tests/DirectPostGatewayTest.php @@ -2,9 +2,9 @@ namespace Omnipay\NABTransact; +use Omnipay\NABTransact\Tests\Support\GatewayTestCase; use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\NABTransact\Tests\Support\GatewayTestCase; class DirectPostGatewayTest extends GatewayTestCase { @@ -105,7 +105,7 @@ public function testCreateEmv3dsOrder() public function testSetTransportAndTimeout() { - $transport = new class implements TransportInterface { + $transport = new class() implements TransportInterface { public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) { return new TransportResponse(200, '{}'); diff --git a/tests/Message/DirectPostApiResponseTest.php b/tests/Message/DirectPostApiResponseTest.php index fdf5044..3c1147c 100644 --- a/tests/Message/DirectPostApiResponseTest.php +++ b/tests/Message/DirectPostApiResponseTest.php @@ -11,11 +11,11 @@ public function testSuccessfulByResponseCode() $request = new DirectPostRefundRequest($this->getHttpClient(), $this->getHttpRequest()); $response = new DirectPostApiResponse($request, [ - 'rescode' => '00', - 'restext' => 'Approved', - 'txnid' => 'TXN-100', + 'rescode' => '00', + 'restext' => 'Approved', + 'txnid' => 'TXN-100', 'http_status_code' => 500, - 'raw' => 'rescode=00', + 'raw' => 'rescode=00', ]); $this->assertTrue($response->isSuccessful()); @@ -32,7 +32,7 @@ public function testSuccessfulByHttpStatusWhenNoResultCode() $response = new DirectPostApiResponse($request, [ 'http_status_code' => 200, - 'message' => 'OK', + 'message' => 'OK', ]); $this->assertTrue($response->isSuccessful()); diff --git a/tests/Message/DirectPostCaptureRequestTest.php b/tests/Message/DirectPostCaptureRequestTest.php index 74c7484..e65d8af 100644 --- a/tests/Message/DirectPostCaptureRequestTest.php +++ b/tests/Message/DirectPostCaptureRequestTest.php @@ -2,9 +2,9 @@ namespace Omnipay\NABTransact\Message; +use Omnipay\NABTransact\Tests\Support\TestCase; use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\NABTransact\Tests\Support\TestCase; class DirectPostCaptureRequestTest extends TestCase { @@ -14,11 +14,11 @@ protected function setUp(): void $this->request = new DirectPostCaptureRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ - 'merchantId' => 'XYZ0010', - 'transactionPassword' => 'abcd1234', - 'amount' => '12.00', - 'currency' => 'AUD', - 'transactionId' => 'CAPTURE-ORDER-100', + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'amount' => '12.00', + 'currency' => 'AUD', + 'transactionId' => 'CAPTURE-ORDER-100', 'transactionReference' => 'NAB-ORIG-100', ]); } @@ -38,7 +38,7 @@ public function testSendParsesQueryStringResponse() { $request = $this->request; - $request->setTransport(new class implements TransportInterface { + $request->setTransport(new class() implements TransportInterface { public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) { return new TransportResponse(200, 'rescode=00&restext=Approved&txnid=CAPTURED-1'); diff --git a/tests/Message/DirectPostRefundRequestTest.php b/tests/Message/DirectPostRefundRequestTest.php index ed7eb66..e88bdab 100644 --- a/tests/Message/DirectPostRefundRequestTest.php +++ b/tests/Message/DirectPostRefundRequestTest.php @@ -2,9 +2,9 @@ namespace Omnipay\NABTransact\Message; +use Omnipay\NABTransact\Tests\Support\TestCase; use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\NABTransact\Tests\Support\TestCase; class DirectPostRefundRequestTest extends TestCase { @@ -14,10 +14,10 @@ protected function setUp(): void $this->request = new DirectPostRefundRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ - 'merchantId' => 'XYZ0010', - 'transactionPassword' => 'abcd1234', - 'amount' => '5.00', - 'transactionId' => 'REFUND-ORDER-100', + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'amount' => '5.00', + 'transactionId' => 'REFUND-ORDER-100', 'transactionReference' => 'NAB-TXN-100', ]); } @@ -35,7 +35,7 @@ public function testSendParsesJsonResponse() { $request = $this->request; - $request->setTransport(new class implements TransportInterface { + $request->setTransport(new class() implements TransportInterface { public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) { return new TransportResponse(200, '{"rescode":"00","restext":"Refunded","txnid":"REF-1"}'); diff --git a/tests/Message/DirectPostReversalRequestTest.php b/tests/Message/DirectPostReversalRequestTest.php index 52a8c59..a40ab52 100644 --- a/tests/Message/DirectPostReversalRequestTest.php +++ b/tests/Message/DirectPostReversalRequestTest.php @@ -2,9 +2,9 @@ namespace Omnipay\NABTransact\Message; +use Omnipay\NABTransact\Tests\Support\TestCase; use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\NABTransact\Tests\Support\TestCase; class DirectPostReversalRequestTest extends TestCase { @@ -14,10 +14,10 @@ protected function setUp(): void $this->request = new DirectPostReversalRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ - 'merchantId' => 'XYZ0010', - 'transactionPassword' => 'abcd1234', - 'amount' => '12.00', - 'transactionId' => 'VOID-ORDER-100', + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'amount' => '12.00', + 'transactionId' => 'VOID-ORDER-100', 'transactionReference' => 'NAB-TXN-VOID-100', ]); } @@ -35,7 +35,7 @@ public function testSendParsesXmlResponse() { $request = $this->request; - $request->setTransport(new class implements TransportInterface { + $request->setTransport(new class() implements TransportInterface { public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) { return new TransportResponse(200, '00ReversedVOID-1'); diff --git a/tests/Message/DirectPostStoreRequestTest.php b/tests/Message/DirectPostStoreRequestTest.php index 9620057..6d57851 100644 --- a/tests/Message/DirectPostStoreRequestTest.php +++ b/tests/Message/DirectPostStoreRequestTest.php @@ -12,15 +12,15 @@ protected function setUp(): void $this->request = new DirectPostStoreRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ - 'merchantId' => 'XYZ0010', + 'merchantId' => 'XYZ0010', 'transactionPassword' => 'abcd1234', - 'returnUrl' => 'https://www.example.com/return', - 'transactionId' => 'STORE-ORDER-100', - 'card' => [ - 'number' => '4444333322221111', + 'returnUrl' => 'https://www.example.com/return', + 'transactionId' => 'STORE-ORDER-100', + 'card' => [ + 'number' => '4444333322221111', 'expiryMonth' => '12', - 'expiryYear' => '2030', - 'cvv' => '123', + 'expiryYear' => '2030', + 'cvv' => '123', ], ]); } diff --git a/tests/Message/DirectPostTransactionTypeRequestTest.php b/tests/Message/DirectPostTransactionTypeRequestTest.php index 9cf21a1..cb4d08b 100644 --- a/tests/Message/DirectPostTransactionTypeRequestTest.php +++ b/tests/Message/DirectPostTransactionTypeRequestTest.php @@ -11,37 +11,37 @@ protected function setUp(): void parent::setUp(); $this->purchaseRequest = new DirectPostPurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); $this->purchaseRequest->initialize([ - 'merchantId' => 'XYZ0010', - 'transactionPassword' => 'abcd1234', - 'amount' => '12.00', - 'returnUrl' => 'https://www.example.com/return', - 'transactionId' => 'ORDER-100', + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'amount' => '12.00', + 'returnUrl' => 'https://www.example.com/return', + 'transactionId' => 'ORDER-100', 'transactionReference' => 'ORDER-REF-100', - 'card' => [ - 'firstName' => 'Example', - 'lastName' => 'User', - 'number' => '4444333322221111', + 'card' => [ + 'firstName' => 'Example', + 'lastName' => 'User', + 'number' => '4444333322221111', 'expiryMonth' => '12', - 'expiryYear' => '2030', - 'cvv' => '123', + 'expiryYear' => '2030', + 'cvv' => '123', ], ]); $this->authorizeRequest = new DirectPostAuthorizeRequest($this->getHttpClient(), $this->getHttpRequest()); $this->authorizeRequest->initialize([ - 'merchantId' => 'XYZ0010', - 'transactionPassword' => 'abcd1234', - 'amount' => '12.00', - 'returnUrl' => 'https://www.example.com/return', - 'transactionId' => 'ORDER-101', + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'amount' => '12.00', + 'returnUrl' => 'https://www.example.com/return', + 'transactionId' => 'ORDER-101', 'transactionReference' => 'ORDER-REF-101', - 'card' => [ - 'firstName' => 'Example', - 'lastName' => 'User', - 'number' => '4444333322221111', + 'card' => [ + 'firstName' => 'Example', + 'lastName' => 'User', + 'number' => '4444333322221111', 'expiryMonth' => '12', - 'expiryYear' => '2030', - 'cvv' => '123', + 'expiryYear' => '2030', + 'cvv' => '123', ], ]); } diff --git a/tests/Message/DirectPostWebhookRequestTest.php b/tests/Message/DirectPostWebhookRequestTest.php index 9ca73f4..0ac6446 100644 --- a/tests/Message/DirectPostWebhookRequestTest.php +++ b/tests/Message/DirectPostWebhookRequestTest.php @@ -9,15 +9,15 @@ class DirectPostWebhookRequestTest extends TestCase public function testVerifyFingerprintAndTypoAliasAreBackwardCompatible() { $payload = [ - 'merchant' => 'XYZ0010', + 'merchant' => 'XYZ0010', 'txn_password' => 'abcd1234', - 'refid' => 'ORDER-123', - 'amount' => '10.00', - 'timestamp' => '20260222000000', - 'summarycode' => '1', - 'restext' => 'Approved', - 'rescode' => '00', - 'txnid' => '10001', + 'refid' => 'ORDER-123', + 'amount' => '10.00', + 'timestamp' => '20260222000000', + 'summarycode' => '1', + 'restext' => 'Approved', + 'rescode' => '00', + 'txnid' => '10001', ]; $request = new DirectPostWebhookRequest($payload); @@ -33,15 +33,15 @@ public function testVerifyFingerprintAndTypoAliasAreBackwardCompatible() public function testInvalidFingerprintMarksFailure() { $payload = [ - 'merchant' => 'XYZ0010', + 'merchant' => 'XYZ0010', 'txn_password' => 'abcd1234', - 'refid' => 'ORDER-123', - 'amount' => '10.00', - 'timestamp' => '20260222000000', - 'summarycode' => '1', - 'restext' => 'Approved', - 'rescode' => '00', - 'txnid' => '10001', + 'refid' => 'ORDER-123', + 'amount' => '10.00', + 'timestamp' => '20260222000000', + 'summarycode' => '1', + 'restext' => 'Approved', + 'rescode' => '00', + 'txnid' => '10001', ]; $request = new DirectPostWebhookRequest($payload); diff --git a/tests/Message/EMV3DSOrderRequestTest.php b/tests/Message/EMV3DSOrderRequestTest.php index 78b1ff0..4f1e0b4 100644 --- a/tests/Message/EMV3DSOrderRequestTest.php +++ b/tests/Message/EMV3DSOrderRequestTest.php @@ -2,9 +2,9 @@ namespace Omnipay\NABTransact\Message; +use Omnipay\NABTransact\Tests\Support\TestCase; use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\NABTransact\Tests\Support\TestCase; class EMV3DSOrderRequestTest extends TestCase { @@ -14,11 +14,11 @@ protected function setUp(): void $this->request = new EMV3DSOrderRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ - 'merchantId' => 'XYZ0010', - 'transactionPassword' => 'abcd1234', - 'amount' => '12.00', - 'currency' => 'AUD', - 'clientIp' => '1.2.3.4', + 'merchantId' => 'XYZ0010', + 'transactionPassword' => 'abcd1234', + 'amount' => '12.00', + 'currency' => 'AUD', + 'clientIp' => '1.2.3.4', 'transactionReference' => 'EMV-ORDER-100', ]); } @@ -81,7 +81,7 @@ public function send($method, $url, array $headers = [], $body = '', $timeoutSec public function testSendHandlesInvalidJsonResponse() { $request = $this->request; - $request->setTransport(new class implements TransportInterface { + $request->setTransport(new class() implements TransportInterface { public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) { return new TransportResponse(500, 'Internal Error'); diff --git a/tests/Message/HostedPaymentCompletePurchaseRequestTest.php b/tests/Message/HostedPaymentCompletePurchaseRequestTest.php index a85ac0c..4549ac0 100644 --- a/tests/Message/HostedPaymentCompletePurchaseRequestTest.php +++ b/tests/Message/HostedPaymentCompletePurchaseRequestTest.php @@ -16,9 +16,9 @@ public function testReadDataFromQuery() { $this->getHttpRequest()->query->replace([ 'summarycode' => '1', - 'rescode' => '00', - 'restext' => 'Approved', - 'txnid' => 'TXN-100', + 'rescode' => '00', + 'restext' => 'Approved', + 'txnid' => 'TXN-100', ]); $response = $this->request->send(); @@ -33,9 +33,9 @@ public function testReadDataFromRequestBodyWhenQueryMissing() { $this->getHttpRequest()->request->replace([ 'summarycode' => '3', - 'rescode' => '06', - 'restext' => 'Declined', - 'txnid' => 'TXN-101', + 'rescode' => '06', + 'restext' => 'Declined', + 'txnid' => 'TXN-101', ]); $response = $this->request->send(); diff --git a/tests/Message/HostedPaymentPurchaseRequestTest.php b/tests/Message/HostedPaymentPurchaseRequestTest.php index 0bf2365..fd0eb0c 100644 --- a/tests/Message/HostedPaymentPurchaseRequestTest.php +++ b/tests/Message/HostedPaymentPurchaseRequestTest.php @@ -12,14 +12,14 @@ protected function setUp(): void $this->request = new HostedPaymentPurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ - 'merchantId' => 'XYZ0010', + 'merchantId' => 'XYZ0010', 'paymentAlertEmail' => 'merchant@example.com', - 'amount' => '10.00', - 'currency' => 'AUD', - 'transactionId' => 'ORDER-123', - 'returnUrl' => 'https://example.com/return', - 'notifyUrl' => 'https://example.com/notify', - 'returnUrlText' => 'Return', + 'amount' => '10.00', + 'currency' => 'AUD', + 'transactionId' => 'ORDER-123', + 'returnUrl' => 'https://example.com/return', + 'notifyUrl' => 'https://example.com/notify', + 'returnUrlText' => 'Return', ]); } diff --git a/tests/Message/SecureXMLRiskPurchaseRequestTest.php b/tests/Message/SecureXMLRiskPurchaseRequestTest.php index 6f10a6c..1526924 100644 --- a/tests/Message/SecureXMLRiskPurchaseRequestTest.php +++ b/tests/Message/SecureXMLRiskPurchaseRequestTest.php @@ -12,23 +12,23 @@ protected function setUp(): void $this->request = new SecureXMLRiskPurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); $this->request->initialize([ - 'merchantId' => 'XYZ0010', + 'merchantId' => 'XYZ0010', 'transactionPassword' => 'abcd1234', - 'testMode' => true, - 'amount' => '12.00', - 'transactionId' => '1234', - 'ip' => '1.1.1.1', - 'card' => [ - 'firstName' => 'Example', - 'lastName' => 'User', - 'number' => '4444333322221111', - 'expiryMonth' => '12', - 'expiryYear' => '2030', - 'cvv' => '123', - 'email' => 'example@example.com', + 'testMode' => true, + 'amount' => '12.00', + 'transactionId' => '1234', + 'ip' => '1.1.1.1', + 'card' => [ + 'firstName' => 'Example', + 'lastName' => 'User', + 'number' => '4444333322221111', + 'expiryMonth' => '12', + 'expiryYear' => '2030', + 'cvv' => '123', + 'email' => 'example@example.com', 'billingPostcode' => '12345', - 'billingCity' => 'Billstown', - 'billingCountry' => 'US', + 'billingCity' => 'Billstown', + 'billingCountry' => 'US', ], ]); } diff --git a/tests/Message/SecureXMLTransportRequestTest.php b/tests/Message/SecureXMLTransportRequestTest.php index 974d375..d374e61 100644 --- a/tests/Message/SecureXMLTransportRequestTest.php +++ b/tests/Message/SecureXMLTransportRequestTest.php @@ -2,9 +2,9 @@ namespace Omnipay\NABTransact\Message; +use Omnipay\NABTransact\Tests\Support\TestCase; use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\NABTransact\Tests\Support\TestCase; class SecureXMLTransportRequestTest extends TestCase { @@ -13,16 +13,16 @@ public function testUsesCustomTransportWhenProvided() $request = new SecureXMLPurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); $request->initialize([ - 'merchantId' => 'XYZ0010', + 'merchantId' => 'XYZ0010', 'transactionPassword' => 'abcd1234', - 'testMode' => true, - 'amount' => '12.00', - 'transactionId' => '1234', - 'card' => [ - 'number' => '4444333322221111', - 'expiryMonth' => '12', - 'expiryYear' => '2030', - 'cvv' => '123', + 'testMode' => true, + 'amount' => '12.00', + 'transactionId' => '1234', + 'card' => [ + 'number' => '4444333322221111', + 'expiryMonth' => '12', + 'expiryYear' => '2030', + 'cvv' => '123', 'cardHolderName' => 'Sujip Thapa', ], ]); @@ -43,7 +43,9 @@ public function send($method, $url, array $headers = [], $body = '', $timeoutSec $this->capture->method = $method; $this->capture->url = $url; - return new TransportResponse(200, << 20260222000000000+0000 000Normal diff --git a/tests/Message/UnionPayCompletePurchaseRequestTest.php b/tests/Message/UnionPayCompletePurchaseRequestTest.php index a31e8ae..43e96d5 100644 --- a/tests/Message/UnionPayCompletePurchaseRequestTest.php +++ b/tests/Message/UnionPayCompletePurchaseRequestTest.php @@ -9,10 +9,10 @@ class UnionPayCompletePurchaseRequestTest extends TestCase public function testUnionPayCompletePurchaseSuccess() { $data = [ - 'restext' => 'Approved', - 'rescode' => '00', + 'restext' => 'Approved', + 'rescode' => '00', 'summarycode' => '1', - 'txnid' => '12345', + 'txnid' => '12345', ]; $response = new UnionPayCompletePurchaseResponse($this->getMockRequest(), $data); @@ -29,10 +29,10 @@ public function testUnionPayCompletePurchaseSuccess() public function testUnionPayCompletePurchaseFailure() { $data = [ - 'restext' => 'Error', - 'txnid' => '12345', + 'restext' => 'Error', + 'txnid' => '12345', 'summarycode' => '3', - 'rescode' => '06', + 'rescode' => '06', ]; $response = new UnionPayCompletePurchaseResponse($this->getMockRequest(), $data); diff --git a/tests/SecureXMLGatewayTest.php b/tests/SecureXMLGatewayTest.php index 0ad5b7f..4112383 100644 --- a/tests/SecureXMLGatewayTest.php +++ b/tests/SecureXMLGatewayTest.php @@ -2,9 +2,9 @@ namespace Omnipay\NABTransact; +use Omnipay\NABTransact\Tests\Support\GatewayTestCase; use Omnipay\NABTransact\Transport\TransportInterface; use Omnipay\NABTransact\Transport\TransportResponse; -use Omnipay\NABTransact\Tests\Support\GatewayTestCase; class SecureXMLGatewayTest extends GatewayTestCase { @@ -74,7 +74,7 @@ public function testRefund() public function testTransportAndTimeoutArePassedToRequest() { - $transport = new class implements TransportInterface { + $transport = new class() implements TransportInterface { public function send($method, $url, array $headers = [], $body = '', $timeoutSeconds = 60) { return new TransportResponse(200, '000NormalEcho'); diff --git a/tests/Support/MockHttpClient.php b/tests/Support/MockHttpClient.php index 0c153a2..dec39db 100644 --- a/tests/Support/MockHttpClient.php +++ b/tests/Support/MockHttpClient.php @@ -40,10 +40,10 @@ public function getRequests(): array public function request($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1'): ResponseInterface { $this->requests[] = [ - 'method' => (string) $method, - 'uri' => (string) $uri, - 'headers' => $headers, - 'body' => $body, + 'method' => (string) $method, + 'uri' => (string) $uri, + 'headers' => $headers, + 'body' => $body, 'protocolVersion' => (string) $protocolVersion, ]; diff --git a/tests/Support/MockHttpResponse.php b/tests/Support/MockHttpResponse.php index a8c7fd2..7b1798f 100644 --- a/tests/Support/MockHttpResponse.php +++ b/tests/Support/MockHttpResponse.php @@ -39,8 +39,7 @@ public function __construct( $body = '', string $protocolVersion = '1.1', string $reasonPhrase = '' - ) - { + ) { $this->statusCode = $statusCode; $this->headers = []; diff --git a/tests/Support/TestCase.php b/tests/Support/TestCase.php index 232f473..652a000 100644 --- a/tests/Support/TestCase.php +++ b/tests/Support/TestCase.php @@ -64,16 +64,16 @@ protected function fixtureContents(string $filename): string protected function getValidCard(): array { return [ - 'firstName' => 'Example', - 'lastName' => 'User', - 'number' => '4444333322221111', + 'firstName' => 'Example', + 'lastName' => 'User', + 'number' => '4444333322221111', 'expiryMonth' => '12', - 'expiryYear' => '2030', - 'cvv' => '123', - 'address1' => '123 Test Street', - 'city' => 'Billstown', - 'postcode' => '12345', - 'country' => 'US', + 'expiryYear' => '2030', + 'cvv' => '123', + 'address1' => '123 Test Street', + 'city' => 'Billstown', + 'postcode' => '12345', + 'country' => 'US', ]; } } From 124ea0dc7369d3ff6bef93ff8cdd59d02674c460 Mon Sep 17 00:00:00 2001 From: Sujip Thapa Date: Sun, 22 Feb 2026 14:54:12 +0545 Subject: [PATCH 7/9] fix: resolve CI failures in test harness and fixture parsing --- composer.json | 7 +++- phpunit.xml.dist | 13 +++---- src/Message/DirectPostOperationRequest.php | 12 ++++-- .../DirectPostAuthorizeRequestTest.php | 2 +- .../DirectPostCompletePurchaseRequestTest.php | 4 +- .../Message/DirectPostPurchaseRequestTest.php | 2 +- .../Message/SecureXMLAuthorizeRequestTest.php | 10 ++--- .../Message/SecureXMLEchoTestRequestTest.php | 2 +- .../Message/SecureXMLPurchaseRequestTest.php | 2 +- .../UnionPayCompletePurchaseRequestTest.php | 4 +- tests/Message/UnionPayPurchaseRequestTest.php | 2 +- tests/Support/MockHttpClient.php | 9 ++++- tests/Support/MockHttpResponse.php | 39 ++++++++++--------- tests/Support/MockStream.php | 36 +++++++++-------- tests/Support/TestCase.php | 9 +++++ 15 files changed, 89 insertions(+), 64 deletions(-) diff --git a/composer.json b/composer.json index baab2ab..6a5ebeb 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ }, "require-dev": { "phpstan/phpstan": "^1.12", - "phpunit/phpunit": "^8.5 || ^9.6", + "phpunit/phpunit": "^8.5.14 || ^9.6", "squizlabs/php_codesniffer": "^3" }, "extra": { @@ -50,7 +50,10 @@ "fix-style": "phpcbf -p --standard=PSR12 src tests" }, "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "php-http/discovery": true + } }, "prefer-stable": true } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f888782..8623653 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -7,16 +7,15 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="false" - syntaxCheck="false"> + stopOnFailure="false"> ./tests/ - - - ./src - - + + + ./src + + diff --git a/src/Message/DirectPostOperationRequest.php b/src/Message/DirectPostOperationRequest.php index 7681e6b..0c09866 100644 --- a/src/Message/DirectPostOperationRequest.php +++ b/src/Message/DirectPostOperationRequest.php @@ -95,6 +95,8 @@ public function sendData($data) protected function parseTransportResponse(TransportResponse $response) { $body = trim((string) $response->getBody()); + $trimmedBody = ltrim($body); + $looksLikeXml = $trimmedBody !== '' && strpos($trimmedBody, '<') === 0; $parsed = [ 'http_status_code' => $response->getStatusCode(), @@ -110,10 +112,12 @@ protected function parseTransportResponse(TransportResponse $response) return array_merge($parsed, $this->normalizeKeys($json)); } - $query = []; - parse_str($body, $query); - if (!empty($query)) { - return array_merge($parsed, $this->normalizeKeys($query)); + if (!$looksLikeXml) { + $query = []; + parse_str($body, $query); + if (!empty($query)) { + return array_merge($parsed, $this->normalizeKeys($query)); + } } if (function_exists('simplexml_load_string')) { diff --git a/tests/Message/DirectPostAuthorizeRequestTest.php b/tests/Message/DirectPostAuthorizeRequestTest.php index e2eb83d..eebde96 100644 --- a/tests/Message/DirectPostAuthorizeRequestTest.php +++ b/tests/Message/DirectPostAuthorizeRequestTest.php @@ -37,7 +37,7 @@ public function testSend() { $response = $this->request->send(); - $this->assertInstanceOf(Omnipay\NABTransact\Message\DirectPostAuthorizeResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostAuthorizeResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertTrue($response->isRedirect()); $this->assertNull($response->getTransactionReference()); diff --git a/tests/Message/DirectPostCompletePurchaseRequestTest.php b/tests/Message/DirectPostCompletePurchaseRequestTest.php index 0fb7a68..ada860e 100644 --- a/tests/Message/DirectPostCompletePurchaseRequestTest.php +++ b/tests/Message/DirectPostCompletePurchaseRequestTest.php @@ -57,7 +57,7 @@ public function testSuccess() $response = $this->request->send(); - $this->assertInstanceOf(Omnipay\NABTransact\Message\DirectPostCompletePurchaseResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostCompletePurchaseResponse::class, $response); $this->assertTrue($response->isSuccessful()); $this->assertFalse($response->isRedirect()); @@ -90,7 +90,7 @@ public function testFailure() $response = $this->request->send(); - $this->assertInstanceOf(Omnipay\NABTransact\Message\DirectPostCompletePurchaseResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostCompletePurchaseResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertFalse($response->isRedirect()); diff --git a/tests/Message/DirectPostPurchaseRequestTest.php b/tests/Message/DirectPostPurchaseRequestTest.php index f4e62d4..89d2493 100644 --- a/tests/Message/DirectPostPurchaseRequestTest.php +++ b/tests/Message/DirectPostPurchaseRequestTest.php @@ -37,7 +37,7 @@ public function testSend() { $response = $this->request->send(); - $this->assertInstanceOf(Omnipay\NABTransact\Message\DirectPostAuthorizeResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\DirectPostAuthorizeResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertTrue($response->isRedirect()); $this->assertNull($response->getTransactionReference()); diff --git a/tests/Message/SecureXMLAuthorizeRequestTest.php b/tests/Message/SecureXMLAuthorizeRequestTest.php index a9794a2..418db9e 100644 --- a/tests/Message/SecureXMLAuthorizeRequestTest.php +++ b/tests/Message/SecureXMLAuthorizeRequestTest.php @@ -35,7 +35,7 @@ public function testSendSuccess() $data = $response->getData(); - $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertTrue($response->isSuccessful()); $this->assertFalse($response->isRedirect()); @@ -54,7 +54,7 @@ public function testSendFailure() $this->queueFixtureResponse('SecureXMLAuthorizeRequestFail.txt'); $response = $this->request->send(); - $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertFalse($response->isRedirect()); @@ -68,7 +68,7 @@ public function testInsufficientFundsFailure() $this->queueFixtureResponse('SecureXMLAuthorizeRequestInsufficientFundsFail.txt'); $response = $this->request->send(); - $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertFalse($response->isRedirect()); @@ -82,7 +82,7 @@ public function testInvalidMerchantFailure() $this->queueFixtureResponse('SecureXMLAuthorizeRequestInvalidMerchantFail.txt'); $response = $this->request->send(); - $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertFalse($response->isRedirect()); @@ -96,7 +96,7 @@ public function testInvalidMerchantIDFailure() $this->queueFixtureResponse('SecureXMLAuthorizeRequestInvalidMerchantIDFail.txt'); $response = $this->request->send(); - $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertFalse($response->isRedirect()); diff --git a/tests/Message/SecureXMLEchoTestRequestTest.php b/tests/Message/SecureXMLEchoTestRequestTest.php index fc6e6f6..fc718d3 100644 --- a/tests/Message/SecureXMLEchoTestRequestTest.php +++ b/tests/Message/SecureXMLEchoTestRequestTest.php @@ -24,7 +24,7 @@ public function testSuccess() $response = $this->request->send(); - $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertSame('Normal', $response->getMessage()); $this->assertFalse($response->isRedirect()); $this->assertSame('000', $response->getStatusCode()); diff --git a/tests/Message/SecureXMLPurchaseRequestTest.php b/tests/Message/SecureXMLPurchaseRequestTest.php index 9a33489..bd3ea12 100644 --- a/tests/Message/SecureXMLPurchaseRequestTest.php +++ b/tests/Message/SecureXMLPurchaseRequestTest.php @@ -34,7 +34,7 @@ public function testSendSuccess() $response = $this->request->send(); $data = $response->getData(); - $this->assertInstanceOf(Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\SecureXMLResponse::class, $response); $this->assertTrue($response->isSuccessful()); $this->assertFalse($response->isRedirect()); diff --git a/tests/Message/UnionPayCompletePurchaseRequestTest.php b/tests/Message/UnionPayCompletePurchaseRequestTest.php index 43e96d5..e172d99 100644 --- a/tests/Message/UnionPayCompletePurchaseRequestTest.php +++ b/tests/Message/UnionPayCompletePurchaseRequestTest.php @@ -17,7 +17,7 @@ public function testUnionPayCompletePurchaseSuccess() $response = new UnionPayCompletePurchaseResponse($this->getMockRequest(), $data); - $this->assertInstanceOf(Omnipay\NABTransact\Message\UnionPayCompletePurchaseResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\UnionPayCompletePurchaseResponse::class, $response); $this->assertTrue($response->isSuccessful()); $this->assertFalse($response->isRedirect()); $this->assertSame('12345', $response->getTransactionReference()); @@ -37,7 +37,7 @@ public function testUnionPayCompletePurchaseFailure() $response = new UnionPayCompletePurchaseResponse($this->getMockRequest(), $data); - $this->assertInstanceOf(Omnipay\NABTransact\Message\UnionPayCompletePurchaseResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\UnionPayCompletePurchaseResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertFalse($response->isRedirect()); $this->assertSame('12345', $response->getTransactionReference()); diff --git a/tests/Message/UnionPayPurchaseRequestTest.php b/tests/Message/UnionPayPurchaseRequestTest.php index b8211c5..a264366 100644 --- a/tests/Message/UnionPayPurchaseRequestTest.php +++ b/tests/Message/UnionPayPurchaseRequestTest.php @@ -32,7 +32,7 @@ public function testPurchase() { $response = $this->request->send(); - $this->assertInstanceOf(Omnipay\NABTransact\Message\UnionPayPurchaseResponse::class, $response); + $this->assertInstanceOf(\Omnipay\NABTransact\Message\UnionPayPurchaseResponse::class, $response); $this->assertFalse($response->isSuccessful()); $this->assertTrue($response->isRedirect()); diff --git a/tests/Support/MockHttpClient.php b/tests/Support/MockHttpClient.php index dec39db..192f751 100644 --- a/tests/Support/MockHttpClient.php +++ b/tests/Support/MockHttpClient.php @@ -6,6 +6,7 @@ use Omnipay\Common\Http\ClientInterface; use Psr\Http\Message\ResponseInterface; +use RuntimeException; final class MockHttpClient implements ClientInterface { @@ -48,11 +49,15 @@ public function request($method, $uri, array $headers = [], $body = null, $proto ]; if (empty($this->responses)) { - return new MockHttpResponse(200, [], ''); + throw new RuntimeException('No queued mock HTTP response for '.$method.' '.$uri); } $response = array_shift($this->responses); - return $response instanceof ResponseInterface ? $response : new MockHttpResponse(200, [], ''); + if (!$response instanceof ResponseInterface) { + throw new RuntimeException('Queued mock HTTP response is invalid.'); + } + + return $response; } } diff --git a/tests/Support/MockHttpResponse.php b/tests/Support/MockHttpResponse.php index 7b1798f..6418b7e 100644 --- a/tests/Support/MockHttpResponse.php +++ b/tests/Support/MockHttpResponse.php @@ -4,6 +4,7 @@ namespace Omnipay\NABTransact\Tests\Support; +use Psr\Http\Message\MessageInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; @@ -21,7 +22,7 @@ final class MockHttpResponse implements ResponseInterface private string $reasonPhrase; /** @var array */ - private static $defaultPhrases = [ + private static array $defaultPhrases = [ 200 => 'OK', 400 => 'Bad Request', 401 => 'Unauthorized', @@ -45,20 +46,20 @@ public function __construct( foreach ($headers as $name => $value) { $values = is_array($value) ? $value : [(string) $value]; - $this->headers[strtolower($name)] = array_values(array_map('strval', $values)); + $this->headers[strtolower((string) $name)] = array_values(array_map('strval', $values)); } $this->body = $body instanceof StreamInterface ? $body : new MockStream((string) $body); - $this->protocolVersion = $protocolVersion; - $this->reasonPhrase = $reasonPhrase; + $this->protocolVersion = (string) $protocolVersion; + $this->reasonPhrase = (string) $reasonPhrase; } - public function getProtocolVersion() + public function getProtocolVersion(): string { return $this->protocolVersion; } - public function withProtocolVersion($version) + public function withProtocolVersion($version): MessageInterface { $clone = clone $this; $clone->protocolVersion = (string) $version; @@ -66,29 +67,29 @@ public function withProtocolVersion($version) return $clone; } - public function getHeaders() + public function getHeaders(): array { return $this->headers; } - public function hasHeader($name) + public function hasHeader($name): bool { return array_key_exists(strtolower((string) $name), $this->headers); } - public function getHeader($name) + public function getHeader($name): array { $key = strtolower((string) $name); return $this->headers[$key] ?? []; } - public function getHeaderLine($name) + public function getHeaderLine($name): string { return implode(', ', $this->getHeader($name)); } - public function withHeader($name, $value) + public function withHeader($name, $value): MessageInterface { $clone = clone $this; $values = is_array($value) ? $value : [(string) $value]; @@ -97,7 +98,7 @@ public function withHeader($name, $value) return $clone; } - public function withAddedHeader($name, $value) + public function withAddedHeader($name, $value): MessageInterface { $clone = clone $this; $key = strtolower((string) $name); @@ -112,7 +113,7 @@ public function withAddedHeader($name, $value) return $clone; } - public function withoutHeader($name) + public function withoutHeader($name): MessageInterface { $clone = clone $this; unset($clone->headers[strtolower((string) $name)]); @@ -120,25 +121,25 @@ public function withoutHeader($name) return $clone; } - public function getBody() + public function getBody(): StreamInterface { return $this->body; } - public function withBody(StreamInterface $body) + public function withBody($body): MessageInterface { $clone = clone $this; - $clone->body = $body; + $clone->body = $body instanceof StreamInterface ? $body : new MockStream((string) $body); return $clone; } - public function getStatusCode() + public function getStatusCode(): int { return $this->statusCode; } - public function withStatus($code, $reasonPhrase = '') + public function withStatus($code, $reasonPhrase = ''): ResponseInterface { $clone = clone $this; $clone->statusCode = (int) $code; @@ -147,7 +148,7 @@ public function withStatus($code, $reasonPhrase = '') return $clone; } - public function getReasonPhrase() + public function getReasonPhrase(): string { if ($this->reasonPhrase !== '') { return $this->reasonPhrase; diff --git a/tests/Support/MockStream.php b/tests/Support/MockStream.php index a4546d9..1cc1175 100644 --- a/tests/Support/MockStream.php +++ b/tests/Support/MockStream.php @@ -17,12 +17,12 @@ public function __construct(string $content = '') $this->content = $content; } - public function __toString() + public function __toString(): string { return $this->content; } - public function close() + public function close(): void { // no-op } @@ -32,56 +32,59 @@ public function detach() return null; } - public function getSize() + public function getSize(): ?int { return strlen($this->content); } - public function tell() + public function tell(): int { return $this->position; } - public function eof() + public function eof(): bool { return $this->position >= strlen($this->content); } - public function isSeekable() + public function isSeekable(): bool { return true; } - public function seek($offset, $whence = SEEK_SET) + public function seek($offset, $whence = SEEK_SET): void { + $offset = (int) $offset; + $whence = (int) $whence; + if ($whence === SEEK_SET) { - $this->position = max(0, (int) $offset); + $this->position = max(0, $offset); return; } if ($whence === SEEK_CUR) { - $this->position = max(0, $this->position + (int) $offset); + $this->position = max(0, $this->position + $offset); return; } if ($whence === SEEK_END) { - $this->position = max(0, strlen($this->content) + (int) $offset); + $this->position = max(0, strlen($this->content) + $offset); } } - public function rewind() + public function rewind(): void { $this->position = 0; } - public function isWritable() + public function isWritable(): bool { return true; } - public function write($string) + public function write($string): int { $string = (string) $string; @@ -99,12 +102,12 @@ public function write($string) return strlen($string); } - public function isReadable() + public function isReadable(): bool { return true; } - public function read($length) + public function read($length): string { $length = max(0, (int) $length); @@ -118,7 +121,7 @@ public function read($length) return $chunk; } - public function getContents() + public function getContents(): string { if ($this->eof()) { return ''; @@ -139,3 +142,4 @@ public function getMetadata($key = null) return null; } } + diff --git a/tests/Support/TestCase.php b/tests/Support/TestCase.php index 652a000..e2159e2 100644 --- a/tests/Support/TestCase.php +++ b/tests/Support/TestCase.php @@ -58,6 +58,15 @@ protected function fixtureContents(string $filename): string $this->fail('Unable to read mock HTTP fixture: '.$filename); } + // Legacy fixture files may contain an HTTP status line and headers. + // Keep only the actual payload body for request parsers. + if (preg_match('/^HTTP\\/\\d(?:\\.\\d)?\\s+\\d+/', $body) === 1) { + $parts = preg_split("/\\r?\\n\\r?\\n/", $body, 2); + if (is_array($parts) && isset($parts[1])) { + $body = $parts[1]; + } + } + return $body; } From 4c4aff876a84132321f9dc2dd45f6824f32de76a Mon Sep 17 00:00:00 2001 From: Sujip Thapa Date: Sun, 22 Feb 2026 15:08:57 +0545 Subject: [PATCH 8/9] fix: resolve phpstan errors across gateway and message layers --- phpstan.neon.dist | 4 -- src/DirectPostGateway.php | 58 +++++++++++++++---- src/HostedPaymentGateway.php | 12 +++- src/Message/DirectPostAbstractRequest.php | 10 +++- .../DirectPostCompletePurchaseRequest.php | 6 +- .../DirectPostCompletePurchaseResponse.php | 6 +- src/Message/SecureXMLAbstractRequest.php | 18 +++--- src/Message/SecureXMLResponse.php | 10 ++-- src/SecureXMLGateway.php | 38 +++++++++--- src/Transport/OmnipayHttpClientTransport.php | 10 +--- src/UnionPayGateway.php | 13 ++++- 11 files changed, 129 insertions(+), 56 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index e990136..423e577 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -2,8 +2,4 @@ parameters: level: 5 paths: - src - - tests inferPrivatePropertyTypeFromConstructor: true - checkMissingIterableValueType: false - ignoreErrors: - - '#Call to deprecated method vefiyFingerPrint\(\)#' diff --git a/src/DirectPostGateway.php b/src/DirectPostGateway.php index 82ca3d5..c62edfe 100644 --- a/src/DirectPostGateway.php +++ b/src/DirectPostGateway.php @@ -3,6 +3,15 @@ namespace Omnipay\NABTransact; use Omnipay\Common\AbstractGateway; +use Omnipay\NABTransact\Message\DirectPostAuthorizeRequest; +use Omnipay\NABTransact\Message\DirectPostCaptureRequest; +use Omnipay\NABTransact\Message\DirectPostCompletePurchaseRequest; +use Omnipay\NABTransact\Message\DirectPostPurchaseRequest; +use Omnipay\NABTransact\Message\DirectPostRefundRequest; +use Omnipay\NABTransact\Message\DirectPostReversalRequest; +use Omnipay\NABTransact\Message\DirectPostStoreRequest; +use Omnipay\NABTransact\Message\DirectPostWebhookRequest; +use Omnipay\NABTransact\Message\EMV3DSOrderRequest; use Omnipay\NABTransact\Transport\TransportInterface; /** @@ -154,7 +163,10 @@ public function setRiskManagement($value) */ public function authorize(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\DirectPostAuthorizeRequest', $parameters); + $request = $this->createRequest(DirectPostAuthorizeRequest::class, $parameters); + /** @var DirectPostAuthorizeRequest $request */ + + return $request; } /** @@ -164,17 +176,23 @@ public function authorize(array $parameters = []) */ public function completeAuthorize(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\DirectPostCompletePurchaseRequest', $parameters); + $request = $this->createRequest(DirectPostCompletePurchaseRequest::class, $parameters); + /** @var DirectPostCompletePurchaseRequest $request */ + + return $request; } /** * @param array $parameters * - * @return \Omnipay\NABTransact\Message\DirectPostPurchaseRequest + * @return \Omnipay\Common\Message\AbstractRequest */ public function purchase(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\DirectPostPurchaseRequest', $parameters); + $request = $this->createRequest(DirectPostPurchaseRequest::class, $parameters); + /** @var DirectPostPurchaseRequest $request */ + + return $request; } /** @@ -184,7 +202,10 @@ public function purchase(array $parameters = []) */ public function completePurchase(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\DirectPostCompletePurchaseRequest', $parameters); + $request = $this->createRequest(DirectPostCompletePurchaseRequest::class, $parameters); + /** @var DirectPostCompletePurchaseRequest $request */ + + return $request; } /** @@ -196,7 +217,10 @@ public function completePurchase(array $parameters = []) */ public function capture(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\DirectPostCaptureRequest', $parameters); + $request = $this->createRequest(DirectPostCaptureRequest::class, $parameters); + /** @var DirectPostCaptureRequest $request */ + + return $request; } /** @@ -208,7 +232,10 @@ public function capture(array $parameters = []) */ public function refund(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\DirectPostRefundRequest', $parameters); + $request = $this->createRequest(DirectPostRefundRequest::class, $parameters); + /** @var DirectPostRefundRequest $request */ + + return $request; } /** @@ -220,7 +247,10 @@ public function refund(array $parameters = []) */ public function void(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\DirectPostReversalRequest', $parameters); + $request = $this->createRequest(DirectPostReversalRequest::class, $parameters); + /** @var DirectPostReversalRequest $request */ + + return $request; } /** @@ -232,7 +262,10 @@ public function void(array $parameters = []) */ public function createEMV3DSOrder(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\EMV3DSOrderRequest', $parameters); + $request = $this->createRequest(EMV3DSOrderRequest::class, $parameters); + /** @var EMV3DSOrderRequest $request */ + + return $request; } /** @@ -244,7 +277,10 @@ public function createEMV3DSOrder(array $parameters = []) */ public function store(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\DirectPostStoreRequest', $parameters); + $request = $this->createRequest(DirectPostStoreRequest::class, $parameters); + /** @var DirectPostStoreRequest $request */ + + return $request; } /** @@ -256,6 +292,6 @@ public function store(array $parameters = []) */ public function webhook(array $data = []) { - return new \Omnipay\NABTransact\Message\DirectPostWebhookRequest($data); + return new DirectPostWebhookRequest($data); } } diff --git a/src/HostedPaymentGateway.php b/src/HostedPaymentGateway.php index 6a3272c..089f372 100644 --- a/src/HostedPaymentGateway.php +++ b/src/HostedPaymentGateway.php @@ -3,6 +3,8 @@ namespace Omnipay\NABTransact; use Omnipay\Common\AbstractGateway; +use Omnipay\NABTransact\Message\HostedPaymentCompletePurchaseRequest; +use Omnipay\NABTransact\Message\HostedPaymentPurchaseRequest; /** * HostedPayment Gateway. @@ -26,7 +28,10 @@ public function getDefaultParameters() */ public function completePurchase(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\HostedPaymentCompletePurchaseRequest', $parameters); + $request = $this->createRequest(HostedPaymentCompletePurchaseRequest::class, $parameters); + /** @var HostedPaymentCompletePurchaseRequest $request */ + + return $request; } /** @@ -85,7 +90,10 @@ public function setReturnUrlText($value) */ public function purchase(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\HostedPaymentPurchaseRequest', $parameters); + $request = $this->createRequest(HostedPaymentPurchaseRequest::class, $parameters); + /** @var HostedPaymentPurchaseRequest $request */ + + return $request; } /** diff --git a/src/Message/DirectPostAbstractRequest.php b/src/Message/DirectPostAbstractRequest.php index 2fab9c1..9412f16 100644 --- a/src/Message/DirectPostAbstractRequest.php +++ b/src/Message/DirectPostAbstractRequest.php @@ -2,6 +2,7 @@ namespace Omnipay\NABTransact\Message; +use Omnipay\Common\CreditCard; use Omnipay\NABTransact\Enums\TransactionType; /** @@ -19,6 +20,11 @@ abstract class DirectPostAbstractRequest extends AbstractRequest */ public $liveEndpoint = 'https://transact.nab.com.au/live/directpostv2/authorise'; + /** + * @var string|int + */ + protected $txnType = '0'; + /** * @return string */ @@ -93,9 +99,9 @@ public function getBaseData() $data['EPS_CURRENCY'] = $currency; } - $card = $this->getCard(); + $card = $this->getParameter('card'); - if ($card) { + if ($card instanceof CreditCard) { if ($billingFirstName = $card->getBillingFirstName()) { $data['EPS_FIRSTNAME'] = $billingFirstName; } diff --git a/src/Message/DirectPostCompletePurchaseRequest.php b/src/Message/DirectPostCompletePurchaseRequest.php index d4b1e39..cf8f528 100644 --- a/src/Message/DirectPostCompletePurchaseRequest.php +++ b/src/Message/DirectPostCompletePurchaseRequest.php @@ -10,7 +10,7 @@ class DirectPostCompletePurchaseRequest extends DirectPostAbstractRequest { /** - * @return array|Exception + * @return array */ public function getData() { @@ -24,7 +24,9 @@ public function getData() } /** - * @param $data + * @param array $data + * + * @return string */ public function generateResponseFingerprint($data) { diff --git a/src/Message/DirectPostCompletePurchaseResponse.php b/src/Message/DirectPostCompletePurchaseResponse.php index 933c214..7630a26 100644 --- a/src/Message/DirectPostCompletePurchaseResponse.php +++ b/src/Message/DirectPostCompletePurchaseResponse.php @@ -23,7 +23,7 @@ public function summaryCode() } /** - * @return string + * @return string|null */ public function getMessage() { @@ -35,7 +35,7 @@ public function getMessage() } /** - * @return string + * @return string|null */ public function getCode() { @@ -47,7 +47,7 @@ public function getCode() } /** - * @return string + * @return string|null */ public function getTransactionReference() { diff --git a/src/Message/SecureXMLAbstractRequest.php b/src/Message/SecureXMLAbstractRequest.php index 7c6c84f..62676e2 100644 --- a/src/Message/SecureXMLAbstractRequest.php +++ b/src/Message/SecureXMLAbstractRequest.php @@ -27,9 +27,9 @@ abstract class SecureXMLAbstractRequest extends AbstractRequest protected $requestType = 'Payment'; /** - * @var string + * @var int */ - protected $txnType; + protected $txnType = 0; /** * @var array @@ -118,7 +118,7 @@ protected function getBaseXML() $messageInfo = $xml->addChild('MessageInfo'); $messageInfo->messageID = $this->getMessageId(); $messageInfo->addChild('messageTimestamp', $this->generateTimestamp()); - $messageInfo->addChild('timeoutValue', 60); + $messageInfo->addChild('timeoutValue', '60'); $messageInfo->addChild('apiVersion', 'xml-4.2'); $merchantInfo = $xml->addChild('MerchantInfo'); @@ -141,13 +141,13 @@ protected function getBasePaymentXML() $payment = $xml->addChild('Payment'); $txnList = $payment->addChild('TxnList'); - $txnList->addAttribute('count', 1); + $txnList->addAttribute('count', '1'); $transaction = $txnList->addChild('Txn'); - $transaction->addAttribute('ID', 1); - $transaction->addChild('txnType', $this->txnType); - $transaction->addChild('txnSource', 23); - $transaction->addChild('txnChannel', 0); - $transaction->addChild('amount', $this->getAmountInteger()); + $transaction->addAttribute('ID', '1'); + $transaction->addChild('txnType', (string) $this->txnType); + $transaction->addChild('txnSource', '23'); + $transaction->addChild('txnChannel', '0'); + $transaction->addChild('amount', (string) $this->getAmountInteger()); $transaction->addChild('currency', $this->getCurrency()); $transaction->addChild('purchaseOrderNo', $this->getTransactionId()); diff --git a/src/Message/SecureXMLResponse.php b/src/Message/SecureXMLResponse.php index 60f1059..c82cc95 100644 --- a/src/Message/SecureXMLResponse.php +++ b/src/Message/SecureXMLResponse.php @@ -55,7 +55,7 @@ public function getRequestType() /** * Gateway approved string if available. * - * @return string + * @return bool */ public function getApproved() { @@ -107,7 +107,7 @@ public function getMessageTimestamp() } /** - * @return string Unique NABTransact bank transaction reference. + * @return string|null Unique NABTransact bank transaction reference. */ public function getTransactionReference() { @@ -117,7 +117,7 @@ public function getTransactionReference() } /** - * @return string Unique NABTransact bank transaction reference. + * @return string|null Unique NABTransact bank transaction reference. */ public function getTransactionId() { @@ -129,7 +129,7 @@ public function getTransactionId() /** * NABTransact transaction amount. * - * @return string + * @return string|null */ public function getTransactionAmount() { @@ -141,7 +141,7 @@ public function getTransactionAmount() /** * NABTransact transaction currency. * - * @return string + * @return string|null */ public function getTransactionCurrency() { diff --git a/src/SecureXMLGateway.php b/src/SecureXMLGateway.php index f620ab5..e4441fd 100644 --- a/src/SecureXMLGateway.php +++ b/src/SecureXMLGateway.php @@ -3,6 +3,12 @@ namespace Omnipay\NABTransact; use Omnipay\Common\AbstractGateway; +use Omnipay\NABTransact\Message\SecureXMLAuthorizeRequest; +use Omnipay\NABTransact\Message\SecureXMLCaptureRequest; +use Omnipay\NABTransact\Message\SecureXMLEchoTestRequest; +use Omnipay\NABTransact\Message\SecureXMLPurchaseRequest; +use Omnipay\NABTransact\Message\SecureXMLRefundRequest; +use Omnipay\NABTransact\Message\SecureXMLRiskPurchaseRequest; use Omnipay\NABTransact\Transport\TransportInterface; /** @@ -125,7 +131,10 @@ public function getTimeoutSeconds() */ public function authorize(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\SecureXMLAuthorizeRequest', $parameters); + $request = $this->createRequest(SecureXMLAuthorizeRequest::class, $parameters); + /** @var SecureXMLAuthorizeRequest $request */ + + return $request; } /** @@ -135,21 +144,30 @@ public function authorize(array $parameters = []) */ public function capture(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\SecureXMLCaptureRequest', $parameters); + $request = $this->createRequest(SecureXMLCaptureRequest::class, $parameters); + /** @var SecureXMLCaptureRequest $request */ + + return $request; } /** * @param array $parameters * - * @return \Omnipay\NABTransact\Message\SecureXMLPurchaseRequest + * @return \Omnipay\NABTransact\Message\SecureXMLPurchaseRequest|\Omnipay\NABTransact\Message\SecureXMLRiskPurchaseRequest */ public function purchase(array $parameters = []) { if ($this->getRiskManagement()) { - return $this->createRequest('\Omnipay\NABTransact\Message\SecureXMLRiskPurchaseRequest', $parameters); + $request = $this->createRequest(SecureXMLRiskPurchaseRequest::class, $parameters); + /** @var SecureXMLRiskPurchaseRequest $request */ + + return $request; } - return $this->createRequest('\Omnipay\NABTransact\Message\SecureXMLPurchaseRequest', $parameters); + $request = $this->createRequest(SecureXMLPurchaseRequest::class, $parameters); + /** @var SecureXMLPurchaseRequest $request */ + + return $request; } /** @@ -159,7 +177,10 @@ public function purchase(array $parameters = []) */ public function refund(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\SecureXMLRefundRequest', $parameters); + $request = $this->createRequest(SecureXMLRefundRequest::class, $parameters); + /** @var SecureXMLRefundRequest $request */ + + return $request; } /** @@ -169,6 +190,9 @@ public function refund(array $parameters = []) */ public function echoTest(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\SecureXMLEchoTestRequest', $parameters); + $request = $this->createRequest(SecureXMLEchoTestRequest::class, $parameters); + /** @var SecureXMLEchoTestRequest $request */ + + return $request; } } diff --git a/src/Transport/OmnipayHttpClientTransport.php b/src/Transport/OmnipayHttpClientTransport.php index c2f204c..96d4f91 100644 --- a/src/Transport/OmnipayHttpClientTransport.php +++ b/src/Transport/OmnipayHttpClientTransport.php @@ -36,15 +36,7 @@ public function send($method, $url, array $headers = [], $body = '', $timeoutSec $responseBody = ''; if (method_exists($response, 'getBody')) { - $stream = $response->getBody(); - - if (is_string($stream)) { - $responseBody = $stream; - } elseif (method_exists($stream, '__toString')) { - $responseBody = (string) $stream; - } elseif (method_exists($stream, 'getContents')) { - $responseBody = (string) $stream->getContents(); - } + $responseBody = (string) $response->getBody(); } return new TransportResponse($statusCode, $responseBody); diff --git a/src/UnionPayGateway.php b/src/UnionPayGateway.php index a275ac1..66b4e5d 100644 --- a/src/UnionPayGateway.php +++ b/src/UnionPayGateway.php @@ -2,6 +2,9 @@ namespace Omnipay\NABTransact; +use Omnipay\NABTransact\Message\UnionPayCompletePurchaseRequest; +use Omnipay\NABTransact\Message\UnionPayPurchaseRequest; + /** * NABTransact UnionPay Gateway. */ @@ -19,7 +22,10 @@ public function getName() */ public function purchase(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\UnionPayPurchaseRequest', $parameters); + $request = $this->createRequest(UnionPayPurchaseRequest::class, $parameters); + /** @var UnionPayPurchaseRequest $request */ + + return $request; } /** @@ -29,6 +35,9 @@ public function purchase(array $parameters = []) */ public function completePurchase(array $parameters = []) { - return $this->createRequest('\Omnipay\NABTransact\Message\UnionPayCompletePurchaseRequest', $parameters); + $request = $this->createRequest(UnionPayCompletePurchaseRequest::class, $parameters); + /** @var UnionPayCompletePurchaseRequest $request */ + + return $request; } } From 53c7fee79e34dabd26f8b838b0504071579a3b84 Mon Sep 17 00:00:00 2001 From: Sujip Thapa Date: Sun, 22 Feb 2026 15:42:16 +0545 Subject: [PATCH 9/9] fix: make curl transport phpstan-safe and modernize README arrays --- README.md | 36 ++++++++++++++++----------------- src/Transport/CurlTransport.php | 4 ---- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 5aca3b3..844b08c 100644 --- a/README.md +++ b/README.md @@ -117,23 +117,23 @@ The following gateways are provided by this package: $gateway->setTestMode(true); $gateway->setHasEMV3DSEnabled(true); - $card = new CreditCard(array( + $card = new CreditCard([ 'firstName' => 'Sujip', 'lastName' => 'Thapa', 'number' => '4444333322221111', 'expiryMonth' => '10', 'expiryYear' => '2030', 'cvv' => '123', - )); + ]); - $response = $gateway->purchase(array( + $response = $gateway->purchase([ 'amount' => '12.00', 'transactionId' => 'ORDER-ZYX8', 'transactionReference' => '11fc42b0-bb7a-41a4-8b3c-096b3fd4d402', 'currency' => 'AUD', 'card' => $card, 'clientIp' => '192.168.1.1' - )) + ]) ->send(); if ($response->isRedirect()) { @@ -156,11 +156,11 @@ The following gateways are provided by this package: $gateway->setTransactionPassword('abcd1234'); $gateway->setTestMode(true); - $response = $gateway->store(array( + $response = $gateway->store([ 'transactionId' => 'STORE-ORDER-100', 'returnUrl' => 'http://example.com/payment/response', 'card' => $card, - ))->send(); + ])->send(); if ($response->isRedirect()) { $response->redirect(); @@ -175,12 +175,12 @@ The following gateways are provided by this package: $gateway->setTransactionPassword('abcd1234'); $gateway->setTestMode(true); - $response = $gateway->capture(array( + $response = $gateway->capture([ 'transactionId' => 'CAPTURE-ORDER-100', 'transactionReference' => 'NAB-ORIGINAL-TXN-ID', 'amount' => '12.00', 'currency' => 'AUD', - ))->send(); + ])->send(); if ($response->isSuccessful()) { echo 'Capture successful: '.$response->getTransactionReference(); @@ -190,33 +190,33 @@ The following gateways are provided by this package: #### DirectPost Refund (Server-to-Server) ```php - $response = $gateway->refund(array( + $response = $gateway->refund([ 'transactionId' => 'REFUND-ORDER-100', 'transactionReference' => 'NAB-SETTLED-TXN-ID', 'amount' => '5.00', 'currency' => 'AUD', - ))->send(); + ])->send(); ``` #### DirectPost Reversal/Void (Server-to-Server) ```php - $response = $gateway->void(array( + $response = $gateway->void([ 'transactionId' => 'VOID-ORDER-100', 'transactionReference' => 'NAB-AUTH-TXN-ID', 'amount' => '12.00', - ))->send(); + ])->send(); ``` #### EMV 3DS Order Creation API ```php - $response = $gateway->createEMV3DSOrder(array( + $response = $gateway->createEMV3DSOrder([ 'amount' => '12.00', 'currency' => 'AUD', 'clientIp' => '203.0.113.10', 'transactionReference' => 'ORDER-REF-100', - ))->send(); + ])->send(); if ($response->isSuccessful()) { echo 'Order ID: '.$response->getOrderId(); @@ -238,12 +238,12 @@ The following gateways are provided by this package: * The parameter transactionId must be alpha-numeric and 8 to 32 characters in length */ - $response = $gateway->purchase(array( + $response = $gateway->purchase([ 'amount' => '12.00', 'transactionId' => '1234566789205067', 'currency' => 'AUD', 'returnUrl' => 'http://example.com/payment/response', - )) + ]) ->send(); if ($response->isRedirect()) { @@ -261,13 +261,13 @@ The following gateways are provided by this package: $gateway->setTestMode(true); - $response = $gateway->completePurchase(array( + $response = $gateway->completePurchase([ 'amount' => '12.00', 'transactionId' => '1234566789205067', 'transactionReference' => '11fc42b0-bb7a-41a4-8b3c-096b3fd4d402', 'currency' => 'AUD', 'returnUrl' => 'http://example.com/payment/response', - )) + ]) ->send(); if ($response->isSuccessful()) { diff --git a/src/Transport/CurlTransport.php b/src/Transport/CurlTransport.php index ad5f1d0..7e431a8 100644 --- a/src/Transport/CurlTransport.php +++ b/src/Transport/CurlTransport.php @@ -23,10 +23,6 @@ public function send($method, $url, array $headers = [], $body = '', $timeoutSec $handle = curl_init(); - if ($handle === false) { - throw new RuntimeException('Unable to initialize cURL transport.'); - } - $formattedHeaders = []; foreach ($headers as $name => $value) { $formattedHeaders[] = $name.': '.$value;