diff --git a/.github/workflows/codestyle.yml b/.github/workflows/codestyle.yml
index db0ac22a..ea849d3b 100644
--- a/.github/workflows/codestyle.yml
+++ b/.github/workflows/codestyle.yml
@@ -1,35 +1,37 @@
-name: CheckStyleAndLint
+name: Code Style
on:
pull_request:
- paths:
- - "src/**.php"
- - "example/**.php"
-
jobs:
- phpcs:
+ php-cs-fixer:
runs-on: ubuntu-latest
- strategy:
- fail-fast: true
- matrix:
- php: [7.4, 8.0, 8.1, 8.2, 8.3, 8.4]
- stability: [prefer-stable]
- name: PHP ${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }}
+ name: PHP CS Fixer
steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
+ - name: Checkout code
+ uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
- php-version: ${{ matrix.php }}
- tools: cs2pr, phplint, phpcs
+ php-version: '8.2'
+ extensions: json, pcre, fileinfo
+ coverage: none
+
+ - name: Get Composer cache directory
+ id: composer-cache
+ run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
+
+ - name: Cache Composer dependencies
+ uses: actions/cache@v4
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-cs-${{ hashFiles('**/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-cs-
- - name: Run phplint
- run: phplint --no-configuration --no-cache --no-interaction ./src/* ./example/*
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress
- - name: Run phpcs
- run: phpcs -q --report=checkstyle --standard=phpcs.xml --extensions=php ./src/* | cs2pr
+ - name: Run PHP CS Fixer
+ run: composer cs
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 00000000..84c84bcb
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,42 @@
+name: Tests
+
+on:
+ pull_request:
+jobs:
+ tests:
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
+
+ name: PHP ${{ matrix.php }}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: json, pcre, fileinfo
+ coverage: none
+
+ - name: Get Composer cache directory
+ id: composer-cache
+ run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
+
+ - name: Cache Composer dependencies
+ uses: actions/cache@v4
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-${{ matrix.php }}-
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress
+
+ - name: Run tests
+ run: composer test
diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
index 21867d19..3ab1a300 100644
--- a/.php-cs-fixer.php
+++ b/.php-cs-fixer.php
@@ -2,7 +2,7 @@
$finder = Symfony\Component\Finder\Finder::create()
->in([
__DIR__ . '/src',
- __DIR__ . '/example'
+ __DIR__ . '/example',
])
->name('*.php')
->notPath('bootstrap/*')
@@ -17,7 +17,7 @@
'array_syntax' => ['syntax' => 'short'],
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'no_unused_imports' => true,
- 'not_operator_with_successor_space' => true,
+ 'not_operator_with_successor_space' => false,
'trailing_comma_in_multiline' => true,
'phpdoc_scalar' => true,
'unary_operator_spaces' => true,
@@ -25,15 +25,22 @@
'blank_line_before_statement' => [
'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
],
+ 'no_extra_blank_lines' => [
+ 'tokens' => [
+ 'extra',
+ 'throw',
+ 'use',
+ ],
+ ],
'braces' => [
- 'allow_single_line_closure' => false,
+ 'allow_single_line_closure' => true,
'position_after_functions_and_oop_constructs' => 'next',
- 'position_after_anonymous_constructs' => 'next',
- 'position_after_control_structures' => 'next',
+ 'position_after_anonymous_constructs' => 'same',
+ 'position_after_control_structures' => 'same',
],
'curly_braces_position' => [
'classes_opening_brace' => 'next_line_unless_newline_at_signature_end',
- 'control_structures_opening_brace' => 'next_line_unless_newline_at_signature_end'
+ 'control_structures_opening_brace' => 'same_line',
],
'phpdoc_single_line_var_spacing' => true,
'phpdoc_var_without_name' => true,
@@ -43,4 +50,4 @@
],
'single_trait_insert_per_statement' => true,
])
- ->setFinder($finder);
\ No newline at end of file
+ ->setFinder($finder);
diff --git a/composer.json b/composer.json
index 86ca4a15..a7e5705a 100644
--- a/composer.json
+++ b/composer.json
@@ -19,7 +19,8 @@
"symfony/var-dumper": "^5.4.9",
"phpunit/phpunit": "^9.5",
"vlucas/phpdotenv": "^5.4",
- "friendsofphp/php-cs-fixer": "^3.13"
+ "friendsofphp/php-cs-fixer": "^3.13",
+ "mockery/mockery": "^1.6"
},
"authors": [{
"name": "Buckaroo",
@@ -36,5 +37,12 @@
"psr-4": {
"Tests\\": "tests/"
}
+ },
+ "scripts": {
+ "test": "phpunit",
+ "test:unit": "phpunit --testsuite Unit",
+ "test:feature": "phpunit --testsuite Feature",
+ "cs": "php-cs-fixer fix --dry-run --diff",
+ "cs:fix": "php-cs-fixer fix"
}
}
diff --git a/example/additional_services/voucher.php b/example/additional_services/voucher.php
index 0b9eb6c0..86e51c6e 100644
--- a/example/additional_services/voucher.php
+++ b/example/additional_services/voucher.php
@@ -6,7 +6,6 @@
$buckaroo = new BuckarooClient($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
-
//Create voucher
$response = $buckaroo->method('buckaroovoucher')->create(
[
@@ -26,7 +25,6 @@
]
);
-
//Pay
$response = $buckaroo->method('buckaroovoucher')->payRemainder(
[
@@ -37,7 +35,6 @@
]
);
-
//Refund
$response = $buckaroo->method('buckaroovoucher')->refund(
[
diff --git a/example/responses/push.php b/example/responses/push.php
index 6158bd15..a71e30a4 100644
--- a/example/responses/push.php
+++ b/example/responses/push.php
@@ -38,7 +38,6 @@
$reply_handler->isValid(); // Return either true or false
//END HTTP POST PUSH
-
//START JSON PUSH
$auth_header = 'IBjihN7Fhp:0YvyjYAzDQ28W+hQi80f2nhe0Z1QFJLbz7IH//6LsAU=:cad1832100784f57a6e6de835d9f3638:1658227572';
$post_data = '{"Transaction":{"Key":"5340604668D74435AA344E1428ED1292","Invoice":"62d68b6c8ab0c","ServiceCode":"ideal",
diff --git a/example/transactions/applepay_redirect.php b/example/transactions/applepay_redirect.php
index a7ddea61..4fffab0c 100644
--- a/example/transactions/applepay_redirect.php
+++ b/example/transactions/applepay_redirect.php
@@ -15,7 +15,6 @@
'continueOnIncomplete' => '1',
]);
-
//Refund
$response = $buckaroo->method('applepay')->refund([
'amountCredit' => 10,
diff --git a/example/transactions/bancontact.php b/example/transactions/bancontact.php
index 3cee7153..1855d365 100644
--- a/example/transactions/bancontact.php
+++ b/example/transactions/bancontact.php
@@ -35,7 +35,6 @@
'originalTransactionKey' => '91D08EC01F414926A4CA29C059XXXXXX',
]);
-
//Refund
$response = $buckaroo->method('bancontactmrcash')->refund([
'invoice' => '', //Set invoice number of the transaction to refund
diff --git a/example/transactions/billink.php b/example/transactions/billink.php
index 38b11e24..2b50613c 100644
--- a/example/transactions/billink.php
+++ b/example/transactions/billink.php
@@ -78,7 +78,6 @@
],
]);
-
//Refund
$response = $buckaroo->method('billink')->refund([
'amountCredit' => 10,
diff --git a/example/transactions/bizum.php b/example/transactions/bizum.php
index e156731c..2fa6c4e3 100644
--- a/example/transactions/bizum.php
+++ b/example/transactions/bizum.php
@@ -19,4 +19,4 @@
'amountCredit' => 10,
'invoice' => 'testinvoice 123',
'originalTransactionKey' => '4E8BD922192746C3918BF4077CXXXXXX',
-]);
\ No newline at end of file
+]);
diff --git a/example/transactions/blik.php b/example/transactions/blik.php
index d0b81a11..f30ba054 100644
--- a/example/transactions/blik.php
+++ b/example/transactions/blik.php
@@ -9,9 +9,9 @@
//Also accepts json
//Pay
$response = $buckaroo->method('blik')->pay([
- 'currency' => 'PLN',
- 'amountDebit' => 10.00,
- 'invoice' => 'Blik Test Plugins Example',
- 'description' => 'Blik Test Plugins Example',
- 'email' => 'test@buckar00.nl'
-]);
\ No newline at end of file
+ 'currency' => 'PLN',
+ 'amountDebit' => 10.00,
+ 'invoice' => 'Blik Test Plugins Example',
+ 'description' => 'Blik Test Plugins Example',
+ 'email' => 'test@buckar00.nl',
+]);
diff --git a/example/transactions/clicktopay.php b/example/transactions/clicktopay.php
index 01c4358b..42b668bb 100644
--- a/example/transactions/clicktopay.php
+++ b/example/transactions/clicktopay.php
@@ -14,7 +14,7 @@
'description' => "test ClickToPay",
"clientIP" => [
"type" => 0,
- "address" => "0.0.0.0"
+ "address" => "0.0.0.0",
],
'continueOnIncomplete' => "1",
]);
diff --git a/example/transactions/externalPayment.php b/example/transactions/externalPayment.php
index 85a881fa..5f7776ca 100644
--- a/example/transactions/externalPayment.php
+++ b/example/transactions/externalPayment.php
@@ -9,11 +9,11 @@
$response = $buckaroo->method('externalPayment')->pay([
'invoice' => uniqid(),
'amountDebit' => 10.10,
- 'channel' => 'BackOffice'
+ 'channel' => 'BackOffice',
]);
$buckaroo->method('externalPayment')->refund([
'amountCredit' => 10,
'invoice' => 'testinvoice 123',
'originalTransactionKey' => $response->getTransactionKey(),
-]);
\ No newline at end of file
+]);
diff --git a/example/transactions/giftcards_redirect.php b/example/transactions/giftcards_redirect.php
index 038a411c..87d0e437 100644
--- a/example/transactions/giftcards_redirect.php
+++ b/example/transactions/giftcards_redirect.php
@@ -18,7 +18,6 @@
'continueOnIncomplete' => '1',
]);
-
//Refund
$response = $buckaroo->method('giftcard')->refund([
'amountCredit' => 10,
diff --git a/example/transactions/in3-abn-amro-achteraf-betalen.php b/example/transactions/in3-abn-amro-achteraf-betalen.php
index 3d40a9e0..d2d05c63 100644
--- a/example/transactions/in3-abn-amro-achteraf-betalen.php
+++ b/example/transactions/in3-abn-amro-achteraf-betalen.php
@@ -7,24 +7,24 @@
$buckaroo = new BuckarooClient($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
$payload = [
- 'amountDebit' => 52.30,
- 'description' => 'ABN AMRO Achteraf Betalen payment via In3',
- 'order' => uniqid(),
- 'invoice' => uniqid(),
- 'clientIP' => '127.0.0.1',
- 'route' => 'abn_b2b', // This parameter routes the In3 payment to ABN AMRO Achteraf Betalen
- 'billing' => [
- 'recipient' => [
- 'category' => 'B2C',
- 'initials' => 'J',
- 'firstName' => 'John',
- 'lastName' => 'Dona',
- 'birthDate' => '1990-01-01',
+ 'amountDebit' => 52.30,
+ 'description' => 'ABN AMRO Achteraf Betalen payment via In3',
+ 'order' => uniqid(),
+ 'invoice' => uniqid(),
+ 'clientIP' => '127.0.0.1',
+ 'route' => 'abn_b2b', // This parameter routes the In3 payment to ABN AMRO Achteraf Betalen
+ 'billing' => [
+ 'recipient' => [
+ 'category' => 'B2C',
+ 'initials' => 'J',
+ 'firstName' => 'John',
+ 'lastName' => 'Dona',
+ 'birthDate' => '1990-01-01',
'customerNumber' => '12345',
- 'phone' => '0612345678',
- 'country' => 'NL',
- 'companyName' => 'My Company B.V.',
- 'chamberOfCommerce' => '123456'
+ 'phone' => '0612345678',
+ 'country' => 'NL',
+ 'companyName' => 'My Company B.V.',
+ 'chamberOfCommerce' => '123456',
],
'address' => [
'street' => 'Hoofdstraat',
@@ -45,7 +45,7 @@
'careOf' => 'John Smith',
'firstName' => 'John',
'lastName' => 'Do',
- 'chamberOfCommerce' => '123456'
+ 'chamberOfCommerce' => '123456',
],
'address' => [
'street' => 'Kalverstraat',
@@ -84,7 +84,7 @@
'quantity' => '1',
'price' => '2',
],
- ]
+ ],
];
//Also accepts json
@@ -96,4 +96,4 @@
'amountCredit' => 10,
'invoice' => '10000480',
'originalTransactionKey' => '9AA4C81A08A84FA7B68E6A6A6291XXXX',
-]);
\ No newline at end of file
+]);
diff --git a/example/transactions/in3.php b/example/transactions/in3.php
index 1fdbddb2..5d90789b 100644
--- a/example/transactions/in3.php
+++ b/example/transactions/in3.php
@@ -7,23 +7,23 @@
$buckaroo = new BuckarooClient($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
$payload = [
- 'amountDebit' => 52.30,
- 'description' => 'in3 pay',
- 'order' => uniqid(),
- 'invoice' => uniqid(),
- 'clientIP' => '127.0.0.1',
- 'billing' => [
- 'recipient' => [
- 'category' => 'B2C',
- 'initials' => 'J',
- 'firstName' => 'John',
- 'lastName' => 'Dona',
- 'birthDate' => '1990-01-01',
- 'customerNumber' => '12345',
- 'phone' => '0612345678',
- 'country' => 'NL',
+ 'amountDebit' => 52.30,
+ 'description' => 'in3 pay',
+ 'order' => uniqid(),
+ 'invoice' => uniqid(),
+ 'clientIP' => '127.0.0.1',
+ 'billing' => [
+ 'recipient' => [
+ 'category' => 'B2C',
+ 'initials' => 'J',
+ 'firstName' => 'John',
+ 'lastName' => 'Dona',
+ 'birthDate' => '1990-01-01',
+ 'customerNumber' => '12345',
+ 'phone' => '0612345678',
+ 'country' => 'NL',
'companyName' => 'My Company B.V.',
- 'chamberOfCommerce' => '123456'
+ 'chamberOfCommerce' => '123456',
],
'address' => [
'street' => 'Hoofdstraat',
@@ -44,7 +44,7 @@
'careOf' => 'John Smith',
'firstName' => 'John',
'lastName' => 'Do',
- 'chamberOfCommerce' => '123456'
+ 'chamberOfCommerce' => '123456',
],
'address' => [
'street' => 'Kalverstraat',
@@ -83,7 +83,7 @@
'quantity' => '1',
'price' => '2',
],
- ]
+ ],
];
//Also accepts json
diff --git a/example/transactions/kbc.php b/example/transactions/kbc.php
index 1f6ef22b..b545ca32 100644
--- a/example/transactions/kbc.php
+++ b/example/transactions/kbc.php
@@ -13,7 +13,6 @@
'amountDebit' => 10.10,
]);
-
//Refund
$response = $buckaroo->method('kbcpaymentbutton')->refund([
'invoice' => '', //Set invoice number of the transaction to refund
diff --git a/example/transactions/noservicespecified.php b/example/transactions/noservicespecified.php
index 31f2c3f5..e7d09535 100644
--- a/example/transactions/noservicespecified.php
+++ b/example/transactions/noservicespecified.php
@@ -18,7 +18,6 @@
'continueOnIncomplete' => '1',
]);
-
//Refund
$response = $buckaroo->method(null)->refund([
'invoice' => '', //Set invoice number of the transaction to refund
diff --git a/example/transactions/payment_initiation.php b/example/transactions/payment_initiation.php
index c003e853..9b627e82 100644
--- a/example/transactions/payment_initiation.php
+++ b/example/transactions/payment_initiation.php
@@ -19,4 +19,4 @@
'invoice' => '', //Set invoice number of the transaction to refund
'originalTransactionKey' => '', //Set transaction key of the transaction to refund
'amountCredit' => 10,
-]);
\ No newline at end of file
+]);
diff --git a/example/transactions/swish.php b/example/transactions/swish.php
index 2c1ff995..a9d7737a 100644
--- a/example/transactions/swish.php
+++ b/example/transactions/swish.php
@@ -20,4 +20,4 @@
'amountCredit' => 10,
'invoice' => 'testinvoice 123',
'originalTransactionKey' => '4E8BD922192746C3918BF4077CXXXXXX',
-]);
\ No newline at end of file
+]);
diff --git a/example/transactions/thunes.php b/example/transactions/thunes.php
index 78d94847..574a0ad6 100644
--- a/example/transactions/thunes.php
+++ b/example/transactions/thunes.php
@@ -3,7 +3,6 @@
require_once '../bootstrap.php';
use Buckaroo\BuckarooClient;
-use Buckaroo\Resources\Constants\RecipientCategory;
$buckaroo = new BuckarooClient($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
diff --git a/example/transactions/twint.php b/example/transactions/twint.php
index 240ceca2..6b1fd429 100644
--- a/example/transactions/twint.php
+++ b/example/transactions/twint.php
@@ -20,4 +20,4 @@
'amountCredit' => 10,
'invoice' => 'testinvoice 123',
'originalTransactionKey' => '4E8BD922192746C3918BF4077CXXXXXX',
-]);
\ No newline at end of file
+]);
diff --git a/example/transactions/wero.php b/example/transactions/wero.php
index b8909940..d25da329 100644
--- a/example/transactions/wero.php
+++ b/example/transactions/wero.php
@@ -40,4 +40,4 @@
'amountCredit' => 10,
'invoice' => 'testinvoice 123',
'originalTransactionKey' => '4E8BD922192746C3918BF4077CXXXXXX',
-]);
\ No newline at end of file
+]);
diff --git a/phpcs.xml b/phpcs.xml
index c11c96bc..f1969caa 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -21,5 +21,8 @@
src/PaymentMethods/iDealProcessing/iDealProcessing.php
+
+ tests/*
+
\ No newline at end of file
diff --git a/phpunit.xml b/phpunit.xml
index b57d1976..a601caf6 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,12 +1,24 @@
+
-
- tests
+
+ tests/Unit
+
+
+ tests/Feature
+
+
+ src
+
+
+
+
+
diff --git a/src/BuckarooClient.php b/src/BuckarooClient.php
index 684f8419..c8a52f12 100644
--- a/src/BuckarooClient.php
+++ b/src/BuckarooClient.php
@@ -53,13 +53,11 @@ class BuckarooClient
*/
public function __construct($websiteKey, ?string $secretKey = null, ?string $mode = null)
{
- if ($websiteKey instanceof Config)
- {
+ if ($websiteKey instanceof Config) {
$this->config = $websiteKey;
}
- if (is_string($websiteKey))
- {
+ if (is_string($websiteKey)) {
$this->config = $this->getConfig($websiteKey, $secretKey, $mode);
}
@@ -75,7 +73,6 @@ public function method(?string $method = null): PaymentFacade
return new PaymentFacade($this->client, $method);
}
-
public function getActiveSubscriptions(): array
{
return (new ActiveSubscriptions($this->client))->get();
@@ -148,8 +145,7 @@ public function client()
*/
private function getConfig(string $websiteKey, string $secretKey, ?string $mode = null): ?Config
{
- if ($websiteKey && $secretKey)
- {
+ if ($websiteKey && $secretKey) {
return new DefaultConfig($websiteKey, $secretKey, $mode);
}
diff --git a/src/Config/Config.php b/src/Config/Config.php
index e8b3f38f..572454c3 100644
--- a/src/Config/Config.php
+++ b/src/Config/Config.php
@@ -197,8 +197,7 @@ public function isLiveMode(): bool
*/
public function mode(?string $mode = null): string
{
- if ($mode && in_array($mode, [self::LIVE_MODE, self::TEST_MODE]))
- {
+ if ($mode && in_array($mode, [self::LIVE_MODE, self::TEST_MODE])) {
$this->mode = $mode;
}
@@ -282,8 +281,7 @@ public function moduleVersion(): string
*/
public function culture(): string
{
- if (! empty($this->culture))
- {
+ if (!empty($this->culture)) {
return $this->culture;
}
@@ -295,10 +293,10 @@ public function culture(): string
*/
public function channel(): string
{
- if (! empty($this->channel))
- {
+ if (!empty($this->channel)) {
return $this->channel;
}
+
return 'Web';
}
@@ -310,10 +308,8 @@ public function merge(array $payload)
{
$payload = $this->filterNonUpdatableKeys($payload);
- foreach ($payload as $key => $value)
- {
- if (property_exists($this, $key))
- {
+ foreach ($payload as $key => $value) {
+ if (property_exists($this, $key)) {
$this->$key = $value;
}
}
@@ -330,7 +326,7 @@ private function filterNonUpdatableKeys($payload)
$filter = ['websiteKey', 'secretKey'];
return array_filter($payload, function ($k) use ($filter) {
- return ! in_array($k, $filter);
+ return !in_array($k, $filter);
}, ARRAY_FILTER_USE_KEY);
}
@@ -342,10 +338,8 @@ public function get(array $properties = [])
{
$values = [];
- foreach ($properties as $property)
- {
- if (method_exists($this, $property))
- {
+ foreach ($properties as $property) {
+ if (method_exists($this, $property)) {
$values[$property] = $this->$property();
}
}
diff --git a/src/Exceptions/BuckarooException.php b/src/Exceptions/BuckarooException.php
index 9b40638d..9d28464c 100644
--- a/src/Exceptions/BuckarooException.php
+++ b/src/Exceptions/BuckarooException.php
@@ -51,8 +51,7 @@ public function __construct(?Subject $logger, string $message = "", int $code =
*/
private function log($logger, $message)
{
- if ($logger)
- {
+ if ($logger) {
$this->logger = $logger;
$this->logger->error($message);
}
diff --git a/src/Handlers/Credentials.php b/src/Handlers/Credentials.php
index 5fd81fe5..e367e667 100644
--- a/src/Handlers/Credentials.php
+++ b/src/Handlers/Credentials.php
@@ -58,16 +58,13 @@ public function confirm(): bool
$request = new TransactionRequest;
- try
- {
+ try {
$response = $this->client->specification('ideal', 2, $request);
- } catch (BuckarooException $e)
- {
+ } catch (BuckarooException $e) {
return false;
}
- if ($response->getHttpResponse()->getStatusCode() == 200)
- {
+ if ($response->getHttpResponse()->getStatusCode() == 200) {
return true;
}
diff --git a/src/Handlers/HMAC/Hmac.php b/src/Handlers/HMAC/Hmac.php
index 09d16362..ed003316 100644
--- a/src/Handlers/HMAC/Hmac.php
+++ b/src/Handlers/HMAC/Hmac.php
@@ -28,8 +28,7 @@ abstract class Hmac
*/
public function uri($uri = null)
{
- if ($uri)
- {
+ if ($uri) {
$uri = preg_replace("#^[^:/.]*[:/]+#i", "", $uri);
$this->uri = strtolower(urlencode($uri));
@@ -46,10 +45,8 @@ public function base64Data($data = null)
{
$this->base64Data = '';
- if ($data)
- {
- if (is_array($data))
- {
+ if ($data) {
+ if (is_array($data)) {
$data = mb_convert_encoding(
json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION),
'UTF-8',
@@ -71,8 +68,7 @@ public function base64Data($data = null)
*/
public function nonce($nonce = null)
{
- if ($nonce)
- {
+ if ($nonce) {
$this->nonce = $nonce;
}
@@ -85,8 +81,7 @@ public function nonce($nonce = null)
*/
public function time($time = null)
{
- if ($time)
- {
+ if ($time) {
$this->time = $time;
}
diff --git a/src/Handlers/HMAC/Validator.php b/src/Handlers/HMAC/Validator.php
index b3efd00b..2a60731c 100644
--- a/src/Handlers/HMAC/Validator.php
+++ b/src/Handlers/HMAC/Validator.php
@@ -95,8 +95,7 @@ public function validate(string $header, string $uri, string $method, $data)
*/
public function validateOrFail(string $header, string $uri, string $method, $data)
{
- if ($this->validate($header, $uri, $method, $data))
- {
+ if ($this->validate($header, $uri, $method, $data)) {
return true;
}
diff --git a/src/Handlers/Logging/DefaultLogger.php b/src/Handlers/Logging/DefaultLogger.php
index a0b4fccc..11c53d7b 100644
--- a/src/Handlers/Logging/DefaultLogger.php
+++ b/src/Handlers/Logging/DefaultLogger.php
@@ -37,13 +37,11 @@ class DefaultLogger implements Subject, LoggerInterface
*/
public function __construct()
{
- if (($_ENV['BPE_LOG'] ?? false) === 'true')
- {
+ if (($_ENV['BPE_LOG'] ?? false) === 'true') {
$this->attach(new Monolog());
}
- if (($_ENV['BPE_REPORT_ERROR'] ?? false) === 'true')
- {
+ if (($_ENV['BPE_REPORT_ERROR'] ?? false) === 'true') {
$this->attach(new ErrorReporter());
}
}
@@ -54,18 +52,15 @@ public function __construct()
*/
public function attach($observer)
{
- if (is_array($observer))
- {
- foreach ($observer as $singleObserver)
- {
+ if (is_array($observer)) {
+ foreach ($observer as $singleObserver) {
$this->attach($singleObserver);
}
return $this;
}
- if ($observer instanceof Observer)
- {
+ if ($observer instanceof Observer) {
$this->observers[] = $observer;
}
@@ -162,8 +157,7 @@ public function info($message, array $context = []): void
*/
public function debug($message, array $context = []): void
{
- if ($_ENV['BPE_DEBUG'] ?? false)
- {
+ if ($_ENV['BPE_DEBUG'] ?? false) {
$this->notify('debug', $message, $context);
}
}
@@ -187,8 +181,7 @@ public function log($level, $message, array $context = []): void
*/
public function notify(string $method, string $message, array $context = [])
{
- foreach ($this->observers as $observer)
- {
+ foreach ($this->observers as $observer) {
$observer->handle($method, $message, $context);
}
diff --git a/src/Handlers/Logging/Observers/ErrorReporter.php b/src/Handlers/Logging/Observers/ErrorReporter.php
index b98a7575..177cf820 100644
--- a/src/Handlers/Logging/Observers/ErrorReporter.php
+++ b/src/Handlers/Logging/Observers/ErrorReporter.php
@@ -14,8 +14,7 @@ class ErrorReporter implements Observer
public function handle(string $method, string $message, array $context = [])
{
- if (in_array($method, $this->reportables))
- {
+ if (in_array($method, $this->reportables)) {
//print("Fire off message to mail/report server/slack");
}
diff --git a/src/Handlers/Reply/ReplyHandler.php b/src/Handlers/Reply/ReplyHandler.php
index d9d04e10..1cde9e24 100644
--- a/src/Handlers/Reply/ReplyHandler.php
+++ b/src/Handlers/Reply/ReplyHandler.php
@@ -87,23 +87,20 @@ private function setStrategy()
{
$data = $this->data;
- if (is_string($data))
- {
+ if (is_string($data)) {
$data = json_decode($data, true);
}
if (($this->contains('Transaction', $data) || $this->contains('DataRequest', $data)) &&
$this->auth_header &&
$this->uri
- )
- {
+ ) {
$this->strategy = new Json($this->config, $data, $this->auth_header, $this->uri);
return $this;
}
- if ($this->contains('brq_', $data) || $this->contains('BRQ_', $data))
- {
+ if ($this->contains('brq_', $data) || $this->contains('BRQ_', $data)) {
$this->strategy = new HttpPost($this->config, $data);
return $this;
@@ -119,15 +116,12 @@ private function setStrategy()
*/
private function contains(string $needle, array $data, bool $strict = false): bool
{
- foreach (array_keys($data) as $key)
- {
- if ($strict && $key == $needle)
- {
+ foreach (array_keys($data) as $key) {
+ if ($strict && $key == $needle) {
return true;
}
- if (! $strict && str_contains($key, $needle))
- {
+ if (!$strict && strpos($key, $needle) !== false) {
return true;
}
}
@@ -148,8 +142,7 @@ public function isValid()
*/
public function data($key = null)
{
- if ($key)
- {
+ if ($key) {
return $this->data[$key] ?? $this->data[strtolower($key)] ?? $this->data[strtoupper($key)] ?? null;
}
diff --git a/src/Models/Adapters/ServiceParametersKeysAdapter.php b/src/Models/Adapters/ServiceParametersKeysAdapter.php
index 46c13e82..67c07d46 100644
--- a/src/Models/Adapters/ServiceParametersKeysAdapter.php
+++ b/src/Models/Adapters/ServiceParametersKeysAdapter.php
@@ -52,8 +52,7 @@ public function __construct(Model $model)
*/
public function __get($property)
{
- if (property_exists($this->model, $property))
- {
+ if (property_exists($this->model, $property)) {
return $this->model->$property;
}
diff --git a/src/Models/AdditionalParameters.php b/src/Models/AdditionalParameters.php
index 5d45265c..79b8c740 100644
--- a/src/Models/AdditionalParameters.php
+++ b/src/Models/AdditionalParameters.php
@@ -53,10 +53,8 @@ public function __construct(?array $values = null, $isDataRequest = false)
*/
public function setProperties(?array $data)
{
- foreach ($data ?? [] as $name => $value)
- {
- if($this->isDataRequest)
- {
+ foreach ($data ?? [] as $name => $value) {
+ if ($this->isDataRequest) {
$this->List[] = [
'Value' => $value,
'Name' => $name,
diff --git a/src/Models/ClientIP.php b/src/Models/ClientIP.php
index 13818dcb..80c6423e 100644
--- a/src/Models/ClientIP.php
+++ b/src/Models/ClientIP.php
@@ -72,8 +72,7 @@ private function getRemoteIp()
/**
* Get the forwarded IP if it exists
*/
- if (isset($headers['X-Forwarded-For']) && filter_var($headers['X-Forwarded-For'], FILTER_VALIDATE_IP))
- {
+ if (isset($headers['X-Forwarded-For']) && filter_var($headers['X-Forwarded-For'], FILTER_VALIDATE_IP)) {
return $headers['X-Forwarded-For'];
}
@@ -83,8 +82,7 @@ private function getRemoteIp()
return $headers['HTTP_X_FORWARDED_FOR'];
}
- if (isset($_SERVER['REMOTE_ADDR']) && filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP))
- {
+ if (isset($_SERVER['REMOTE_ADDR']) && filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP)) {
return $_SERVER['REMOTE_ADDR'];
}
diff --git a/src/Models/CustomParameters.php b/src/Models/CustomParameters.php
index 3702b86d..dfd2a8eb 100644
--- a/src/Models/CustomParameters.php
+++ b/src/Models/CustomParameters.php
@@ -25,7 +25,7 @@ class CustomParameters extends Model
/**
* @var array
*/
- protected array $List;
+ protected array $List = [];
/**
* @param array|null $data
@@ -33,8 +33,7 @@ class CustomParameters extends Model
*/
public function setProperties(?array $data)
{
- foreach ($data ?? [] as $name => $value)
- {
+ foreach ($data ?? [] as $name => $value) {
$this->List[] = [
'Value' => $value,
'Name' => $name,
diff --git a/src/Models/Model.php b/src/Models/Model.php
index 16f2b799..4f35bee2 100644
--- a/src/Models/Model.php
+++ b/src/Models/Model.php
@@ -38,8 +38,7 @@ public function __construct(?array $values = null)
*/
public function __get($property)
{
- if (property_exists($this, $property) && isset($this->$property))
- {
+ if (property_exists($this, $property) && isset($this->$property)) {
return $this->$property;
}
@@ -53,8 +52,7 @@ public function __get($property)
*/
public function __set($property, $value)
{
- if (property_exists($this, $property))
- {
+ if (property_exists($this, $property)) {
$this->$property = $value;
}
@@ -75,10 +73,8 @@ public function getObjectVars()
*/
public function setProperties(?array $data)
{
- if ($data)
- {
- foreach ($data ?? [] as $property => $value)
- {
+ if ($data) {
+ foreach ($data ?? [] as $property => $value) {
$this->$property = $value;
}
}
@@ -109,15 +105,12 @@ public function toArray() : array
*/
private function recursiveToArray(array $array) : array
{
- foreach ($array as $key => $value)
- {
- if (is_array($value))
- {
+ foreach ($array as $key => $value) {
+ if (is_array($value)) {
$array[$key] = $this->recursiveToArray($value);
}
- if (is_a($value, Arrayable::class))
- {
+ if (is_a($value, Arrayable::class)) {
$array[$key] = $value->toArray();
}
}
diff --git a/src/Models/Payload/DataRequestPayload.php b/src/Models/Payload/DataRequestPayload.php
index a9c0e323..176a0399 100644
--- a/src/Models/Payload/DataRequestPayload.php
+++ b/src/Models/Payload/DataRequestPayload.php
@@ -33,8 +33,7 @@ class DataRequestPayload extends Payload
*/
public function setProperties(?array $data)
{
- if (isset($data['additionalParameters']))
- {
+ if (isset($data['additionalParameters'])) {
$this->additionalParameters = new AdditionalParameters($data['additionalParameters'], true);
unset($data['additionalParameters']);
diff --git a/src/Models/Payload/Payload.php b/src/Models/Payload/Payload.php
index 7015f926..26e3adcd 100644
--- a/src/Models/Payload/Payload.php
+++ b/src/Models/Payload/Payload.php
@@ -123,22 +123,19 @@ class Payload extends Model
public function setProperties(?array $data)
{
- if (isset($data['customParameters']))
- {
+ if (isset($data['customParameters'])) {
$this->customParameters = new CustomParameters($data['customParameters']);
unset($data['customParameters']);
}
- if (isset($data['additionalParameters']))
- {
+ if (isset($data['additionalParameters'])) {
$this->additionalParameters = new AdditionalParameters($data['additionalParameters']);
unset($data['additionalParameters']);
}
- if (isset($data['clientIP']))
- {
+ if (isset($data['clientIP'])) {
$this->clientIP = new ClientIP($data['clientIP']['address'] ?? null, $data['clientIP']['type'] ?? null);
unset($data['clientIP']);
diff --git a/src/Models/ServiceList.php b/src/Models/ServiceList.php
index d5b28b40..ff8e0364 100644
--- a/src/Models/ServiceList.php
+++ b/src/Models/ServiceList.php
@@ -61,8 +61,7 @@ public function __construct(string $name, int $version, string $action, ?Model $
$this->parameterService = new DefaultParameters($this);
- if ($model)
- {
+ if ($model) {
$this->decorateParameters($model);
$this->parameterService->data();
}
@@ -86,18 +85,15 @@ public function parameters(): array
public function appendParameter($value, $key = null)
{
/* Check value pass multiple, iterate through it*/
- if (is_array($value) && is_array(current($value)))
- {
- foreach ($value as $singleValue)
- {
+ if (is_array($value) && is_array(current($value))) {
+ foreach ($value as $singleValue) {
$this->appendParameter($singleValue, $key);
}
return $this;
}
- if ($key)
- {
+ if ($key) {
$this->parameters[$key] = $value;
return $this;
@@ -131,10 +127,8 @@ protected function decorateParameters(Model $model, ?string $groupType = null, ?
*/
protected function iterateThroughObject(Model $model, array $array, ?string $keyName = null)
{
- foreach ($array as $key => $value)
- {
- if ($model instanceof ServiceParameter && $value instanceof Model)
- {
+ foreach ($array as $key => $value) {
+ if ($model instanceof ServiceParameter && $value instanceof Model) {
$this->decorateParameters(
$value,
$model->getGroupType($keyName ?? $key),
@@ -144,8 +138,7 @@ protected function iterateThroughObject(Model $model, array $array, ?string $key
continue;
}
- if (is_array($value) && count($value))
- {
+ if (is_array($value) && count($value)) {
$this->iterateThroughObject($model, $value, $key);
}
}
diff --git a/src/Models/ServiceParameter.php b/src/Models/ServiceParameter.php
index 80903470..256a5835 100644
--- a/src/Models/ServiceParameter.php
+++ b/src/Models/ServiceParameter.php
@@ -33,10 +33,8 @@ class ServiceParameter extends Model
*/
public function setProperties(?array $data)
{
- foreach ($data ?? [] as $property => $value)
- {
- if (method_exists($this, $property))
- {
+ foreach ($data ?? [] as $property => $value) {
+ if (method_exists($this, $property)) {
$this->$property($value);
continue;
diff --git a/src/PaymentMethods/Afterpay/Models/Pay.php b/src/PaymentMethods/Afterpay/Models/Pay.php
index f8200e3c..16ac9ce1 100644
--- a/src/PaymentMethods/Afterpay/Models/Pay.php
+++ b/src/PaymentMethods/Afterpay/Models/Pay.php
@@ -87,8 +87,7 @@ class Pay extends ServiceParameter
*/
public function billing($billing = null)
{
- if (is_array($billing))
- {
+ if (is_array($billing)) {
$this->billingRecipient = new Recipient('Billing', $billing);
$this->shippingRecipient = new Recipient('Shipping', $billing);
}
@@ -102,8 +101,7 @@ public function billing($billing = null)
*/
public function shipping($shipping = null)
{
- if (is_array($shipping))
- {
+ if (is_array($shipping)) {
$this->shippingRecipient = new Recipient('Shipping', $shipping);
}
@@ -116,10 +114,8 @@ public function shipping($shipping = null)
*/
public function articles(?array $articles = null)
{
- if (is_array($articles))
- {
- foreach ($articles as $article)
- {
+ if (is_array($articles)) {
+ foreach ($articles as $article) {
$this->articles[] = new ArticleAdapter(new Article($article));
}
}
diff --git a/src/PaymentMethods/Afterpay/Models/Recipient.php b/src/PaymentMethods/Afterpay/Models/Recipient.php
index 2677b3be..7b988ef0 100644
--- a/src/PaymentMethods/Afterpay/Models/Recipient.php
+++ b/src/PaymentMethods/Afterpay/Models/Recipient.php
@@ -73,8 +73,7 @@ public function __construct(string $type, ?array $values = null)
*/
public function recipient($recipient = null)
{
- if (is_array($recipient))
- {
+ if (is_array($recipient)) {
$this->recipient = $this->getRecipientObject($recipient);
}
@@ -87,8 +86,7 @@ public function recipient($recipient = null)
*/
public function address($address = null)
{
- if (is_array($address))
- {
+ if (is_array($address)) {
$this->address = new AddressAdapter(new Address($address));
}
@@ -101,8 +99,7 @@ public function address($address = null)
*/
public function phone($phone = null)
{
- if (is_array($phone))
- {
+ if (is_array($phone)) {
$this->phone = new PhoneAdapter(new Phone($phone));
}
@@ -115,8 +112,7 @@ public function phone($phone = null)
*/
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new Email($email);
}
@@ -130,8 +126,7 @@ public function email($email = null)
*/
private function getRecipientObject(array $recipient) : RecipientInterface
{
- switch ($recipient['category'])
- {
+ switch ($recipient['category']) {
case RecipientCategory::COMPANY:
return new RecipientAdapter(new Company($recipient));
case RecipientCategory::PERSON:
diff --git a/src/PaymentMethods/Afterpay/Models/Refund.php b/src/PaymentMethods/Afterpay/Models/Refund.php
index 8ac00a53..e701c933 100644
--- a/src/PaymentMethods/Afterpay/Models/Refund.php
+++ b/src/PaymentMethods/Afterpay/Models/Refund.php
@@ -45,10 +45,8 @@ class Refund extends ServiceParameter
*/
public function articles(?array $articles = null)
{
- if (is_array($articles))
- {
- foreach ($articles as $article)
- {
+ if (is_array($articles)) {
+ foreach ($articles as $article) {
$this->articles[] = new ArticleAdapter(new Article($article));
}
}
diff --git a/src/PaymentMethods/Afterpay/Service/ParameterKeys/PhoneAdapter.php b/src/PaymentMethods/Afterpay/Service/ParameterKeys/PhoneAdapter.php
index 6a4d52fb..5c8e9d5f 100644
--- a/src/PaymentMethods/Afterpay/Service/ParameterKeys/PhoneAdapter.php
+++ b/src/PaymentMethods/Afterpay/Service/ParameterKeys/PhoneAdapter.php
@@ -26,6 +26,6 @@ class PhoneAdapter extends ServiceParametersKeysAdapter
{
protected array $keys = [
'landLine' => 'Phone',
- 'mobile' => 'MobilePhone'
+ 'mobile' => 'MobilePhone',
];
}
diff --git a/src/PaymentMethods/AfterpayDigiAccept/Models/Pay.php b/src/PaymentMethods/AfterpayDigiAccept/Models/Pay.php
index 897d47e5..38c6c59c 100644
--- a/src/PaymentMethods/AfterpayDigiAccept/Models/Pay.php
+++ b/src/PaymentMethods/AfterpayDigiAccept/Models/Pay.php
@@ -96,8 +96,7 @@ class Pay extends ServiceParameter
*/
public function billing($billing = null)
{
- if (is_array($billing))
- {
+ if (is_array($billing)) {
$this->billingRecipient = new Recipient('Billing', $billing);
$this->shippingRecipient = new Recipient('Shipping', $billing);
}
@@ -111,8 +110,7 @@ public function billing($billing = null)
*/
public function shipping($shipping = null)
{
- if (is_array($shipping))
- {
+ if (is_array($shipping)) {
$this->addressesDiffer = true;
$this->shippingRecipient = new Recipient('Shipping', $shipping);
@@ -127,10 +125,8 @@ public function shipping($shipping = null)
*/
public function articles(?array $articles = null)
{
- if (is_array($articles))
- {
- foreach ($articles as $article)
- {
+ if (is_array($articles)) {
+ foreach ($articles as $article) {
$this->articles[] = new ArticleAdapter(new Article($article));
}
}
diff --git a/src/PaymentMethods/AfterpayDigiAccept/Models/Recipient.php b/src/PaymentMethods/AfterpayDigiAccept/Models/Recipient.php
index 05735b86..dadc75e0 100644
--- a/src/PaymentMethods/AfterpayDigiAccept/Models/Recipient.php
+++ b/src/PaymentMethods/AfterpayDigiAccept/Models/Recipient.php
@@ -73,8 +73,7 @@ public function __construct(string $type, ?array $values = null)
*/
public function recipient($recipient = null)
{
- if (is_array($recipient))
- {
+ if (is_array($recipient)) {
$this->recipient = $this->getRecipientObject($recipient);
}
@@ -87,8 +86,7 @@ public function recipient($recipient = null)
*/
public function address($address = null)
{
- if (is_array($address))
- {
+ if (is_array($address)) {
$this->address = new AddressAdapter($this->type, new Address($address));
}
@@ -101,8 +99,7 @@ public function address($address = null)
*/
public function phone($phone = null)
{
- if (is_array($phone))
- {
+ if (is_array($phone)) {
$this->phone = new PhoneAdapter($this->type, new Phone($phone));
}
@@ -115,8 +112,7 @@ public function phone($phone = null)
*/
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new EmailAdapter($this->type, new Email($email));
}
diff --git a/src/PaymentMethods/AfterpayDigiAccept/Models/Refund.php b/src/PaymentMethods/AfterpayDigiAccept/Models/Refund.php
index dd5a52f3..2c9b004f 100644
--- a/src/PaymentMethods/AfterpayDigiAccept/Models/Refund.php
+++ b/src/PaymentMethods/AfterpayDigiAccept/Models/Refund.php
@@ -52,10 +52,8 @@ class Refund extends ServiceParameter
*/
public function articles(?array $articles = null)
{
- if (is_array($articles))
- {
- foreach ($articles as $article)
- {
+ if (is_array($articles)) {
+ foreach ($articles as $article) {
$this->articles[] = new ArticleAdapter(new Article($article));
}
}
diff --git a/src/PaymentMethods/AfterpayDigiAccept/Service/ParameterKeys/AddressAdapter.php b/src/PaymentMethods/AfterpayDigiAccept/Service/ParameterKeys/AddressAdapter.php
index d2c0e275..542d2072 100644
--- a/src/PaymentMethods/AfterpayDigiAccept/Service/ParameterKeys/AddressAdapter.php
+++ b/src/PaymentMethods/AfterpayDigiAccept/Service/ParameterKeys/AddressAdapter.php
@@ -36,8 +36,7 @@ class AddressAdapter extends ServiceAdapter
*/
public function serviceParameterKeyOf($propertyName): string
{
- if ($this->prefix == 'Shipping' && $propertyName == 'country')
- {
+ if ($this->prefix == 'Shipping' && $propertyName == 'country') {
return 'ShippingCountryCode';
}
diff --git a/src/PaymentMethods/AfterpayDigiAccept/Service/ParameterKeys/PhoneAdapter.php b/src/PaymentMethods/AfterpayDigiAccept/Service/ParameterKeys/PhoneAdapter.php
index d8fd974d..d3fb8802 100644
--- a/src/PaymentMethods/AfterpayDigiAccept/Service/ParameterKeys/PhoneAdapter.php
+++ b/src/PaymentMethods/AfterpayDigiAccept/Service/ParameterKeys/PhoneAdapter.php
@@ -23,6 +23,6 @@
class PhoneAdapter extends ServiceAdapter
{
protected array $keys = [
- 'mobile' => 'PhoneNumber'
+ 'mobile' => 'PhoneNumber',
];
}
diff --git a/src/PaymentMethods/AfterpayDigiAccept/Service/ParameterKeys/RecipientAdapter.php b/src/PaymentMethods/AfterpayDigiAccept/Service/ParameterKeys/RecipientAdapter.php
index dd2af1c3..68ef3647 100644
--- a/src/PaymentMethods/AfterpayDigiAccept/Service/ParameterKeys/RecipientAdapter.php
+++ b/src/PaymentMethods/AfterpayDigiAccept/Service/ParameterKeys/RecipientAdapter.php
@@ -37,8 +37,7 @@ class RecipientAdapter extends ServiceAdapter
*/
public function serviceParameterKeyOf($propertyName): string
{
- if (in_array($propertyName, ['companyName', 'chamberOfCommerce', 'vatNumber']))
- {
+ if (in_array($propertyName, ['companyName', 'chamberOfCommerce', 'vatNumber'])) {
return (isset($this->keys[$propertyName]))? $this->keys[$propertyName] : ucfirst($propertyName);
}
diff --git a/src/PaymentMethods/BankTransfer/Models/Pay.php b/src/PaymentMethods/BankTransfer/Models/Pay.php
index 7b55677d..c6e2d83f 100644
--- a/src/PaymentMethods/BankTransfer/Models/Pay.php
+++ b/src/PaymentMethods/BankTransfer/Models/Pay.php
@@ -56,8 +56,7 @@ class Pay extends ServiceParameter
*/
public function customer($customer = null)
{
- if (is_array($customer))
- {
+ if (is_array($customer)) {
$this->customer = new CustomerAdapter(new Person($customer));
}
@@ -70,8 +69,7 @@ public function customer($customer = null)
*/
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new EmailAdapter(new Email($email));
}
diff --git a/src/PaymentMethods/Billink/Billink.php b/src/PaymentMethods/Billink/Billink.php
index d99432b9..4de7b438 100644
--- a/src/PaymentMethods/Billink/Billink.php
+++ b/src/PaymentMethods/Billink/Billink.php
@@ -22,7 +22,6 @@
use Buckaroo\Models\Model;
use Buckaroo\PaymentMethods\Billink\Models\Pay;
-use Buckaroo\PaymentMethods\Billink\Models\Refund;
use Buckaroo\PaymentMethods\PayablePaymentMethod;
use Buckaroo\Transaction\Response\TransactionResponse;
diff --git a/src/PaymentMethods/Billink/Models/Pay.php b/src/PaymentMethods/Billink/Models/Pay.php
index 2b7d661f..f0f22eb8 100644
--- a/src/PaymentMethods/Billink/Models/Pay.php
+++ b/src/PaymentMethods/Billink/Models/Pay.php
@@ -71,8 +71,7 @@ class Pay extends ServiceParameter
*/
public function billing($billing = null)
{
- if (is_array($billing))
- {
+ if (is_array($billing)) {
$this->billingRecipient = new Recipient('Billing', $billing);
$this->shippingRecipient = new Recipient('Billing', $billing);
}
@@ -86,8 +85,7 @@ public function billing($billing = null)
*/
public function shipping($shipping = null)
{
- if (is_array($shipping))
- {
+ if (is_array($shipping)) {
$this->shippingRecipient = new Recipient('Shipping', $shipping);
}
@@ -100,10 +98,8 @@ public function shipping($shipping = null)
*/
public function articles(?array $articles = null)
{
- if (is_array($articles))
- {
- foreach ($articles as $article)
- {
+ if (is_array($articles)) {
+ foreach ($articles as $article) {
$this->articles[] = new ArticleAdapter(new Article($article));
}
}
diff --git a/src/PaymentMethods/Billink/Models/Recipient.php b/src/PaymentMethods/Billink/Models/Recipient.php
index 3d42e629..488b3a7f 100644
--- a/src/PaymentMethods/Billink/Models/Recipient.php
+++ b/src/PaymentMethods/Billink/Models/Recipient.php
@@ -72,8 +72,7 @@ public function __construct(string $type, ?array $values = null)
*/
public function recipient($recipient = null)
{
- if (is_array($recipient))
- {
+ if (is_array($recipient)) {
$this->recipient = $this->getRecipientObject($recipient);
}
@@ -86,8 +85,7 @@ public function recipient($recipient = null)
*/
public function address($address = null)
{
- if (is_array($address))
- {
+ if (is_array($address)) {
$this->address = new AddressAdapter(new Address($address));
}
@@ -100,8 +98,7 @@ public function address($address = null)
*/
public function phone($phone = null)
{
- if (is_array($phone))
- {
+ if (is_array($phone)) {
$this->phone = new PhoneAdapter(new Phone($phone));
}
@@ -114,8 +111,7 @@ public function phone($phone = null)
*/
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new Email($email);
}
@@ -129,10 +125,8 @@ public function email($email = null)
*/
private function getRecipientObject(array $recipient) : RecipientInterface
{
- if (isset($recipient['category']))
- {
- switch ($recipient['category'])
- {
+ if (isset($recipient['category'])) {
+ switch ($recipient['category']) {
case 'B2B':
return new RecipientAdapter(new Company($recipient));
case 'B2C':
diff --git a/src/PaymentMethods/Billink/Service/ParameterKeys/PhoneAdapter.php b/src/PaymentMethods/Billink/Service/ParameterKeys/PhoneAdapter.php
index 98a4a3d5..ee3d9ee1 100644
--- a/src/PaymentMethods/Billink/Service/ParameterKeys/PhoneAdapter.php
+++ b/src/PaymentMethods/Billink/Service/ParameterKeys/PhoneAdapter.php
@@ -25,6 +25,6 @@
class PhoneAdapter extends ServiceParametersKeysAdapter
{
protected array $keys = [
- 'mobile' => 'MobilePhone'
+ 'mobile' => 'MobilePhone',
];
}
diff --git a/src/PaymentMethods/BuckarooVoucher/BuckarooVoucher.php b/src/PaymentMethods/BuckarooVoucher/BuckarooVoucher.php
index 7dcbacff..46db4793 100644
--- a/src/PaymentMethods/BuckarooVoucher/BuckarooVoucher.php
+++ b/src/PaymentMethods/BuckarooVoucher/BuckarooVoucher.php
@@ -49,7 +49,6 @@ public function pay(?Model $model = null): TransactionResponse
return parent::pay($model ?? $pay);
}
-
/**
* @param Model|null $model
* @return TransactionResponse
diff --git a/src/PaymentMethods/BuckarooWallet/Models/Wallet.php b/src/PaymentMethods/BuckarooWallet/Models/Wallet.php
index 923b6328..26606013 100644
--- a/src/PaymentMethods/BuckarooWallet/Models/Wallet.php
+++ b/src/PaymentMethods/BuckarooWallet/Models/Wallet.php
@@ -67,8 +67,7 @@ class Wallet extends ServiceParameter
*/
public function customer($customer = null)
{
- if (is_array($customer))
- {
+ if (is_array($customer)) {
$this->customer = new CustomerAdapter(new Person($customer));
}
@@ -81,8 +80,7 @@ public function customer($customer = null)
*/
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new EmailAdapter(new Email($email));
}
@@ -95,8 +93,7 @@ public function email($email = null)
*/
public function bankAccount($bankAccount = null)
{
- if (is_array($bankAccount))
- {
+ if (is_array($bankAccount)) {
$this->bankAccount = new BankAccountAdapter(new BankAccount($bankAccount));
}
diff --git a/src/PaymentMethods/CreditCard/CreditCard.php b/src/PaymentMethods/CreditCard/CreditCard.php
index fa5bec11..ab269f5f 100644
--- a/src/PaymentMethods/CreditCard/CreditCard.php
+++ b/src/PaymentMethods/CreditCard/CreditCard.php
@@ -195,8 +195,7 @@ public function cancelAuthorize(): TransactionResponse
*/
public function paymentName(): string
{
- if (isset($this->payload['name']))
- {
+ if (isset($this->payload['name'])) {
return $this->payload['name'];
}
diff --git a/src/PaymentMethods/CreditManagement/CreditManagement.php b/src/PaymentMethods/CreditManagement/CreditManagement.php
index 1044ed77..5c14cb3d 100644
--- a/src/PaymentMethods/CreditManagement/CreditManagement.php
+++ b/src/PaymentMethods/CreditManagement/CreditManagement.php
@@ -21,7 +21,6 @@
namespace Buckaroo\PaymentMethods\CreditManagement;
use Buckaroo\Models\Payload\DataRequestPayload;
-use Buckaroo\Models\Payload\PayPayload;
use Buckaroo\PaymentMethods\CreditManagement\Models\AddOrUpdateProductLines;
use Buckaroo\PaymentMethods\CreditManagement\Models\CreditNote;
use Buckaroo\PaymentMethods\CreditManagement\Models\Debtor;
diff --git a/src/PaymentMethods/CreditManagement/Models/AddOrUpdateProductLines.php b/src/PaymentMethods/CreditManagement/Models/AddOrUpdateProductLines.php
index 2bff3a54..d2923ff1 100644
--- a/src/PaymentMethods/CreditManagement/Models/AddOrUpdateProductLines.php
+++ b/src/PaymentMethods/CreditManagement/Models/AddOrUpdateProductLines.php
@@ -57,10 +57,8 @@ class AddOrUpdateProductLines extends ServiceParameter
*/
public function articles(?array $articles = null)
{
- if (is_array($articles))
- {
- foreach ($articles as $article)
- {
+ if (is_array($articles)) {
+ foreach ($articles as $article) {
$this->articles[] = new ArticleAdapter(new Article($article));
}
}
diff --git a/src/PaymentMethods/CreditManagement/Models/DebtorInfo.php b/src/PaymentMethods/CreditManagement/Models/DebtorInfo.php
index 8c999e02..dd3c9db0 100644
--- a/src/PaymentMethods/CreditManagement/Models/DebtorInfo.php
+++ b/src/PaymentMethods/CreditManagement/Models/DebtorInfo.php
@@ -46,8 +46,7 @@ class DebtorInfo extends ServiceParameter
*/
public function debtor($debtor = null)
{
- if (is_array($debtor))
- {
+ if (is_array($debtor)) {
$this->debtor = new DebtorInfoAdapter(new Debtor($debtor));
}
diff --git a/src/PaymentMethods/CreditManagement/Models/Invoice.php b/src/PaymentMethods/CreditManagement/Models/Invoice.php
index 412547cc..8b2af4aa 100644
--- a/src/PaymentMethods/CreditManagement/Models/Invoice.php
+++ b/src/PaymentMethods/CreditManagement/Models/Invoice.php
@@ -158,8 +158,7 @@ class Invoice extends ServiceParameter
*/
public function address($address = null)
{
- if (is_array($address))
- {
+ if (is_array($address)) {
$this->address = new Address($address);
}
@@ -172,8 +171,7 @@ public function address($address = null)
*/
public function company($company = null)
{
- if (is_array($company))
- {
+ if (is_array($company)) {
$this->company = new Company($company);
}
@@ -186,8 +184,7 @@ public function company($company = null)
*/
public function person($person = null)
{
- if (is_array($person))
- {
+ if (is_array($person)) {
$this->person = new Person($person);
}
@@ -200,8 +197,7 @@ public function person($person = null)
*/
public function debtor($debtor = null)
{
- if (is_array($debtor))
- {
+ if (is_array($debtor)) {
$this->debtor = new Debtor($debtor);
}
@@ -214,8 +210,7 @@ public function debtor($debtor = null)
*/
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new Email($email);
}
@@ -228,8 +223,7 @@ public function email($email = null)
*/
public function phone($phone = null)
{
- if (is_array($phone))
- {
+ if (is_array($phone)) {
$this->phone = new Phone($phone);
}
@@ -242,10 +236,8 @@ public function phone($phone = null)
*/
public function articles(?array $articles = null)
{
- if (is_array($articles))
- {
- foreach ($articles as $article)
- {
+ if (is_array($articles)) {
+ foreach ($articles as $article) {
$this->articles[] = new ArticleAdapter(new Article($article));
}
}
diff --git a/src/PaymentMethods/CreditManagement/Models/MultipleInvoiceInfo.php b/src/PaymentMethods/CreditManagement/Models/MultipleInvoiceInfo.php
index 80f2d970..80b96056 100644
--- a/src/PaymentMethods/CreditManagement/Models/MultipleInvoiceInfo.php
+++ b/src/PaymentMethods/CreditManagement/Models/MultipleInvoiceInfo.php
@@ -43,10 +43,8 @@ class MultipleInvoiceInfo extends ServiceParameter
*/
public function invoices(?array $invoices = null)
{
- if (is_array($invoices))
- {
- foreach ($invoices as $invoice)
- {
+ if (is_array($invoices)) {
+ foreach ($invoices as $invoice) {
$this->invoices[] = new Invoice($invoice);
}
}
diff --git a/src/PaymentMethods/GiftCard/GiftCard.php b/src/PaymentMethods/GiftCard/GiftCard.php
index 336d8ec5..0b456106 100644
--- a/src/PaymentMethods/GiftCard/GiftCard.php
+++ b/src/PaymentMethods/GiftCard/GiftCard.php
@@ -80,8 +80,7 @@ public function payRemainder(?Model $model = null)
*/
public function paymentName(): string
{
- if (isset($this->payload['name']))
- {
+ if (isset($this->payload['name'])) {
return $this->payload['name'];
}
diff --git a/src/PaymentMethods/In3/In3.php b/src/PaymentMethods/In3/In3.php
index 4f659a0b..11e3314d 100644
--- a/src/PaymentMethods/In3/In3.php
+++ b/src/PaymentMethods/In3/In3.php
@@ -22,7 +22,6 @@
use Buckaroo\Models\Model;
use Buckaroo\PaymentMethods\In3\Models\Pay;
-use Buckaroo\PaymentMethods\In3\Models\PayPayload;
use Buckaroo\PaymentMethods\PayablePaymentMethod;
use Buckaroo\Transaction\Response\TransactionResponse;
diff --git a/src/PaymentMethods/In3/Models/Pay.php b/src/PaymentMethods/In3/Models/Pay.php
index d45e6a5a..23823a00 100644
--- a/src/PaymentMethods/In3/Models/Pay.php
+++ b/src/PaymentMethods/In3/Models/Pay.php
@@ -22,7 +22,6 @@
use Buckaroo\Models\ServiceParameter;
use Buckaroo\PaymentMethods\In3\Service\ParameterKeys\ArticleAdapter;
-use Buckaroo\PaymentMethods\In3\Service\ParameterKeys\CompanyAdapter;
use Buckaroo\PaymentMethods\Traits\CountableGroupKey;
class Pay extends ServiceParameter
@@ -93,8 +92,7 @@ class Pay extends ServiceParameter
*/
public function billing($billing = null)
{
- if (is_array($billing))
- {
+ if (is_array($billing)) {
$this->billingRecipient = new Recipient('Billing', $billing);
$this->shippingRecipient = new Recipient('Shipping', $billing);
}
@@ -108,8 +106,7 @@ public function billing($billing = null)
*/
public function shipping($shipping = null)
{
- if (is_array($shipping))
- {
+ if (is_array($shipping)) {
$this->shippingRecipient = new Recipient('Shipping', $shipping);
}
@@ -122,10 +119,8 @@ public function shipping($shipping = null)
*/
public function articles(?array $articles = null)
{
- if (is_array($articles))
- {
- foreach ($articles as $article)
- {
+ if (is_array($articles)) {
+ foreach ($articles as $article) {
$this->articles[] = new ArticleAdapter(new Article($article));
}
}
diff --git a/src/PaymentMethods/In3/Models/Recipient.php b/src/PaymentMethods/In3/Models/Recipient.php
index fb71faf9..331ad2f2 100644
--- a/src/PaymentMethods/In3/Models/Recipient.php
+++ b/src/PaymentMethods/In3/Models/Recipient.php
@@ -28,7 +28,6 @@
use Buckaroo\PaymentMethods\In3\Service\ParameterKeys\AddressAdapter;
use Buckaroo\PaymentMethods\In3\Service\ParameterKeys\PhoneAdapter;
use Buckaroo\PaymentMethods\In3\Service\ParameterKeys\RecipientAdapter;
-use Buckaroo\Resources\Constants\RecipientCategory;
class Recipient extends ServiceParameter
{
@@ -72,8 +71,7 @@ public function __construct(string $type, ?array $values = null)
*/
public function recipient($recipient = null)
{
- if (is_array($recipient))
- {
+ if (is_array($recipient)) {
$this->recipient = $this->getRecipientObject($recipient);
}
@@ -86,8 +84,7 @@ public function recipient($recipient = null)
*/
public function address($address = null)
{
- if (is_array($address))
- {
+ if (is_array($address)) {
$this->address = new AddressAdapter(new Address($address));
}
@@ -100,8 +97,7 @@ public function address($address = null)
*/
public function phone($phone = null)
{
- if (is_array($phone))
- {
+ if (is_array($phone)) {
$this->phone = new PhoneAdapter(new Phone($phone));
}
@@ -114,8 +110,7 @@ public function phone($phone = null)
*/
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new Email($email);
}
@@ -129,10 +124,8 @@ public function email($email = null)
*/
private function getRecipientObject(array $recipient) : RecipientInterface
{
- if (isset($recipient['category']))
- {
- switch ($recipient['category'])
- {
+ if (isset($recipient['category'])) {
+ switch ($recipient['category']) {
case 'B2B':
return new RecipientAdapter(new Company($recipient));
case 'B2C':
diff --git a/src/PaymentMethods/In3/Models/Refund.php b/src/PaymentMethods/In3/Models/Refund.php
index 4a8f0f58..663a2555 100644
--- a/src/PaymentMethods/In3/Models/Refund.php
+++ b/src/PaymentMethods/In3/Models/Refund.php
@@ -45,10 +45,8 @@ class Refund extends ServiceParameter
*/
public function articles(?array $articles = null)
{
- if (is_array($articles))
- {
- foreach ($articles as $article)
- {
+ if (is_array($articles)) {
+ foreach ($articles as $article) {
$this->articles[] = new ArticleAdapter(new Article($article));
}
}
diff --git a/src/PaymentMethods/In3/Service/ParameterKeys/AddressAdapter.php b/src/PaymentMethods/In3/Service/ParameterKeys/AddressAdapter.php
index c73ef28b..55c51fc9 100644
--- a/src/PaymentMethods/In3/Service/ParameterKeys/AddressAdapter.php
+++ b/src/PaymentMethods/In3/Service/ParameterKeys/AddressAdapter.php
@@ -28,6 +28,6 @@ class AddressAdapter extends ServiceParametersKeysAdapter
'houseNumber' => 'StreetNumber',
'houseNumberAdditional' => 'StreetNumberSuffix',
'zipcode' => 'PostalCode',
- 'country' => 'CountryCode'
+ 'country' => 'CountryCode',
];
}
diff --git a/src/PaymentMethods/In3/Service/ParameterKeys/PhoneAdapter.php b/src/PaymentMethods/In3/Service/ParameterKeys/PhoneAdapter.php
index b14298c5..27fbe7c6 100644
--- a/src/PaymentMethods/In3/Service/ParameterKeys/PhoneAdapter.php
+++ b/src/PaymentMethods/In3/Service/ParameterKeys/PhoneAdapter.php
@@ -25,6 +25,6 @@
class PhoneAdapter extends ServiceParametersKeysAdapter
{
protected array $keys = [
- 'mobile' => 'Phone'
+ 'mobile' => 'Phone',
];
}
diff --git a/src/PaymentMethods/In3Old/Models/Pay.php b/src/PaymentMethods/In3Old/Models/Pay.php
index d542bb50..155e40bd 100644
--- a/src/PaymentMethods/In3Old/Models/Pay.php
+++ b/src/PaymentMethods/In3Old/Models/Pay.php
@@ -111,10 +111,8 @@ class Pay extends ServiceParameter
*/
public function articles(?array $articles = null)
{
- if (is_array($articles))
- {
- foreach ($articles as $article)
- {
+ if (is_array($articles)) {
+ foreach ($articles as $article) {
$this->articles[] = new ArticleAdapter(new Article($article));
}
}
@@ -128,8 +126,7 @@ public function articles(?array $articles = null)
*/
public function company($company = null)
{
- if (is_array($company))
- {
+ if (is_array($company)) {
$this->company = new CompanyAdapter(new Company($company));
}
@@ -142,8 +139,7 @@ public function company($company = null)
*/
public function customer($customer = null)
{
- if (is_array($customer))
- {
+ if (is_array($customer)) {
$this->customer = new Person($customer);
}
@@ -156,8 +152,7 @@ public function customer($customer = null)
*/
public function address($address = null)
{
- if (is_array($address))
- {
+ if (is_array($address)) {
$this->address = new AddressAdapter(new Address($address));
}
@@ -170,8 +165,7 @@ public function address($address = null)
*/
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new Email($email);
}
@@ -184,8 +178,7 @@ public function email($email = null)
*/
public function phone($phone = null)
{
- if (is_array($phone))
- {
+ if (is_array($phone)) {
$this->phone = new PhoneAdapter(new Phone($phone));
}
@@ -198,10 +191,8 @@ public function phone($phone = null)
*/
public function subtotals(?array $subtotals = null)
{
- if (is_array($subtotals))
- {
- foreach ($subtotals as $subtotal)
- {
+ if (is_array($subtotals)) {
+ foreach ($subtotals as $subtotal) {
$this->subtotals[] = new Subtotal($subtotal);
}
}
diff --git a/src/PaymentMethods/In3Old/Service/ParameterKeys/PhoneAdapter.php b/src/PaymentMethods/In3Old/Service/ParameterKeys/PhoneAdapter.php
index 17af7e7c..14bb2750 100644
--- a/src/PaymentMethods/In3Old/Service/ParameterKeys/PhoneAdapter.php
+++ b/src/PaymentMethods/In3Old/Service/ParameterKeys/PhoneAdapter.php
@@ -25,6 +25,6 @@
class PhoneAdapter extends ServiceParametersKeysAdapter
{
protected array $keys = [
- 'mobile' => 'Phone'
+ 'mobile' => 'Phone',
];
}
diff --git a/src/PaymentMethods/KlarnaKP/Models/Payload.php b/src/PaymentMethods/KlarnaKP/Models/Payload.php
index 8d8da241..63de80e4 100644
--- a/src/PaymentMethods/KlarnaKP/Models/Payload.php
+++ b/src/PaymentMethods/KlarnaKP/Models/Payload.php
@@ -49,8 +49,7 @@ class Payload extends ServiceParameter
public function billing($billing = null)
{
- if (is_array($billing))
- {
+ if (is_array($billing)) {
$this->billingRecipient = new Recipient('Billing', $billing);
$this->shippingRecipient = new Recipient('Shipping', $billing);
}
@@ -60,8 +59,7 @@ public function billing($billing = null)
public function shipping($shipping = null)
{
- if (is_array($shipping))
- {
+ if (is_array($shipping)) {
$this->shippingSameAsBilling = false;
$this->shippingRecipient = new Recipient('Shipping', $shipping);
@@ -72,10 +70,8 @@ public function shipping($shipping = null)
public function articles(?array $articles = null)
{
- if (is_array($articles))
- {
- foreach ($articles as $article)
- {
+ if (is_array($articles)) {
+ foreach ($articles as $article) {
$this->articles[] = new ArticleAdapter(new Article($article));
}
}
diff --git a/src/PaymentMethods/KlarnaKP/Models/Recipient.php b/src/PaymentMethods/KlarnaKP/Models/Recipient.php
index 242a0836..e56d39ff 100644
--- a/src/PaymentMethods/KlarnaKP/Models/Recipient.php
+++ b/src/PaymentMethods/KlarnaKP/Models/Recipient.php
@@ -33,8 +33,7 @@ public function __construct(string $type, ?array $values = null)
public function phone($phone = null)
{
- if (is_array($phone))
- {
+ if (is_array($phone)) {
$this->phone = new PhoneAdapter(new Phone($phone), $this->type);
}
@@ -43,8 +42,7 @@ public function phone($phone = null)
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new EmailAdapter(new Email($email), $this->type);
}
@@ -53,8 +51,7 @@ public function email($email = null)
public function address($address = null)
{
- if (is_array($address))
- {
+ if (is_array($address)) {
$this->address = new AddressAdapter(new Address($address), $this->type);
}
@@ -63,8 +60,7 @@ public function address($address = null)
public function recipient($recipient = null)
{
- if (is_array($recipient))
- {
+ if (is_array($recipient)) {
$this->recipient = new RecipientAdapter(new Person($recipient), $this->type);
}
diff --git a/src/PaymentMethods/KlarnaKP/Service/ParameterKeys/PhoneAdapter.php b/src/PaymentMethods/KlarnaKP/Service/ParameterKeys/PhoneAdapter.php
index f345e2ad..036358ab 100644
--- a/src/PaymentMethods/KlarnaKP/Service/ParameterKeys/PhoneAdapter.php
+++ b/src/PaymentMethods/KlarnaKP/Service/ParameterKeys/PhoneAdapter.php
@@ -25,6 +25,6 @@
class PhoneAdapter extends RecipientAdapter
{
protected array $keys = [
- 'mobile' => 'CellPhoneNumber'
+ 'mobile' => 'CellPhoneNumber',
];
}
diff --git a/src/PaymentMethods/KlarnaPay/KlarnaPay.php b/src/PaymentMethods/KlarnaPay/KlarnaPay.php
index 3f0e564d..6acc01f1 100644
--- a/src/PaymentMethods/KlarnaPay/KlarnaPay.php
+++ b/src/PaymentMethods/KlarnaPay/KlarnaPay.php
@@ -60,10 +60,10 @@ public function payInInstallments(): TransactionResponse
return $this->postRequest();
}
- /**
- * @param Model|null $model
- * @return TransactionResponse
- */
+ /**
+ * @param Model|null $model
+ * @return TransactionResponse
+ */
public function payRemainder(?Model $model = null): TransactionResponse
{
return parent::payRemainder($model ?? new Pay($this->payload));
diff --git a/src/PaymentMethods/KlarnaPay/Models/Pay.php b/src/PaymentMethods/KlarnaPay/Models/Pay.php
index 8697ad98..1e9c110f 100644
--- a/src/PaymentMethods/KlarnaPay/Models/Pay.php
+++ b/src/PaymentMethods/KlarnaPay/Models/Pay.php
@@ -63,8 +63,7 @@ class Pay extends ServiceParameter
*/
public function billing($billing = null)
{
- if (is_array($billing))
- {
+ if (is_array($billing)) {
$this->billingRecipient = new Recipient('Billing', $billing);
$this->shippingRecipient = new Recipient('Shipping', $billing);
}
@@ -78,8 +77,7 @@ public function billing($billing = null)
*/
public function shipping($shipping = null)
{
- if (is_array($shipping))
- {
+ if (is_array($shipping)) {
$this->shippingRecipient = new Recipient('Shipping', $shipping);
}
@@ -92,10 +90,8 @@ public function shipping($shipping = null)
*/
public function articles(?array $articles = null)
{
- if (is_array($articles))
- {
- foreach ($articles as $article)
- {
+ if (is_array($articles)) {
+ foreach ($articles as $article) {
$this->articles[] = new ArticleAdapter(new Article($article));
}
}
diff --git a/src/PaymentMethods/KlarnaPay/Models/Recipient.php b/src/PaymentMethods/KlarnaPay/Models/Recipient.php
index 2dd3ae28..0c33fb51 100644
--- a/src/PaymentMethods/KlarnaPay/Models/Recipient.php
+++ b/src/PaymentMethods/KlarnaPay/Models/Recipient.php
@@ -71,8 +71,7 @@ public function __construct(string $type, ?array $values = null)
*/
public function recipient($recipient = null)
{
- if (is_array($recipient))
- {
+ if (is_array($recipient)) {
$this->recipient = new Person($recipient);
}
@@ -85,8 +84,7 @@ public function recipient($recipient = null)
*/
public function address($address = null)
{
- if (is_array($address))
- {
+ if (is_array($address)) {
$this->address = new AddressAdapter(new Address($address));
}
@@ -99,8 +97,7 @@ public function address($address = null)
*/
public function phone($phone = null)
{
- if (is_array($phone))
- {
+ if (is_array($phone)) {
$this->phone = new PhoneAdapter(new Phone($phone));
}
@@ -113,8 +110,7 @@ public function phone($phone = null)
*/
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new Email($email);
}
@@ -128,8 +124,7 @@ public function email($email = null)
*/
private function getRecipientObject(array $recipient) : RecipientInterface
{
- switch ($recipient['category'])
- {
+ switch ($recipient['category']) {
case 'B2B':
return new Company($recipient);
case 'B2C':
diff --git a/src/PaymentMethods/KlarnaPay/Service/ParameterKeys/PhoneAdapter.php b/src/PaymentMethods/KlarnaPay/Service/ParameterKeys/PhoneAdapter.php
index 306bfff9..da15397f 100644
--- a/src/PaymentMethods/KlarnaPay/Service/ParameterKeys/PhoneAdapter.php
+++ b/src/PaymentMethods/KlarnaPay/Service/ParameterKeys/PhoneAdapter.php
@@ -25,6 +25,6 @@
class PhoneAdapter extends ServiceParametersKeysAdapter
{
protected array $keys = [
- 'mobile' => 'Phone'
+ 'mobile' => 'Phone',
];
}
diff --git a/src/PaymentMethods/KnakenPay/KnakenPay.php b/src/PaymentMethods/KnakenPay/KnakenPay.php
index 1ceb1559..b406f4f3 100644
--- a/src/PaymentMethods/KnakenPay/KnakenPay.php
+++ b/src/PaymentMethods/KnakenPay/KnakenPay.php
@@ -23,9 +23,9 @@
namespace Buckaroo\PaymentMethods\KnakenPay;
use Buckaroo\Models\Model;
+use Buckaroo\Models\ServiceParameter;
use Buckaroo\PaymentMethods\PayablePaymentMethod;
use Buckaroo\Transaction\Response\TransactionResponse;
-use Buckaroo\Models\ServiceParameter;
class KnakenPay extends PayablePaymentMethod
{
diff --git a/src/PaymentMethods/Marketplaces/Models/ServiceList.php b/src/PaymentMethods/Marketplaces/Models/ServiceList.php
index 99e96293..aa31a354 100644
--- a/src/PaymentMethods/Marketplaces/Models/ServiceList.php
+++ b/src/PaymentMethods/Marketplaces/Models/ServiceList.php
@@ -64,8 +64,7 @@ class ServiceList extends ServiceParameter
*/
public function marketplace($marketplace = null)
{
- if (is_array($marketplace))
- {
+ if (is_array($marketplace)) {
$this->marketplace = new Marketplace($marketplace);
}
@@ -78,10 +77,8 @@ public function marketplace($marketplace = null)
*/
public function sellers($sellers = null)
{
- if (is_array($sellers))
- {
- foreach ($sellers as $seller)
- {
+ if (is_array($sellers)) {
+ foreach ($sellers as $seller) {
$this->sellers[] = new Seller($seller);
}
}
diff --git a/src/PaymentMethods/PayPerEmail/Models/PaymentInvitation.php b/src/PaymentMethods/PayPerEmail/Models/PaymentInvitation.php
index 5c37879c..34cdfe5a 100644
--- a/src/PaymentMethods/PayPerEmail/Models/PaymentInvitation.php
+++ b/src/PaymentMethods/PayPerEmail/Models/PaymentInvitation.php
@@ -74,8 +74,7 @@ class PaymentInvitation extends ServiceParameter
*/
public function customer($customer = null)
{
- if (is_array($customer))
- {
+ if (is_array($customer)) {
$this->customer = new CustomerAdapter(new Person($customer));
}
@@ -88,8 +87,7 @@ public function customer($customer = null)
*/
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new EmailAdapter(new Email($email));
}
@@ -102,10 +100,8 @@ public function email($email = null)
*/
public function attachments(?array $attachments = null)
{
- if (is_array($attachments))
- {
- foreach ($attachments as $attachment)
- {
+ if (is_array($attachments)) {
+ foreach ($attachments as $attachment) {
$this->attachments[] = new AttachmentAdapter(new Attachment($attachment));
}
}
diff --git a/src/PaymentMethods/PaymentFacade.php b/src/PaymentMethods/PaymentFacade.php
index bf863bae..f1d2353d 100644
--- a/src/PaymentMethods/PaymentFacade.php
+++ b/src/PaymentMethods/PaymentFacade.php
@@ -136,18 +136,15 @@ public function manually()
*/
public function combine($combinablePayment)
{
- if (is_array($combinablePayment))
- {
- foreach ($combinablePayment as $combinable_payment)
- {
+ if (is_array($combinablePayment)) {
+ foreach ($combinablePayment as $combinable_payment) {
$this->combine($combinable_payment);
}
return $this;
}
- if ($combinablePayment instanceof Combinable)
- {
+ if ($combinablePayment instanceof Combinable) {
$this->paymentMethod->combinePayment($combinablePayment);
}
@@ -171,7 +168,7 @@ public function paymentMethod(): PaymentMethod
public function __call(?string $name, array $arguments)
{
if (method_exists($this->paymentMethod, $name)) {
- if($name === 'setServiceVersion') {
+ if ($name === 'setServiceVersion') {
$this->paymentMethod->setServiceVersion($arguments[0]);
return $this;
diff --git a/src/PaymentMethods/PaymentMethod.php b/src/PaymentMethods/PaymentMethod.php
index 7f5e0c43..8afe7136 100644
--- a/src/PaymentMethods/PaymentMethod.php
+++ b/src/PaymentMethods/PaymentMethod.php
@@ -114,8 +114,7 @@ public function setPayload(array $payload)
*/
protected function postRequest()
{
- if ($this->isManually)
- {
+ if ($this->isManually) {
return $this;
}
@@ -130,8 +129,7 @@ protected function postRequest()
*/
protected function dataRequest()
{
- if ($this->isManually)
- {
+ if ($this->isManually) {
return $this;
}
@@ -187,8 +185,7 @@ public function setServiceVersion(int $serviceVersion): PaymentInterface
*/
public function manually(?bool $isManually = null)
{
- if ($isManually !== null)
- {
+ if ($isManually !== null) {
$this->isManually = $isManually;
}
@@ -204,16 +201,14 @@ public function combinePayment(Combinable $combinablePayment)
$this->combinablePayment = $combinablePayment;
$payload_data = array_filter($combinablePayment->request->data(), function ($key) {
- return ! in_array($key, ['Services']);
+ return !in_array($key, ['Services']);
}, ARRAY_FILTER_USE_KEY);
- foreach ($payload_data as $key => $value)
- {
+ foreach ($payload_data as $key => $value) {
$this->request->setData($key, $value);
}
- foreach ($this->combinablePayment->request->getServices()->serviceList() as $serviceList)
- {
+ foreach ($this->combinablePayment->request->getServices()->serviceList() as $serviceList) {
$this->request->getServices()->pushServiceList($serviceList);
}
diff --git a/src/PaymentMethods/PaymentMethodFactory.php b/src/PaymentMethods/PaymentMethodFactory.php
index 8db8f281..d45c1e95 100644
--- a/src/PaymentMethods/PaymentMethodFactory.php
+++ b/src/PaymentMethods/PaymentMethodFactory.php
@@ -20,57 +20,57 @@
namespace Buckaroo\PaymentMethods;
-use Buckaroo\Transaction\Client;
-use Buckaroo\PaymentMethods\EPS\EPS;
-use Buckaroo\PaymentMethods\In3\In3;
-use Buckaroo\PaymentMethods\KBC\KBC;
-use Buckaroo\PaymentMethods\iDin\iDin;
-use Buckaroo\PaymentMethods\SEPA\SEPA;
-use Buckaroo\PaymentMethods\iDeal\iDeal;
-use Buckaroo\PaymentMethods\iDealProcessing\iDealProcessing;
-use Buckaroo\PaymentMethods\MBWay\MBWay;
use Buckaroo\Exceptions\BuckarooException;
+use Buckaroo\PaymentMethods\Afterpay\Afterpay;
+use Buckaroo\PaymentMethods\AfterpayDigiAccept\AfterpayDigiAccept;
use Buckaroo\PaymentMethods\Alipay\Alipay;
-use Buckaroo\PaymentMethods\In3Old\In3Old;
-use Buckaroo\PaymentMethods\Paypal\Paypal;
-use Buckaroo\PaymentMethods\Thunes\Thunes;
+use Buckaroo\PaymentMethods\ApplePay\ApplePay;
+use Buckaroo\PaymentMethods\Bancontact\Bancontact;
+use Buckaroo\PaymentMethods\BankTransfer\BankTransfer;
use Buckaroo\PaymentMethods\Belfius\Belfius;
use Buckaroo\PaymentMethods\Billink\Billink;
-use Buckaroo\PaymentMethods\iDealQR\iDealQR;
-use Buckaroo\PaymentMethods\Surepay\Surepay;
-use Buckaroo\PaymentMethods\Swish\Swish;
-use Buckaroo\PaymentMethods\Trustly\Trustly;
-use Buckaroo\PaymentMethods\Afterpay\Afterpay;
-use Buckaroo\PaymentMethods\ApplePay\ApplePay;
-use Buckaroo\PaymentMethods\GooglePay\GooglePay;
+use Buckaroo\PaymentMethods\Bizum\Bizum;
+use Buckaroo\PaymentMethods\Blik\Blik;
+use Buckaroo\PaymentMethods\BuckarooVoucher\BuckarooVoucher;
+use Buckaroo\PaymentMethods\BuckarooWallet\BuckarooWallet;
+use Buckaroo\PaymentMethods\ClickToPay\ClickToPay;
+use Buckaroo\PaymentMethods\CreditCard\CreditCard;
+use Buckaroo\PaymentMethods\CreditManagement\CreditManagement;
+use Buckaroo\PaymentMethods\Emandates\Emandates;
+use Buckaroo\PaymentMethods\EPS\EPS;
+use Buckaroo\PaymentMethods\ExternalPayment\ExternalPayment;
use Buckaroo\PaymentMethods\GiftCard\GiftCard;
+use Buckaroo\PaymentMethods\GooglePay\GooglePay;
+use Buckaroo\PaymentMethods\iDeal\iDeal;
+use Buckaroo\PaymentMethods\iDealProcessing\iDealProcessing;
+use Buckaroo\PaymentMethods\iDealQR\iDealQR;
+use Buckaroo\PaymentMethods\iDin\iDin;
+use Buckaroo\PaymentMethods\In3\In3;
+use Buckaroo\PaymentMethods\In3Old\In3Old;
+use Buckaroo\PaymentMethods\KBC\KBC;
use Buckaroo\PaymentMethods\KlarnaKP\KlarnaKP;
-use Buckaroo\PaymentMethods\KnakenPay\KnakenPay;
-use Buckaroo\PaymentMethods\Payconiq\Payconiq;
-use Buckaroo\PaymentMethods\Emandates\Emandates;
use Buckaroo\PaymentMethods\KlarnaPay\KlarnaPay;
-use Buckaroo\PaymentMethods\WeChatPay\WeChatPay;
-use Buckaroo\PaymentMethods\Bancontact\Bancontact;
-use Buckaroo\PaymentMethods\Bizum\Bizum;
-use Buckaroo\PaymentMethods\CreditCard\CreditCard;
+use Buckaroo\PaymentMethods\KnakenPay\KnakenPay;
+use Buckaroo\PaymentMethods\Marketplaces\Marketplaces;
+use Buckaroo\PaymentMethods\MBWay\MBWay;
use Buckaroo\PaymentMethods\Multibanco\Multibanco;
-use Buckaroo\PaymentMethods\Przelewy24\Przelewy24;
+use Buckaroo\PaymentMethods\NoServiceSpecifiedPayment\NoServiceSpecifiedPayment;
+use Buckaroo\PaymentMethods\Payconiq\Payconiq;
+use Buckaroo\PaymentMethods\PaymentInitiation\PaymentInitiation;
+use Buckaroo\PaymentMethods\Paypal\Paypal;
use Buckaroo\PaymentMethods\PayPerEmail\PayPerEmail;
use Buckaroo\PaymentMethods\PointOfSale\PointOfSale;
-use Buckaroo\PaymentMethods\BankTransfer\BankTransfer;
-use Buckaroo\PaymentMethods\Marketplaces\Marketplaces;
+use Buckaroo\PaymentMethods\Przelewy24\Przelewy24;
+use Buckaroo\PaymentMethods\SEPA\SEPA;
use Buckaroo\PaymentMethods\Subscriptions\Subscriptions;
-use Buckaroo\PaymentMethods\BuckarooWallet\BuckarooWallet;
-use Buckaroo\PaymentMethods\BuckarooVoucher\BuckarooVoucher;
-use Buckaroo\PaymentMethods\ExternalPayment\ExternalPayment;
-use Buckaroo\PaymentMethods\CreditManagement\CreditManagement;
-use Buckaroo\PaymentMethods\PaymentInitiation\PaymentInitiation;
-use Buckaroo\PaymentMethods\AfterpayDigiAccept\AfterpayDigiAccept;
-use Buckaroo\PaymentMethods\Blik\Blik;
-use Buckaroo\PaymentMethods\ClickToPay\ClickToPay;
+use Buckaroo\PaymentMethods\Surepay\Surepay;
+use Buckaroo\PaymentMethods\Swish\Swish;
+use Buckaroo\PaymentMethods\Thunes\Thunes;
+use Buckaroo\PaymentMethods\Trustly\Trustly;
use Buckaroo\PaymentMethods\Twint\Twint;
-use Buckaroo\PaymentMethods\NoServiceSpecifiedPayment\NoServiceSpecifiedPayment;
+use Buckaroo\PaymentMethods\WeChatPay\WeChatPay;
use Buckaroo\PaymentMethods\Wero\Wero;
+use Buckaroo\Transaction\Client;
class PaymentMethodFactory
{
@@ -173,12 +173,9 @@ public function __construct(Client $client, ?string $paymentMethod)
*/
public function getPaymentMethod(): PaymentMethod
{
- if ($this->paymentMethod)
- {
- foreach (self::$payments as $class => $alias)
- {
- if (in_array($this->paymentMethod, $alias))
- {
+ if ($this->paymentMethod) {
+ foreach (self::$payments as $class => $alias) {
+ if (in_array($this->paymentMethod, $alias)) {
return new $class($this->client, $this->paymentMethod);
}
}
diff --git a/src/PaymentMethods/Paypal/Models/ExtraInfo.php b/src/PaymentMethods/Paypal/Models/ExtraInfo.php
index 96665915..ee5bf840 100644
--- a/src/PaymentMethods/Paypal/Models/ExtraInfo.php
+++ b/src/PaymentMethods/Paypal/Models/ExtraInfo.php
@@ -56,8 +56,7 @@ class ExtraInfo extends ServiceParameter
*/
public function address($address = null)
{
- if (is_array($address))
- {
+ if (is_array($address)) {
$this->address = new AddressAdapter(new Address($address));
}
@@ -70,8 +69,7 @@ public function address($address = null)
*/
public function customer($customer = null)
{
- if (is_array($customer))
- {
+ if (is_array($customer)) {
$this->customer = new Person($customer);
}
@@ -84,8 +82,7 @@ public function customer($customer = null)
*/
public function phone($phone = null)
{
- if (is_array($phone))
- {
+ if (is_array($phone)) {
$this->phone = new PhoneAdapter(new Phone($phone));
}
diff --git a/src/PaymentMethods/Paypal/Service/ParameterKeys/PhoneAdapter.php b/src/PaymentMethods/Paypal/Service/ParameterKeys/PhoneAdapter.php
index ba83d94b..c86325eb 100644
--- a/src/PaymentMethods/Paypal/Service/ParameterKeys/PhoneAdapter.php
+++ b/src/PaymentMethods/Paypal/Service/ParameterKeys/PhoneAdapter.php
@@ -25,6 +25,6 @@
class PhoneAdapter extends ServiceParametersKeysAdapter
{
protected array $keys = [
- 'mobile' => 'Phone'
+ 'mobile' => 'Phone',
];
}
diff --git a/src/PaymentMethods/Przelewy24/Models/Pay.php b/src/PaymentMethods/Przelewy24/Models/Pay.php
index c76466ff..f9453e47 100644
--- a/src/PaymentMethods/Przelewy24/Models/Pay.php
+++ b/src/PaymentMethods/Przelewy24/Models/Pay.php
@@ -43,8 +43,7 @@ class Pay extends ServiceParameter
*/
public function customer($customer = null)
{
- if (is_array($customer))
- {
+ if (is_array($customer)) {
$this->customer = new CustomerAdapter(new Person($customer));
}
@@ -57,8 +56,7 @@ public function customer($customer = null)
*/
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new EmailAdapter(new Email($email));
}
diff --git a/src/PaymentMethods/SEPA/Models/ExtraInfo.php b/src/PaymentMethods/SEPA/Models/ExtraInfo.php
index cd8dca24..15ee40e8 100644
--- a/src/PaymentMethods/SEPA/Models/ExtraInfo.php
+++ b/src/PaymentMethods/SEPA/Models/ExtraInfo.php
@@ -53,8 +53,7 @@ class ExtraInfo extends Pay
*/
public function address($address = null)
{
- if (is_array($address))
- {
+ if (is_array($address)) {
$this->address = new AddressAdapter(new Address($address));
}
diff --git a/src/PaymentMethods/SEPA/Models/Pay.php b/src/PaymentMethods/SEPA/Models/Pay.php
index f56b164d..9840d0b3 100644
--- a/src/PaymentMethods/SEPA/Models/Pay.php
+++ b/src/PaymentMethods/SEPA/Models/Pay.php
@@ -58,8 +58,7 @@ class Pay extends ServiceParameter
*/
public function customer($customer = null)
{
- if (is_array($customer))
- {
+ if (is_array($customer)) {
$this->customer = new CustomerAdapter(new Person($customer));
}
diff --git a/src/PaymentMethods/Subscriptions/Models/Subscription.php b/src/PaymentMethods/Subscriptions/Models/Subscription.php
index d5397547..eb5408e6 100644
--- a/src/PaymentMethods/Subscriptions/Models/Subscription.php
+++ b/src/PaymentMethods/Subscriptions/Models/Subscription.php
@@ -29,8 +29,8 @@
use Buckaroo\Models\Person;
use Buckaroo\Models\Phone;
use Buckaroo\Models\ServiceParameter;
-use Buckaroo\PaymentMethods\Subscriptions\Service\ParameterKeys\BankAccountAdapter;
use Buckaroo\PaymentMethods\Subscriptions\Service\ParameterKeys\AddressAdapter;
+use Buckaroo\PaymentMethods\Subscriptions\Service\ParameterKeys\BankAccountAdapter;
use Buckaroo\PaymentMethods\Subscriptions\Service\ParameterKeys\CompanyAdapter;
class Subscription extends ServiceParameter
@@ -207,8 +207,7 @@ class Subscription extends ServiceParameter
*/
public function debtor($debtor = null)
{
- if (is_array($debtor))
- {
+ if (is_array($debtor)) {
$this->debtor = new Debtor($debtor);
}
@@ -221,8 +220,7 @@ public function debtor($debtor = null)
*/
public function bankAccount($bankAccount = null)
{
- if (is_array($bankAccount))
- {
+ if (is_array($bankAccount)) {
$this->bankAccount = new BankAccountAdapter(new BankAccount($bankAccount));
}
@@ -235,8 +233,7 @@ public function bankAccount($bankAccount = null)
*/
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new Email($email);
}
@@ -249,8 +246,7 @@ public function email($email = null)
*/
public function phone($phone = null)
{
- if (is_array($phone))
- {
+ if (is_array($phone)) {
$this->phone = new Phone($phone);
}
@@ -263,8 +259,7 @@ public function phone($phone = null)
*/
public function address($address = null)
{
- if (is_array($address))
- {
+ if (is_array($address)) {
$this->address = new AddressAdapter(new Address($address));
}
@@ -277,8 +272,7 @@ public function address($address = null)
*/
public function person($person = null)
{
- if (is_array($person))
- {
+ if (is_array($person)) {
$this->person = new Person($person);
}
@@ -291,8 +285,7 @@ public function person($person = null)
*/
public function company($company = null)
{
- if (is_array($company))
- {
+ if (is_array($company)) {
$this->company = new CompanyAdapter(new Company($company));
}
@@ -305,8 +298,7 @@ public function company($company = null)
*/
public function configuration($configuration = null)
{
- if (is_array($configuration))
- {
+ if (is_array($configuration)) {
$this->configuration = new Configuration($configuration);
}
@@ -319,10 +311,8 @@ public function configuration($configuration = null)
*/
public function ratePlans($rate_plans = null)
{
- if (is_array($rate_plans))
- {
- foreach ($rate_plans as $type => $rate_plan)
- {
+ if (is_array($rate_plans)) {
+ foreach ($rate_plans as $type => $rate_plan) {
$property = $type . 'RatePlan';
$this->$property = new RatePlan($rate_plan);
@@ -338,10 +328,8 @@ public function ratePlans($rate_plans = null)
*/
public function ratePlanCharges($rate_plan_charges = null)
{
- if (is_array($rate_plan_charges))
- {
- foreach ($rate_plan_charges as $type => $rate_plan_charge)
- {
+ if (is_array($rate_plan_charges)) {
+ foreach ($rate_plan_charges as $type => $rate_plan_charge) {
$property = $type . 'RatePlanCharge';
$this->$property = new RatePlanCharge($rate_plan_charge);
diff --git a/src/PaymentMethods/Subscriptions/Service/ParameterKeys/BankAccountAdapter.php b/src/PaymentMethods/Subscriptions/Service/ParameterKeys/BankAccountAdapter.php
index acec2739..cb7ba0bf 100644
--- a/src/PaymentMethods/Subscriptions/Service/ParameterKeys/BankAccountAdapter.php
+++ b/src/PaymentMethods/Subscriptions/Service/ParameterKeys/BankAccountAdapter.php
@@ -28,8 +28,8 @@ class BankAccountAdapter extends ServiceParametersKeysAdapter
* @var array|string[]
*/
protected array $keys = [
- 'iban' => 'CustomerIBAN',
- 'accountName' => 'CustomerAccountName',
- 'bic' => 'CustomerBIC'
+ 'iban' => 'CustomerIBAN',
+ 'accountName' => 'CustomerAccountName',
+ 'bic' => 'CustomerBIC',
];
}
diff --git a/src/PaymentMethods/Subscriptions/Subscriptions.php b/src/PaymentMethods/Subscriptions/Subscriptions.php
index 65b09e7a..b58d013e 100644
--- a/src/PaymentMethods/Subscriptions/Subscriptions.php
+++ b/src/PaymentMethods/Subscriptions/Subscriptions.php
@@ -26,7 +26,6 @@
use Buckaroo\PaymentMethods\Subscriptions\Models\CombinedPayload;
use Buckaroo\PaymentMethods\Subscriptions\Models\ResumeSubscription;
use Buckaroo\PaymentMethods\Subscriptions\Models\Subscription;
-use function Ramsey\Uuid\v1;
class Subscriptions extends PaymentMethod implements Combinable
{
diff --git a/src/PaymentMethods/Surepay/Models/Verify.php b/src/PaymentMethods/Surepay/Models/Verify.php
index 512bff6d..1350d68e 100644
--- a/src/PaymentMethods/Surepay/Models/Verify.php
+++ b/src/PaymentMethods/Surepay/Models/Verify.php
@@ -37,8 +37,7 @@ class Verify extends ServiceParameter
*/
public function bankAccount($bankAccount = null)
{
- if (is_array($bankAccount))
- {
+ if (is_array($bankAccount)) {
$this->bankAccount = new BankAccountAdapter(new BankAccount($bankAccount));
}
diff --git a/src/PaymentMethods/Thunes/Models/Pay.php b/src/PaymentMethods/Thunes/Models/Pay.php
index 5bb06890..3feb8dd0 100644
--- a/src/PaymentMethods/Thunes/Models/Pay.php
+++ b/src/PaymentMethods/Thunes/Models/Pay.php
@@ -43,10 +43,8 @@ class Pay extends ServiceParameter
];
public function articles(?array $articles = null)
{
- if (is_array($articles))
- {
- foreach ($articles as $article)
- {
+ if (is_array($articles)) {
+ foreach ($articles as $article) {
$this->articles[] = new ArticleAdapter(new Article($article));
}
}
diff --git a/src/PaymentMethods/Thunes/Thunes.php b/src/PaymentMethods/Thunes/Thunes.php
index 65c6cf76..5a0805a8 100644
--- a/src/PaymentMethods/Thunes/Thunes.php
+++ b/src/PaymentMethods/Thunes/Thunes.php
@@ -51,8 +51,7 @@ public function pay(?Model $model = null): TransactionResponse
*/
public function paymentName(): string
{
- if (isset($this->payload['name']))
- {
+ if (isset($this->payload['name'])) {
return $this->payload['name'];
}
diff --git a/src/PaymentMethods/Traits/CountableGroupKey.php b/src/PaymentMethods/Traits/CountableGroupKey.php
index b2b07461..973942d9 100644
--- a/src/PaymentMethods/Traits/CountableGroupKey.php
+++ b/src/PaymentMethods/Traits/CountableGroupKey.php
@@ -29,8 +29,7 @@ trait CountableGroupKey
*/
public function getGroupKey(string $key, ?int $keyCount = 0): ?int
{
- if ($this->countable($key, $keyCount))
- {
+ if ($this->countable($key, $keyCount)) {
return intval($keyCount) + 1;
}
diff --git a/src/PaymentMethods/Trustly/Models/Pay.php b/src/PaymentMethods/Trustly/Models/Pay.php
index a9b8218a..7ead572e 100644
--- a/src/PaymentMethods/Trustly/Models/Pay.php
+++ b/src/PaymentMethods/Trustly/Models/Pay.php
@@ -20,8 +20,8 @@
namespace Buckaroo\PaymentMethods\Trustly\Models;
-use Buckaroo\Models\Person;
use Buckaroo\Models\Email;
+use Buckaroo\Models\Person;
use Buckaroo\Models\ServiceParameter;
use Buckaroo\PaymentMethods\Trustly\Service\ParameterKeys\CustomerAdapter;
use Buckaroo\PaymentMethods\Trustly\Service\ParameterKeys\EmailAdapter;
@@ -49,8 +49,7 @@ class Pay extends ServiceParameter
*/
public function customer($customer = null)
{
- if (is_array($customer))
- {
+ if (is_array($customer)) {
$this->customer = new CustomerAdapter(new Person($customer));
}
@@ -63,8 +62,7 @@ public function customer($customer = null)
*/
public function email($email = null)
{
- if (is_string($email))
- {
+ if (is_string($email)) {
$this->email = new EmailAdapter(new Email($email));
}
diff --git a/src/PaymentMethods/iDin/iDin.php b/src/PaymentMethods/iDin/iDin.php
index 1855a6a1..7ce9e099 100644
--- a/src/PaymentMethods/iDin/iDin.php
+++ b/src/PaymentMethods/iDin/iDin.php
@@ -21,7 +21,6 @@
namespace Buckaroo\PaymentMethods\iDin;
use Buckaroo\Models\Payload\DataRequestPayload;
-use Buckaroo\Models\Payload\PayPayload;
use Buckaroo\PaymentMethods\iDin\Models\Issuer;
use Buckaroo\PaymentMethods\iDin\Service\ParameterKeys\IssuerAdapter;
use Buckaroo\PaymentMethods\PaymentMethod;
diff --git a/src/Services/ActiveSubscriptions.php b/src/Services/ActiveSubscriptions.php
index e5fb4f84..6a7f57ba 100644
--- a/src/Services/ActiveSubscriptions.php
+++ b/src/Services/ActiveSubscriptions.php
@@ -20,15 +20,14 @@
namespace Buckaroo\Services;
-use Exception;
-use SimpleXMLElement;
use Buckaroo\Models\ServiceList;
use Buckaroo\Transaction\Client;
use Buckaroo\Transaction\Request\TransactionRequest;
+use Exception;
+use SimpleXMLElement;
class ActiveSubscriptions
{
-
private const SERVICE_CODE_AND_ACTION = 'GetActiveSubscriptions';
private const VERSION_ZERO = 0;
@@ -76,12 +75,13 @@ private function buildTransaction(): TransactionRequest
self::SERVICE_CODE_AND_ACTION
)
);
+
return $transaction;
}
private function format($data): array
{
- $decoded = json_decode(json_encode($data), true);
+ $decoded = json_decode(json_encode($data), true);
if (!is_array($decoded)) {
return [];
}
@@ -95,6 +95,7 @@ private function format($data): array
}
$formated[] = $formatedSubscription;
}
+
return $formated;
}
diff --git a/src/Services/PayloadService.php b/src/Services/PayloadService.php
index 2e83965d..4715746f 100644
--- a/src/Services/PayloadService.php
+++ b/src/Services/PayloadService.php
@@ -45,20 +45,17 @@ public function __construct($payload)
*/
protected function setPayload($payload)
{
- if (is_array($payload))
- {
+ if (is_array($payload)) {
$this->payload = $payload;
return $this;
}
- if (is_string($payload))
- {
+ if (is_string($payload)) {
$this->payload = json_decode($payload, true);
}
- if ($this->payload == null)
- {
+ if ($this->payload == null) {
throw new \Exception("Invalid or empty payload. Array or json format required.");
}
diff --git a/src/Services/ServiceListParameters/ModelParameters.php b/src/Services/ServiceListParameters/ModelParameters.php
index 63b58bf4..f43c2479 100644
--- a/src/Services/ServiceListParameters/ModelParameters.php
+++ b/src/Services/ServiceListParameters/ModelParameters.php
@@ -63,10 +63,8 @@ public function __construct(
*/
public function data(): ServiceList
{
- foreach ($this->model->toArray() as $key => $value)
- {
- if (! is_array($value))
- {
+ foreach ($this->model->toArray() as $key => $value) {
+ if (!is_array($value)) {
$this->appendParameter(
$this->groupKey($key),
$this->groupType($key),
@@ -85,8 +83,7 @@ public function data(): ServiceList
*/
private function groupKey($key)
{
- if ($this->model instanceof ServiceParameter && ! $this->groupKey)
- {
+ if ($this->model instanceof ServiceParameter && !$this->groupKey) {
return $this->model->getGroupKey($key);
}
@@ -99,8 +96,7 @@ private function groupKey($key)
*/
private function groupType($key)
{
- if ($this->model instanceof ServiceParameter && ! $this->groupType)
- {
+ if ($this->model instanceof ServiceParameter && !$this->groupType) {
return $this->model->getGroupType($key);
}
diff --git a/src/Services/ServiceListParameters/ServiceListParameter.php b/src/Services/ServiceListParameters/ServiceListParameter.php
index 2345a3c4..5101569e 100644
--- a/src/Services/ServiceListParameters/ServiceListParameter.php
+++ b/src/Services/ServiceListParameters/ServiceListParameter.php
@@ -63,8 +63,7 @@ public function data(): ServiceList
*/
protected function appendParameter(?int $groupKey, ?string $groupType, string $name, $value)
{
- if (! is_null($value))
- {
+ if (!is_null($value)) {
$this->serviceList->appendParameter([
"Name" => $name,
"Value" => $value,
diff --git a/src/Services/TraitHelpers/HasIssuers.php b/src/Services/TraitHelpers/HasIssuers.php
index 0f913fab..efb1383e 100644
--- a/src/Services/TraitHelpers/HasIssuers.php
+++ b/src/Services/TraitHelpers/HasIssuers.php
@@ -33,22 +33,17 @@ public function issuers(): array
{
$request = new TransactionRequest;
- try
- {
+ try {
$response = $this->client->specification($this->paymentName, $this->serviceVersion(), $request);
- } catch (BuckarooException $e)
- {
+ } catch (BuckarooException $e) {
return [];
}
$issuerList = [];
- if (isset($response->data()['Actions']['0']['RequestParameters'][0]['ListItemDescriptions']))
- {
+ if (isset($response->data()['Actions']['0']['RequestParameters'][0]['ListItemDescriptions'])) {
$issuersData = $response->data()['Actions']['0']['RequestParameters'][0]['ListItemDescriptions'];
- if (count($issuersData) > 0)
- {
- foreach ($issuersData as $issuer)
- {
+ if (count($issuersData) > 0) {
+ foreach ($issuersData as $issuer) {
$issuerList[] = ['id' => $issuer['Value'], 'name' => $issuer['Description']];
}
}
diff --git a/src/Transaction/Client.php b/src/Transaction/Client.php
index b1e5ce3f..585c1c3e 100644
--- a/src/Transaction/Client.php
+++ b/src/Transaction/Client.php
@@ -26,8 +26,8 @@
use Buckaroo\Exceptions\BuckarooException;
use Buckaroo\Handlers\Logging\Subject;
use Buckaroo\Resources\Constants\Endpoints;
-use Buckaroo\Services\TransactionHeaders\CultureHeader;
use Buckaroo\Services\TransactionHeaders\ChannelHeader;
+use Buckaroo\Services\TransactionHeaders\CultureHeader;
use Buckaroo\Services\TransactionHeaders\DefaultHeader;
use Buckaroo\Services\TransactionHeaders\HmacHeader;
use Buckaroo\Services\TransactionHeaders\SoftwareHeader;
@@ -202,8 +202,7 @@ protected function call($method, string $responseClass, ?Request $data = null, ?
$this->config->getLogger()->info($method . ' ' . $endPoint);
$this->config->getLogger()->info('HEADERS: ' . json_encode($headers));
- if ($data)
- {
+ if ($data) {
$this->config->getLogger()->info(
'PAYLOAD: ' . $data->toJson()
);
@@ -228,13 +227,11 @@ protected function call($method, string $responseClass, ?Request $data = null, ?
*/
public function config(?Config $config = null)
{
- if ($config)
- {
+ if ($config) {
$this->config = $config;
}
- if (! $this->config)
- {
+ if (!$this->config) {
throw new BuckarooException(
$this->logger,
"No config has been configured.
diff --git a/src/Transaction/Request/HttpClient/GuzzleHttpClientV5.php b/src/Transaction/Request/HttpClient/GuzzleHttpClientV5.php
index 3593ef08..c5c6c225 100644
--- a/src/Transaction/Request/HttpClient/GuzzleHttpClientV5.php
+++ b/src/Transaction/Request/HttpClient/GuzzleHttpClientV5.php
@@ -53,8 +53,7 @@ public function call(string $url, array $headers, string $method, ?string $data
'body' => $data,
]);
- try
- {
+ try {
$response = $this->httpClient->send($request);
$result = (string) $response->getBody();
diff --git a/src/Transaction/Request/HttpClient/GuzzleHttpClientV7.php b/src/Transaction/Request/HttpClient/GuzzleHttpClientV7.php
index 6ac73cdf..6c427d2a 100644
--- a/src/Transaction/Request/HttpClient/GuzzleHttpClientV7.php
+++ b/src/Transaction/Request/HttpClient/GuzzleHttpClientV7.php
@@ -52,8 +52,7 @@ public function call(string $url, array $headers, string $method, ?string $data
$request = new Request($method, $url, $headers, $data);
- try
- {
+ try {
$response = $this->httpClient->send($request, ['http_errors' => false]);
$result = (string) $response->getBody();
diff --git a/src/Transaction/Request/HttpClient/HttpClientAbstract.php b/src/Transaction/Request/HttpClient/HttpClientAbstract.php
index 3d70d79b..24b0f6dc 100644
--- a/src/Transaction/Request/HttpClient/HttpClientAbstract.php
+++ b/src/Transaction/Request/HttpClient/HttpClientAbstract.php
@@ -67,8 +67,7 @@ protected function getDecodedResult($response, $result): array
{
$decoded_result = json_decode($result, true);
- if (is_array($decoded_result))
- {
+ if (is_array($decoded_result)) {
return $decoded_result;
}
@@ -89,8 +88,7 @@ protected function convertHeadersFormat(array $headers): array
{
$resultHeaders = [];
- foreach ($headers as $header)
- {
+ foreach ($headers as $header) {
$headerName = substr($header, 0, strpos($header, ':'));
$headerValue = substr($header, strpos($header, ':') + 2);
$resultHeaders[$headerName] = $headerValue;
diff --git a/src/Transaction/Request/HttpClient/HttpClientFactory.php b/src/Transaction/Request/HttpClient/HttpClientFactory.php
index ce0ab9cf..02f115f9 100644
--- a/src/Transaction/Request/HttpClient/HttpClientFactory.php
+++ b/src/Transaction/Request/HttpClient/HttpClientFactory.php
@@ -3,9 +3,9 @@
namespace Buckaroo\Transaction\Request\HttpClient;
use Buckaroo\Config\Config;
+use function defined;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
-use function defined;
class HttpClientFactory
{
diff --git a/src/Transaction/Request/Request.php b/src/Transaction/Request/Request.php
index c91911c2..3eccc5ce 100644
--- a/src/Transaction/Request/Request.php
+++ b/src/Transaction/Request/Request.php
@@ -24,7 +24,6 @@
use ArrayAccess;
use Buckaroo\Resources\Arrayable;
-use Exception;
use JsonSerializable;
class Request implements JsonSerializable, ArrayAccess, Arrayable
@@ -45,11 +44,9 @@ class Request implements JsonSerializable, ArrayAccess, Arrayable
*/
public function offsetSet($offset, $value): void
{
- if (is_null($offset))
- {
+ if (is_null($offset)) {
$this->data[] = $value;
- } else
- {
+ } else {
$this->data[$offset] = $value;
}
}
@@ -67,13 +64,13 @@ public function offsetUnset($offset): void
}
/** Implement ArrayAccess */
- public function offsetGet($offset): mixed
+ public function offsetGet($offset)
{
return isset($this->data[$offset]) ? $this->data[$offset] : null;
}
/** Implement JsonSerializable */
- public function jsonSerialize(): mixed
+ public function jsonSerialize()
{
return $this->data;
}
@@ -107,8 +104,7 @@ public function setHeader($name, $value)
*/
public function getHeader($name)
{
- if (isset($this->headers[strtolower($name)]))
- {
+ if (isset($this->headers[strtolower($name)])) {
return $this->headers[strtolower($name)];
}
diff --git a/src/Transaction/Request/TransactionRequest.php b/src/Transaction/Request/TransactionRequest.php
index bd0bb600..f1cadac1 100644
--- a/src/Transaction/Request/TransactionRequest.php
+++ b/src/Transaction/Request/TransactionRequest.php
@@ -40,8 +40,7 @@ public function __construct()
*/
public function setPayload(Model $model)
{
- foreach ($model->toArray() as $key => $value)
- {
+ foreach ($model->toArray() as $key => $value) {
$this->data[$model->serviceParameterKeyOf($key)] = $value;
}
@@ -83,10 +82,8 @@ public function getServices() : Services
*/
public function toArray(): array
{
- foreach ($this->data as $key => $value)
- {
- if (is_a($value, Arrayable::class))
- {
+ foreach ($this->data as $key => $value) {
+ if (is_a($value, Arrayable::class)) {
$this->data[$key] = $value->toArray();
}
}
diff --git a/src/Transaction/Response/Response.php b/src/Transaction/Response/Response.php
index 6646db74..863e3501 100644
--- a/src/Transaction/Response/Response.php
+++ b/src/Transaction/Response/Response.php
@@ -63,7 +63,7 @@ public function offsetUnset($offset): void
}
/** Implement ArrayAccess */
- public function offsetGet($offset): mixed
+ public function offsetGet($offset)
{
return isset($this->data[$offset]) ? $this->data[$offset] : null;
}
@@ -83,8 +83,7 @@ public function __call($method, $args)
$param = substr($method, 3);
// $arg = isset($args[0]) ? $args[0] : null;
- if ($prefix === 'get')
- {
+ if ($prefix === 'get') {
return $this->offsetGet($param);
}
diff --git a/src/Transaction/Response/TransactionResponse.php b/src/Transaction/Response/TransactionResponse.php
index 0c419593..7d16535f 100644
--- a/src/Transaction/Response/TransactionResponse.php
+++ b/src/Transaction/Response/TransactionResponse.php
@@ -105,10 +105,8 @@ public function isValidationFailure(): bool
*/
public function data(?string $key = null)
{
- if ($key)
- {
- if (isset($this->data[$key]))
- {
+ if ($key) {
+ if (isset($this->data[$key])) {
return $this->data[$key];
}
@@ -123,7 +121,7 @@ public function data(?string $key = null)
*/
public function hasRedirect(): bool
{
- return ! empty($this->data['RequiredAction']['RedirectURL'])
+ return !empty($this->data['RequiredAction']['RedirectURL'])
&& $this->data['RequiredAction']['Name'] == 'Redirect';
}
@@ -132,8 +130,7 @@ public function hasRedirect(): bool
*/
public function getRedirectUrl(): string
{
- if ($this->hasRedirect())
- {
+ if ($this->hasRedirect()) {
return $this->data['RequiredAction']['RedirectURL'];
}
@@ -163,14 +160,12 @@ public function getServiceAction(): string
*/
public function getServiceParameters()
{
- if (! empty($this->data['Services'][0]['Parameters']))
- {
+ if (!empty($this->data['Services'][0]['Parameters'])) {
$parameters = $this->data['Services'][0]['Parameters'];
$params = [];
- foreach ($parameters as $key => $parameter)
- {
+ foreach ($parameters as $key => $parameter) {
// key to lowercase to be consistent with PaymentResult version of getServiceParameters
$params[strtolower($parameter['Name'])] = $parameter['Value'];
}
@@ -186,14 +181,12 @@ public function getServiceParameters()
*/
public function getCustomParameters(): array
{
- if (! empty($this->data['CustomParameters']['List']))
- {
+ if (!empty($this->data['CustomParameters']['List'])) {
$parameters = $this->data['CustomParameters']['List'];
$params = [];
- foreach ($parameters as $key => $parameter)
- {
+ foreach ($parameters as $key => $parameter) {
$params[$parameter['Name']] = $parameter['Value'];
}
@@ -208,14 +201,12 @@ public function getCustomParameters(): array
*/
public function getAdditionalParameters(): array
{
- if (! empty($this->data['AdditionalParameters']['AdditionalParameter']))
- {
+ if (!empty($this->data['AdditionalParameters']['AdditionalParameter'])) {
$parameters = $this->data['AdditionalParameters']['AdditionalParameter'];
$params = [];
- foreach ($parameters as $key => $parameter)
- {
+ foreach ($parameters as $key => $parameter) {
$params[$parameter['Name']] = $parameter['Value'];
}
@@ -290,8 +281,7 @@ public function getInvoice(): string
*/
public function getStatusCode(): ?int
{
- if (! empty($this->data['Status']['Code']['Code']))
- {
+ if (!empty($this->data['Status']['Code']['Code'])) {
return $this->data['Status']['Code']['Code'];
}
@@ -303,8 +293,7 @@ public function getStatusCode(): ?int
*/
public function getSubStatusCode(): ?string
{
- if (! empty($this->data['Status']['SubCode']['Code']))
- {
+ if (!empty($this->data['Status']['SubCode']['Code'])) {
return $this->data['Status']['SubCode']['Code'];
}
@@ -318,7 +307,7 @@ public function hasSomeError(): bool
{
$getError = $this->getSomeError();
- return ! empty($getError);
+ return !empty($getError);
}
/**
@@ -326,25 +315,21 @@ public function hasSomeError(): bool
*/
public function getSomeError(): string
{
- if ($this->hasError())
- {
+ if ($this->hasError()) {
$error = $this->getFirstError();
return $error['ErrorMessage'];
}
- if ($this->hasConsumerMessage())
- {
+ if ($this->hasConsumerMessage()) {
return $this->getConsumerMessage();
}
- if ($this->hasMessage())
- {
+ if ($this->hasMessage()) {
return $this->getMessage();
}
- if ($this->hasSubCodeMessage())
- {
+ if ($this->hasSubCodeMessage()) {
return $this->getSubCodeMessage();
}
@@ -356,12 +341,12 @@ public function getSomeError(): string
*/
public function hasError(): bool
{
- return ! empty($this->data['RequestErrors']) && (
- ! empty($this->data['RequestErrors']['ChannelErrors']) ||
- ! empty($this->data['RequestErrors']['ServiceErrors']) ||
- ! empty($this->data['RequestErrors']['ActionErrors']) ||
- ! empty($this->data['RequestErrors']['ParameterErrors']) ||
- ! empty($this->data['RequestErrors']['CustomParameterErrors'])
+ return !empty($this->data['RequestErrors']) && (
+ !empty($this->data['RequestErrors']['ChannelErrors']) ||
+ !empty($this->data['RequestErrors']['ServiceErrors']) ||
+ !empty($this->data['RequestErrors']['ActionErrors']) ||
+ !empty($this->data['RequestErrors']['ParameterErrors']) ||
+ !empty($this->data['RequestErrors']['CustomParameterErrors'])
);
}
@@ -372,12 +357,9 @@ public function getFirstError(): array
{
$errorTypes = ['ChannelErrors', 'ServiceErrors', 'ActionErrors', 'ParameterErrors', 'CustomParameterErrors'];
- if ($this->hasError())
- {
- foreach ($errorTypes as $errorType)
- {
- if (! empty($this->data['RequestErrors'][$errorType]))
- {
+ if ($this->hasError()) {
+ foreach ($errorTypes as $errorType) {
+ if (!empty($this->data['RequestErrors'][$errorType])) {
return $this->data['RequestErrors'][$errorType][0];
}
}
@@ -391,7 +373,7 @@ public function getFirstError(): array
*/
public function hasMessage(): bool
{
- return ! empty($this->data['Message']);
+ return !empty($this->data['Message']);
}
/**
@@ -407,7 +389,7 @@ public function getMessage(): string
*/
public function hasConsumerMessage(): bool
{
- return ! empty($this->data['ConsumerMessage']['HtmlText']);
+ return !empty($this->data['ConsumerMessage']['HtmlText']);
}
/**
@@ -415,8 +397,7 @@ public function hasConsumerMessage(): bool
*/
public function getConsumerMessage(): string
{
- if ($this->hasConsumerMessage())
- {
+ if ($this->hasConsumerMessage()) {
return $this->data['ConsumerMessage']['HtmlText'];
}
@@ -428,7 +409,7 @@ public function getConsumerMessage(): string
*/
public function hasSubCodeMessage(): bool
{
- return ! empty($this->data['Status']['SubCode']['Description']);
+ return !empty($this->data['Status']['SubCode']['Description']);
}
/**
@@ -436,8 +417,7 @@ public function hasSubCodeMessage(): bool
*/
public function getSubCodeMessage(): string
{
- if ($this->hasSubCodeMessage())
- {
+ if ($this->hasSubCodeMessage()) {
return $this->data['Status']['SubCode']['Description'];
}
diff --git a/tests/Buckaroo/BuckarooTestCase.php b/tests/Buckaroo/BuckarooTestCase.php
deleted file mode 100644
index 0b85162b..00000000
--- a/tests/Buckaroo/BuckarooTestCase.php
+++ /dev/null
@@ -1,267 +0,0 @@
-load();
-
- $this->buckaroo = new BuckarooClient(new DefaultConfig(
- $_ENV['BPE_WEBSITE_KEY'],
- $_ENV['BPE_SECRET_KEY'],
- $_ENV['BPE_MODE'] ?? null,
- $_ENV['BPE_CURRENCY_CODE'] ?? null,
- $_ENV['BPE_RETURN_URL'] ?? null,
- $_ENV['BPE_RETURN_URL_CANCEL'] ?? null,
- $_ENV['BPE_PUSH_URL'] ?? null,
- 'TestingPlatform',
- '3.0.0',
- 'TestingModule',
- 'Testing',
- '2.4.0',
- 'nl-NL'
- ));
-
- parent::__construct();
- }
-
- protected function getPayPayload(?array $overrides = []): array
- {
- $payload = [
- 'billing' => $this->getBillingPayload(),
- 'shipping' => $this->getShippingPayload(),
- ];
-
- return array_merge($this->getBasePayPayload(), $payload, $overrides);
- }
-
- protected function getRefundPayload(?array $overrides = []): array
- {
- $payload = [
- 'amountCredit' => 0.01,
- 'invoice' => uniqid(),
- ];
- return array_merge($payload, $overrides);
- }
-
- protected function getBasePayPayload(?array $exceptKeys = [], ?array $overrides = []): array
- {
- $payload = [
- 'clientIP' => '127.0.0.1',
- 'invoice' => uniqid(),
- 'currency' => 'EUR',
- 'amountDebit' => 100.30,
- 'order' => uniqid(),
- 'description' => 'Buckaroo SDK Test Transaction',
- ];
-
- if ($exceptKeys) {
- foreach ($exceptKeys as $key) {
- $this->removeKeyFromArray($payload, $key);
- }
- }
-
- return array_merge($payload, $overrides);
- }
-
- protected function getBillingPayload(?array $exceptKeys = [], ?array $overrides = []): array
- {
- $payload = [
- 'recipient' => [
- 'category' => 'B2C',
- 'title' => 'Female',
- 'careOf' => 'John Smith',
- 'firstName' => 'John',
- 'lastName' => 'Doe',
- 'initials' => 'JD',
- 'salutation' => 'Male',
- 'birthDate' => '1990-01-01',
- 'companyName' => 'Buckaroo Test',
- 'email' => 'test@buckaroo.nl',
- 'phone' => ['mobile' => '0698765433'],
- 'conversationLanguage' => 'NL',
- 'identificationNumber' => 'IdNumber12345',
- 'customerNumber' => 'customerNumber12345',
- ],
- 'address' => [
- 'street' => 'Hoofdstraat',
- 'houseNumber' => '13',
- 'houseNumberSuffix' => 'A',
- 'zipcode' => '1234AB',
- 'city' => 'Heerenveen',
- 'country' => 'NL',
- ],
- 'phone' => ['mobile' => '0698765433'],
- 'email' => 'test@buckaroo.nl',
- ];
-
- if ($exceptKeys) {
- foreach ($exceptKeys as $key) {
- $this->removeKeyFromArray($payload, $key);
- }
- }
-
- return array_merge($payload, $overrides);
- }
-
- protected function getShippingPayload(?array $exceptKeys = [], ?array $overrides = []): array
- {
- $payload = [
- 'recipient' => [
- 'category' => 'B2C',
- 'careOf' => 'John Smith',
- 'title' => 'Male',
- 'initials' => 'JD',
- 'salutation' => 'Male',
- 'companyName' => 'Buckaroo B.V.',
- 'firstName' => 'John',
- 'lastName' => 'Doe',
- 'birthDate' => '1990-01-01',
- 'phone' => ['mobile' => '0698765433'],
- 'email' => 'test@buckaroo.nl',
- 'conversationLanguage' => 'NL',
- 'identificationNumber' => 'IdNumber12345',
- 'customerNumber' => 'customerNumber12345',
- ],
- 'email' => 'test@buckaroo.nl',
- 'address' => [
- 'street' => 'Kalverstraat',
- 'houseNumber' => '13',
- 'houseNumberSuffix' => 'A',
- 'zipcode' => '4321EB',
- 'city' => 'Amsterdam',
- 'country' => 'NL',
- 'addressType' => 'Standard'
- ],
- ];
-
- if ($exceptKeys) {
- foreach ($exceptKeys as $key) {
- $this->removeKeyFromArray($payload, $key);
- }
- }
-
- return array_merge($payload, $overrides);
- }
-
- private function removeKeyFromArray(array &$array, $keyToRemove): void
- {
- foreach ($array as $key => &$value) {
- if ($key === $keyToRemove) {
- unset($array[$key]);
- } elseif (is_array($value)) {
- $this->removeKeyFromArray($value, $keyToRemove);
- }
- }
- }
-
- protected function getArticlesPayload(?array $exceptKeys = [], ?array $overrides = []): array
- {
- $payload = [
- [
- 'identifier' => 'Articlenumber1',
- 'description' => 'Blue Toy Car',
- 'vatPercentage' => '21',
- 'quantity' => '2',
- 'price' => '25.10',
- 'imageUrl' => 'https://www.buckaroo.nl/img/logo_menu.png',
- 'UnitCode' => 'Pieces'
- ],
- [
- 'identifier' => 'Articlenumber2',
- 'description' => 'Red Toy Car',
- 'vatPercentage' => '21',
- 'quantity' => '1',
- 'price' => '50.10',
- 'imageUrl' => 'https://www.buckaroo.nl/img/logo_menu.png',
- 'UnitCode' => 'Pieces'
- ],
- ];
-
- if ($exceptKeys) {
- foreach ($exceptKeys as $key) {
- $this->removeKeyFromArray($payload, $key);
- }
- }
-
- return array_merge($payload, $overrides);
- }
-
- protected function getInvoicePayload(array $append = []): array
- {
- return array_merge($append, [
- 'applyStartRecurrent' => 'False',
- 'invoiceAmount' => 10.00,
- 'invoiceAmountVAT' => 1.00,
- 'invoiceDate' => '2024-01-01',
- 'dueDate' => '2030-01-01',
- 'schemeKey' => 's31w5d',
- 'maxStepIndex' => 1,
- 'allowedServices' => 'ideal,mastercard',
- 'allowedServicesAfterDueDate' => 'ideal,mastercard',
- 'debtor' => [
- 'code' => 'johnsmith4',
- ],
- 'email' => 'youremail@example.nl',
- 'phone' => [
- 'mobile' => '06198765432',
- ],
- 'person' => [
- 'culture' => 'nl-NL',
- 'title' => 'Msc',
- 'initials' => 'JS',
- 'firstName' => 'Test',
- 'lastNamePrefix' => 'Jones',
- 'lastName' => 'Aflever',
- 'gender' => Gender::MALE,
- ],
- 'company' => [
- 'culture' => 'nl-NL',
- 'name' => 'My Company Corporation',
- 'vatApplicable' => true,
- 'vatNumber' => 'NL140619562B01',
- 'chamberOfCommerce' => '20091741',
- ],
- 'address' => [
- 'street' => 'Hoofdtraat',
- 'houseNumber' => '90',
- 'houseNumberSuffix' => 'A',
- 'zipcode' => '8441ER',
- 'city' => 'Heerenveen',
- 'state' => 'Friesland',
- 'country' => 'NL',
- ],
- ]);
- }
-}
diff --git a/tests/Buckaroo/ConfirmingCredentialsTest.php b/tests/Buckaroo/ConfirmingCredentialsTest.php
deleted file mode 100644
index 758d54c0..00000000
--- a/tests/Buckaroo/ConfirmingCredentialsTest.php
+++ /dev/null
@@ -1,37 +0,0 @@
-buckaroo->confirmCredential();
-
- $this->assertTrue($response);
- }
-}
diff --git a/tests/Buckaroo/GetActiveSubscription.php b/tests/Buckaroo/GetActiveSubscription.php
deleted file mode 100644
index cc83d082..00000000
--- a/tests/Buckaroo/GetActiveSubscription.php
+++ /dev/null
@@ -1,38 +0,0 @@
-buckaroo->getActiveSubscriptions();
- $this->assertIsArray($response);
- $this->assertArrayHasKey('serviceCode', $response[0]);
- $this->assertArrayHasKey('currencies', $response[0]);
- }
-}
diff --git a/tests/Buckaroo/Payments/AfterpayDigiAcceptTest.php b/tests/Buckaroo/Payments/AfterpayDigiAcceptTest.php
deleted file mode 100644
index 9c246165..00000000
--- a/tests/Buckaroo/Payments/AfterpayDigiAcceptTest.php
+++ /dev/null
@@ -1,158 +0,0 @@
-buckaroo->method('afterpaydigiaccept')->pay(array_merge(
- $this->getBasePayPayload(),
- [
- 'b2b' => true,
- 'addressesDiffer' => true,
- 'customerIPAddress' => '0.0.0.0',
- 'amountDebit' => 100.80,
- 'shippingCosts' => 0.5,
- 'costCentre' => 'Test',
- 'department' => 'Test',
- 'establishmentNumber' => '123456',
- 'billing' => $this->getBillingPayload(['category', 'careOf']),
- 'shipping' => $this->getShippingPayload(['category', 'careOf']),
- 'articles' => $this->getArticlesPayload(['type', 'vatPercentage']),
- ]
- ));
-
- self::$payTransactionKey = $response->getTransactionKey();
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_afterpaydigiaccept_authorize()
- {
- $response = $this->buckaroo->method('afterpaydigiaccept')->authorize(array_merge(
- $this->getBasePayPayload(),
- [
- 'b2b' => true,
- 'addressesDiffer' => true,
- 'customerIPAddress' => '0.0.0.0',
- 'amountDebit' => 100.80,
- 'shippingCosts' => 0.5,
- 'costCentre' => 'Test',
- 'department' => 'Test',
- 'establishmentNumber' => '123456',
- 'billing' => $this->getBillingPayload(['category', 'careOf']),
- 'shipping' => $this->getShippingPayload(['category', 'careOf']),
- 'articles' => $this->getArticlesPayload(['type', 'vatPercentage']),
- ]
- ));
-
- self::$authorizeTransactionKey = $response->getTransactionKey();
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- * @depends it_creates_a_afterpaydigiaccept_authorize
- */
- public function it_creates_a_afterpaydigiaccept_cancelAuthorize()
- {
- if (empty(self::$authorizeTransactionKey)) {
- $this->markTestSkipped('Skipping cancelAuthorize: No authorization transaction key is set.');
- }
-
- $response = $this->buckaroo->method('afterpaydigiaccept')->cancelAuthorize($this->getRefundPayload([
- 'shippingCosts' => 0.5,
- 'amountCredit' => 0.50,
- 'originalTransactionKey' => self::$authorizeTransactionKey,
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_afterpaydigiaccept_capture()
- {
- $response = $this->buckaroo->method('afterpaydigiaccept')->authorize(array_merge(
- $this->getBasePayPayload(),
- [
- 'b2b' => true,
- 'addressesDiffer' => true,
- 'customerIPAddress' => '0.0.0.0',
- 'amountDebit' => 100.80,
- 'shippingCosts' => 0.5,
- 'costCentre' => 'Test',
- 'department' => 'Test',
- 'establishmentNumber' => '123456',
- 'billing' => $this->getBillingPayload(['category', 'careOf']),
- 'shipping' => $this->getShippingPayload(['category', 'careOf']),
- 'articles' => $this->getArticlesPayload(['type', 'vatPercentage']),
- ]
- ));
-
- self::$authorizeTransactionKey = $response->getTransactionKey();
- $this->assertTrue($response->isSuccess());
-
- $response = $this->buckaroo->method('afterpaydigiaccept')->capture($this->getPayPayload([
- 'originalTransactionKey' => self::$authorizeTransactionKey,
- 'amountDebit' => 100.80,
- 'billing' => $this->getBillingPayload(['category', 'careOf']),
- 'shipping' => $this->getShippingPayload(['category', 'careOf']),
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @test
- * @depends it_creates_a_afterpaydigiaccept_payment
- */
- public function it_creates_a_afterpaydigiaccept_refund()
- {
- if (empty(self::$payTransactionKey)) {
- $this->markTestSkipped('Skipping refund: No original transaction key is set.');
- }
-
- $response = $this->buckaroo->method('afterpaydigiaccept')->refund(
- $this->getRefundPayload([
- 'amountCredit' => 100.80,
- 'originalTransactionKey' => self::$payTransactionKey,
- ])
- );
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/AfterpayTest.php b/tests/Buckaroo/Payments/AfterpayTest.php
deleted file mode 100644
index bdf41421..00000000
--- a/tests/Buckaroo/Payments/AfterpayTest.php
+++ /dev/null
@@ -1,141 +0,0 @@
-buckaroo->method('afterpay')->setServiceVersion(3)->pay($this->getPaymentPayload());
-
- self::$payTransactionKey = $response->getTransactionKey();
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_afterpay_authorize()
- {
- $response = $this->buckaroo->method('afterpay')->authorize($this->getPaymentPayload());
-
- self::$authorizeTransactionKey = $response->getTransactionKey();
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- * @depends it_creates_a_afterpay_authorize
- */
- public function it_creates_a_afterpay_cancelAuthorize()
- {
- if (empty(self::$authorizeTransactionKey)) {
- $this->markTestSkipped('Skipping cancelAuthorize: No authorization transaction key is set.');
- }
-
- $response = $this->buckaroo->method('afterpay')->cancelAuthorize($this->getPaymentPayload([
- 'originalTransactionKey' => self::$authorizeTransactionKey,
- 'amountCredit' => 100.30,
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- * @depends it_creates_a_afterpay_authorize
- */
- public function it_creates_a_afterpay_capture()
- {
- $response = $this->buckaroo->method('afterpay')->setServiceVersion(3)->authorize($this->getPaymentPayload());
-
- $this->assertTrue($response->isSuccess());
-
- $response = $this->buckaroo->method('afterpay')->capture($this->getPaymentPayload([
- 'originalTransactionKey' => $response->getTransactionKey(),
- 'amountCredit' => 100.30,
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- * @depends it_creates_a_afterpay_payment
- */
- public function it_creates_a_afterpay_refund()
- {
- if (empty(self::$payTransactionKey)) {
- $this->markTestSkipped('Skipping refund: No original transaction key is set.');
- }
-
- $response = $this->buckaroo->method('afterpay')->refund(
- $this->getRefundPayload([
- 'originalTransactionKey' => self::$payTransactionKey,
- 'amountCredit' => 50.20,
- 'articles' => [
- [
- 'refundType' => 'Return',
- 'identifier' => 'Articlenumber1',
- 'description' => 'Blue Toy Car',
- 'vatPercentage' => '21',
- 'quantity' => '2',
- 'price' => '25.10',
- ],
- ],
- ])
- );
-
- $this->assertTrue($response->isSuccess());
- }
-
- private function getPaymentPayload(?array $additional = null): array
- {
- $payload = array_merge($this->getBasePayPayload(), [
- 'billing' => $this->getBillingPayload(['initials', 'title', 'companyName']),
- 'shipping' => $this->getShippingPayload(['initials', 'title', 'companyName']),
- 'articles' => $this->getArticlesPayload(),
- ]);
-
- $payload['billing']['recipient']['category'] = RecipientCategory::PERSON;
- $payload['shipping']['recipient']['category'] = RecipientCategory::PERSON;
-
-
- if ($additional) {
- $payload = array_merge($additional, $payload);
- }
-
- return $payload;
- }
-}
diff --git a/tests/Buckaroo/Payments/AlipayTest.php b/tests/Buckaroo/Payments/AlipayTest.php
deleted file mode 100644
index d29cf1e5..00000000
--- a/tests/Buckaroo/Payments/AlipayTest.php
+++ /dev/null
@@ -1,74 +0,0 @@
-buckaroo->method('alipay')->pay($this->getBasePayPayload([], [
- 'useMobileView' => true,
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_alipay_pay_remainder()
- {
- $giftCardResponse = $this->buckaroo->method('giftcard')->pay($this->getBasePayPayload([], [
- 'amountDebit' => 10,
- 'name' => 'boekenbon',
- 'intersolveCardnumber' => '0000000000000000001',
- 'intersolvePIN' => '500',
- ]));
-
- $this->assertTrue($giftCardResponse->isSuccess());
-
- $response = $this->buckaroo->method('alipay')->payRemainder($this->getBasePayPayload([], [
- 'originalTransactionKey' => $giftCardResponse->data('RelatedTransactions')[0]['RelatedTransactionKey'],
- 'amountDebit' => 9.50,
- 'useMobileView' => true,
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_alipay_refund()
- {
- $response = $this->buckaroo->method('alipay')->refund($this->getRefundPayload([
- 'originalTransactionKey' => 'CF16AD6BAB2F4B0C8C435654AE5E5740',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/ApplepayTest.php b/tests/Buckaroo/Payments/ApplepayTest.php
deleted file mode 100644
index 0949a1f1..00000000
--- a/tests/Buckaroo/Payments/ApplepayTest.php
+++ /dev/null
@@ -1,70 +0,0 @@
-buckaroo->payment('applepay')->pay([
-// 'amountDebit' => 10,
-// 'invoice' => uniqid(),
-// 'paymentData' => 'XXXXXXXXXXXXX',
-// 'customerCardName' => 'XXXXXXXXXXXXX'
-// ]);
-//
-// $this->assertTrue($response->isPendingProcessing());
-// }
-
- /**
- * @test
- */
- public function it_creates_a_applepay_redirect_payment()
- {
- $response = $this->buckaroo->method('applepay')->payRedirect([
- 'amountDebit' => 10,
- 'invoice' => uniqid(),
- 'servicesSelectableByClient' => 'applepay',
- 'continueOnIncomplete' => '1',
- ]);
-
- $this->assertTrue($response->isWaitingOnUserInput());
- }
-
- /**
- * @test
- */
- public function it_creates_a_applepay_refund()
- {
- $response = $this->buckaroo->method('applepay')->refund([
- 'amountCredit' => 10,
- 'invoice' => '10000480',
- 'originalTransactionKey' => '9AA4C81A08A84FA7B68E6A6A6291XXXX',
- ]);
-
- $this->assertTrue($response->isFailed());
- }
-}
diff --git a/tests/Buckaroo/Payments/BancontactTest.php b/tests/Buckaroo/Payments/BancontactTest.php
deleted file mode 100644
index 9b15ef71..00000000
--- a/tests/Buckaroo/Payments/BancontactTest.php
+++ /dev/null
@@ -1,148 +0,0 @@
-buckaroo->method('bancontactmrcash')->pay($this->getBasePayPayload([],[
- 'saveToken' => true,
- ]));
-
- $this->assertTrue($response->isWaitingOnUserInput());
- }
-
- /**
- * @test
- */
- public function it_creates_a_bancontact_refund()
- {
- $response = $this->buckaroo->method('bancontactmrcash')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '77FDD0E0CF9C4AF1B85CEA2942DE27DC',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_bancontact_encrypted_payment()
- {
- $response = $this->buckaroo->method('bancontactmrcash')->payEncrypted($this->getBasePayPayload([],[
- 'encryptedCardData' => '001SlXfd8MbiTd/JFwCiGVs3f6o4x6xt0aN29NzOSNZHPKlVsz/EWeQmyhb1gGZ86VY88DP7gf
- DV+UyjcPfpVfHZd7u+WkO71hnV2QfYILCBNqE1aiPv2GQVGdaGbuoQloKu1o3o3I1UDmVxivXTMQX76ovot89geA6hqbtakmpm
- vxeiwwea3l4htNoX1IlD1hfYkDDl9rzSu5ypcjvVs6aRGXK5iMHnyrmEsEnfdj/Q5XWbsD5xAm4u3y6J8d4UP7LB31VLECzZUT
- iJOtKKcCQlT01YThIkQlj8PWBBMtt4H52VN3IH2+wPYtR8HiOZzcA2HA7UxozogIpS53tIURj/g==',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_bancontact_recurring_payment()
- {
- $response = $this->buckaroo->method('bancontactmrcash')->payRecurring($this->getBasePayPayload([], [
- 'originalTransactionKey' => '77FDD0E0CF9C4AF1B85CEA2942DE27DC',
- 'order' => '',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_bancontact_pay_one_click_payment()
- {
- $response = $this->buckaroo->method('bancontactmrcash')->payOneClick($this->getBasePayPayload([],[
- 'originalTransactionKey' => '77FDD0E0CF9C4AF1B85CEA2942DE27DC',
- 'order' => '',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_bancontact_authorize()
- {
- $response = $this->buckaroo->method('bancontactmrcash')->authorize($this->getBasePayPayload([], [
- 'savetoken' => false,
- ]));
-
- self::$authorizeTransactionKey = $response->getTransactionKey();
-
- $this->assertTrue($response->isWaitingOnUserInput());
- }
-
- /**
- * @return void
- * @test
- */
- // Replace the transaction key with the key from a successful authorization
- public function it_creates_a_bancontact_cancel_authorize()
- {
- $response = $this->buckaroo->method('bancontactmrcash')->cancelAuthorize($this->getRefundPayload([
- 'originalTransactionKey' => self::$authorizeTransactionKey,
- 'amountCredit' => 100.30,
- 'amount' => 100.30,
- 'creditAmount' => 100.30,
-
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- // Replace the transaction key with the key from a successful authorization
- public function it_creates_a_bancontact_capture()
- {
- $response = $this->buckaroo->method('bancontactmrcash')->authorize($this->getBasePayPayload([], [
- 'savetoken' => false,
- ]));
-
- self::$authorizeTransactionKey = $response->getTransactionKey();
-
- $response = $this->buckaroo->method('bancontactmrcash')->capture($this->getBasePayPayload([], [
- 'originalTransactionKey' => self::$authorizeTransactionKey,
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/BatchTest.php b/tests/Buckaroo/Payments/BatchTest.php
deleted file mode 100644
index cf522899..00000000
--- a/tests/Buckaroo/Payments/BatchTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
-buckaroo->method('credit_management')->manually()->createCombinedInvoice($this->getInvoicePayload(['invoice' => uniqid()]));
-
- $transactions[] = $this->buckaroo->method('sepadirectdebit')->combine($invoice)->manually()->pay($this->getBasePayPayload([],[
- 'iban' => 'NL13TEST0123456789',
- 'bic' => 'TESTNL2A',
- 'collectdate' => date('Y-m-d'),
- 'mandateReference' => '1DCtestreference',
- 'mandateDate' => '2022-07-03',
- 'customer' => [
- 'name' => 'John Smith',
- ],
- ]));
- }
-
- $response = $this->buckaroo->batch($transactions)->execute();
-
- $this->assertTrue($response->data('Message') == '3 data requests were queued for processing.');
- }
-}
diff --git a/tests/Buckaroo/Payments/BelfiusTest.php b/tests/Buckaroo/Payments/BelfiusTest.php
deleted file mode 100644
index 52912ac8..00000000
--- a/tests/Buckaroo/Payments/BelfiusTest.php
+++ /dev/null
@@ -1,51 +0,0 @@
-buckaroo->method('belfius')->pay($this->getBasePayPayload());
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_belfius_refund()
- {
- $response = $this->buckaroo->method('belfius')->refund(
- $this->getRefundPayload([
- 'originalTransactionKey' => '11FCBD00A4D8454DA4AFD1EF8F8046F0',
- ])
- );
-
- $this->assertTrue($response->isSuccess());
- }
-}
\ No newline at end of file
diff --git a/tests/Buckaroo/Payments/BillinkTest.php b/tests/Buckaroo/Payments/BillinkTest.php
deleted file mode 100644
index 6cf2018d..00000000
--- a/tests/Buckaroo/Payments/BillinkTest.php
+++ /dev/null
@@ -1,55 +0,0 @@
-buckaroo->method('billink')->pay($this->getPayPayload([
- 'trackAndTrace' => 'TR0F123456789',
- 'vatNumber' => '2',
- 'articles' => $this->getArticlesPayload(),
- ]));
-
- self::$payTransactionKey = $response->getTransactionKey();
-
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @test
- */
- public function it_creates_a_billink_refund()
- {
- $response = $this->buckaroo->method('billink')->refund($this->getRefundPayload([
- 'originalTransactionKey' => self::$payTransactionKey,
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/BizumTest.php b/tests/Buckaroo/Payments/BizumTest.php
deleted file mode 100644
index 92c6a743..00000000
--- a/tests/Buckaroo/Payments/BizumTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-buckaroo->method('bizum')->pay($this->getBasePayPayload());
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_bizum_refund()
- {
- $response = $this->buckaroo->method('bizum')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '2D04704995B74D679AACC59F87XXXXXX',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
\ No newline at end of file
diff --git a/tests/Buckaroo/Payments/BlikTest.php b/tests/Buckaroo/Payments/BlikTest.php
deleted file mode 100644
index c2893233..00000000
--- a/tests/Buckaroo/Payments/BlikTest.php
+++ /dev/null
@@ -1,54 +0,0 @@
-buckaroo->method('blik')->pay($this->getBasePayPayload([], [
- 'currency' => 'PLN',
- 'email' => 'test@buckar00.nl'
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_blik_refund()
- {
- $response = $this->buckaroo->method('blik')->refund(
- $this->getRefundPayload([
- 'originalTransactionKey' => 'C3B303C9DEA4401BA6732055030C2BD8',
- 'currency' => 'PLN'
- ])
- );
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/BuckarooVoucherTest.php b/tests/Buckaroo/Payments/BuckarooVoucherTest.php
deleted file mode 100644
index ce05edb9..00000000
--- a/tests/Buckaroo/Payments/BuckarooVoucherTest.php
+++ /dev/null
@@ -1,140 +0,0 @@
-buckaroo->method('buckaroovoucher')->create(
- [
- 'usageType' => '2',
- 'validFrom' => '2025-01-01',
- 'validUntil' => '2030-01-01',
- 'creationBalance' => '10',
- ]
- );
-
- $this->assertTrue($response->isSuccess());
-
- self::$voucherCode = $response->getServiceParameters()['vouchercode'];
- }
-
- /**
- * @return void
- * @test
- * @depends it_creates_a_buckaroo_voucher
- */
- public function it_creates_a_buckaroo_payment()
- {
- $response = $this->buckaroo->method('buckaroovoucher')->pay($this->getPayPayload([
- 'vouchercode' => self::$voucherCode,
- 'amountDebit' => 5,
- ]));
-
- $this->assertTrue($response->isSuccess());
-
- self::$payTransactionKey = $response->getTransactionKey();
- }
-
- /**
- * @return void
- * @test
- * @depends it_creates_a_buckaroo_voucher
- */
- public function it_creates_a_buckaroo_pay_remainder()
- {
- $giftCardResponse = $this->buckaroo->method('giftcard')->pay($this->getBasePayPayload([], [
- 'amountDebit' => 10,
- 'name' => 'boekenbon',
- 'intersolveCardnumber' => '0000000000000000001',
- 'intersolvePIN' => '500',
- ]));
-
- $this->assertTrue($giftCardResponse->isSuccess());
-
- $response = $this->buckaroo->method('buckaroovoucher')->payRemainder($this->getBasePayPayload([], [
- 'originalTransactionKey' => $giftCardResponse->data('RelatedTransactions')[0]['RelatedTransactionKey'],
- 'amountDebit' => 9.50,
- 'invoice' => uniqid(),
- 'vouchercode' => self::$voucherCode,
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- * @depends it_creates_a_buckaroo_payment
- */
- public function it_creates_a_buckaroo_refund()
- {
- $response = $this->buckaroo->method('buckaroovoucher')->refund(
- [
- 'amountCredit' => 5,
- 'invoice' => uniqid(),
- 'originalTransactionKey' => self::$payTransactionKey,
- ]
- );
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- * @depends it_creates_a_buckaroo_voucher
- */
- public function it_creates_a_buckaroo_get_balance()
- {
- $response = $this->buckaroo->method('buckaroovoucher')->getBalance(
- [
- 'vouchercode' => self::$voucherCode,
- ]
- );
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- * @depends it_creates_a_buckaroo_voucher
- */
- public function it_deactivate_a_buckaroo_voucher()
- {
- $response = $this->buckaroo->method('buckaroovoucher')->deactivate(
- [
- 'vouchercode' => self::$voucherCode,
- ]
- );
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/BuckarooWalletTest.php b/tests/Buckaroo/Payments/BuckarooWalletTest.php
deleted file mode 100644
index 792b0d2a..00000000
--- a/tests/Buckaroo/Payments/BuckarooWalletTest.php
+++ /dev/null
@@ -1,186 +0,0 @@
-buckaroo->method('buckaroo_wallet')->createWallet([
- 'walletId' => uniqid(),
- 'currency' => 'EUR',
- 'customer' => [
- 'firstName' => 'John',
- 'lastName' => 'Doe',
- 'email' => 'test@buckaroo.nl',
- ],
- 'bankAccount' => [
- 'iban' => 'NL13TEST0123456789',
- ]
- ]);
-
- $this->assertTrue($response->isSuccess());
-
- self::$walletId = $response->getServiceParameters()['walletid'];
- }
-
- /**
- * @return void
- * @test
- */
- public function it_updates_a_buckaroo_wallet()
- {
- $response = $this->buckaroo->method('buckaroo_wallet')->updateWallet([
- 'walletId' => self::$walletId,
- 'status' => 'Active',
- 'customer' => [
- 'firstName' => 'John',
- 'lastName' => 'Doe',
- 'email' => 'test@buckaroo.nl',
- ],
- 'bankAccount' => [
- 'iban' => 'NL13TEST0123456789',
- ],
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_gets_buckaroo_wallet_info()
- {
- $response = $this->buckaroo->method('buckaroo_wallet')->getInfo([
- 'walletId' => self::$walletId,
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_deposit_to_buckaroo_wallet()
- {
- $response = $this->buckaroo->method('buckaroo_wallet')->deposit([
- 'invoice' => 'BuckarooWalletInvoiceId',
- 'originalTransactionKey' => '46CA38B421194B6FAE5AFD42619715FD',
- 'amountCredit' => 10,
- 'walletId' => self::$walletId,
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_reserve_to_buckaroo_wallet()
- {
- $response = $this->buckaroo->method('buckaroo_wallet')->reserve([
- 'invoice' => 'BuckarooWalletInvoiceId',
- 'originalTransactionKey' => '46CA38B421194B6FAE5AFD42619715FD',
- 'amountCredit' => 10,
- 'walletId' => self::$walletId,
- ]);
-
- self::$reservationId = $response->getTransactionKey();
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_releases_buckaroo_wallet()
- {
- $response = $this->buckaroo->method('buckaroo_wallet')->release([
- 'amountCredit' => 10,
- 'walletId' => self::$walletId,
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_withdrawal_from_buckaroo_wallet()
- {
- $response = $this->buckaroo->method('buckaroo_wallet')->withdrawal([
- 'invoice' => 'BuckarooWalletInvoiceId',
- 'originalTransactionKey' => self::$reservationId,
- 'amountDebit' => 1,
- 'walletId' => self::$walletId,
- 'order' => ''
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_payment_on_buckaroo_wallet()
- {
- $response = $this->buckaroo->method('buckaroo_wallet')->pay([
- 'invoice' => 'BuckarooWalletInvoiceId',
- 'description' => 'Test',
- 'amountDebit' => 1,
- 'walletId' => self::$walletId,
- ]);
-
- $this->assertTrue($response->isSuccess());
-
- self::$payTransactionKey = $response->getTransactionKey();
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_refund_on_buckaroo_wallet()
- {
- $response = $this->buckaroo->method('buckaroo_wallet')->refund([
- 'amountCredit' => 1,
- 'invoice' => 'testinvoice 123',
- 'originalTransactionKey' => self::$payTransactionKey,
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/ClickToPayTest.php b/tests/Buckaroo/Payments/ClickToPayTest.php
deleted file mode 100644
index 96a26b92..00000000
--- a/tests/Buckaroo/Payments/ClickToPayTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-buckaroo->method('clicktopay')->pay($this->getBasePayPayload([], [
- 'amountDebit' => 0.01,
- 'continueOnIncomplete' => "1",
- ]));
-
- $this->assertTrue($response->isWaitingOnUserInput());
- }
-}
diff --git a/tests/Buckaroo/Payments/CreditManagementTest.php b/tests/Buckaroo/Payments/CreditManagementTest.php
deleted file mode 100644
index 467b6136..00000000
--- a/tests/Buckaroo/Payments/CreditManagementTest.php
+++ /dev/null
@@ -1,317 +0,0 @@
-buckaroo->method('credit_management')->createInvoice($this->getInvoicePayload(['invoice' => uniqid()]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_credit_management_invoice_with_product_lines()
- {
- $response = $this->buckaroo->method('credit_management')->createInvoice([
- 'invoice' => uniqid(),
- 'description' => 'buckaroo_schema_test_PDF',
- 'invoiceAmount' => 217.80,
- 'invoiceDate' => '2022-01-01',
- 'dueDate' => '2030-01-01',
- 'schemeKey' => 's31w5d',
- 'poNumber' => 'PO-12345',
- 'debtor' => [
- 'code' => 'johnsmith4',
- ],
- 'articles' => [
- [
- 'productGroupName' => 'Toys',
- 'productGroupOrderIndex' => 1,
- 'productOrderIndex' => 1,
- 'type' => 'Regular',
- 'identifier' => 'ART12',
- 'description' => 'Blue Toy Car',
- 'quantity' => 3,
- 'unitOfMeasurement' => 'piece(s)',
- 'price' => 10,
- 'discountPercentage' => 20,
- 'totalDiscount' => 6,
- 'vatPercentage' => 21,
- 'totalVat' => 0.6,
- 'totalAmountExVat' => 8.40,
- 'totalAmount' => 123,
- ],
- [
- 'productGroupName' => 'Toys',
- 'productGroupOrderIndex' => 1,
- 'productOrderIndex' => 2,
- 'type' => 'Regular',
- 'identifier' => 'ART12',
- 'description' => 'Blue Toy Car',
- 'quantity' => 3,
- 'unitOfMeasurement' => 'piece(s)',
- 'price' => 10,
- 'discountPercentage' => 20,
- 'totalDiscount' => 6,
- 'vatPercentage' => 21,
- 'totalVat' => 0.6,
- 'totalAmountExVat' => 8.40,
- 'totalAmount' => 123,
- ],
- ],
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_credit_management_combined_invoice()
- {
- $invoice = $this->buckaroo->method('credit_management')->manually()->createCombinedInvoice($this->getInvoicePayload());
-
- $response = $this->buckaroo->method('sepadirectdebit')->combine($invoice)
- ->pay([
- 'invoice' => uniqid(),
- 'amountDebit' => 10.10,
- 'iban' => 'NL13TEST0123456789',
- 'bic' => 'TESTNL2A',
- 'collectdate' => '2030-01-01',
- 'mandateReference' => '1DCtestreference',
- 'mandateDate' => '2022-07-03',
- 'customer' => [
- 'name' => 'John Smith',
- ],
- ]);
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- //Get the invoice number from CreditManagements -> Invoices
- public function it_creates_a_credit_management_credit_note()
- {
- $response = $this->buckaroo->method('credit_management')->createCreditNote([
- 'invoice' => uniqid(),
- 'originalInvoiceNumber' => '682dc27aa66ef',
- 'invoiceDate' => '2024-01-01',
- 'invoiceAmount' => 10.00,
- 'invoiceAmountVAT' => 1.00,
- 'sendCreditNoteMessage' => 'Email',
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_credit_management_add_or_update_debtor()
- {
- $response = $this->buckaroo->method('credit_management')->addOrUpdateDebtor($this->getInvoicePayload([
- 'addressUnreachable' => false,
- 'emailUnreachable' => false,
- 'mobileUnreachable' => false
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- // /**
- // * @return void
- // * @test
- // */
- //// Todo: Fix - No active payment plan could be found for the given invoice
- // public function it_creates_a_credit_management_payment_plan()
- // {
- // $response = $this->buckaroo->method('credit_management')->createPaymentPlan([
- // 'description' => 'Payment in two intstallments',
- // 'includedInvoiceKey' => '7661B8F35D3B417EAF18439B5ED724E5',
- // 'dossierNumber' => 'PaymentplanJohnsmith123',
- // 'installmentCount' => 2,
- // 'initialAmount' => 2,
- // 'startDate' => (new DateTime('+90 days'))->format('Y-m-d'),
- // 'interval' => CreditManagementInstallmentInterval::MONTH,
- // 'paymentPlanCostAmount' => 1,
- // // 'paymentPlanCostAmountVat' => 1.20,
- // 'recipientEmail' => 'test@buckaroo.nl',
- // ]);
-
- // $this->assertTrue($response->isSuccess());
- // }
-
- // /**
- // * @return void
- // * @test
- // */
- // public function it_creates_a_credit_management_terminate_payment_plan()
- // {
- // $response = $this->buckaroo->method('credit_management')->terminatePaymentPlan([
- // 'includedInvoiceKey' => '7661B8F35D3B417EAF18439B5ED724E5',
- // ]);
-
- // $this->assertTrue($response->isSuccess());
- // }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_credit_management_pause_invoice()
- {
- $response = $this->buckaroo->method('credit_management')->pauseInvoice([
- 'invoice' => '682dc27aa66ef',
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_credit_management_unpause_invoice()
- {
- $response = $this->buckaroo->method('credit_management')->unpauseInvoice([
- 'invoice' => '682dc27aa66ef',
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_credit_management_invoice_info()
- {
- $response = $this->buckaroo->method('credit_management')->invoiceInfo([
- 'invoice' => '682dc27aa66ef',
- 'invoices' => [ // If you want to check multiple invoices
- [
- 'invoiceNumber' => 'INV001',
- ],
- [
- 'invoiceNumber' => 'INV002',
- ],
- ],
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_credit_management_debtor_info()
- {
- $response = $this->buckaroo->method('credit_management')->debtorInfo([
- 'debtor' => [
- 'code' => 'johnsmith4',
- ],
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_credit_management_add_or_update_product_lines()
- {
- $response = $this->buckaroo->method('credit_management')->addOrUpdateProductLines([
- 'invoiceKey' => '60F15FD00E164602A2080B09B9B26F4A',
- 'articles' => [
- [
- 'type' => 'Regular',
- 'identifier' => 'Articlenumber1',
- 'description' => 'Blue Toy Car',
- 'vatPercentage' => '21',
- 'totalVat' => 12,
- 'totalAmount' => 123,
- 'quantity' => '2',
- 'price' => '20.10',
- ],
- [
- 'type' => 'Regular',
- 'identifier' => 'Articlenumber2',
- 'description' => 'Red Toy Car',
- 'vatPercentage' => '21',
- 'totalVat' => 12,
- 'totalAmount' => 123,
- 'quantity' => '1',
- 'price' => '10.10',
- ],
- ],
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- // /**
- // * @return void
- // * @test
- // */
- //// No debtor files found
- // public function it_creates_a_credit_management_pause_debtor_file()
- // {
- // $response = $this->buckaroo->method('credit_management')->pauseDebtorFile([
- // 'debtorFileGuid' => 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
- // ]);
-
- // $this->assertTrue($response->isValidationFailure());
- // }
-
- // /**
- // * @return void
- // * @test
- // */
- // public function it_creates_a_credit_management_resume_debtor_file()
- // {
- // $response = $this->buckaroo->method('credit_management')->resumeDebtorFile([
- // 'debtorFileGuid' => 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
- // ]);
-
- // $this->assertTrue($response->isValidationFailure());
- // }
-}
diff --git a/tests/Buckaroo/Payments/CreditcardTest.php b/tests/Buckaroo/Payments/CreditcardTest.php
deleted file mode 100644
index 30672ea6..00000000
--- a/tests/Buckaroo/Payments/CreditcardTest.php
+++ /dev/null
@@ -1,197 +0,0 @@
-buckaroo->method('creditcard')->pay($this->getBasePayPayload([],[
- 'name' => 'visa',
- ]));
-
- $this->assertTrue($response->isWaitingOnUserInput());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_creditcard_encrypted_payment()
- {
- $response = $this->buckaroo->method('creditcard')->payEncrypted($this->getBasePayPayload([],[
- 'name' => 'mastercard',
- 'encryptedCardData' => '001u8gJNwngKubFCO6FmJod6aESlIFATkKYaj47KlgBp7f3NeVxUzChg1Aug7WD2vc5wut2KU9NPLUaO0tFmzhVLZoDWn7dX4AzGxSjPrsPmDMWYcEkIwMZfcyJqoRfFkF3j15mil3muXxhR1a609NfkTo11J3ENVsvU3k60z+x0jCw6NjzbrweVQhBRkrbs7TBJkS4tR38JiDsXyH2E1JmRHE+o2P9qz4at6w3zggmwImvjt5IIjEr6g8KfsIDXfv7YjEzhJ3P+7uuGoyG2WYm/Pr0+iEmTj5Q/ijkxu1+cDqv5eiB+80KgffPItUZDrnv9sKlVBAr+f53nm1G+Sxp0Q==',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_creditcard_security_code_payment()
- {
- $response = $this->buckaroo->method('creditcard')->payWithSecurityCode($this->getBasePayPayload([],[
- 'originalTransactionKey' => '4D81D54DAA77407689208A7609795B8F',
- 'name' => 'mastercard',
- 'encryptedSecurityCode' => '001F3AJT7wkJa04zE8c78P7spOAgHSKH1YKgPlOwXhW049VfIXMwZO32RYna9xZRyUCtfODIoCL8GRQoaZbStlBT4rbF5e4PPvWFSKdvua4rq+GQDNAghfa+ZQz0BzBPfjS0WBdFape9n3zH2vC/0m+wI3QZiDpYYgyWC1/Y3udJDU7JRTVMq/BDHGet+IZ2CDnkeGl813kkYymzYon/QeuQRQ0Wsec5bmVQNYGx62fz70/vLgs0ffff+6DtZtnZWfByRkTwMNebJotlOsSkbhVR5FrHpAbNPCJI+LvJcJL7Eoo+ZuX5/LWGmsT6qnR/uLiIw1DI7mTKGy6/P7IljAE+g==',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_creditcard_refund()
- {
- $response = $this->buckaroo->method('creditcard')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '3D40E227D336441689092DDFC388810B',
- 'name' => 'mastercard',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_creditcard_authorize()
- {
- $response = $this->buckaroo->method('creditcard')->authorize($this->getBasePayPayload([],[
- 'name' => 'mastercard',
- ]));
-
- $this->assertTrue($response->isWaitingOnUserInput());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_creditcard_encrypted_authorize()
- {
- $response = $this->buckaroo->method('creditcard')->authorizeEncrypted($this->getBasePayPayload([],[
- 'name' => 'mastercard',
- 'encryptedCardData' => '001u8gJNwngKubFCO6FmJod6aESlIFATkKYaj47KlgBp7f3NeVxUzChg1Aug7WD2vc5wut2KU9NPLUaO0tFmzhVLZoDWn7dX4AzGxSjPrsPmDMWYcEkIwMZfcyJqoRfFkF3j15mil3muXxhR1a609NfkTo11J3ENVsvU3k60z+x0jCw6NjzbrweVQhBRkrbs7TBJkS4tR38JiDsXyH2E1JmRHE+o2P9qz4at6w3zggmwImvjt5IIjEr6g8KfsIDXfv7YjEzhJ3P+7uuGoyG2WYm/Pr0+iEmTj5Q/ijkxu1+cDqv5eiB+80KgffPItUZDrnv9sKlVBAr+f53nm1G+Sxp0Q==',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- // 492 - Technical failure
- /**
- * @return void
- * @test
- */
- public function it_creates_a_creditcard_security_code_authorize()
- {
- $response = $this->buckaroo->method('creditcard')->authorizeWithSecurityCode($this->getBasePayPayload([],[
- 'originalTransactionKey' => '4D81D54DAA77407689208A7609795B8F',
- 'name' => 'mastercard',
- 'encryptedSecurityCode' => '001F3AJT7wkJa04zE8c78P7spOAgHSKH1YKgPlOwXhW049VfIXMwZO32RYna9xZRyUCtfODIoCL8GRQoaZbStlBT4rbF5e4PPvWFSKdvua4rq+GQDNAghfa+ZQz0BzBPfjS0WBdFape9n3zH2vC/0m+wI3QZiDpYYgyWC1/Y3udJDU7JRTVMq/BDHGet+IZ2CDnkeGl813kkYymzYon/QeuQRQ0Wsec5bmVQNYGx62fz70/vLgs0ffff+6DtZtnZWfByRkTwMNebJotlOsSkbhVR5FrHpAbNPCJI+LvJcJL7Eoo+ZuX5/LWGmsT6qnR/uLiIw1DI7mTKGy6/P7IljAE+g==',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_creditcard_capture()
- {
- $response = $this->buckaroo->method('creditcard')->capture($this->getBasePayPayload([],[
- 'originalTransactionKey' => '78ADA073DAD74E14BFC83EE308B70374',
- 'name' => 'mastercard',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_creditcard_pay_recurrent()
- {
- $response = $this->buckaroo->method('creditcard')->payRecurrent($this->getBasePayPayload([],[
- 'originalTransactionKey' => '6C5DBB69E74644958F8C25199514DC6C',
- 'name' => 'mastercard',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- // 491 - Validation failure (no info)
- /**
- * @return void
- * @test
- */
- public function it_creates_a_creditcard_cancel_authorize()
- {
- $response = $this->buckaroo->method('creditcard')->cancelAuthorize($this->getBasePayPayload([],[
- 'originalTransactionKey' => '41733E4210684988939CEE58AC899602',
- 'name' => 'mastercard',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_creditcard_token_authorize()
- {
- $response = $this->buckaroo->method('creditcard')->authorizeWithToken($this->getBasePayPayload([],[
- 'name' => 'visa',
- 'sessionId' => 'hf_43ab37u53XIDOpJg',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_creditcard_hosted_fields_payment()
- {
- $response = $this->buckaroo->method('creditcard')->payWithToken($this->getBasePayPayload([],[
- 'name' => 'visa',
- 'sessionId' => 'hf_43ab37u53XIDOpJg',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-}
diff --git a/tests/Buckaroo/Payments/EPSTest.php b/tests/Buckaroo/Payments/EPSTest.php
deleted file mode 100644
index 6aa146af..00000000
--- a/tests/Buckaroo/Payments/EPSTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-buckaroo->method('eps')->pay($this->getBasePayPayload());
-
- $this->assertTrue($response->isSuccess());
-
- self::$payTransactionKey = $response->getTransactionKey();
- }
-
- /**
- * @test
- */
- public function it_creates_a_eps_refund()
- {
- $response = $this->buckaroo->method('eps')->refund(
- $this->getRefundPayload([
- 'originalTransactionKey' => self::$payTransactionKey,
- ])
- );
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/EmandatesTest.php b/tests/Buckaroo/Payments/EmandatesTest.php
deleted file mode 100644
index d9dbf75f..00000000
--- a/tests/Buckaroo/Payments/EmandatesTest.php
+++ /dev/null
@@ -1,99 +0,0 @@
-buckaroo->method('emandates')->issuerList();
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_mandante_on_emandates()
- {
- $response = $this->buckaroo->method('emandates')->createMandate([
- 'emandatereason' => 'testing',
- 'sequencetype' => '1',
- 'purchaseid' => 'purchaseid1234',
- 'debtorbankid' => 'INGBNL2A',
- 'debtorreference' => 'klant1234',
- 'language' => 'nl',
- 'mandateid' => '1DC' . strtoupper(uniqid(mt_rand())),
- ]);
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_get_status_from_emandates()
- {
- $response = $this->buckaroo->method('emandates')->status([
- 'mandateid' => '1DC1234567890',
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_modifies_mandante_on_emandates()
- {
- $response = $this->buckaroo->method('emandates')->modifyMandate([
- 'originalMandateId' => '1DC1234567890',
- 'debtorbankid' => 'ABNANL2A',
- ]);
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- //todo: "Unknown action 'CancelMandate' used on service 'emandate'."
- /**
- * @return void
- * @test
- */
- public function it_cancels_mandante_on_emandates()
- {
- $response = $this->buckaroo->method('emandates')->cancelMandate([
- 'mandateid' => '1DC169627947667DC2DB3C7DEA',
- 'emandatereason' => 'testing cancel',
- 'purchaseid' => 'purchaseid 1234',
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/ExternalPaymentTest.php b/tests/Buckaroo/Payments/ExternalPaymentTest.php
deleted file mode 100644
index 3648c757..00000000
--- a/tests/Buckaroo/Payments/ExternalPaymentTest.php
+++ /dev/null
@@ -1,56 +0,0 @@
-buckaroo->method('externalPayment')->pay($this->getBasePayPayload([], [
- 'channel' => 'BACKOFFICE'
- ]));
-
- $this->assertTrue($response->isSuccess());
-
- self::$payTransactionKey = $response->getTransactionKey();
- }
-
- /**
- * @test
- */
- public function it_creates_a_external_refund()
- {
- $response = $this->buckaroo->method('externalPayment')->refund(
- $this->getRefundPayload([
- 'channel' => 'BACKOFFICE',
- 'originalTransactionKey' => self::$payTransactionKey,
- ])
- );
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/GiftcardsTest.php b/tests/Buckaroo/Payments/GiftcardsTest.php
deleted file mode 100644
index c7b35cba..00000000
--- a/tests/Buckaroo/Payments/GiftcardsTest.php
+++ /dev/null
@@ -1,92 +0,0 @@
-buckaroo->method('giftcard')->pay($this->getBasePayPayload([], [
- 'amountDebit' => 40.30,
- 'name' => 'boekenbon',
- 'intersolveCardnumber' => '0000000000000000001',
- 'intersolvePIN' => '4030',
- 'email' => 'test@buckar00.nl',
- 'lastName' => 'Test'
- ]));
-
- $this->assertTrue($response->isSuccess());
-
- self::$payTransactionKey = $response->getTransactionKey();
- }
-
- /**
- * @test
- */
- public function it_creates_a_giftcards_partial_payment()
- {
- $giftCardResponse = $this->buckaroo->method('giftcard')->pay($this->getBasePayPayload([], [
- 'amountDebit' => 10,
- 'name' => 'boekenbon',
- 'intersolveCardnumber' => '0000000000000000001',
- 'intersolvePIN' => '0500',
- 'email' => 'test@buckar00.nl',
- 'lastName' => 'Test'
-
- ]));
-
- $this->assertTrue($giftCardResponse->isSuccess());
-
- $response = $this->buckaroo->method('giftcard')->payRemainder($this->getBasePayPayload([], [
- 'originalTransactionKey' => $giftCardResponse->data('RelatedTransactions')[0]['RelatedTransactionKey'],
- 'amountDebit' => 5.00,
- 'name' => 'boekenbon',
- 'intersolveCardnumber' => '0000000000000000001',
- 'intersolvePIN' => '0500',
- 'email' => 'test@buckar00.nl',
- 'lastName' => 'Test'
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @test
- */
- public function it_creates_a_giftcards_refund()
- {
- $response = $this->buckaroo->method('giftcard')->refund(
- $this->getRefundPayload([
- 'originalTransactionKey' => self::$payTransactionKey,
- 'name' => 'boekenbon',
- 'email' => 'test@buckar00.nl',
- 'lastName' => 'Test'
- ])
- );
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/GooglepayTest.php b/tests/Buckaroo/Payments/GooglepayTest.php
deleted file mode 100644
index 394a100c..00000000
--- a/tests/Buckaroo/Payments/GooglepayTest.php
+++ /dev/null
@@ -1,51 +0,0 @@
-buckaroo->payment('googlepay')->pay($this->getBasePayPayload([], [
- 'paymentData' => 'XXXXXXXXXXXXX',
- 'customerCardName' => 'XXXXXXXXXXXXX'
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_googlepay_refund()
- {
- $response = $this->buckaroo->method('googlepay')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '77FDD0E0CF9C4AF1B85CEA2942DE27DC',
- ]));
-
- $this->assertTrue($response->isFailed());
- }
-}
diff --git a/tests/Buckaroo/Payments/IdealProcessingTest.php b/tests/Buckaroo/Payments/IdealProcessingTest.php
deleted file mode 100644
index badb457d..00000000
--- a/tests/Buckaroo/Payments/IdealProcessingTest.php
+++ /dev/null
@@ -1,60 +0,0 @@
-buckaroo->method('idealprocessing')->issuers();
-
- $this->assertIsArray($response);
- foreach ($response as $item)
- {
- $this->assertIsArray($item);
- $this->assertArrayHasKey('id', $item);
- $this->assertArrayHasKey('name', $item);
- }
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_an_idealprocessing_payment()
- {
- $response = $this->buckaroo->method('idealprocessing')->pay($this->getBasePayPayload([],[
- 'pushURL' => 'https://buckaroo.dev/push',
- 'issuer' => 'ABNANL2A',
- 'customParameters' => [
- 'CustomerBillingFirstName' => 'Test'
- ],
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-}
diff --git a/tests/Buckaroo/Payments/IdealQRTest.php b/tests/Buckaroo/Payments/IdealQRTest.php
deleted file mode 100644
index 3e135e35..00000000
--- a/tests/Buckaroo/Payments/IdealQRTest.php
+++ /dev/null
@@ -1,46 +0,0 @@
-buckaroo->method('ideal_qr')->generate($this->getBasePayPayload(['invoice'],[
- 'minAmount' => '0.10',
- 'maxAmount' => '10.0',
- 'imageSize' => '2000',
- 'purchaseId' => 'Testpurchase123',
- 'isOneOff' => false,
- 'amount' => '1.00',
- 'amountIsChangeable' => true,
- 'expiration' => '2030-09-30',
- 'isProcessing' => false,
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/IdealTest.php b/tests/Buckaroo/Payments/IdealTest.php
deleted file mode 100644
index b36d7f8f..00000000
--- a/tests/Buckaroo/Payments/IdealTest.php
+++ /dev/null
@@ -1,131 +0,0 @@
-buckaroo->method('ideal')->issuers();
-
- $this->assertIsArray($response);
- foreach ($response as $item) {
- $this->assertIsArray($item);
- $this->assertArrayHasKey('id', $item);
- $this->assertArrayHasKey('name', $item);
- }
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_ideal_payment()
- {
- $response = $this->buckaroo->method('ideal')->pay($this->getBasePayPayload([], [
- 'pushURL' => 'https://example.com/buckaroo/push',
- 'issuer' => 'ABNANL2A',
- 'customParameters' => [
- 'CustomerBillingFirstName' => 'Test'
- ],
- ]));
-
- $this->assertTrue($response->isWaitingOnUserInput());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_ideal_fast_checkout_payment()
- {
- $response = $this->buckaroo->method('ideal')->payFastCheckout($this->getBasePayPayload([], [
- 'pushURL' => 'https://buckaroo.dev/push',
- 'customParameters' => [
- 'CustomerBillingFirstName' => 'Test'
- ],
- ]));
-
- $this->assertTrue($response->isWaitingOnUserInput());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_ideal_pay_remainder()
- {
- $giftCardResponse = $this->buckaroo->method('giftcard')->pay($this->getBasePayPayload([], [
- 'amountDebit' => 10,
- 'name' => 'boekenbon',
- 'intersolveCardnumber' => '0000000000000000001',
- 'intersolvePIN' => '500',
- ]));
-
- $this->assertTrue($giftCardResponse->isSuccess());
-
- $response = $this->buckaroo->method('ideal')->payRemainder($this->getBasePayPayload([], [
- 'originalTransactionKey' => $giftCardResponse->data('RelatedTransactions')[0]['RelatedTransactionKey'],
- 'amountDebit' => 5.00,
- 'pushURL' => 'https://buckaroo.dev/push',
- 'issuer' => 'ABNANL2A',
- 'customParameters' => [
- 'CustomerBillingFirstName' => 'Test'
- ],
- ]));
-
- $this->assertTrue($response->isWaitingOnUserInput());
- }
-
- /**
- * @test
- */
- public function it_creates_a_ideal_refund()
- {
- $response = $this->buckaroo->method('ideal')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '4EADF2E4BDFA41AD85BDDAB026529D65',
- 'pushURL' => 'https://buckaroo.dev/push',
-
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @test
- */
- public function it_creates_a_ideal_instant_refund()
- {
- $response = $this->buckaroo->method('ideal')->instantRefund($this->getRefundPayload([
- 'originalTransactionKey' => '4EADF2E4BDFA41AD85BDDAB026529D65',
- 'pushURL' => 'https://buckaroo.dev/push',
-
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-}
diff --git a/tests/Buckaroo/Payments/In3OldTest.php b/tests/Buckaroo/Payments/In3OldTest.php
deleted file mode 100644
index bebc9c14..00000000
--- a/tests/Buckaroo/Payments/In3OldTest.php
+++ /dev/null
@@ -1,113 +0,0 @@
-buckaroo->method('in3old')->pay($this->getPaymentPayload());
-
- // $this->assertTrue($response->isSuccess());
- // }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_in3old_installments_payment()
- {
- $response = $this->buckaroo->method('in3Old')->payInInstallments($this->getPaymentPayload());
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_in3old_refund()
- {
- $response = $this->buckaroo->method('in3Old')->refund($this->getRefundPayload([
- 'originalTransactionKey' => 'C1311F77EB3F48CDB5774F7C6842FE12',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- private function getPaymentPayload(): array
- {
- return $this->getPayPayload(
- [
- 'invoiceDate' => '22-01-2018',
- 'customerType' => 'Company',
- 'articles' => $this->getArticlesPayload(['vatPercentage']),
- 'email' => 'test@buckaroo.nl',
- 'phone' => [
- 'mobile' => '0612345678',
- ],
- 'company' => [
- 'companyName' => 'My Company B.V.',
- 'chamberOfCommerce' => '123456',
- ],
- 'customer' => [
- 'gender' => Gender::FEMALE,
- 'initials' => 'J.S.',
- 'lastName' => 'Aflever',
- 'email' => 'billingcustomer@buckaroo.nl',
- 'phone' => '0610000000',
- 'culture' => 'nl-NL',
- 'birthDate' => '1990-01-01',
- ],
- 'address' => [
- 'street' => 'Hoofdstraat',
- 'houseNumber' => '2',
- 'houseNumberAdditional' => 'a',
- 'zipcode' => '8441EE',
- 'city' => 'Heerenveen',
- 'country' => 'NL',
- ],
- 'subtotals' => [
- [
- 'name' => 'Korting',
- 'value' => -2.00,
- ],
- [
- 'name' => 'Betaaltoeslag',
- 'value' => 0.50,
- ],
- [
- 'name' => 'Verzendkosten',
- 'value' => 1.00,
- ],
- ],
- ]
- );
- }
-}
diff --git a/tests/Buckaroo/Payments/In3Test.php b/tests/Buckaroo/Payments/In3Test.php
deleted file mode 100644
index 8b435cff..00000000
--- a/tests/Buckaroo/Payments/In3Test.php
+++ /dev/null
@@ -1,56 +0,0 @@
-buckaroo->method('in3')->pay($this->getPayPayload([
- 'billing' => $this->getBillingPayload(['title', 'conversationLanguage', 'identificationNumber']),
- 'shipping' => $this->getShippingPayload(['title', 'conversationLanguage', 'identificationNumber']),
- 'articles' => $this->getArticlesPayload(),
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_in3_refund()
- {
- $response = $this->buckaroo->method('in3')->refund($this->getRefundPayload([
- 'originalTransactionKey' => 'B535EC6AC624431ABA27D849F44700BA',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/KBCTest.php b/tests/Buckaroo/Payments/KBCTest.php
deleted file mode 100644
index a4813290..00000000
--- a/tests/Buckaroo/Payments/KBCTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-buckaroo->method('kbcpaymentbutton')->pay($this->getBasePayPayload());
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_kbc_refund()
- {
- $response = $this->buckaroo->method('kbcpaymentbutton')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '150355695CBD47CC8A0D7F14E994F202'
- ]));
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/KlarnaKPTest.php b/tests/Buckaroo/Payments/KlarnaKPTest.php
deleted file mode 100644
index d2d663e7..00000000
--- a/tests/Buckaroo/Payments/KlarnaKPTest.php
+++ /dev/null
@@ -1,144 +0,0 @@
-buckaroo->method('klarnakp')->reserve($this->getPaymentPayload());
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_klarnakp_payment()
- {
- $response = $this->buckaroo->method('klarnakp')->pay($this->getPaymentPayload([
- 'reservationNumber' => 'f055e53d-6da2-4f90-945e-73e65fa391ad',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_klarnakp_cancel_reservation()
- {
- $response = $this->buckaroo->method('klarnakp')->cancelReserve($this->getPaymentPayload([
- 'reservationNumber' => 'fe65cf62-94a2-4609-a4d8-23c369969f31',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_klarnakp_update_reservation()
- {
- $response = $this->buckaroo->method('klarnakp')->updateReserve([
- 'reservationNumber' => '3a9c8f5d-ffef-4f53-af40-2c1198539d6d',
- 'invoice' => 'testinvoice 12345',
- 'shipping' => [
- 'recipient' => [
- 'careOf' => 'Company',
- 'firstName' => 'John',
- 'lastName' => 'Do',
- ],
- 'address' => [
- 'street' => 'Kalverstraat',
- 'houseNumber' => '13',
- 'houseNumberAdditional' => 'b',
- 'zipcode' => '4321EB',
- 'city' => 'Amsterdam',
- 'country' => 'GB',
- ],
- 'email' => 'test@buckaroo.nl',
- ],
- 'articles' => [
- [
- 'identifier' => 'Articlenumber1',
- 'description' => 'Blue Toy Car',
- 'vatPercentage' => '21',
- 'quantity' => '2',
- 'price' => '20.10',
- ],
- [
- 'identifier' => 'Articlenumber2',
- 'description' => 'Red Toy Car',
- 'vatPercentage' => '21',
- 'quantity' => '1',
- 'price' => '10.10',
- ],
- ],
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_klarnakp_refund()
- {
- $response = $this->buckaroo->method('klarnakp')->refund($this->getRefundPayload([
- 'originalTransactionKey' => 'FB4E1A0F4D714B19BF9272D3B826E09A',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- private function getPaymentPayload(?array $additional = null): array
- {
- $payload = array_merge(
- $this->getBasePayPayload([], [
- 'clientIP' => '198.162.1.1',
- 'gender' => "1",
- 'operatingCountry' => 'NL',
- ]),
- [
- 'billing' => $this->getBillingPayload(['careOf', 'title', 'initials', 'category', 'birthDate']),
- 'shipping' => $this->getShippingPayload(['careOf', 'title', 'initials', 'category', 'birthDate']),
- 'articles' => $this->getArticlesPayload(),
- ]
- );
-
- if ($additional) {
- return array_merge($additional, $payload);
- }
-
- return $payload;
- }
-}
diff --git a/tests/Buckaroo/Payments/KlarnaTest.php b/tests/Buckaroo/Payments/KlarnaTest.php
deleted file mode 100644
index 51a3d3e9..00000000
--- a/tests/Buckaroo/Payments/KlarnaTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
-buckaroo->method('klarna')->pay($this->getPaymentPayload());
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_klarna_payment_installment()
- {
- $response = $this->buckaroo->method('klarna')->payInInstallments($this->getPaymentPayload('GB', 'GBP'));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- private function getPaymentPayload(string $country = 'NL', string $currency = 'EUR'): array
- {
- $payload = array_merge(
- $this->getBasePayPayload([],[
- 'currency' => $currency,
- ]),
- [
- 'billing' => $this->getBillingPayload(['careOf', 'title', 'initials']),
- 'shipping' => $this->getShippingPayload(['careOf', 'title', 'initials']),
- 'articles' => $this->getArticlesPayload(),
- ]
- );
- $payload['billing']['address']['country'] = $country;
- $payload['billing']['recipient']['gender'] = 'female';
- $payload['shipping']['address']['country'] = $country;
- $payload['shipping']['recipient']['gender'] = 'male';
-
- return $payload;
- }
-}
diff --git a/tests/Buckaroo/Payments/KnakenPayTest.php b/tests/Buckaroo/Payments/KnakenPayTest.php
deleted file mode 100644
index ca93ce7f..00000000
--- a/tests/Buckaroo/Payments/KnakenPayTest.php
+++ /dev/null
@@ -1,61 +0,0 @@
-buckaroo->method('knaken')->pay($this->getBasePayPayload([],[
- 'returnURL'=> 'https://example.com/buckaroo/return',
- 'returnURLCancel' => 'https://example.com/buckaroo/cancel',
- 'returnURLError' => 'https://example.com/buckaroo/error',
- 'returnURLReject' => 'https://example.com/buckaroo/reject',
- 'pushURL' => 'https://example.com/buckaroo/push',
- 'returnURLCancel' => 'https://example.com/buckaroo/cancel',
- 'pushURLFailure' => 'https://example.com/buckaroo/push-failure',
- 'invoice' => uniqid(),
- 'amountDebit' => 0.1,
- "CustomerName"=> "Rico",
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- // Only one refund allowed per transaction
- /**
- * @test
- */
- public function it_creates_a_knaken_refund()
- {
- $response = $this->buckaroo->method('knaken')->refund($this->getRefundPayload([
- 'originalTransactionKey' => 'E29EB7DF6EA8448A87FC6A03E6EFA0A3',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/MBWayTest.php b/tests/Buckaroo/Payments/MBWayTest.php
deleted file mode 100644
index 0cb90c08..00000000
--- a/tests/Buckaroo/Payments/MBWayTest.php
+++ /dev/null
@@ -1,49 +0,0 @@
-buckaroo->method('mbway')->pay($this->getBasePayPayload());
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_mbway_refund()
- {
- $response = $this->buckaroo->method('mbway')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '83A9AD305D71405FAC3D3E37DBA51D99',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/MarketplacesTest.php b/tests/Buckaroo/Payments/MarketplacesTest.php
deleted file mode 100644
index d301385b..00000000
--- a/tests/Buckaroo/Payments/MarketplacesTest.php
+++ /dev/null
@@ -1,108 +0,0 @@
-buckaroo->method('marketplaces')->manually()->split([
- 'daysUntilTransfer' => 2,
- 'marketplace' => [
- 'amount' => 10,
- 'description' => 'INV0001 Commission Marketplace',
- ],
- 'sellers' => [
- [
- 'accountId' => '789C60F316D24B088ACD471',
- 'amount' => 50,
- 'description' => 'INV001 Payout Make-Up Products BV',
- ],
- [
- 'accountId' => '369C60F316D24B088ACD238',
- 'amount' => 35,
- 'description' => 'INV0001 Payout Beauty Products BV',
- ],
- ],
- ]);
-
- $response = $this->buckaroo->method('ideal')->combine($marketplace)->pay([
- 'invoice' => uniqid(),
- 'amountDebit' => 95.00,
- 'issuer' => 'ABNANL2A',
- ]);
-
- $this->assertTrue($response->isValidationFailure());
- }
-
- /**
- * @test
- */
- public function it_creates_marketplaces_transfer()
- {
- $response = $this->buckaroo->method('marketplaces')->transfer([
- 'originalTransactionKey' => 'D3732474ED0',
- 'marketplace' => [
- 'amount' => 10,
- 'description' => 'INV0001 Commission Marketplace',
- ],
- 'sellers' => [
- [
- 'accountId' => '789C60F316D24B088ACD471',
- 'amount' => 50,
- 'description' => 'INV001 Payout Make-Up Products BV',
- ],
- ],
- ]);
-
- $this->assertTrue($response->isValidationFailure());
- }
-
- /**
- * @test
- */
- public function it_creates_marketplaces_refund()
- {
- $marketplace = $this->buckaroo->method('marketplaces')->manually()->refundSupplementary([
- 'sellers' => [
- [
- 'accountId' => '789C60F316D24B088ACD471',
-// 'amount' => 30,
- 'description' => 'INV001 Payout Make-Up Products BV',
- ],
- ],
- ]);
-
- $response = $this->buckaroo->method('ideal')->combine($marketplace)->refund([
- 'invoice' => 'testinvoice 123', //Set invoice number of the transaction to refund
- 'originalTransactionKey' => '4E8BD922192746C3918BF4077CXXXXXX',
- //Set transaction key of the transaction to refund
- 'amountCredit' => 30,
- ]);
-
- $this->assertTrue($response->isValidationFailure());
- }
-}
diff --git a/tests/Buckaroo/Payments/MultibancoTest.php b/tests/Buckaroo/Payments/MultibancoTest.php
deleted file mode 100644
index d7835800..00000000
--- a/tests/Buckaroo/Payments/MultibancoTest.php
+++ /dev/null
@@ -1,49 +0,0 @@
-buckaroo->method('multibanco')->pay($this->getBasePayPayload());
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_multibanco_refund()
- {
- $response = $this->buckaroo->method('multibanco')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '9BDDFD2D455A41298713089E7056FADF',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/NoServiceSpecifiedPaymentTest.php b/tests/Buckaroo/Payments/NoServiceSpecifiedPaymentTest.php
deleted file mode 100644
index 199494ec..00000000
--- a/tests/Buckaroo/Payments/NoServiceSpecifiedPaymentTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-buckaroo->method('noservice')->pay($this->getBasePayPayload([], [
- 'servicesSelectableByClient' => 'ideal,mbway,bancontactmrcash,paypal',
- 'servicesExcludedForClient' => 'ideal',
- 'continueOnIncomplete' => '1',
- ]));
-
- $this->assertTrue($response->isWaitingOnUserInput());
- }
-}
diff --git a/tests/Buckaroo/Payments/PayPerEmailTest.php b/tests/Buckaroo/Payments/PayPerEmailTest.php
deleted file mode 100644
index b2ce97d4..00000000
--- a/tests/Buckaroo/Payments/PayPerEmailTest.php
+++ /dev/null
@@ -1,51 +0,0 @@
-buckaroo->method('payperemail')->paymentInvitation([
- 'amountDebit' => 10,
- 'invoice' => 'testinvoice 123',
- 'merchantSendsEmail' => false,
- 'email' => 'johnsmith@gmail.com',
- 'expirationDate' => '2030-01-01',
- 'paymentMethodsAllowed' => 'ideal,mastercard,paypal',
- 'attachment' => '',
- 'customer' => [
- 'gender' => Gender::FEMALE,
- 'firstName' => 'John',
- 'lastName' => 'Smith',
- ],
- ]);
-
- $this->assertTrue($response->isAwaitingConsumer());
- }
-}
diff --git a/tests/Buckaroo/Payments/PayconiqTest.php b/tests/Buckaroo/Payments/PayconiqTest.php
deleted file mode 100644
index cc9b7ec4..00000000
--- a/tests/Buckaroo/Payments/PayconiqTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-buckaroo->method('payconiq')->pay($this->getBasePayPayload());
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_payconiq_refund()
- {
- $response = $this->buckaroo->method('payconiq')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '3531593A0AC04B449B6CAE51FE1D6DE7',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/PaymentInitiationTest.php b/tests/Buckaroo/Payments/PaymentInitiationTest.php
deleted file mode 100644
index 9d867f3f..00000000
--- a/tests/Buckaroo/Payments/PaymentInitiationTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-buckaroo->method('paybybank')->issuers();
-
- $this->assertIsArray($response);
- foreach ($response as $item)
- {
- $this->assertIsArray($item);
- $this->assertArrayHasKey('id', $item);
- $this->assertArrayHasKey('name', $item);
- }
- }
-
- /**
- * @test
- */
- public function it_creates_a_payment_initiation_payment()
- {
- $response = $this->buckaroo->method('paybybank')->pay($this->getBasePayPayload([], [
- 'issuer' => 'ABNANL2A',
- 'countryCode' => 'NL',
- 'pushURL' => 'https://example.com/buckaroo/push',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_payment_initiation_refund()
- {
- $response = $this->buckaroo->method('paybybank')->refund($this->getRefundPayload([
- 'originalTransactionKey' => 'F7B4C318221D41F185728116F05C9EF7',
- 'invoice' => '670fa9e86d347',
- 'pushURL' => 'https://example.com/buckaroo/push',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/PaypalTest.php b/tests/Buckaroo/Payments/PaypalTest.php
deleted file mode 100644
index 7795ec27..00000000
--- a/tests/Buckaroo/Payments/PaypalTest.php
+++ /dev/null
@@ -1,107 +0,0 @@
-buckaroo->method('paypal')->pay($this->getBasePayPayload());
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_paypal_recurrent_payment()
- {
- $response = $this->buckaroo->method('paypal')->payRecurrent($this->getBasePayPayload([],[
- 'originalTransactionKey' => '8E84AF7D9BDF45368D60AC4ED7EA1733',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @test
- */
- public function it_creates_a_paypal_extra_info()
- {
- $response = $this->buckaroo->method('paypal')->extraInfo($this->getBasePayPayload([],[
- 'customer' => [
- 'name' => 'John Smith',
- ],
- 'address' => [
- 'street' => 'Hoofstraat 90',
- 'street2' => 'Street 2',
- 'city' => 'Heerenveen',
- 'state' => 'Friesland',
- 'zipcode' => '8441AB',
- 'country' => 'NL',
- ],
- 'phone' => [
- 'mobile' => '0612345678',
- ],
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_paypal_refund()
- {
- $response = $this->buckaroo->method('paypal')->refund($this->getRefundPayload([
- 'originalTransactionKey' => 'CE26373CFB64485CB7DFB1BD656066C1',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @test
- */
- public function it_creates_a_paypal_pay_remainder()
- {
- $giftCardResponse = $this->buckaroo->method('giftcard')->pay($this->getBasePayPayload([], [
- 'amountDebit' => 10,
- 'name' => 'boekenbon',
- 'intersolveCardnumber' => '0000000000000000001',
- 'intersolvePIN' => '500',
- ]));
-
- $this->assertTrue($giftCardResponse->isSuccess());
-
- $response = $this->buckaroo->method('paypal')->payRemainder($this->getBasePayPayload([], [
- 'originalTransactionKey' => $giftCardResponse->data('RelatedTransactions')[0]['RelatedTransactionKey'],
- 'amountDebit' => 9.50,
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-}
diff --git a/tests/Buckaroo/Payments/PosTest.php b/tests/Buckaroo/Payments/PosTest.php
deleted file mode 100644
index a722268e..00000000
--- a/tests/Buckaroo/Payments/PosTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
-buckaroo->method('pospayment')->pay($this->getBasePayPayload([],[
- 'terminalID' => '50000001',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/Przelewy24Test.php b/tests/Buckaroo/Payments/Przelewy24Test.php
deleted file mode 100644
index 34b2db68..00000000
--- a/tests/Buckaroo/Payments/Przelewy24Test.php
+++ /dev/null
@@ -1,82 +0,0 @@
-buckaroo->method("przelewy24")->pay($this->getBasePayPayload([],[
- 'email' => 'test@test.nl',
- 'customer' => [
- 'firstName' => 'John',
- 'lastName' => 'Smith',
- ],
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_przelewy24_pay_remainder()
- {
- $giftCardResponse = $this->buckaroo->method('giftcard')->pay($this->getBasePayPayload([], [
- 'amountDebit' => 10,
- 'name' => 'boekenbon',
- 'intersolveCardnumber' => '0000000000000000001',
- 'intersolvePIN' => '500',
- ]));
-
- $this->assertTrue($giftCardResponse->isSuccess());
-
- $response = $this->buckaroo->method('przelewy24')->payRemainder($this->getBasePayPayload([], [
- 'originalTransactionKey' => $giftCardResponse->data('RelatedTransactions')[0]['RelatedTransactionKey'],
- 'amountDebit' => 5.00,
- 'email' => 'test@test.nl',
- 'customer' => [
- 'firstName' => 'John',
- 'lastName' => 'Smith',
- ],
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_przelewy24_refund()
- {
- $response = $this->buckaroo->method('przelewy24')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '4298F2C861B741959613EEC6121406B3',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/PushTest.php b/tests/Buckaroo/Payments/PushTest.php
deleted file mode 100644
index 09e5d835..00000000
--- a/tests/Buckaroo/Payments/PushTest.php
+++ /dev/null
@@ -1,50 +0,0 @@
-buckaroo->client()->config(), $post_data, $auth_header, $uri);
- $reply_handler->validate();
-
- $this->assertTrue($reply_handler->isValid());
- }
-}
diff --git a/tests/Buckaroo/Payments/SepaTest.php b/tests/Buckaroo/Payments/SepaTest.php
deleted file mode 100644
index 9e3bd151..00000000
--- a/tests/Buckaroo/Payments/SepaTest.php
+++ /dev/null
@@ -1,125 +0,0 @@
-buckaroo->method('sepadirectdebit')->pay($this->getBasePayPayload([], [
- 'iban' => 'NL13TEST0123456789',
- 'bic' => 'TESTNL2A',
- 'startRecurrent' => true,
- 'collectdate' => '2022-12-01',
- 'mandateReference' => '1DCtestreference',
- 'mandateDate' => '2022-07-03',
- 'customer' => [
- 'name' => 'John Smith',
- ],
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_sepa_refund()
- {
- $response = $this->buckaroo->method('sepadirectdebit')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '5221F6CECF4E4C7791BB57BC78C0CF7A',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_sepa_authorize()
- {
- $response = $this->buckaroo->method('sepadirectdebit')->authorize([
- 'amountDebit' => 10,
- 'invoice' => uniqid(),
- 'iban' => 'NL13TEST0123456789',
- 'bic' => 'TESTNL2A',
- 'collectdate' => '2025-12-01',
- 'mandateReference' => '1DC326734AB3084FC7',
- 'mandateDate' => '2025-07-03',
- 'startRecurrent' => true,
- 'channel' => 'BackOffice',
- 'customer' => [
- 'name' => 'John Smith',
- ],
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_sepa_recurrent_payment()
- {
- $response = $this->buckaroo->method('sepadirectdebit')->payRecurrent($this->getBasePayPayload([], [
- 'originalTransactionKey' => '3CE4E4D07CE74B5BBD78809DE6671B75',
- 'collectdate' => '2030-07-03',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_sepa_extra_info()
- {
- $response = $this->buckaroo->method('sepadirectdebit')->extraInfo([
- 'amountDebit' => 10,
- 'invoice' => 'testinvoice 123',
- 'iban' => 'NL13TEST0123456789',
- 'bic' => 'TESTNL2A',
- 'contractID' => 'TEST',
- 'mandateDate' => '2022-07-03',
- 'customerReferencePartyName' => 'Lorem',
- 'customer' => [
- 'name' => 'John Smith',
- ],
- 'address' => [
- 'street' => 'Hoofdstraat',
- 'houseNumber' => '13',
- 'houseNumberAdditional' => 'a',
- 'zipcode' => '1234AB',
- 'city' => 'Heerenveen',
- 'country' => 'NL',
- ],
- ]);
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_sepa_pay_with_emandate()
- {
- $response = $this->buckaroo->method('sepadirectdebit')->payWithEmandate($this->getBasePayPayload([], [
- 'mandateReference' => '1DC326734AB3084FC7',
- 'order' => '',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-}
diff --git a/tests/Buckaroo/Payments/SubscriptionsTest.php b/tests/Buckaroo/Payments/SubscriptionsTest.php
deleted file mode 100644
index 9b90f687..00000000
--- a/tests/Buckaroo/Payments/SubscriptionsTest.php
+++ /dev/null
@@ -1,237 +0,0 @@
-buckaroo->method('subscriptions')->create([
- 'startDate' => date('Y-m-d'),
- 'ratePlans' => [
- 'add' => [
- 'startDate' => date('Y-m-d'),
- 'billingTiming' => 1,
- 'ratePlanName' => 'Test Rate Plan',
- 'ratePlanDescription' => 'Test Rate Plan',
- 'currency' => 'EUR',
- 'billingInterval' => 'Weekly',
- 'termStartDay' => '1',
- ],
- ],
- 'ratePlanCharges' => [
- 'add' => [
- 'ratePlanChargeName' => 'Rate Plan Charge',
- 'rateplanChargeDescription' => 'Rate Plan Charge Description',
- 'unitOfMeasure' => 'Quantity',
- 'baseNumberOfUnits' => '1',
- 'partialBilling' => 'Billfull',
- 'pricePerUnit' => '2',
- 'priceIncludesVat' => true,
- 'vatPercentage' => '21',
- 'ratePlanChargeType' => 'Recurring',
- ],
- ],
- 'debtor' => [
- 'code' => 'johnsmith4',
- ],
- ]);
-
- self::$payTransactionKey = $response->getServiceParameters()['subscriptionguid'];
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_combined_subscription()
- {
- $subscriptions = $this->buckaroo->method('subscriptions')->manually()->createCombined([
- 'pushURL' => 'https://example.com/buckaroo/push',
- 'includeTransaction' => false,
- 'transactionVatPercentage' => 5,
- 'configurationCode' => '7esem6f7',
- 'email' => 'test@buckaroo.nl',
- 'ratePlans' => [
- 'add' => [
- 'startDate' => date('Y-m-d'),
- 'ratePlanCode' => '9863hdcj',
- ],
- ],
- 'phone' => [
- 'mobile' => '0612345678',
- ],
- 'debtor' => [
- 'code' => 'johnsmith4',
- ],
- 'company' => [
- 'culture' => 'nl-NL',
- 'companyName' => 'My Company Coporation',
- 'vatApplicable' => true,
- 'vatNumber' => 'NL140619562B01',
- 'chamberOfCommerce' => '20091741',
- ],
- 'address' => [
- 'street' => 'Hoofdstraat',
- 'houseNumber' => '90',
- 'houseNumberAdditional' => 'a',
- 'zipcode' => '8441ER',
- 'city' => 'Heerenveen',
- 'country' => 'NL',
- ],
- ]);
-
- $response = $this->buckaroo->method('ideal')->combine($subscriptions)->pay([
- 'startRecurrent' => true,
- 'invoice' => uniqid(),
- 'amountDebit' => 10.10,
- 'issuer' => 'ABNANL2A',
- ]);
-
- $this->assertTrue($response->isWaitingOnUserInput());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_updates_subscription()
- {
- $response = $this->buckaroo->method('subscriptions')->update([
- 'subscriptionGuid' => self::$payTransactionKey,
- 'configurationCode' => '7esem6f7',
- 'ratePlans' => [
- 'update' => [
- 'ratePlanGuid' => '56CC308A1D694CF19F808993DD42BE7B',
- 'endDate' => '2030-01-01',
- 'charge' => [
- 'ratePlanChargeGuid' => '15C2CEEB39E34C86AAD0038ED73807B0',
- 'baseNumberOfUnits' => '1',
- 'pricePerUnit' => 5,
- ],
- ],
- ],
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_updates_combined_subscription()
- {
- $subscription = $this->buckaroo->method('subscriptions')->manually()->updateCombined([
- 'startRecurrent' => true,
- 'subscriptionGuid' => '36F17939D56549BD91C64A00FAE8161A',
- ]);
-
- $response = $this->buckaroo->method('ideal')->combine($subscription)->pay([
- 'invoice' => uniqid(),
- 'amountDebit' => 10.10,
- 'issuer' => 'ABNANL2A',
- ]);
-
-
- $this->assertTrue($response->isWaitingOnUserInput());
- }
-
- /**
- * @return void
- * @depends it_creates_a_subscription
- * @test
- */
- public function it_stops_subscription()
- {
- $response = $this->buckaroo->method('subscriptions')->stop([
- 'subscriptionGuid' => self::$payTransactionKey,
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @depends it_creates_a_subscription
- * @test
- */
- public function it_get_info_of_subscription()
- {
- $response = $this->buckaroo->method('subscriptions')->info([
- 'subscriptionGuid' => self::$payTransactionKey,
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @depends it_creates_a_subscription
- * @test
- */
- public function it_delete_payment_config_of_subscription()
- {
- $response = $this->buckaroo->method('subscriptions')->deletePaymentConfig([
- 'subscriptionGuid' => self::$payTransactionKey,
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @depends it_creates_a_subscription
- * @test
- */
- public function it_pauses_of_subscription()
- {
- $response = $this->buckaroo->method('subscriptions')->pause([
- 'resumeDate' => '2030-01-01',
- 'subscriptionGuid' => self::$payTransactionKey,
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @return void
- * @depends it_creates_a_subscription
- * @test
- */
- public function it_resumes_of_subscription()
- {
- $response = $this->buckaroo->method('subscriptions')->resume([
- 'resumeDate' => '2030-01-01',
- 'subscriptionGuid' => self::$payTransactionKey,
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/SurepayTest.php b/tests/Buckaroo/Payments/SurepayTest.php
deleted file mode 100644
index 410bce1a..00000000
--- a/tests/Buckaroo/Payments/SurepayTest.php
+++ /dev/null
@@ -1,43 +0,0 @@
-buckaroo->method('surepay')->verify([
- 'currency' => '',
- 'bankAccount' => [
- 'iban' => 'NLXXTESTXXXXXXXXXX',
- 'accountName' => 'Test Acceptatie',
- ]
- ]);
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/SwishTest.php b/tests/Buckaroo/Payments/SwishTest.php
deleted file mode 100644
index a800d8fd..00000000
--- a/tests/Buckaroo/Payments/SwishTest.php
+++ /dev/null
@@ -1,50 +0,0 @@
-buckaroo->method('swish')->pay($this->getBasePayPayload([], [
- 'currency' => 'SEK',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_swish_refund()
- {
- $response = $this->buckaroo->method('swish')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '2D04704995B74D679AACC59F87XXXXXX',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
\ No newline at end of file
diff --git a/tests/Buckaroo/Payments/ThunesTest.php b/tests/Buckaroo/Payments/ThunesTest.php
deleted file mode 100644
index 068a2ff7..00000000
--- a/tests/Buckaroo/Payments/ThunesTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
-buckaroo->method('thunes')->pay($this->getPayPayload([
- 'name' => 'belfius',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-}
diff --git a/tests/Buckaroo/Payments/TransferTest.php b/tests/Buckaroo/Payments/TransferTest.php
deleted file mode 100644
index 0987f264..00000000
--- a/tests/Buckaroo/Payments/TransferTest.php
+++ /dev/null
@@ -1,60 +0,0 @@
-buckaroo->method('transfer')->pay($this->getBasePayPayload([],[
- 'email' => 'your@email.com',
- 'country' => 'NL',
- 'dateDue' => date("Y-m-d"),
- 'sendMail' => false,
- 'customer' => [
- 'gender' => Gender::MALE,
- 'firstName' => 'John',
- 'lastName' => 'Smith',
- ],
- ]));
-
- $this->assertTrue($response->isAwaitingConsumer());
- }
-
- /**
- * @test
- */
- public function it_creates_a_transfer_refund()
- {
- $response = $this->buckaroo->method('transfer')->refund($this->getRefundPayload([
- 'originalTransactionKey' => 'CA18006C913A47E58D830C7D7CC42A6E',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/TrustlyTest.php b/tests/Buckaroo/Payments/TrustlyTest.php
deleted file mode 100644
index 40e55d90..00000000
--- a/tests/Buckaroo/Payments/TrustlyTest.php
+++ /dev/null
@@ -1,57 +0,0 @@
-buckaroo->method('trustly')->pay($this->getBasePayPayload([], [
- 'country' => 'NL',
- 'continueOnIncomplete'=> true,
- 'customer' => [
- 'firstName' => 'Test',
- 'lastName' => 'Aflever',
- ],
-
- ]));
-
- $this->assertTrue($response->isWaitingOnUserInput());
- }
-
- /**
- * @test
- */
- public function it_creates_a_trustly_refund()
- {
- $response = $this->buckaroo->method('trustly')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '7F796BBC52664FCA936C4C3A1DD18996',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
-
diff --git a/tests/Buckaroo/Payments/TwintTest.php b/tests/Buckaroo/Payments/TwintTest.php
deleted file mode 100644
index 37675523..00000000
--- a/tests/Buckaroo/Payments/TwintTest.php
+++ /dev/null
@@ -1,50 +0,0 @@
-buckaroo->method('twint')->pay($this->getBasePayPayload([], [
- 'currency' => 'CHF',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_twint_refund()
- {
- $response = $this->buckaroo->method('twint')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '2D04704995B74D679AACC59F87XXXXXX',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
\ No newline at end of file
diff --git a/tests/Buckaroo/Payments/WeChatPayTest.php b/tests/Buckaroo/Payments/WeChatPayTest.php
deleted file mode 100644
index f4a4761c..00000000
--- a/tests/Buckaroo/Payments/WeChatPayTest.php
+++ /dev/null
@@ -1,50 +0,0 @@
-buckaroo->method('wechatpay')->pay($this->getBasePayPayload([
- 'locale' => 'en-US',
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_creates_a_wechat_refund()
- {
- $response = $this->buckaroo->method('wechatpay')->refund($this->getRefundPayload([
- 'originalTransactionKey' => '67E70539519D427595D86AC50DA4F7CE',
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/WeroTest.php b/tests/Buckaroo/Payments/WeroTest.php
deleted file mode 100644
index 3641869a..00000000
--- a/tests/Buckaroo/Payments/WeroTest.php
+++ /dev/null
@@ -1,92 +0,0 @@
-buckaroo->method('Wero')->pay($this->getBasePayPayload());
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_wero_authorize()
- {
- $response = $this->buckaroo->method('Wero')->authorize($this->getBasePayPayload());
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_wero_cancel_authorize()
- {
- $response = $this->buckaroo->method('Wero')->cancelAuthorize($this->getRefundPayload([
- 'originalTransactionKey' => 'B591116039094602B6D899A1XXXXXXXX',
- 'amountCredit' => 100.30,
-
- ]));
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @return void
- * @test
- */
- public function it_creates_a_wero_capture()
- {
- $response = $this->buckaroo->method('Wero')->capture($this->getBasePayPayload([], [
- 'originalTransactionKey' => '981A2018935A4EADB374E479XXXXXXXX',
- 'amountDebit' => 100.30,
- ]));
-
- $this->assertTrue($response->isSuccess());
- }
-
- /**
- * @test
- */
- public function it_creates_a_wero_refund()
- {
- $response = $this->buckaroo->method('Wero')->refund(
- $this->getRefundPayload([
- 'originalTransactionKey' => '13B9F93D925E4694AB5FB005XXXXXXXX',
- ])
- );
-
- $this->assertTrue($response->isSuccess());
- }
-}
diff --git a/tests/Buckaroo/Payments/iDinTest.php b/tests/Buckaroo/Payments/iDinTest.php
deleted file mode 100644
index 8b1cabc9..00000000
--- a/tests/Buckaroo/Payments/iDinTest.php
+++ /dev/null
@@ -1,62 +0,0 @@
-buckaroo->method('idin')->identify([
- 'issuer' => 'BANKNL2Y',
- ]);
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_verify_with_idin()
- {
- $response = $this->buckaroo->method('idin')->verify([
- 'issuer' => 'BANKNL2Y',
- ]);
-
- $this->assertTrue($response->isPendingProcessing());
- }
-
- /**
- * @test
- */
- public function it_login_with_idin()
- {
- $response = $this->buckaroo->method('idin')->login([
- 'issuer' => 'BANKNL2Y',
- ]);
-
- $this->assertTrue($response->isPendingProcessing());
- }
-}
diff --git a/tests/Feature/ClientTest.php b/tests/Feature/ClientTest.php
new file mode 100644
index 00000000..02c8d9f7
--- /dev/null
+++ b/tests/Feature/ClientTest.php
@@ -0,0 +1,66 @@
+assertInstanceOf(\Buckaroo\BuckarooClient::class, $this->buckaroo);
+ }
+
+ /**
+ * @test
+ * @group slow
+ */
+ public function it_can_fetch_ideal_issuers(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('GET', '*/json/Transaction/Specification/ideal*', [
+ 'Actions' => [
+ [
+ 'RequestParameters' => [
+ [
+ 'ListItemDescriptions' => [
+ [
+ 'Value' => 'ABNANL2A',
+ 'Description' => 'ABN AMRO Bank',
+ ],
+ [
+ 'Value' => 'INGBNL2A',
+ 'Description' => 'ING Bank',
+ ],
+ [
+ 'Value' => 'RABONL2U',
+ 'Description' => 'Rabobank',
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ]),
+ ]);
+
+ $issuers = $this->buckaroo->method('ideal')->issuers();
+
+ $this->assertIsArray($issuers);
+ $this->assertNotEmpty($issuers);
+ $this->assertCount(3, $issuers);
+ $this->assertSame('ABNANL2A', $issuers[0]['id']);
+ $this->assertSame('ABN AMRO Bank', $issuers[0]['name']);
+ }
+}
diff --git a/tests/Feature/CredentialsTest.php b/tests/Feature/CredentialsTest.php
new file mode 100644
index 00000000..b7a01f41
--- /dev/null
+++ b/tests/Feature/CredentialsTest.php
@@ -0,0 +1,65 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Transaction/Specification/ideal*',
+ TestHelpers::successResponse([
+ 'Key' => 'SPEC-123',
+ 'ServiceList' => [
+ [
+ 'Name' => 'ideal',
+ 'SupportedCurrencies' => 'EUR',
+ ],
+ ],
+ ]),
+ 200
+ ),
+ ]);
+
+ $credentials = new Credentials($this->buckaroo->client(), $this->buckaroo->client()->config());
+ $isValid = $credentials->confirm();
+
+ $this->assertTrue($isValid);
+ }
+
+ /** @test */
+ public function it_rejects_credentials_for_non_200_status_codes(): void
+ {
+ $statusCodes = [400, 401, 403, 404, 500, 503];
+
+ foreach ($statusCodes as $statusCode) {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Transaction/Specification/ideal*',
+ TestHelpers::failedResponse("HTTP {$statusCode}"),
+ $statusCode
+ ),
+ ]);
+
+ $credentials = new Credentials($this->buckaroo->client(), $this->buckaroo->client()->config());
+ $isValid = $credentials->confirm();
+
+ $this->assertFalse($isValid, "Expected false for HTTP {$statusCode}");
+ }
+ }
+}
diff --git a/tests/Feature/PaymentMethods/AfterpayDigiAcceptTest.php b/tests/Feature/PaymentMethods/AfterpayDigiAcceptTest.php
new file mode 100644
index 00000000..80184d72
--- /dev/null
+++ b/tests/Feature/PaymentMethods/AfterpayDigiAcceptTest.php
@@ -0,0 +1,488 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpaydigiaccept',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-DIGIACCEPT-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 199.99,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpaydigiaccept')->pay([
+ 'amountDebit' => 199.99,
+ 'invoice' => 'INV-DIGIACCEPT-001',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-DIGIACCEPT-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(199.99, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_pay_transaction_with_complete_billing_data(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpaydigiaccept',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-DIGIACCEPT-FULL-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 149.50,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpaydigiaccept')->pay([
+ 'amountDebit' => 149.50,
+ 'invoice' => 'INV-DIGIACCEPT-FULL-001',
+ 'currency' => 'EUR',
+ 'billing' => [
+ 'recipient' => [
+ 'category' => 'Person',
+ 'firstName' => 'Thomas',
+ 'lastName' => 'Anderson',
+ ],
+ 'address' => [
+ 'street' => 'Prinsengracht',
+ 'houseNumber' => '789',
+ 'zipcode' => '1017JK',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ 'email' => 'thomas.anderson@example.com',
+ ],
+ 'articles' => [
+ [
+ 'identifier' => 'PROD-500',
+ 'description' => 'Gaming Console',
+ 'quantity' => 1,
+ 'price' => 149.50,
+ 'vatPercentage' => 21,
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-DIGIACCEPT-FULL-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(149.50, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_authorize_transaction(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Authorization successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpaydigiaccept',
+ 'Action' => 'Authorize',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-DIGIACCEPT-AUTH-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 350.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpaydigiaccept')->authorize([
+ 'amountDebit' => 350.00,
+ 'invoice' => 'INV-DIGIACCEPT-AUTH-001',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-DIGIACCEPT-AUTH-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(350.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_captures_authorized_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_DIGIACCEPT_AUTH_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Capture successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpaydigiaccept',
+ 'Action' => 'Capture',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-DIGIACCEPT-CAPTURE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 350.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpaydigiaccept')->capture([
+ 'amountDebit' => 350.00,
+ 'invoice' => 'INV-DIGIACCEPT-CAPTURE-001',
+ 'currency' => 'EUR',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-DIGIACCEPT-CAPTURE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(350.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_cancels_authorize_transaction(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_DIGIACCEPT_AUTH_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Cancellation successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpaydigiaccept',
+ 'Action' => 'CancelAuthorize',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-DIGIACCEPT-CANCEL-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 350.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpaydigiaccept')->cancelAuthorize([
+ 'amountCredit' => 350.00,
+ 'invoice' => 'INV-DIGIACCEPT-CANCEL-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-DIGIACCEPT-CANCEL-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(350.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_refunds_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_DIGIACCEPT_PAY_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpaydigiaccept',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-DIGIACCEPT-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 49.99,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpaydigiaccept')->refund([
+ 'amountCredit' => 49.99,
+ 'invoice' => 'INV-DIGIACCEPT-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-DIGIACCEPT-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(49.99, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_processes_payment_with_multiple_articles(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpaydigiaccept',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-DIGIACCEPT-MULTI-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 425.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpaydigiaccept')->pay([
+ 'amountDebit' => 425.00,
+ 'invoice' => 'INV-DIGIACCEPT-MULTI-001',
+ 'currency' => 'EUR',
+ 'articles' => [
+ [
+ 'identifier' => 'PROD-300',
+ 'description' => 'Tablet',
+ 'quantity' => 1,
+ 'price' => 299.00,
+ 'vatPercentage' => 21,
+ ],
+ [
+ 'identifier' => 'PROD-301',
+ 'description' => 'Tablet Case',
+ 'quantity' => 1,
+ 'price' => 49.00,
+ 'vatPercentage' => 21,
+ ],
+ [
+ 'identifier' => 'PROD-302',
+ 'description' => 'Stylus Pen',
+ 'quantity' => 1,
+ 'price' => 77.00,
+ 'vatPercentage' => 21,
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-DIGIACCEPT-MULTI-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(425.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_processes_payment_with_separate_shipping_address(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpaydigiaccept',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-DIGIACCEPT-SHIPPING-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 225.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpaydigiaccept')->pay([
+ 'amountDebit' => 225.00,
+ 'invoice' => 'INV-DIGIACCEPT-SHIPPING-001',
+ 'currency' => 'EUR',
+ 'billing' => [
+ 'recipient' => [
+ 'category' => 'Person',
+ 'firstName' => 'Alice',
+ 'lastName' => 'Johnson',
+ ],
+ 'address' => [
+ 'street' => 'Billing Lane',
+ 'houseNumber' => '15',
+ 'zipcode' => '1100CC',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'email' => 'alice.johnson@example.com',
+ ],
+ 'shipping' => [
+ 'recipient' => [
+ 'category' => 'Person',
+ 'firstName' => 'Bob',
+ 'lastName' => 'Johnson',
+ ],
+ 'address' => [
+ 'street' => 'Shipping Avenue',
+ 'houseNumber' => '25',
+ 'zipcode' => '3000DD',
+ 'city' => 'Rotterdam',
+ 'country' => 'NL',
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-DIGIACCEPT-SHIPPING-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(225.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpaydigiaccept',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-DIGIACCEPT-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpaydigiaccept')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-DIGIACCEPT-STATUS-001',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'waiting_on_user_input' => [790, 'isWaitingOnUserInput'],
+ 'pending_processing' => [791, 'isPendingProcessing'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/AfterpayTest.php b/tests/Feature/PaymentMethods/AfterpayTest.php
new file mode 100644
index 00000000..e903d496
--- /dev/null
+++ b/tests/Feature/PaymentMethods/AfterpayTest.php
@@ -0,0 +1,531 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AFTERPAY-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 250.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpay')->pay([
+ 'amountDebit' => 250.00,
+ 'invoice' => 'INV-AFTERPAY-001',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-AFTERPAY-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(250.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_pay_transaction_with_complete_billing_data(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AFTERPAY-FULL-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 150.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpay')->pay([
+ 'amountDebit' => 150.00,
+ 'invoice' => 'INV-AFTERPAY-FULL-001',
+ 'currency' => 'EUR',
+ 'billing' => [
+ 'recipient' => [
+ 'category' => 'Person',
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ ],
+ 'address' => [
+ 'street' => 'Keizersgracht',
+ 'houseNumber' => '456',
+ 'zipcode' => '1016DK',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'phone' => [
+ 'mobile' => '0687654321',
+ ],
+ 'email' => 'jane.smith@example.com',
+ ],
+ 'articles' => [
+ [
+ 'identifier' => 'PROD-100',
+ 'description' => 'Laptop Computer',
+ 'quantity' => 1,
+ 'price' => 150.00,
+ 'vatPercentage' => 21,
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-AFTERPAY-FULL-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(150.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_authorize_transaction(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Authorization successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpay',
+ 'Action' => 'Authorize',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AFTERPAY-AUTH-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 300.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpay')->authorize([
+ 'amountDebit' => 300.00,
+ 'invoice' => 'INV-AFTERPAY-AUTH-001',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-AFTERPAY-AUTH-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(300.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_captures_authorized_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_AFTERPAY_AUTH_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Capture successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpay',
+ 'Action' => 'Capture',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AFTERPAY-CAPTURE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 300.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpay')->capture([
+ 'amountDebit' => 300.00,
+ 'invoice' => 'INV-AFTERPAY-CAPTURE-001',
+ 'currency' => 'EUR',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-AFTERPAY-CAPTURE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(300.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_cancels_authorize_transaction(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_AFTERPAY_AUTH_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Cancellation successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpay',
+ 'Action' => 'CancelAuthorize',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AFTERPAY-CANCEL-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 300.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpay')->cancelAuthorize([
+ 'amountCredit' => 300.00,
+ 'invoice' => 'INV-AFTERPAY-CANCEL-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-AFTERPAY-CANCEL-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(300.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_refunds_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_AFTERPAY_PAY_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpay',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AFTERPAY-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpay')->refund([
+ 'amountCredit' => 75.00,
+ 'invoice' => 'INV-AFTERPAY-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-AFTERPAY-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_pays_remainder_amount(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_AFTERPAY_PARTIAL_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'PayRemainder successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpay',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AFTERPAY-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpay')->payRemainder([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-AFTERPAY-REMAINDER-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-AFTERPAY-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_processes_payment_with_multiple_articles(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AFTERPAY-MULTI-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 375.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpay')->pay([
+ 'amountDebit' => 375.00,
+ 'invoice' => 'INV-AFTERPAY-MULTI-001',
+ 'currency' => 'EUR',
+ 'articles' => [
+ [
+ 'identifier' => 'PROD-200',
+ 'description' => 'Smartphone',
+ 'quantity' => 1,
+ 'price' => 250.00,
+ 'vatPercentage' => 21,
+ ],
+ [
+ 'identifier' => 'PROD-201',
+ 'description' => 'Phone Case',
+ 'quantity' => 2,
+ 'price' => 50.00,
+ 'vatPercentage' => 21,
+ ],
+ [
+ 'identifier' => 'PROD-202',
+ 'description' => 'Screen Protector',
+ 'quantity' => 1,
+ 'price' => 25.00,
+ 'vatPercentage' => 21,
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-AFTERPAY-MULTI-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(375.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_processes_payment_with_separate_shipping_address(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AFTERPAY-SHIPPING-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 200.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpay')->pay([
+ 'amountDebit' => 200.00,
+ 'invoice' => 'INV-AFTERPAY-SHIPPING-001',
+ 'currency' => 'EUR',
+ 'billing' => [
+ 'recipient' => [
+ 'category' => 'Person',
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ 'address' => [
+ 'street' => 'Billing Street',
+ 'houseNumber' => '10',
+ 'zipcode' => '1000AA',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'email' => 'john.doe@example.com',
+ ],
+ 'shipping' => [
+ 'recipient' => [
+ 'category' => 'Person',
+ 'firstName' => 'Jane',
+ 'lastName' => 'Doe',
+ ],
+ 'address' => [
+ 'street' => 'Shipping Street',
+ 'houseNumber' => '20',
+ 'zipcode' => '2000BB',
+ 'city' => 'Rotterdam',
+ 'country' => 'NL',
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-AFTERPAY-SHIPPING-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(200.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'afterpay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AFTERPAY-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('afterpay')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-AFTERPAY-STATUS-001',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'waiting_on_user_input' => [790, 'isWaitingOnUserInput'],
+ 'pending_processing' => [791, 'isPendingProcessing'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/AlipayTest.php b/tests/Feature/PaymentMethods/AlipayTest.php
new file mode 100644
index 00000000..6a72bf0d
--- /dev/null
+++ b/tests/Feature/PaymentMethods/AlipayTest.php
@@ -0,0 +1,216 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Alipay'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'alipay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-ALIPAY-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 45.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('alipay')->pay([
+ 'amountDebit' => 45.00,
+ 'invoice' => 'INV-ALIPAY-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-ALIPAY-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(45.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_an_alipay_payment_with_mobile_view(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://mobile.alipay.com/checkout?token=' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Alipay mobile'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'alipay',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'UseMobileView', 'Value' => true],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-ALIPAY-MOBILE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 99.99,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('alipay')->pay([
+ 'amountDebit' => 99.99,
+ 'invoice' => 'INV-ALIPAY-MOBILE-001',
+ 'useMobileView' => true,
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals('INV-ALIPAY-MOBILE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(99.99, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_creates_an_alipay_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://www.alipay.com/checkout?token=' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Alipay'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'alipay',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 30.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('alipay')->payRemainder([
+ 'amountDebit' => 30.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(30.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'alipay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('alipay')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/ApplePayTest.php b/tests/Feature/PaymentMethods/ApplePayTest.php
new file mode 100644
index 00000000..49d2380e
--- /dev/null
+++ b/tests/Feature/PaymentMethods/ApplePayTest.php
@@ -0,0 +1,210 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'applepay',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'paymentData', 'Value' => 'encrypted_payment_data'],
+ ['Name' => 'customerCardName', 'Value' => 'John Doe'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-APPLEPAY-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 99.99,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('applepay')->pay([
+ 'amountDebit' => 99.99,
+ 'invoice' => 'INV-APPLEPAY-001',
+ 'paymentData' => 'encrypted_payment_data',
+ 'customerCardName' => 'John Doe',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-APPLEPAY-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(99.99, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_an_applepay_payment_with_redirect(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://checkout.buckaroo.nl/applepay/redirect?trx=' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to payment page'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'applepay',
+ 'Action' => 'PayRedirect',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-APPLEPAY-REDIRECT-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 75.50,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('applepay')->payRedirect([
+ 'amountDebit' => 75.50,
+ 'invoice' => 'INV-APPLEPAY-REDIRECT-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-APPLEPAY-REDIRECT-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(75.50, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_an_applepay_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_APPLEPAY_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'applepay',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('applepay')->refund([
+ 'amountCredit' => 25.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'applepay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('applepay')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ 'paymentData' => 'encrypted_payment_data',
+ 'customerCardName' => 'Test User',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/BancontactTest.php b/tests/Feature/PaymentMethods/BancontactTest.php
new file mode 100644
index 00000000..844e8c51
--- /dev/null
+++ b/tests/Feature/PaymentMethods/BancontactTest.php
@@ -0,0 +1,421 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Bancontact'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'bancontactmrcash',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-BANCONTACT-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('bancontact')->pay([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-BANCONTACT-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-BANCONTACT-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_encrypted_bancontact_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://bancontact.be/secure/payment?trx=' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Bancontact'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'bancontactmrcash',
+ 'Action' => 'PayEncrypted',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-ENCRYPTED-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('bancontact')->payEncrypted([
+ 'amountDebit' => 75.00,
+ 'invoice' => 'INV-ENCRYPTED-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals('INV-ENCRYPTED-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_creates_recurring_bancontact_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Recurring payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'bancontactmrcash',
+ 'Action' => 'PayRecurring',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-RECURRING-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 30.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('bancontact')->payRecurring([
+ 'amountDebit' => 30.00,
+ 'invoice' => 'INV-RECURRING-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-RECURRING-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(30.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_one_click_bancontact_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'One-click payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'bancontactmrcash',
+ 'Action' => 'PayOneClick',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-ONE-CLICK-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 20.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('bancontact')->payOneClick([
+ 'amountDebit' => 20.00,
+ 'invoice' => 'INV-ONE-CLICK-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-ONE-CLICK-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(20.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_authorizes_bancontact_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://bancontact.be/secure/authorize?trx=' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting for authorization'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'bancontactmrcash',
+ 'Action' => 'Authorize',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AUTHORIZE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('bancontact')->authorize([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-AUTHORIZE-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals('INV-AUTHORIZE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_captures_authorized_bancontact_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_AUTHORIZE_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Capture successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'bancontactmrcash',
+ 'Action' => 'Capture',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-CAPTURE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('bancontact')->capture([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-CAPTURE-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-CAPTURE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_cancels_authorized_bancontact_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_AUTHORIZE_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Authorization cancelled'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'bancontactmrcash',
+ 'Action' => 'CancelAuthorize',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-CANCEL-001',
+ 'Currency' => 'EUR',
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('bancontact')->cancelAuthorize([
+ 'invoice' => 'INV-CANCEL-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-CANCEL-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_bancontact_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_BANCONTACT_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'bancontactmrcash',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('bancontact')->refund([
+ 'amountCredit' => 25.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'bancontactmrcash',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('bancontact')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/BankTransferTest.php b/tests/Feature/PaymentMethods/BankTransferTest.php
new file mode 100644
index 00000000..487dda17
--- /dev/null
+++ b/tests/Feature/PaymentMethods/BankTransferTest.php
@@ -0,0 +1,156 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'transfer',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-TRANSFER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('transfer')->pay([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-TRANSFER-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-TRANSFER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_bank_transfer_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_TRANSFER_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'transfer',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-TRANSFER-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('transfer')->refund([
+ 'amountCredit' => 50.00,
+ 'invoice' => 'INV-TRANSFER-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-TRANSFER-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'transfer',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('transfer')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/BatchTransactionsTest.php b/tests/Feature/PaymentMethods/BatchTransactionsTest.php
new file mode 100644
index 00000000..c1c1c5cb
--- /dev/null
+++ b/tests/Feature/PaymentMethods/BatchTransactionsTest.php
@@ -0,0 +1,255 @@
+buckaroo->batch([]);
+
+ $this->assertInstanceOf(BatchTransactions::class, $batch);
+ }
+
+ /** @test */
+ public function it_accepts_payment_methods(): void
+ {
+ $client = $this->buckaroo->client();
+
+ $payment = new iDeal($client, 'ideal');
+ $payment->manually(true);
+ $payment->setPayload([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-001',
+ 'issuer' => 'ABNANL2A',
+ ]);
+ $payment->pay();
+
+ $batch = $this->buckaroo->batch([$payment]);
+
+ $this->assertInstanceOf(BatchTransactions::class, $batch);
+ }
+
+ /** @test */
+ public function it_sends_to_batch_endpoint(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/batch/DataRequests', []),
+ ]);
+
+ $response = $this->buckaroo->batch([])->execute();
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ }
+
+ /** @test */
+ public function it_returns_response(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/batch/DataRequests', [
+ $this->successBatchItem('BATCH-001'),
+ ]),
+ ]);
+
+ $response = $this->executeBatchWithPayment('BATCH-001', 25.00);
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ }
+
+ /** @test */
+ public function it_executes_with_multiple_payments(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/batch/DataRequests', [
+ $this->successBatchItem('MULTI-001'),
+ $this->successBatchItem('MULTI-002'),
+ ]),
+ ]);
+
+ $client = $this->buckaroo->client();
+
+ $payment1 = $this->createIdealPayment($client, 'MULTI-001', 10.00);
+ $payment2 = $this->createIdealPayment($client, 'MULTI-002', 20.00);
+
+ $response = (new BatchTransactions($client, [$payment1, $payment2]))->execute();
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ }
+
+ /** @test */
+ public function it_executes_with_three_payments(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/batch/DataRequests', [
+ $this->successBatchItem('A'),
+ $this->successBatchItem('B'),
+ $this->successBatchItem('C'),
+ ]),
+ ]);
+
+ $client = $this->buckaroo->client();
+
+ $batch = new BatchTransactions($client, [
+ $this->createIdealPayment($client, 'A', 10.00),
+ $this->createIdealPayment($client, 'B', 20.00),
+ $this->createIdealPayment($client, 'C', 30.00),
+ ]);
+
+ $this->assertInstanceOf(TransactionResponse::class, $batch->execute());
+ }
+
+ /** @test */
+ public function it_handles_failed_transaction(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/batch/DataRequests', [
+ $this->failedBatchItem('FAILED-001'),
+ ]),
+ ]);
+
+ $response = $this->executeBatchWithPayment('FAILED-001', 10.00);
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ }
+
+ /** @test */
+ public function it_handles_partial_failure(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/batch/DataRequests', [
+ $this->successBatchItem('SUCCESS'),
+ $this->failedBatchItem('FAIL'),
+ ]),
+ ]);
+
+ $client = $this->buckaroo->client();
+
+ $batch = new BatchTransactions($client, [
+ $this->createIdealPayment($client, 'SUCCESS', 10.00),
+ $this->createIdealPayment($client, 'FAIL', 20.00),
+ ]);
+
+ $this->assertInstanceOf(TransactionResponse::class, $batch->execute());
+ }
+
+ /** @test */
+ public function it_handles_all_failed(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/batch/DataRequests', [
+ $this->failedBatchItem('FAIL-1'),
+ $this->failedBatchItem('FAIL-2'),
+ ]),
+ ]);
+
+ $client = $this->buckaroo->client();
+
+ $batch = new BatchTransactions($client, [
+ $this->createIdealPayment($client, 'FAIL-1', 10.00),
+ $this->createIdealPayment($client, 'FAIL-2', 20.00),
+ ]);
+
+ $this->assertInstanceOf(TransactionResponse::class, $batch->execute());
+ }
+
+ /** @test */
+ public function it_handles_pending_transaction(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/batch/DataRequests', [
+ $this->pendingBatchItem('PENDING-001'),
+ ]),
+ ]);
+
+ $response = $this->executeBatchWithPayment('PENDING-001', 10.00);
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ }
+
+ /** @test */
+ public function it_executes_empty_batch(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/batch/DataRequests', []),
+ ]);
+
+ $response = $this->buckaroo->batch([])->execute();
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ }
+
+ private function createIdealPayment($client, string $invoice, float $amount): iDeal
+ {
+ $payment = new iDeal($client, 'ideal');
+ $payment->manually(true);
+ $payment->setPayload([
+ 'amountDebit' => $amount,
+ 'invoice' => $invoice,
+ 'issuer' => 'ABNANL2A',
+ ]);
+ $payment->pay();
+
+ return $payment;
+ }
+
+ private function executeBatchWithPayment(string $invoice, float $amount): TransactionResponse
+ {
+ $client = $this->buckaroo->client();
+ $payment = $this->createIdealPayment($client, $invoice, $amount);
+
+ return (new BatchTransactions($client, [$payment]))->execute();
+ }
+
+ private function successBatchItem(string $invoice): array
+ {
+ return [
+ 'Key' => TestHelpers::generateTransactionKey(),
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Invoice' => $invoice,
+ ];
+ }
+
+ private function failedBatchItem(string $invoice): array
+ {
+ return [
+ 'Key' => TestHelpers::generateTransactionKey(),
+ 'Status' => [
+ 'Code' => ['Code' => 490, 'Description' => 'Failed'],
+ 'SubCode' => ['Code' => 'S991'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Invoice' => $invoice,
+ ];
+ }
+
+ private function pendingBatchItem(string $invoice): array
+ {
+ return [
+ 'Key' => TestHelpers::generateTransactionKey(),
+ 'Status' => [
+ 'Code' => ['Code' => 792, 'Description' => 'Pending'],
+ 'SubCode' => ['Code' => 'S001'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Invoice' => $invoice,
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/BelfiusTest.php b/tests/Feature/PaymentMethods/BelfiusTest.php
new file mode 100644
index 00000000..20d96e03
--- /dev/null
+++ b/tests/Feature/PaymentMethods/BelfiusTest.php
@@ -0,0 +1,209 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Belfius'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'belfius',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-BELFIUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 35.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('belfius')->pay([
+ 'amountDebit' => 35.00,
+ 'invoice' => 'INV-BELFIUS-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-BELFIUS-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(35.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_belfius_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_BELFIUS_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'belfius',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 15.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('belfius')->refund([
+ 'amountCredit' => 15.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(15.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_belfius_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://belfius.be/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Belfius'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'belfius',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('belfius')->payRemainder([
+ 'amountDebit' => 25.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'belfius',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('belfius')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/BillinkTest.php b/tests/Feature/PaymentMethods/BillinkTest.php
new file mode 100644
index 00000000..ad914546
--- /dev/null
+++ b/tests/Feature/PaymentMethods/BillinkTest.php
@@ -0,0 +1,388 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'billink',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-BILLINK-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 250.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('billink')->pay([
+ 'amountDebit' => 250.00,
+ 'invoice' => 'INV-BILLINK-001',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-BILLINK-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(250.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_pay_transaction_with_billing_data(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'billink',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-BILLINK-BILLING-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 175.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('billink')->pay([
+ 'amountDebit' => 175.00,
+ 'invoice' => 'INV-BILLINK-BILLING-001',
+ 'currency' => 'EUR',
+ 'billing' => [
+ 'recipient' => [
+ 'category' => 'B2B',
+ 'careOf' => 'Finance Department',
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ 'birthDate' => '1980-01-15',
+ ],
+ 'address' => [
+ 'street' => 'Kalverstraat',
+ 'houseNumber' => '1',
+ 'zipcode' => '1012NX',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ 'email' => 'john.doe@example.com',
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-BILLINK-BILLING-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(175.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_pay_transaction_with_separate_shipping_address(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'billink',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-BILLINK-SHIPPING-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 199.99,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('billink')->pay([
+ 'amountDebit' => 199.99,
+ 'invoice' => 'INV-BILLINK-SHIPPING-001',
+ 'currency' => 'EUR',
+ 'billing' => [
+ 'recipient' => [
+ 'category' => 'B2C',
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ ],
+ 'address' => [
+ 'street' => 'Billing Street',
+ 'houseNumber' => '10',
+ 'zipcode' => '1000AA',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'email' => 'jane.smith@example.com',
+ ],
+ 'shipping' => [
+ 'recipient' => [
+ 'category' => 'B2C',
+ 'firstName' => 'John',
+ 'lastName' => 'Smith',
+ ],
+ 'address' => [
+ 'street' => 'Shipping Street',
+ 'houseNumber' => '20',
+ 'zipcode' => '2000BB',
+ 'city' => 'Rotterdam',
+ 'country' => 'NL',
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-BILLINK-SHIPPING-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(199.99, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_pay_transaction_with_articles(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'billink',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-BILLINK-ARTICLES-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 375.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('billink')->pay([
+ 'amountDebit' => 375.00,
+ 'invoice' => 'INV-BILLINK-ARTICLES-001',
+ 'currency' => 'EUR',
+ 'articles' => [
+ [
+ 'identifier' => 'PROD-100',
+ 'description' => 'Office Chair',
+ 'quantity' => 2,
+ 'price' => 150.00,
+ 'vatPercentage' => 21,
+ ],
+ [
+ 'identifier' => 'PROD-101',
+ 'description' => 'Desk Lamp',
+ 'quantity' => 1,
+ 'price' => 75.00,
+ 'vatPercentage' => 21,
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-BILLINK-ARTICLES-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(375.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_pays_remainder_amount(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_BILLINK_PARTIAL_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'PayRemainder successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'billink',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-BILLINK-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('billink')->payRemainder([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-BILLINK-REMAINDER-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-BILLINK-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_refunds_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_BILLINK_PAY_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'billink',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-BILLINK-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('billink')->refund([
+ 'amountCredit' => 75.00,
+ 'invoice' => 'INV-BILLINK-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-BILLINK-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'billink',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-BILLINK-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('billink')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-BILLINK-STATUS-001',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'waiting_on_user_input' => [790, 'isWaitingOnUserInput'],
+ 'pending_processing' => [791, 'isPendingProcessing'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/BizumTest.php b/tests/Feature/PaymentMethods/BizumTest.php
new file mode 100644
index 00000000..3849771a
--- /dev/null
+++ b/tests/Feature/PaymentMethods/BizumTest.php
@@ -0,0 +1,209 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Bizum'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Bizum',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-BIZUM-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('bizum')->pay([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-BIZUM-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-BIZUM-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_bizum_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_BIZUM_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Bizum',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('bizum')->refund([
+ 'amountCredit' => 25.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_bizum_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://bizum.es/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Bizum'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Bizum',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 30.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('bizum')->payRemainder([
+ 'amountDebit' => 30.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(30.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Bizum',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('bizum')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/BlikTest.php b/tests/Feature/PaymentMethods/BlikTest.php
new file mode 100644
index 00000000..5cbbec9e
--- /dev/null
+++ b/tests/Feature/PaymentMethods/BlikTest.php
@@ -0,0 +1,259 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Blik'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Blik',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-BLIK-001',
+ 'Currency' => 'PLN',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('blik')->pay([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-BLIK-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-BLIK-001', $response->getInvoice());
+ $this->assertEquals('PLN', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_payment_with_email(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://blik.pl/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Blik'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Blik',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'CustomerEmail', 'Value' => 'jan.kowalski@example.pl'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-BLIK-EMAIL-001',
+ 'Currency' => 'PLN',
+ 'AmountDebit' => 150.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('blik')->pay([
+ 'amountDebit' => 150.00,
+ 'invoice' => 'INV-BLIK-EMAIL-001',
+ 'email' => 'jan.kowalski@example.pl',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-BLIK-EMAIL-001', $response->getInvoice());
+ $this->assertEquals('PLN', $response->getCurrency());
+ $this->assertEquals(150.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /** @test */
+ public function it_creates_a_blik_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_BLIK_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Blik',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'PLN',
+ 'AmountCredit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('blik')->refund([
+ 'amountCredit' => 50.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('PLN', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_blik_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://blik.pl/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Blik'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Blik',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'PLN',
+ 'AmountDebit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('blik')->payRemainder([
+ 'amountDebit' => 75.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('PLN', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Blik',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'PLN',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('blik')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/BuckarooVoucherTest.php b/tests/Feature/PaymentMethods/BuckarooVoucherTest.php
new file mode 100644
index 00000000..ef1a445f
--- /dev/null
+++ b/tests/Feature/PaymentMethods/BuckarooVoucherTest.php
@@ -0,0 +1,290 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'buckaroovoucher',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'vouchercode', 'Value' => 'VOUCHER-123456'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-VOUCHER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroovoucher')->pay([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-VOUCHER-001',
+ 'vouchercode' => 'VOUCHER-123456',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-VOUCHER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_voucher_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'buckaroovoucher',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [
+ ['Name' => 'vouchercode', 'Value' => 'VOUCHER-123456'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroovoucher')->payRemainder([
+ 'amountDebit' => 25.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ 'vouchercode' => 'VOUCHER-123456',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_retrieves_voucher_balance(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Balance retrieved'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'buckaroovoucher',
+ 'Action' => 'GetBalance',
+ 'Parameters' => [
+ ['Name' => 'vouchercode', 'Value' => 'VOUCHER-123456'],
+ ['Name' => 'balance', 'Value' => '75.00'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-BALANCE-001',
+ 'Currency' => 'EUR',
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroovoucher')->getBalance([
+ 'vouchercode' => 'VOUCHER-123456',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-BALANCE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_new_voucher(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Voucher created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'buckaroovoucher',
+ 'Action' => 'CreateApplication',
+ 'Parameters' => [
+ ['Name' => 'groupReference', 'Value' => 'GROUP-001'],
+ ['Name' => 'usageType', 'Value' => 'SINGLE'],
+ ['Name' => 'validFrom', 'Value' => '2026-01-01'],
+ ['Name' => 'validUntil', 'Value' => '2026-12-31'],
+ ['Name' => 'creationBalance', 'Value' => '100.00'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-CREATE-001',
+ 'Currency' => 'EUR',
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroovoucher')->create([
+ 'groupReference' => 'GROUP-001',
+ 'usageType' => 'SINGLE',
+ 'validFrom' => '2026-01-01',
+ 'validUntil' => '2026-12-31',
+ 'creationBalance' => '100.00',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-CREATE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_deactivates_a_voucher(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Voucher deactivated'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'buckaroovoucher',
+ 'Action' => 'DeactivateVoucher',
+ 'Parameters' => [
+ ['Name' => 'vouchercode', 'Value' => 'VOUCHER-123456'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-DEACTIVATE-001',
+ 'Currency' => 'EUR',
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroovoucher')->deactivate([
+ 'vouchercode' => 'VOUCHER-123456',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-DEACTIVATE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'buckaroovoucher',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroovoucher')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ 'vouchercode' => 'VOUCHER-123456',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/BuckarooWalletTest.php b/tests/Feature/PaymentMethods/BuckarooWalletTest.php
new file mode 100644
index 00000000..43044b83
--- /dev/null
+++ b/tests/Feature/PaymentMethods/BuckarooWalletTest.php
@@ -0,0 +1,400 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Wallet created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'BuckarooWalletCollecting',
+ 'Action' => 'Create',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroo_wallet')->createWallet([
+ 'currency' => 'EUR',
+ 'customer' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_updates_wallet(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S002', 'Description' => 'Wallet updated'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'BuckarooWalletCollecting',
+ 'Action' => 'Update',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroo_wallet')->updateWallet([
+ 'walletId' => 'WALLET-123',
+ 'customer' => [
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_retrieves_wallet_info(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S003', 'Description' => 'Wallet info retrieved'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'BuckarooWalletCollecting',
+ 'Action' => 'GetInfo',
+ 'Parameters' => [
+ ['Name' => 'WalletId', 'Value' => 'WALLET-INFO-123'],
+ ['Name' => 'Status', 'Value' => 'Active'],
+ ],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroo_wallet')->getInfo([
+ 'walletId' => 'WALLET-INFO-123',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+
+ $params = $response->getServiceParameters();
+ $this->assertEquals('WALLET-INFO-123', $params['walletid']);
+ $this->assertEquals('Active', $params['status']);
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_releases_wallet_funds(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S004', 'Description' => 'Funds released'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'BuckarooWalletCollecting',
+ 'Action' => 'Release',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroo_wallet')->release([
+ 'amountCredit' => 25.50,
+ 'walletId' => 'WALLET-RELEASE-123',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_deposits_to_wallet(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S005', 'Description' => 'Deposit successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'BuckarooWalletCollecting',
+ 'Action' => 'Deposit',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ 'Invoice' => 'INV-DEPOSIT-123',
+ 'AmountCredit' => 50.00,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroo_wallet')->deposit([
+ 'invoice' => 'INV-DEPOSIT-123',
+ 'amountCredit' => 50.00,
+ 'walletId' => 'WALLET-DEPOSIT-123',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-DEPOSIT-123', $response->getInvoice());
+ $this->assertEquals(50.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_reserves_wallet_funds(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S006', 'Description' => 'Funds reserved'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'BuckarooWalletCollecting',
+ 'Action' => 'Reserve',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ 'Invoice' => 'INV-RESERVE-123',
+ 'AmountCredit' => 75.00,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroo_wallet')->reserve([
+ 'invoice' => 'INV-RESERVE-123',
+ 'amountCredit' => 75.00,
+ 'walletId' => 'WALLET-RESERVE-123',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-RESERVE-123', $response->getInvoice());
+ $this->assertEquals(75.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_withdraws_from_wallet(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S007', 'Description' => 'Withdrawal successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'BuckarooWalletCollecting',
+ 'Action' => 'Withdrawal',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroo_wallet')->withdrawal([
+ 'walletId' => 'WALLET-WITHDRAW-123',
+ 'bankAccount' => [
+ 'iban' => 'NL13TEST0123456789',
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_cancels_wallet_transaction(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S008', 'Description' => 'Transaction cancelled'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'BuckarooWalletCollecting',
+ 'Action' => 'Cancel',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroo_wallet')->cancel([
+ 'walletId' => 'WALLET-CANCEL-123',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_processes_wallet_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S009', 'Description' => 'Payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'BuckarooWalletCollecting',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ 'Invoice' => 'INV-PAY-123',
+ 'AmountDebit' => 100.00,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroo_wallet')->pay([
+ 'invoice' => 'INV-PAY-123',
+ 'amountDebit' => 100.00,
+ 'walletId' => 'WALLET-PAY-123',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-PAY-123', $response->getInvoice());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'BuckarooWalletCollecting',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ 'Invoice' => 'INV-STATUS-TEST',
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroo_wallet')->pay([
+ 'invoice' => 'INV-STATUS-TEST',
+ 'amountDebit' => 10.00,
+ 'walletId' => 'WALLET-STATUS-TEST',
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'waiting_on_user_input' => [790, 'isWaitingOnUserInput'],
+ 'pending_processing' => [791, 'isPendingProcessing'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/ClickToPayTest.php b/tests/Feature/PaymentMethods/ClickToPayTest.php
new file mode 100644
index 00000000..612cf52a
--- /dev/null
+++ b/tests/Feature/PaymentMethods/ClickToPayTest.php
@@ -0,0 +1,156 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'ClickToPay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-CTP-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 30.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('clicktopay')->pay([
+ 'amountDebit' => 30.00,
+ 'invoice' => 'INV-CTP-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-CTP-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(30.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_clicktopay_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_CTP_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'ClickToPay',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-CTP-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 15.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('clicktopay')->refund([
+ 'amountCredit' => 15.00,
+ 'invoice' => 'INV-CTP-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-CTP-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(15.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'ClickToPay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('clicktopay')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/CreditCardTest.php b/tests/Feature/PaymentMethods/CreditCardTest.php
new file mode 100644
index 00000000..5fb4bf47
--- /dev/null
+++ b/tests/Feature/PaymentMethods/CreditCardTest.php
@@ -0,0 +1,641 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 790, 'Description' => 'Waiting on user input'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => '3D Secure required'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'visa',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-123456',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 100.30,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->pay([
+ 'amountDebit' => 100.30,
+ 'invoice' => 'INV-123456',
+ 'currency' => 'EUR',
+ 'name' => 'visa',
+ ]);
+
+ $this->assertTrue($response->isWaitingOnUserInput());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-123456', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.30, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_encrypted_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S002', 'Description' => 'Processing'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'mastercard',
+ 'Action' => 'PayEncrypted',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-123456',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->payEncrypted([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-123456',
+ 'currency' => 'EUR',
+ 'name' => 'mastercard',
+ 'encryptedCardData' => 'encrypted_data_here',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertFalse($response->hasRedirect());
+ $this->assertEquals('INV-123456', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_creates_payment_with_token(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'mastercard',
+ 'Action' => 'PayWithToken',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-TOKEN-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 45.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->payWithToken([
+ 'amountDebit' => 45.00,
+ 'invoice' => 'INV-TOKEN-001',
+ 'currency' => 'EUR',
+ 'name' => 'mastercard',
+ 'sessionId' => 'SESSION_TOKEN_123',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-TOKEN-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(45.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_creates_payment_with_security_code(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S002', 'Description' => 'Processing'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'mastercard',
+ 'Action' => 'PayWithSecurityCode',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-SEC-CODE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->payWithSecurityCode([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-SEC-CODE-001',
+ 'currency' => 'EUR',
+ 'name' => 'mastercard',
+ 'originalTransactionKey' => 'ORIG_TX_KEY',
+ 'encryptedSecurityCode' => 'encrypted_cvv_here',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertEquals('INV-SEC-CODE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_creates_recurrent_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_TX_KEY_FOR_RECURRENT';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Recurrent payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'mastercard',
+ 'Action' => 'PayRecurrent',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-RECURRENT-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 29.99,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->payRecurrent([
+ 'amountDebit' => 29.99,
+ 'invoice' => 'INV-RECURRENT-001',
+ 'currency' => 'EUR',
+ 'name' => 'mastercard',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-RECURRENT-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(29.99, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_creates_authorize(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://checkout.buckaroo.nl/redirect/3DSAuth/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 790, 'Description' => 'Waiting on user input'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => '3D Secure required'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'mastercard',
+ 'Action' => 'Authorize',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AUTH-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 200.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->authorize([
+ 'amountDebit' => 200.00,
+ 'invoice' => 'INV-AUTH-001',
+ 'currency' => 'EUR',
+ 'name' => 'mastercard',
+ ]);
+
+ $this->assertTrue($response->isWaitingOnUserInput());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals('INV-AUTH-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(200.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /** @test */
+ public function it_creates_encrypted_authorize(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S002', 'Description' => 'Processing'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'mastercard',
+ 'Action' => 'AuthorizeEncrypted',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AUTH-ENC-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->authorizeEncrypted([
+ 'amountDebit' => 75.00,
+ 'invoice' => 'INV-AUTH-ENC-001',
+ 'currency' => 'EUR',
+ 'name' => 'mastercard',
+ 'encryptedCardData' => 'encrypted_card_data_here',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertEquals('INV-AUTH-ENC-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_captures_authorized_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_AUTH_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'mastercard',
+ 'Action' => 'Capture',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-CAPTURE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 150.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->capture([
+ 'amountDebit' => 150.00,
+ 'invoice' => 'INV-CAPTURE-001',
+ 'currency' => 'EUR',
+ 'name' => 'mastercard',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-CAPTURE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(150.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_refunds_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_PAY_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'mastercard',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->refund([
+ 'amountCredit' => 25.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'name' => 'mastercard',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_cancels_authorized_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_AUTH_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Cancel pending'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'mastercard',
+ 'Action' => 'CancelAuthorize',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-CANCEL-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->cancelAuthorize([
+ 'amountCredit' => 100.00,
+ 'invoice' => 'INV-CANCEL-001',
+ 'name' => 'mastercard',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertEquals('INV-CANCEL-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_extracts_service_parameters_from_response(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'visa',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'CardNumberEnding', 'Value' => '1234'],
+ ['Name' => 'CardExpirationDate', 'Value' => '2025-12'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-PARAMS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->pay([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-PARAMS-001',
+ 'currency' => 'EUR',
+ 'name' => 'visa',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $params = $response->getServiceParameters();
+ $this->assertEquals('1234', $params['cardnumberending']);
+ $this->assertEquals('2025-12', $params['cardexpirationdate']);
+ $this->assertEquals('INV-PARAMS-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_throws_exception_for_missing_card_name(): void
+ {
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage('Missing creditcard name');
+
+ $this->buckaroo->method('creditcard')->pay([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-001',
+ 'currency' => 'EUR',
+ ]);
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'visa',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ 'currency' => 'EUR',
+ 'name' => 'visa',
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'waiting_on_user_input' => [790, 'isWaitingOnUserInput'],
+ 'pending_processing' => [791, 'isPendingProcessing'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+
+ /**
+ * @test
+ * @dataProvider cardTypeProvider
+ */
+ public function it_works_with_different_card_types(string $cardType): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = "https://checkout.buckaroo.nl/redirect/3DSAuth/{$transactionKey}";
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 790, 'Description' => 'Waiting on user input'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => '3D Secure required'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => $cardType,
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-CARD-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-CARD-001',
+ 'currency' => 'EUR',
+ 'name' => $cardType,
+ ]);
+
+ $this->assertTrue($response->isWaitingOnUserInput());
+ $this->assertTrue($response->hasRedirect());
+ }
+
+ public static function cardTypeProvider(): array
+ {
+ return [
+ ['visa'],
+ ['mastercard'],
+ ['amex'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/CreditManagementTest.php b/tests/Feature/PaymentMethods/CreditManagementTest.php
new file mode 100644
index 00000000..35b60793
--- /dev/null
+++ b/tests/Feature/PaymentMethods/CreditManagementTest.php
@@ -0,0 +1,566 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'CM01', 'Description' => 'Invoice created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'CreateInvoice',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => $invoiceNumber,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->createInvoice([
+ 'invoice' => $invoiceNumber,
+ 'invoiceDate' => '2026-01-14',
+ 'dueDate' => '2026-02-14',
+ 'debtor' => [
+ 'code' => 'DEBTOR-001',
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($invoiceNumber, $response->getInvoice());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_combined_invoice(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $invoiceNumber = 'INV-CMB-' . TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'CM02', 'Description' => 'Combined invoice created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'CreateCombinedInvoice',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => $invoiceNumber,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->createCombinedInvoice([
+ 'invoice' => $invoiceNumber,
+ 'invoiceDate' => '2026-01-14',
+ 'debtor' => [
+ 'code' => 'DEBTOR-COMBINED',
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($invoiceNumber, $response->getInvoice());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_credit_note(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $creditNoteNumber = 'CN-' . TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'CM03', 'Description' => 'Credit note created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'CreateCreditNote',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => $creditNoteNumber,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->createCreditNote([
+ 'invoice' => $creditNoteNumber,
+ 'originalInvoiceNumber' => 'INV-ORIGINAL-001',
+ 'debtor' => [
+ 'code' => 'DEBTOR-CN',
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($creditNoteNumber, $response->getInvoice());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_adds_or_updates_debtor(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'CM04', 'Description' => 'Debtor updated'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'AddOrUpdateDebtor',
+ 'Parameters' => [
+ ['Name' => 'Code', 'Value' => 'DEBTOR-UPDATE-001'],
+ ],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->addOrUpdateDebtor([
+ 'code' => 'DEBTOR-UPDATE-001',
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ 'email' => 'john.doe@example.com',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+
+ $params = $response->getServiceParameters();
+ $this->assertEquals('DEBTOR-UPDATE-001', $params['code']);
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_payment_plan(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'CM05', 'Description' => 'Payment plan created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'CreatePaymentPlan',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->createPaymentPlan([
+ 'paymentPlanGuid' => 'PLAN-GUID-' . TestHelpers::generateTransactionKey(),
+ 'description' => 'Monthly payment plan',
+ 'numberOfInstallments' => 12,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_terminates_payment_plan(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $planGuid = 'PLAN-GUID-TERMINATE-' . TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'CM06', 'Description' => 'Payment plan terminated'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'TerminatePaymentPlan',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->terminatePaymentPlan([
+ 'paymentPlanGuid' => $planGuid,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_pauses_invoice(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $invoiceNumber = 'INV-PAUSE-001';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'CM07', 'Description' => 'Invoice paused'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'PauseInvoice',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => $invoiceNumber,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->pauseInvoice([
+ 'invoice' => $invoiceNumber,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($invoiceNumber, $response->getInvoice());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_unpauses_invoice(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $invoiceNumber = 'INV-UNPAUSE-001';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'CM08', 'Description' => 'Invoice unpaused'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'UnPauseInvoice',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => $invoiceNumber,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->unpauseInvoice([
+ 'invoice' => $invoiceNumber,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($invoiceNumber, $response->getInvoice());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_retrieves_invoice_info(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $invoiceNumber = 'INV-INFO-001';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'CM09', 'Description' => 'Invoice info retrieved'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'InvoiceInfo',
+ 'Parameters' => [
+ ['Name' => 'InvoiceNumber', 'Value' => $invoiceNumber],
+ ['Name' => 'Status', 'Value' => 'Open'],
+ ['Name' => 'Amount', 'Value' => '150.00'],
+ ],
+ ],
+ ],
+ 'Invoice' => $invoiceNumber,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->invoiceInfo([
+ 'invoice' => $invoiceNumber,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($invoiceNumber, $response->getInvoice());
+
+ $params = $response->getServiceParameters();
+ $this->assertEquals($invoiceNumber, $params['invoicenumber']);
+ $this->assertEquals('Open', $params['status']);
+ $this->assertEquals('150.00', $params['amount']);
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_retrieves_debtor_info(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'CM10', 'Description' => 'Debtor info retrieved'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'DebtorInfo',
+ 'Parameters' => [
+ ['Name' => 'Code', 'Value' => 'DEBTOR-INFO-001'],
+ ['Name' => 'FirstName', 'Value' => 'Jane'],
+ ['Name' => 'LastName', 'Value' => 'Smith'],
+ ['Name' => 'Email', 'Value' => 'jane.smith@example.com'],
+ ],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->debtorInfo([
+ 'code' => 'DEBTOR-INFO-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+
+ $params = $response->getServiceParameters();
+ $this->assertEquals('DEBTOR-INFO-001', $params['code']);
+ $this->assertEquals('Jane', $params['firstname']);
+ $this->assertEquals('Smith', $params['lastname']);
+ $this->assertEquals('jane.smith@example.com', $params['email']);
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_adds_or_updates_product_lines(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'CM11', 'Description' => 'Product lines updated'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'AddOrUpdateProductLines',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->addOrUpdateProductLines([
+ 'invoice' => 'INV-PRODUCTS-001',
+ 'articles' => [
+ [
+ 'identifier' => 'PRODUCT-001',
+ 'description' => 'Test Product',
+ 'quantity' => 2,
+ 'price' => 50.00,
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_resumes_debtor_file(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'CM12', 'Description' => 'Debtor file resumed'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'ResumeDebtorFile',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->resumeDebtorFile([
+ 'debtorCode' => 'DEBTOR-RESUME-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_pauses_debtor_file(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'CM13', 'Description' => 'Debtor file paused'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'PauseDebtorFile',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->pauseDebtorFile([
+ 'debtorCode' => 'DEBTOR-PAUSE-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'CM01', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'CreditManagement3',
+ 'Action' => 'CreateInvoice',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('credit_management')->createInvoice([
+ 'invoice' => 'INV-STATUS-TEST',
+ 'invoiceDate' => '2026-01-14',
+ 'debtor' => [
+ 'code' => 'DEBTOR-STATUS',
+ ],
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'waiting_on_user_input' => [790, 'isWaitingOnUserInput'],
+ 'pending_processing' => [791, 'isPendingProcessing'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/EPSTest.php b/tests/Feature/PaymentMethods/EPSTest.php
new file mode 100644
index 00000000..4d7ff802
--- /dev/null
+++ b/tests/Feature/PaymentMethods/EPSTest.php
@@ -0,0 +1,209 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to EPS'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'eps',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-EPS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('eps')->pay([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-EPS-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-EPS-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_an_eps_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_EPS_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'eps',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('eps')->refund([
+ 'amountCredit' => 25.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_creates_an_eps_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://eps.at/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to EPS'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'eps',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 30.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('eps')->payRemainder([
+ 'amountDebit' => 30.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(30.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'eps',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('eps')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/EmandatesTest.php b/tests/Feature/PaymentMethods/EmandatesTest.php
new file mode 100644
index 00000000..c4fe5ec7
--- /dev/null
+++ b/tests/Feature/PaymentMethods/EmandatesTest.php
@@ -0,0 +1,269 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Issuer list retrieved'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'emandate',
+ 'Action' => 'GetIssuerList',
+ 'Parameters' => [
+ ['Name' => 'Issuer', 'Value' => 'ABNANL2A'],
+ ['Name' => 'IssuerName', 'Value' => 'ABN AMRO'],
+ ],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('emandates')->issuerList();
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+
+ $params = $response->getServiceParameters();
+ $this->assertEquals('ABNANL2A', $params['issuer']);
+ $this->assertEquals('ABN AMRO', $params['issuername']);
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_mandate(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S002', 'Description' => 'Mandate created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'emandate',
+ 'Action' => 'CreateMandate',
+ 'Parameters' => [
+ ['Name' => 'MandateId', 'Value' => 'MANDATE-123'],
+ ['Name' => 'DebtorReference', 'Value' => 'DEBTOR-REF-456'],
+ ],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('emandates')->createMandate([
+ 'currency' => 'EUR',
+ 'debtorbankid' => 'ABNANL2A',
+ 'debtorreference' => 'DEBTOR-REF-456',
+ 'sequencetype' => 1,
+ 'purchaseid' => 'PURCHASE-789',
+ 'language' => 'NL',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+
+ $params = $response->getServiceParameters();
+ $this->assertEquals('MANDATE-123', $params['mandateid']);
+ $this->assertEquals('DEBTOR-REF-456', $params['debtorreference']);
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_retrieves_mandate_status(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S003', 'Description' => 'Status retrieved'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'emandate',
+ 'Action' => 'GetStatus',
+ 'Parameters' => [
+ ['Name' => 'MandateId', 'Value' => 'MANDATE-STATUS-123'],
+ ['Name' => 'Status', 'Value' => 'Active'],
+ ],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('emandates')->status([
+ 'currency' => 'EUR',
+ 'mandateid' => 'MANDATE-STATUS-123',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+
+ $params = $response->getServiceParameters();
+ $this->assertEquals('MANDATE-STATUS-123', $params['mandateid']);
+ $this->assertEquals('Active', $params['status']);
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_modifies_mandate(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S004', 'Description' => 'Mandate modified'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'emandate',
+ 'Action' => 'ModifyMandate',
+ 'Parameters' => [
+ ['Name' => 'MandateId', 'Value' => 'MANDATE-MODIFY-789'],
+ ['Name' => 'MaxAmount', 'Value' => '500.00'],
+ ],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('emandates')->modifyMandate([
+ 'currency' => 'EUR',
+ 'mandateid' => 'MANDATE-MODIFY-789',
+ 'maxamount' => 500.00,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+
+ $params = $response->getServiceParameters();
+ $this->assertEquals('MANDATE-MODIFY-789', $params['mandateid']);
+ $this->assertEquals('500.00', $params['maxamount']);
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_cancels_mandate(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S005', 'Description' => 'Mandate cancelled'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'emandate',
+ 'Action' => 'CancelMandate',
+ 'Parameters' => [
+ ['Name' => 'MandateId', 'Value' => 'MANDATE-CANCEL-456'],
+ ],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('emandates')->cancelMandate([
+ 'currency' => 'EUR',
+ 'mandateid' => 'MANDATE-CANCEL-456',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+
+ $params = $response->getServiceParameters();
+ $this->assertEquals('MANDATE-CANCEL-456', $params['mandateid']);
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'emandate',
+ 'Action' => 'CreateMandate',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('emandates')->createMandate([
+ 'currency' => 'EUR',
+ 'debtorbankid' => 'ABNANL2A',
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'waiting_on_user_input' => [790, 'isWaitingOnUserInput'],
+ 'pending_processing' => [791, 'isPendingProcessing'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/ExternalPaymentTest.php b/tests/Feature/PaymentMethods/ExternalPaymentTest.php
new file mode 100644
index 00000000..ee9a8d7b
--- /dev/null
+++ b/tests/Feature/PaymentMethods/ExternalPaymentTest.php
@@ -0,0 +1,189 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'ExternalPayment',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-EXT-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('externalpayment')->pay([
+ 'amountDebit' => 25.00,
+ 'invoice' => 'INV-EXT-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-EXT-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_an_external_payment_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_EXT_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'ExternalPayment',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-EXT-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('externalpayment')->refund([
+ 'amountCredit' => 10.00,
+ 'invoice' => 'INV-EXT-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-EXT-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(10.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_an_external_payment_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'ExternalPayment',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-EXT-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 15.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('externalpayment')->payRemainder([
+ 'amountDebit' => 15.00,
+ 'invoice' => 'INV-EXT-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-EXT-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals(15.00, $response->getAmountDebit());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'ExternalPayment',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-EXT-STATUS',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('externalpayment')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-EXT-STATUS',
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/GiftCardTest.php b/tests/Feature/PaymentMethods/GiftCardTest.php
new file mode 100644
index 00000000..5b6bfc3b
--- /dev/null
+++ b/tests/Feature/PaymentMethods/GiftCardTest.php
@@ -0,0 +1,300 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'giftcard',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'cardNumber', 'Value' => '1234567890123456'],
+ ['Name' => 'pin', 'Value' => '1234'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-GIFTCARD-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('giftcard')->pay([
+ 'amountDebit' => 25.00,
+ 'invoice' => 'INV-GIFTCARD-001',
+ 'cardNumber' => '1234567890123456',
+ 'pin' => '1234',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-GIFTCARD-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_giftcard_payment_with_specific_card_name(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'fashioncheque',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'fashionChequeCardNumber', 'Value' => '1234567890123456'],
+ ['Name' => 'fashionChequePin', 'Value' => '1234'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-FASHION-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('giftcard')->pay([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-FASHION-001',
+ 'name' => 'fashioncheque',
+ 'fashionChequeCardNumber' => '1234567890123456',
+ 'fashionChequePin' => '1234',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-FASHION-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_giftcard_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_GIFTCARD_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'giftcard',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('giftcard')->refund([
+ 'amountCredit' => 10.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(10.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_giftcard_payment_with_redirect(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://giftcard.redirect.com/pay?tx=' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to giftcard portal'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'giftcard',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REDIRECT-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 30.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('giftcard')->payRedirect([
+ 'amountDebit' => 30.00,
+ 'invoice' => 'INV-REDIRECT-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REDIRECT-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(30.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_giftcard_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'giftcard',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 15.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('giftcard')->payRemainder([
+ 'amountDebit' => 15.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ 'cardNumber' => '1234567890123456',
+ 'pin' => '1234',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(15.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'giftcard',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('giftcard')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ 'cardNumber' => '1234567890123456',
+ 'pin' => '1234',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/GooglePayTest.php b/tests/Feature/PaymentMethods/GooglePayTest.php
new file mode 100644
index 00000000..d1c2515a
--- /dev/null
+++ b/tests/Feature/PaymentMethods/GooglePayTest.php
@@ -0,0 +1,209 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'googlepay',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'paymentData', 'Value' => $paymentData],
+ ['Name' => 'customerCardName', 'Value' => 'John Doe'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-GOOGLEPAY-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('googlepay')->pay([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-GOOGLEPAY-001',
+ 'paymentData' => $paymentData,
+ 'customerCardName' => 'John Doe',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-GOOGLEPAY-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_googlepay_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_GOOGLEPAY_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'googlepay',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('googlepay')->refund([
+ 'amountCredit' => 25.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_googlepay_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $paymentData = '{"signature":"test","protocolVersion":"ECv1","signedMessage":"test_message"}';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'googlepay',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('googlepay')->payRemainder([
+ 'amountDebit' => 75.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ 'paymentData' => $paymentData,
+ 'customerCardName' => 'John Doe',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $paymentData = '{"signature":"test","protocolVersion":"ECv1","signedMessage":"test_message"}';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'googlepay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('googlepay')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ 'paymentData' => $paymentData,
+ 'customerCardName' => 'John Doe',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/In3OldTest.php b/tests/Feature/PaymentMethods/In3OldTest.php
new file mode 100644
index 00000000..80faf41e
--- /dev/null
+++ b/tests/Feature/PaymentMethods/In3OldTest.php
@@ -0,0 +1,348 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Capayable'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Capayable',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IN3OLD-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('in3old')->pay([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-IN3OLD-001',
+ 'billing' => [
+ 'recipient' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ 'birthDate' => '1990-01-01',
+ ],
+ 'address' => [
+ 'street' => 'Test Street',
+ 'houseNumber' => '1',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ 'email' => 'john.doe@example.com',
+ ],
+ 'articles' => [
+ [
+ 'identifier' => 'ART001',
+ 'description' => 'Test Article',
+ 'quantity' => 1,
+ 'price' => 100.00,
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IN3OLD-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_an_in3old_pay_in_installments(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://capayable.nl/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Capayable'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Capayable',
+ 'Action' => 'PayInInstallments',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IN3OLD-INSTALL-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 500.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('in3old')->payInInstallments([
+ 'amountDebit' => 500.00,
+ 'invoice' => 'INV-IN3OLD-INSTALL-001',
+ 'billing' => [
+ 'recipient' => [
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ 'birthDate' => '1985-06-15',
+ ],
+ 'address' => [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'zipcode' => '5678CD',
+ 'city' => 'Rotterdam',
+ 'country' => 'NL',
+ ],
+ 'phone' => [
+ 'mobile' => '0698765432',
+ ],
+ 'email' => 'jane.smith@example.com',
+ ],
+ 'articles' => [
+ [
+ 'identifier' => 'ART002',
+ 'description' => 'Expensive Item',
+ 'quantity' => 1,
+ 'price' => 500.00,
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IN3OLD-INSTALL-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(500.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_an_in3old_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_IN3OLD_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Capayable',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IN3OLD-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('in3old')->refund([
+ 'amountCredit' => 50.00,
+ 'invoice' => 'INV-IN3OLD-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IN3OLD-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_payment_with_company_data(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://capayable.nl/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Capayable'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Capayable',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IN3OLD-COMPANY-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 1000.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('in3old')->pay([
+ 'amountDebit' => 1000.00,
+ 'invoice' => 'INV-IN3OLD-COMPANY-001',
+ 'billing' => [
+ 'recipient' => [
+ 'firstName' => 'Business',
+ 'lastName' => 'Owner',
+ ],
+ 'address' => [
+ 'street' => 'Business Street',
+ 'houseNumber' => '100',
+ 'zipcode' => '1000AA',
+ 'city' => 'Utrecht',
+ 'country' => 'NL',
+ ],
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ 'email' => 'business@example.com',
+ ],
+ 'company' => [
+ 'name' => 'Test Company B.V.',
+ 'chamberOfCommerce' => '12345678',
+ ],
+ 'articles' => [
+ [
+ 'identifier' => 'ART003',
+ 'description' => 'Business Item',
+ 'quantity' => 2,
+ 'price' => 500.00,
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IN3OLD-COMPANY-001', $response->getInvoice());
+ $this->assertEquals(1000.00, $response->getAmountDebit());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Capayable',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IN3OLD-STATUS',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('in3old')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-IN3OLD-STATUS',
+ 'billing' => [
+ 'recipient' => [
+ 'firstName' => 'Test',
+ 'lastName' => 'User',
+ ],
+ 'address' => [
+ 'street' => 'Test Street',
+ 'houseNumber' => '1',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ 'email' => 'test@example.com',
+ ],
+ 'articles' => [],
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/In3Test.php b/tests/Feature/PaymentMethods/In3Test.php
new file mode 100644
index 00000000..61b025d7
--- /dev/null
+++ b/tests/Feature/PaymentMethods/In3Test.php
@@ -0,0 +1,439 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'in3',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IN3-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 250.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('in3')->pay([
+ 'amountDebit' => 250.00,
+ 'invoice' => 'INV-IN3-001',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IN3-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(250.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_pay_transaction_with_billing_data(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'in3',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IN3-BILLING-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 180.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('in3')->pay([
+ 'amountDebit' => 180.00,
+ 'invoice' => 'INV-IN3-BILLING-001',
+ 'currency' => 'EUR',
+ 'billing' => [
+ 'recipient' => [
+ 'category' => 'B2C',
+ 'firstName' => 'Jane',
+ 'lastName' => 'Doe',
+ 'birthDate' => '1985-05-15',
+ ],
+ 'address' => [
+ 'street' => 'Hoofdstraat',
+ 'houseNumber' => '123',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ 'email' => 'jane.doe@example.com',
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IN3-BILLING-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(180.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_pay_transaction_with_articles(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'in3',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IN3-ARTICLES-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 299.99,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('in3')->pay([
+ 'amountDebit' => 299.99,
+ 'invoice' => 'INV-IN3-ARTICLES-001',
+ 'currency' => 'EUR',
+ 'articles' => [
+ [
+ 'identifier' => 'PROD-100',
+ 'description' => 'Laptop Computer',
+ 'quantity' => 1,
+ 'price' => 249.99,
+ 'vatPercentage' => 21,
+ ],
+ [
+ 'identifier' => 'PROD-101',
+ 'description' => 'Mouse',
+ 'quantity' => 1,
+ 'price' => 50.00,
+ 'vatPercentage' => 21,
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IN3-ARTICLES-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(299.99, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_pay_transaction_with_separate_shipping_address(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'in3',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IN3-SHIPPING-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 199.99,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('in3')->pay([
+ 'amountDebit' => 199.99,
+ 'invoice' => 'INV-IN3-SHIPPING-001',
+ 'currency' => 'EUR',
+ 'billing' => [
+ 'recipient' => [
+ 'category' => 'B2C',
+ 'firstName' => 'John',
+ 'lastName' => 'Smith',
+ ],
+ 'address' => [
+ 'street' => 'Billing Street',
+ 'houseNumber' => '10',
+ 'zipcode' => '1000AA',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'email' => 'john.smith@example.com',
+ ],
+ 'shipping' => [
+ 'recipient' => [
+ 'category' => 'B2C',
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ ],
+ 'address' => [
+ 'street' => 'Shipping Street',
+ 'houseNumber' => '20',
+ 'zipcode' => '2000BB',
+ 'city' => 'Rotterdam',
+ 'country' => 'NL',
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IN3-SHIPPING-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(199.99, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_pays_remainder_amount(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_IN3_PARTIAL_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'PayRemainder successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'in3',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IN3-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('in3')->payRemainder([
+ 'amountDebit' => 75.00,
+ 'invoice' => 'INV-IN3-REMAINDER-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IN3-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_refunds_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_IN3_PAY_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'in3',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IN3-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('in3')->refund([
+ 'amountCredit' => 100.00,
+ 'invoice' => 'INV-IN3-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IN3-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_refunds_payment_with_articles(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_IN3_PAY_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'in3',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IN3-REFUND-ARTICLES-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('in3')->refund([
+ 'amountCredit' => 50.00,
+ 'invoice' => 'INV-IN3-REFUND-ARTICLES-001',
+ 'originalTransactionKey' => $originalTxKey,
+ 'articles' => [
+ [
+ 'identifier' => 'PROD-101',
+ 'description' => 'Mouse',
+ 'quantity' => 1,
+ 'price' => 50.00,
+ 'vatPercentage' => 21,
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IN3-REFUND-ARTICLES-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'in3',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IN3-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('in3')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-IN3-STATUS-001',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'waiting_on_user_input' => [790, 'isWaitingOnUserInput'],
+ 'pending_processing' => [791, 'isPendingProcessing'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/KBCTest.php b/tests/Feature/PaymentMethods/KBCTest.php
new file mode 100644
index 00000000..fc8340c1
--- /dev/null
+++ b/tests/Feature/PaymentMethods/KBCTest.php
@@ -0,0 +1,209 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to KBC'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'kbcpaymentbutton',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KBC-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 45.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('kbc')->pay([
+ 'amountDebit' => 45.00,
+ 'invoice' => 'INV-KBC-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KBC-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(45.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_kbc_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_KBC_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'kbcpaymentbutton',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 20.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('kbc')->refund([
+ 'amountCredit' => 20.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(20.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_kbc_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://kbc.be/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to KBC'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'kbcpaymentbutton',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 30.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('kbc')->payRemainder([
+ 'amountDebit' => 30.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(30.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'kbcpaymentbutton',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('kbc')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/KlarnaKPTest.php b/tests/Feature/PaymentMethods/KlarnaKPTest.php
new file mode 100644
index 00000000..cd311ebb
--- /dev/null
+++ b/tests/Feature/PaymentMethods/KlarnaKPTest.php
@@ -0,0 +1,371 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarnakp',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 150.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarnakp')->pay([
+ 'amountDebit' => 150.00,
+ 'invoice' => 'INV-KLARNA-001',
+ 'currency' => 'EUR',
+ 'operatingCountry' => 'NL',
+ 'pno' => '01011990',
+ 'gender' => 1,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KLARNA-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(150.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_reserve_transaction(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Reservation successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarnakp',
+ 'Action' => 'Reserve',
+ 'Parameters' => [
+ ['Name' => 'ReservationNumber', 'Value' => 'RES-123456'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-RESERVE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 200.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarnakp')->reserve([
+ 'amountDebit' => 200.00,
+ 'invoice' => 'INV-KLARNA-RESERVE-001',
+ 'currency' => 'EUR',
+ 'operatingCountry' => 'NL',
+ 'pno' => '01011990',
+ 'gender' => 1,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KLARNA-RESERVE-001', $response->getInvoice());
+ $params = $response->getServiceParameters();
+ $this->assertEquals('RES-123456', $params['reservationnumber']);
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(200.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_cancels_reservation(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Cancellation successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarnakp',
+ 'Action' => 'CancelReservation',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-CANCEL-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 200.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarnakp')->cancelReserve([
+ 'amountCredit' => 200.00,
+ 'invoice' => 'INV-KLARNA-CANCEL-001',
+ 'reservationNumber' => 'RES-123456',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KLARNA-CANCEL-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(200.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_updates_reservation(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Update successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarnakp',
+ 'Action' => 'UpdateReservation',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-UPDATE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 180.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarnakp')->updateReserve([
+ 'amountDebit' => 180.00,
+ 'invoice' => 'INV-KLARNA-UPDATE-001',
+ 'reservationNumber' => 'RES-123456',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KLARNA-UPDATE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(180.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_refunds_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_KLARNAKP_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarnakp',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarnakp')->refund([
+ 'amountCredit' => 50.00,
+ 'invoice' => 'INV-KLARNA-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KLARNA-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_processes_payment_with_articles_and_billing(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarnakp',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-FULL-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 125.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarnakp')->pay([
+ 'amountDebit' => 125.00,
+ 'invoice' => 'INV-KLARNA-FULL-001',
+ 'currency' => 'EUR',
+ 'operatingCountry' => 'NL',
+ 'pno' => '01011990',
+ 'gender' => 1,
+ 'billing' => [
+ 'recipient' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ 'address' => [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ 'email' => 'john.doe@example.com',
+ ],
+ 'articles' => [
+ [
+ 'identifier' => 'PROD-001',
+ 'description' => 'Test Product',
+ 'quantity' => 2,
+ 'price' => 50.00,
+ 'vatPercentage' => 21,
+ ],
+ [
+ 'identifier' => 'PROD-002',
+ 'description' => 'Another Product',
+ 'quantity' => 1,
+ 'price' => 25.00,
+ 'vatPercentage' => 21,
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KLARNA-FULL-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(125.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarnakp',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarnakp')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-KLARNA-STATUS-001',
+ 'currency' => 'EUR',
+ 'operatingCountry' => 'NL',
+ 'pno' => '01011990',
+ 'gender' => 1,
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'waiting_on_user_input' => [790, 'isWaitingOnUserInput'],
+ 'pending_processing' => [791, 'isPendingProcessing'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/KlarnaPayTest.php b/tests/Feature/PaymentMethods/KlarnaPayTest.php
new file mode 100644
index 00000000..22677b0c
--- /dev/null
+++ b/tests/Feature/PaymentMethods/KlarnaPayTest.php
@@ -0,0 +1,382 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarna',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 150.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarna')->pay([
+ 'amountDebit' => 150.00,
+ 'invoice' => 'INV-KLARNA-001',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KLARNA-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(150.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_pay_in_installments_transaction(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Installment payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarna',
+ 'Action' => 'PayInInstallments',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-INST-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 200.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarna')->payInInstallments([
+ 'amountDebit' => 200.00,
+ 'invoice' => 'INV-KLARNA-INST-001',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KLARNA-INST-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(200.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_pay_remainder_transaction(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_KLARNA_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Remainder payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarna',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-REM-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarna')->payRemainder([
+ 'amountDebit' => 75.00,
+ 'invoice' => 'INV-KLARNA-REM-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KLARNA-REM-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_refunds_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_KLARNA_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarna',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarna')->refund([
+ 'amountCredit' => 50.00,
+ 'invoice' => 'INV-KLARNA-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KLARNA-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_processes_payment_with_billing_and_articles(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarna',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-FULL-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 125.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarna')->pay([
+ 'amountDebit' => 125.00,
+ 'invoice' => 'INV-KLARNA-FULL-001',
+ 'currency' => 'EUR',
+ 'billing' => [
+ 'recipient' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ 'address' => [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ 'email' => 'john.doe@example.com',
+ ],
+ 'articles' => [
+ [
+ 'identifier' => 'PROD-001',
+ 'description' => 'Test Product',
+ 'quantity' => 2,
+ 'price' => 50.00,
+ 'vatPercentage' => 21,
+ ],
+ [
+ 'identifier' => 'PROD-002',
+ 'description' => 'Another Product',
+ 'quantity' => 1,
+ 'price' => 25.00,
+ 'vatPercentage' => 21,
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KLARNA-FULL-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(125.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_processes_payment_with_separate_shipping_address(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarna',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-SHIP-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarna')->pay([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-KLARNA-SHIP-001',
+ 'currency' => 'EUR',
+ 'billing' => [
+ 'recipient' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ 'address' => [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ ],
+ 'shipping' => [
+ 'recipient' => [
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ ],
+ 'address' => [
+ 'street' => 'Other Street',
+ 'houseNumber' => '456',
+ 'zipcode' => '5678CD',
+ 'city' => 'Rotterdam',
+ 'country' => 'NL',
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KLARNA-SHIP-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'klarna',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KLARNA-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('klarna')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-KLARNA-STATUS-001',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'waiting_on_user_input' => [790, 'isWaitingOnUserInput'],
+ 'pending_processing' => [791, 'isPendingProcessing'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/KnakenPayTest.php b/tests/Feature/PaymentMethods/KnakenPayTest.php
new file mode 100644
index 00000000..a103ade8
--- /dev/null
+++ b/tests/Feature/PaymentMethods/KnakenPayTest.php
@@ -0,0 +1,197 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'knaken',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KNAKEN-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 60.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('knaken')->pay([
+ 'amountDebit' => 60.00,
+ 'invoice' => 'INV-KNAKEN-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KNAKEN-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(60.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_knakenpay_payment_with_alias(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'knaken',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KNAKENPAY-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 45.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('knakenpay')->pay([
+ 'amountDebit' => 45.00,
+ 'invoice' => 'INV-KNAKENPAY-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KNAKENPAY-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(45.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_knakenpay_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_KNAKEN_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'knaken',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-KNAKEN-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 30.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('knaken')->refund([
+ 'amountCredit' => 30.00,
+ 'invoice' => 'INV-KNAKEN-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-KNAKEN-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(30.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'knaken',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('knaken')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/MBWayTest.php b/tests/Feature/PaymentMethods/MBWayTest.php
new file mode 100644
index 00000000..f163bebb
--- /dev/null
+++ b/tests/Feature/PaymentMethods/MBWayTest.php
@@ -0,0 +1,209 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to MBWay'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'MBWay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-MBWAY-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 45.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('mbway')->pay([
+ 'amountDebit' => 45.00,
+ 'invoice' => 'INV-MBWAY-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-MBWAY-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(45.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_mbway_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_MBWAY_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'MBWay',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 20.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('mbway')->refund([
+ 'amountCredit' => 20.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(20.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_mbway_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://mbway.pt/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to MBWay'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'MBWay',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 35.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('mbway')->payRemainder([
+ 'amountDebit' => 35.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(35.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'MBWay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('mbway')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/MarketplacesTest.php b/tests/Feature/PaymentMethods/MarketplacesTest.php
new file mode 100644
index 00000000..a6ddacd1
--- /dev/null
+++ b/tests/Feature/PaymentMethods/MarketplacesTest.php
@@ -0,0 +1,250 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Split executed'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Marketplaces',
+ 'Action' => 'Split',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('marketplaces')->split([
+ 'marketplace' => [
+ 'amount' => 10.00,
+ 'description' => 'Marketplace fee',
+ ],
+ 'sellers' => [
+ [
+ 'accountId' => 'SELLER-001',
+ 'amount' => 90.00,
+ 'description' => 'Payment to seller 1',
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_splits_with_multiple_sellers(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Split executed'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Marketplaces',
+ 'Action' => 'Split',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('marketplaces')->split([
+ 'marketplace' => [
+ 'amount' => 15.00,
+ 'description' => 'Platform commission',
+ ],
+ 'sellers' => [
+ [
+ 'accountId' => 'SELLER-001',
+ 'amount' => 50.00,
+ 'description' => 'Payment to seller 1',
+ ],
+ [
+ 'accountId' => 'SELLER-002',
+ 'amount' => 35.00,
+ 'description' => 'Payment to seller 2',
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_transfers_marketplace_funds(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S002', 'Description' => 'Transfer executed'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Marketplaces',
+ 'Action' => 'Transfer',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('marketplaces')->transfer([
+ 'marketplace' => [
+ 'amount' => 5.00,
+ 'description' => 'Transfer fee',
+ ],
+ 'sellers' => [
+ [
+ 'accountId' => 'SELLER-TRANSFER',
+ 'amount' => 95.00,
+ 'description' => 'Seller transfer payment',
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_processes_refund_supplementary(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S003', 'Description' => 'Refund supplementary processed'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Marketplaces',
+ 'Action' => 'RefundSupplementary',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('marketplaces')->refundSupplementary([
+ 'marketplace' => [
+ 'amount' => 10.00,
+ 'description' => 'Marketplace refund',
+ ],
+ 'sellers' => [
+ [
+ 'accountId' => 'SELLER-REFUND',
+ 'amount' => 90.00,
+ 'description' => 'Seller refund portion',
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Marketplaces',
+ 'Action' => 'Split',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('marketplaces')->split([
+ 'marketplace' => [
+ 'amount' => 10.00,
+ 'description' => 'Test',
+ ],
+ 'sellers' => [
+ [
+ 'accountId' => 'SELLER-001',
+ 'amount' => 90.00,
+ 'description' => 'Test seller',
+ ],
+ ],
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'waiting_on_user_input' => [790, 'isWaitingOnUserInput'],
+ 'pending_processing' => [791, 'isPendingProcessing'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/MultibancoTest.php b/tests/Feature/PaymentMethods/MultibancoTest.php
new file mode 100644
index 00000000..5f444eb8
--- /dev/null
+++ b/tests/Feature/PaymentMethods/MultibancoTest.php
@@ -0,0 +1,209 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Multibanco'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Multibanco',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-MULTIBANCO-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('multibanco')->pay([
+ 'amountDebit' => 75.00,
+ 'invoice' => 'INV-MULTIBANCO-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-MULTIBANCO-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_multibanco_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_MULTIBANCO_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Multibanco',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 30.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('multibanco')->refund([
+ 'amountCredit' => 30.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(30.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_creates_a_multibanco_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://multibanco.pt/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Multibanco'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Multibanco',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('multibanco')->payRemainder([
+ 'amountDebit' => 25.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Multibanco',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('multibanco')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/NoServiceSpecifiedPaymentTest.php b/tests/Feature/PaymentMethods/NoServiceSpecifiedPaymentTest.php
new file mode 100644
index 00000000..6b8329c2
--- /dev/null
+++ b/tests/Feature/PaymentMethods/NoServiceSpecifiedPaymentTest.php
@@ -0,0 +1,241 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [],
+ 'Invoice' => 'INV-NOSERVICE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('noservice')->pay([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-NOSERVICE-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-NOSERVICE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_refund_without_service(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_NOSERVICE_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [],
+ 'Invoice' => 'INV-NOSERVICE-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('noservice')->refund([
+ 'amountCredit' => 25.00,
+ 'invoice' => 'INV-NOSERVICE-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-NOSERVICE-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_pay_remainder_without_service(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [],
+ 'Invoice' => 'INV-NOSERVICE-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 30.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('noservice')->payRemainder([
+ 'amountDebit' => 30.00,
+ 'invoice' => 'INV-NOSERVICE-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-NOSERVICE-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals(30.00, $response->getAmountDebit());
+ }
+
+ /** @test */
+ public function it_handles_redirect_response(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://checkout.buckaroo.nl/html/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending input'],
+ 'SubCode' => ['Code' => 'S002', 'Description' => 'Waiting for user'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [],
+ 'Invoice' => 'INV-NOSERVICE-REDIRECT',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('noservice')->pay([
+ 'amountDebit' => 75.00,
+ 'invoice' => 'INV-NOSERVICE-REDIRECT',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_handles_custom_parameters(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [],
+ 'CustomParameters' => [
+ ['Name' => 'custom_param', 'Value' => 'custom_value'],
+ ],
+ 'Invoice' => 'INV-NOSERVICE-CUSTOM',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('noservice')->pay([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-NOSERVICE-CUSTOM',
+ 'customParameters' => [
+ 'custom_param' => 'custom_value',
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [],
+ 'Invoice' => 'INV-NOSERVICE-STATUS',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('noservice')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-NOSERVICE-STATUS',
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/PayPalTest.php b/tests/Feature/PaymentMethods/PayPalTest.php
new file mode 100644
index 00000000..017a1195
--- /dev/null
+++ b/tests/Feature/PaymentMethods/PayPalTest.php
@@ -0,0 +1,304 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to PayPal'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'paypal',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-PAYPAL-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('paypal')->pay([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-PAYPAL-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-PAYPAL-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_paypal_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_PAYPAL_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'paypal',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 20.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('paypal')->refund([
+ 'amountCredit' => 20.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(20.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_creates_a_paypal_recurrent_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_PAYPAL_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Recurrent payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'paypal',
+ 'Action' => 'PayRecurrent',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-RECURRENT-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 9.99,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('paypal')->payRecurrent([
+ 'amountDebit' => 9.99,
+ 'invoice' => 'INV-RECURRENT-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-RECURRENT-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(9.99, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_creates_payment_with_extra_info(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to PayPal'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'paypal',
+ 'Action' => 'Pay,ExtraInfo',
+ 'Parameters' => [
+ ['Name' => 'CustomerEmail', 'Value' => 'customer@example.com'],
+ ['Name' => 'CustomerPhoneNumber', 'Value' => '+31612345678'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-EXTRA-INFO-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('paypal')->extraInfo([
+ 'amountDebit' => 75.00,
+ 'invoice' => 'INV-EXTRA-INFO-001',
+ 'customerEmail' => 'customer@example.com',
+ 'customerPhoneNumber' => '+31612345678',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals('INV-EXTRA-INFO-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /** @test */
+ public function it_creates_a_paypal_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to PayPal'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'paypal',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 35.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('paypal')->payRemainder([
+ 'amountDebit' => 35.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(35.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'paypal',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('paypal')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/PayPerEmailTest.php b/tests/Feature/PaymentMethods/PayPerEmailTest.php
new file mode 100644
index 00000000..9f8233e4
--- /dev/null
+++ b/tests/Feature/PaymentMethods/PayPerEmailTest.php
@@ -0,0 +1,117 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment invitation created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'payperemail',
+ 'Action' => 'PaymentInvitation',
+ 'Parameters' => [
+ ['Name' => 'CustomerEmail', 'Value' => 'customer@example.com'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-PPE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('payperemail')->paymentInvitation([
+ 'amountDebit' => 75.00,
+ 'invoice' => 'INV-PPE-001',
+ 'customerEmail' => 'customer@example.com',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-PPE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'payperemail',
+ 'Action' => 'PaymentInvitation',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('payperemail')->paymentInvitation([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ 'customerEmail' => 'test@example.com',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/PayconiqTest.php b/tests/Feature/PaymentMethods/PayconiqTest.php
new file mode 100644
index 00000000..0bebab58
--- /dev/null
+++ b/tests/Feature/PaymentMethods/PayconiqTest.php
@@ -0,0 +1,252 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Payconiq'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'payconiq',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-PAYCONIQ-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('payconiq')->pay([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-PAYCONIQ-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-PAYCONIQ-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_payconiq_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_PAYCONIQ_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'payconiq',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('payconiq')->refund([
+ 'amountCredit' => 25.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_payconiq_instant_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_PAYCONIQ_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Instant refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'payconiq',
+ 'Action' => 'instantRefund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-INSTANT-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 30.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('payconiq')->instantRefund([
+ 'amountCredit' => 30.00,
+ 'invoice' => 'INV-INSTANT-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-INSTANT-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(30.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_payconiq_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://payconiq.com/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Payconiq'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'payconiq',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 40.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('payconiq')->payRemainder([
+ 'amountDebit' => 40.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(40.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'payconiq',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('payconiq')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/PaymentInitiationTest.php b/tests/Feature/PaymentMethods/PaymentInitiationTest.php
new file mode 100644
index 00000000..b30b794c
--- /dev/null
+++ b/tests/Feature/PaymentMethods/PaymentInitiationTest.php
@@ -0,0 +1,204 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to bank'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'PayByBank',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'issuer', 'Value' => 'BANK123'],
+ ['Name' => 'countryCode', 'Value' => 'NL'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-PBI-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('paybybank')->pay([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-PBI-001',
+ 'issuer' => 'BANK123',
+ 'countryCode' => 'NL',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-PBI-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_fetches_issuers(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('GET', '*/json/Transaction/Specification/PayByBank*', [
+ 'Services' => [
+ [
+ 'Name' => 'PayByBank',
+ 'Parameters' => [
+ ['Name' => 'issuer', 'ListItemDescription' => 'Test Bank NL', 'ListItemID' => 'TESTNL01'],
+ ['Name' => 'issuer', 'ListItemDescription' => 'Test Bank DE', 'ListItemID' => 'TESTDE01'],
+ ],
+ ],
+ ],
+ ]),
+ ]);
+
+ $issuers = $this->buckaroo->method('paybybank')->issuers();
+
+ $this->assertIsArray($issuers);
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'PayByBank',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('paybybank')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ 'issuer' => 'BANK123',
+ 'countryCode' => 'NL',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+
+ /**
+ * @test
+ * @dataProvider countryCodeProvider
+ */
+ public function it_works_with_different_country_codes(string $countryCode): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = "https://bank.example.com/auth?trx={$transactionKey}";
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to bank'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'PayByBank',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-COUNTRY-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('paybybank')->pay([
+ 'amountDebit' => 25.00,
+ 'invoice' => 'INV-COUNTRY-001',
+ 'issuer' => 'BANK123',
+ 'countryCode' => $countryCode,
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertSame($transactionKey, $response->getTransactionKey());
+ $this->assertSame('INV-COUNTRY-001', $response->getInvoice());
+ }
+
+ public static function countryCodeProvider(): array
+ {
+ return [
+ ['NL'],
+ ['DE'],
+ ['BE'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/PointOfSaleTest.php b/tests/Feature/PaymentMethods/PointOfSaleTest.php
new file mode 100644
index 00000000..203bc701
--- /dev/null
+++ b/tests/Feature/PaymentMethods/PointOfSaleTest.php
@@ -0,0 +1,156 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'pospayment',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-POS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('pospayment')->pay([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-POS-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-POS-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_pos_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_POS_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'pospayment',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-POS-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('pospayment')->refund([
+ 'amountCredit' => 25.00,
+ 'invoice' => 'INV-POS-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-POS-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'pospayment',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('pospayment')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/Przelewy24Test.php b/tests/Feature/PaymentMethods/Przelewy24Test.php
new file mode 100644
index 00000000..47a2aab3
--- /dev/null
+++ b/tests/Feature/PaymentMethods/Przelewy24Test.php
@@ -0,0 +1,313 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Przelewy24'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Przelewy24',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-P24-001',
+ 'Currency' => 'PLN',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('przelewy24')->pay([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-P24-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-P24-001', $response->getInvoice());
+ $this->assertEquals('PLN', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_payment_with_customer_details(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://secure.przelewy24.pl/trnRequest/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Przelewy24'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Przelewy24',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'CustomerFirstName', 'Value' => 'Jan'],
+ ['Name' => 'CustomerLastName', 'Value' => 'Kowalski'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-P24-CUST-001',
+ 'Currency' => 'PLN',
+ 'AmountDebit' => 150.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('przelewy24')->pay([
+ 'amountDebit' => 150.00,
+ 'invoice' => 'INV-P24-CUST-001',
+ 'customer' => [
+ 'firstName' => 'Jan',
+ 'lastName' => 'Kowalski',
+ ],
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals('INV-P24-CUST-001', $response->getInvoice());
+ $this->assertEquals('PLN', $response->getCurrency());
+ $this->assertEquals(150.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /** @test */
+ public function it_creates_payment_with_email(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://secure.przelewy24.pl/trnRequest/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Przelewy24'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Przelewy24',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'CustomerEmail', 'Value' => 'jan.kowalski@example.pl'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-P24-EMAIL-001',
+ 'Currency' => 'PLN',
+ 'AmountDebit' => 200.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('przelewy24')->pay([
+ 'amountDebit' => 200.00,
+ 'invoice' => 'INV-P24-EMAIL-001',
+ 'email' => 'jan.kowalski@example.pl',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals('INV-P24-EMAIL-001', $response->getInvoice());
+ $this->assertEquals('PLN', $response->getCurrency());
+ $this->assertEquals(200.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /** @test */
+ public function it_creates_a_przelewy24_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_P24_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Przelewy24',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'PLN',
+ 'AmountCredit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('przelewy24')->refund([
+ 'amountCredit' => 50.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('PLN', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_creates_a_przelewy24_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://secure.przelewy24.pl/trnRequest/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Przelewy24'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Przelewy24',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'PLN',
+ 'AmountDebit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('przelewy24')->payRemainder([
+ 'amountDebit' => 75.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('PLN', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Przelewy24',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'PLN',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('przelewy24')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/REFACTORING_PLAN.md b/tests/Feature/PaymentMethods/REFACTORING_PLAN.md
new file mode 100644
index 00000000..c3759433
--- /dev/null
+++ b/tests/Feature/PaymentMethods/REFACTORING_PLAN.md
@@ -0,0 +1,739 @@
+# Payment Method Tests - Comprehensive Refactoring Plan
+
+**Generated:** 2026-01-14
+**Analysis Depth:** DEEP (All 4 existing test files analyzed line-by-line, all 48+ payment method source files inventoried)
+**Status:** Analysis Complete - Ready for Implementation
+
+---
+
+## Executive Summary
+
+This document provides a **comprehensive, deep analysis** of all payment method tests in the Buckaroo PHP SDK, identifies quality patterns, and outlines a systematic refactoring plan to achieve consistent, high-quality test coverage across all 48+ payment methods.
+
+### Current State
+- **4 test files exist** (iDeal, CreditCard, PayPal, SEPA)
+- **44+ payment methods lack tests**
+- **1,561 total lines** of test code across 4 files
+- **39 test methods** across 4 files
+- **Quality: EXCELLENT** - The 4 existing tests serve as golden standards
+
+### Target State
+- Comprehensive test coverage for all payment methods
+- Consistent quality matching the golden standards
+- ~35-50 test files (one per payment method)
+- Estimated ~15,000-20,000 lines of quality test code
+- ~320-400 total test methods
+
+---
+
+## Section 1: Current State Inventory
+
+### 1.1 Existing Test Files - DETAILED ANALYSIS
+
+| File | Lines | Test Methods | Quality Grade | Assessment |
+|------|-------|--------------|---------------|------------|
+| **iDealTest.php** | 353 | 9 | ✅ **A+** | **GOLDEN STANDARD** - Perfect example of quality patterns |
+| **CreditCardTest.php** | 589 | 16 | ✅ **A+** | **GOLDEN STANDARD** - Most comprehensive, covers 14 payment variants |
+| **PayPalTest.php** | 278 | 7 | ✅ **A** | **GOLDEN STANDARD** - Clean, focused, excellent assertions |
+| **SEPATest.php** | 341 | 7 | ✅ **A+** | **GOLDEN STANDARD** - Demonstrates complex parameter handling |
+| **TOTAL** | **1,561** | **39** | - | **NO REFACTORING NEEDED** |
+
+#### **Test Method Breakdown**
+
+**iDealTest.php (9 tests):**
+1. `it_creates_an_ideal_payment_with_redirect()` - Core pay() functionality
+2. `it_creates_an_ideal_refund()` - Core refund() functionality
+3. `it_creates_an_ideal_instant_refund()` - instantRefund() method
+4. `it_creates_an_ideal_pay_remainder()` - payRemainder() method
+5. `it_creates_fast_checkout_payment()` - payFastCheckout() method
+6. `it_fetches_issuers()` - issuers() method (GET endpoint)
+7. `it_handles_various_status_codes()` - Data provider test (7 status variants)
+8. `it_works_with_different_issuers()` - Data provider test (3 issuer variants)
+9. `statusCodeProvider()` / `issuerProvider()` - Static data providers
+
+**CreditCardTest.php (16 tests):**
+1. `it_creates_a_visa_payment_with_redirect()` - Core pay() with 3DS redirect
+2. `it_creates_encrypted_payment()` - payEncrypted() method
+3. `it_creates_payment_with_token()` - payWithToken() method
+4. `it_creates_payment_with_security_code()` - payWithSecurityCode() method
+5. `it_creates_recurrent_payment()` - payRecurrent() method
+6. `it_creates_authorize()` - authorize() method
+7. `it_creates_encrypted_authorize()` - authorizeEncrypted() method
+8. `it_captures_authorized_payment()` - capture() method
+9. `it_refunds_payment()` - refund() method
+10. `it_cancels_authorized_payment()` - cancelAuthorize() method
+11. `it_extracts_service_parameters_from_response()` - Service parameter validation
+12. `it_throws_exception_for_missing_card_name()` - Exception handling
+13. `it_handles_various_status_codes()` - Data provider test (7 status variants)
+14. `it_works_with_different_card_types()` - Data provider test (visa, mastercard, amex)
+15. `statusCodeProvider()` / `cardTypeProvider()` - Static data providers
+
+**PayPalTest.php (7 tests):**
+1. `it_creates_a_paypal_payment_with_redirect()` - Core pay() functionality
+2. `it_creates_a_paypal_refund()` - refund() method
+3. `it_creates_a_paypal_recurrent_payment()` - payRecurrent() method
+4. `it_creates_payment_with_extra_info()` - extraInfo() method
+5. `it_creates_a_paypal_pay_remainder()` - payRemainder() method
+6. `it_handles_various_status_codes()` - Data provider test (7 status variants)
+7. `statusCodeProvider()` - Static data provider
+
+**SEPATest.php (7 tests):**
+1. `it_creates_a_sepa_direct_debit_payment()` - Core pay() functionality with IBAN/mandate
+2. `it_creates_a_sepa_authorize_transaction()` - authorize() method
+3. `it_creates_a_sepa_recurrent_payment()` - payRecurrent() method
+4. `it_creates_a_sepa_payment_with_extra_info()` - extraInfo() method with full customer data
+5. `it_creates_a_sepa_payment_with_emandate()` - payWithEmandate() method
+6. `it_creates_a_sepa_refund()` - refund() method
+7. `it_handles_various_status_codes()` - Data provider test (7 status variants)
+8. `statusCodeProvider()` - Static data provider
+
+### 1.2 Payment Method Source Classes Inventory
+
+#### **Payment Methods WITH Tests (4 - COMPLETE)**
+| Payment Method | Public Methods | Test Count | Coverage |
+|----------------|----------------|------------|----------|
+| ✅ `iDeal` | 6 methods | 9 tests | **100%** |
+| ✅ `CreditCard` | 14 methods | 16 tests | **100%** |
+| ✅ `Paypal` | 5 methods | 7 tests | **100%** |
+| ✅ `SEPA` | 6 methods | 7 tests | **100%** |
+
+#### **Payment Methods WITHOUT Tests (44+ methods)**
+
+**High Priority - Common Payment Methods (10):**
+1. ❌ **Afterpay** - 6 public methods (pay, authorize, capture, cancelAuthorize, refund, payRemainder)
+2. ❌ **AfterpayDigiAccept** - ~6 public methods (similar to Afterpay)
+3. ❌ **Alipay** - 2 public methods (pay, payRemainder)
+4. ❌ **ApplePay** - 2 public methods (pay, payRedirect)
+5. ❌ **Bancontact** - 8 public methods (pay, payEncrypted, payRecurring, payOneClick, authenticate, authorize, capture, cancelAuthorize)
+6. ❌ **GooglePay** - ~2 public methods (similar to ApplePay)
+7. ❌ **KlarnaKP** - ~6 public methods (BNPL with complex article/recipient models)
+8. ❌ **KlarnaPay** - ~4 public methods (Klarna variant)
+9. ❌ **Przelewy24** - ~4 public methods (Polish payment method)
+10. ❌ **Trustly** - ~4 public methods (Nordic bank payment)
+
+**Medium Priority - Regional/Specialized (25):**
+- BankTransfer, Belfius, Billink, Bizum, Blik, EPS, GiftCard, In3, In3Old, KBC, KnakenPay, MBWay, Multibanco, Payconiq, PaymentInitiation, PointOfSale, Swish, Twint, WeChatPay, Wero, iDealProcessing, iDealQR, iDin, and others
+
+**Low Priority - Complex/Specialized (8):**
+- BuckarooVoucher, BuckarooWallet, ClickToPay, CreditManagement (10+ methods), Emandates, ExternalPayment, Marketplaces, NoServiceSpecifiedPayment, PayPerEmail, Subscriptions (10+ methods), Surepay, Thunes
+
+---
+
+## Section 2: Quality Assessment of Existing Tests
+
+### 2.1 Golden Standard Pattern Analysis
+
+#### **✅ PASS - Test Structure (ALL 4 files)**
+- All use `/** @test */` annotation consistently
+- All use `it_[verb]_[context]()` naming pattern
+- All extend `Tests\TestCase`
+- All use `@runTestsInSeparateProcesses` and `@preserveGlobalState disabled`
+- All use `declare(strict_types=1);`
+
+#### **✅ PASS - HTTP Mocking (ALL 4 files)**
+- Use `BuckarooMockRequest::json()` for all mocked responses
+- Mock structure includes: Key, Status (Code/SubCode/DateTime), RequiredAction, Services, Invoice, Currency, Amount, IsTest
+- Use wildcard patterns: `*/json/Transaction*`
+- **CRITICAL:** NO PHP class mocking - only HTTP layer mocked via `$this->mockBuckaroo`
+
+#### **✅ PASS - Assertion Quality (ALL 4 files)**
+
+**Common Patterns Across All Files:**
+- Assert `transactionKey` (every test)
+- Assert `invoice` (every test)
+- Assert status methods (`isSuccess()`, `isPendingProcessing()`, etc.)
+- Assert `hasRedirect()` and `getRedirectUrl()` for redirect flows
+- Use data providers for status codes (7 variants: 190, 490, 491, 690, 890, 492, 792)
+- Use `uniqid()` for realistic test data
+
+**File-Specific Strengths:**
+
+**iDealTest.php:**
+- ✅ Tests issuer list retrieval (`issuers()` method)
+- ✅ Uses data provider for different issuers (RABONL2U, INGBNL2A, ABNANL2A)
+- ✅ Comprehensive redirect URL validation
+- ✅ Tests all 6 public methods + status variants + issuer variants
+
+**CreditCardTest.php:**
+- ✅ Extracts and validates service parameters from response (`getServiceParameters()`)
+- ✅ Tests exception handling for missing required fields
+- ✅ Uses data provider for card types (visa, mastercard, amex)
+- ✅ Covers 14 different payment method variants (encrypted, token, security code, recurrent, etc.)
+- ✅ Most comprehensive test file - sets the high bar
+
+**PayPalTest.php:**
+- ✅ Clean, minimal, focused - demonstrates simplicity for simple payment methods
+- ✅ Tests extraInfo() method with additional parameters
+- ✅ All 5 public methods covered with status variants
+
+**SEPATest.php:**
+- ✅ Uses `assertSame()` instead of `assertEquals()` for strict type comparison
+- ✅ Demonstrates complex parameter passing (IBAN, BIC, mandate reference, mandate date, collect date, customer data)
+- ✅ Tests full customer/address object structures
+- ✅ All 6 public methods covered + status variants
+
+### 2.2 Quality Issues Identified
+
+### **❌ NONE - All 4 files are exceptional quality**
+
+After deep analysis of all 1,561 lines across 4 test files:
+
+**Strengths:**
+- ✅ Proper consolidation via data providers (not 7 separate status code tests)
+- ✅ Meaningful assertions beyond just `isSuccess()` (always includes transactionKey, invoice, status)
+- ✅ Clean Arrange-Act-Assert structure in every test
+- ✅ Real-world scenarios (3DS redirects, encrypted payments, recurrent flows, mandate handling)
+- ✅ Zero AI slop (no verbose comments, no generic naming, no over-testing)
+- ✅ Consistent formatting and style across all files
+- ✅ PHP 7.4 compatible (no PHP 8.x syntax)
+
+**No Refactoring Needed:**
+The existing 4 test files should serve as **GOLDEN STANDARDS** and be used as templates for all new payment method tests.
+
+### 2.3 Patterns to Replicate
+
+Every new test file MUST follow these exact patterns:
+
+#### **Pattern 1: File Header**
+```php
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'paymentmethod',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ]
+ ],
+ 'Invoice' => 'INV-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('paymentmethod')->pay([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-001', $response->getInvoice());
+}
+```
+
+#### **Pattern 3: Status Code Data Provider**
+```php
+/**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+{
+ $transactionKey = 'TEST_TX_STATUS_' . uniqid();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'paymentmethod',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ]
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('paymentmethod')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+}
+
+public static function statusCodeProvider(): array
+{
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+}
+```
+
+#### **Pattern 4: Variant Data Provider (Example: Card Types, Issuers)**
+```php
+/**
+ * @test
+ * @dataProvider cardTypeProvider
+ */
+public function it_works_with_different_card_types(string $cardType): void
+{
+ $transactionKey = 'TEST_TX_CARD_' . uniqid();
+ $redirectUrl = "https://checkout.buckaroo.nl/redirect/3DSAuth/{$transactionKey}";
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 790, 'Description' => 'Waiting on user input'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => '3D Secure required'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => $cardType,
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ]
+ ],
+ 'Invoice' => 'INV-CARD-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('creditcard')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-CARD-001',
+ 'currency' => 'EUR',
+ 'name' => $cardType,
+ ]);
+
+ $this->assertTrue($response->isWaitingOnUserInput());
+ $this->assertTrue($response->hasRedirect());
+}
+
+public static function cardTypeProvider(): array
+{
+ return [
+ ['visa'],
+ ['mastercard'],
+ ['amex'],
+ ];
+}
+```
+
+---
+
+## Section 3: Missing Tests Analysis
+
+### 3.1 Coverage Gaps by Complexity
+
+**Simple Methods (2-3 public methods) - ~15 payment methods**
+- Alipay, ApplePay, GooglePay, WeChatPay, Belfius, KBC, EPS, etc.
+- **Expected tests per file:** 4-6 tests
+- **Estimated lines per file:** ~200-250
+
+**Medium Methods (4-7 public methods) - ~25 payment methods**
+- Afterpay, Bancontact, In3, Klarna variants, regional banks, etc.
+- **Expected tests per file:** 6-10 tests
+- **Estimated lines per file:** ~300-400
+
+**Complex Methods (8+ public methods) - ~8 payment methods**
+- CreditCard (✅ done), Subscriptions, CreditManagement, Marketplaces, BuckarooWallet, etc.
+- **Expected tests per file:** 12-20 tests
+- **Estimated lines per file:** ~500-700
+
+### 3.2 Detailed Coverage Matrix
+
+| Payment Method | Source Methods | Test File | Tests Needed | Priority | Notes |
+|----------------|----------------|-----------|--------------|----------|-------|
+| iDeal | 6 | ✅ | - | DONE | Golden standard |
+| CreditCard | 14 | ✅ | - | DONE | Golden standard |
+| Paypal | 5 | ✅ | - | DONE | Golden standard |
+| SEPA | 6 | ✅ | - | DONE | Golden standard |
+| Afterpay | 6 | ❌ | 8-10 | **HIGH** | BNPL with articles/recipients |
+| AfterpayDigiAccept | ~6 | ❌ | 8-10 | **HIGH** | Alternative Afterpay |
+| Alipay | 2 | ❌ | 4-5 | **HIGH** | Asian markets |
+| ApplePay | 2 | ❌ | 4-5 | **HIGH** | Digital wallet |
+| Bancontact | 8 | ❌ | 10-12 | **HIGH** | Belgian card - complex |
+| GooglePay | ~2 | ❌ | 4-5 | **HIGH** | Digital wallet |
+| KlarnaKP | ~6 | ❌ | 8-10 | **HIGH** | BNPL leader |
+| Przelewy24 | ~4 | ❌ | 6-8 | MEDIUM | Polish market |
+| Trustly | ~4 | ❌ | 6-8 | MEDIUM | Nordic banking |
+| BankTransfer | ~3 | ❌ | 5-6 | MEDIUM | Manual transfer |
+| GiftCard | ~4 | ❌ | 6-8 | MEDIUM | Gift cards |
+| In3 | ~6 | ❌ | 8-10 | MEDIUM | Dutch BNPL |
+| PaymentInitiation | ~3 | ❌ | 5-6 | MEDIUM | PSD2 |
+| iDealProcessing | ~3 | ❌ | 5-6 | MEDIUM | iDeal variant |
+| [... 20+ more] | Various | ❌ | 4-8 | MEDIUM-LOW | Regional/specialized |
+| Subscriptions | 10+ | ❌ | 15-20 | **LOW*** | Complex but critical business value |
+| CreditManagement | 10+ | ❌ | 15-20 | **LOW*** | Complex but critical business value |
+| Marketplaces | ~8 | ❌ | 10-12 | LOW | Marketplace splits |
+| BuckarooVoucher | 5 | ❌ | 7-9 | LOW | Voucher lifecycle |
+
+**LOW***: Low priority due to complexity, but HIGH business value - requires careful planning
+
+---
+
+## Section 4: Refactoring Tasks
+
+### 4.1 Existing Files: NO REFACTORING NEEDED
+
+**Verdict:** All 4 existing test files (`iDealTest.php`, `CreditCardTest.php`, `PayPalTest.php`, `SEPATest.php`) are **GOLDEN STANDARDS** and require **ZERO refactoring**.
+
+They will serve as templates for all new payment method tests.
+
+### 4.2 New Test Creation Tasks
+
+#### **Task Group 1: High Priority Common Methods (10 files)**
+
+**Estimated Effort:** 3,000-3,500 lines, 70-85 test methods, 2-3 weeks
+
+1. **BancontactTest.php** (P0)
+ - Methods: pay, payEncrypted, payRecurring, payOneClick, authenticate, authorize, capture, cancelAuthorize (8 methods)
+ - Estimated: 10-12 tests, ~450 lines
+ - Complexity: High (multiple auth flows, encrypted payments)
+
+2. **ApplePayTest.php** (P1)
+ - Methods: pay, payRedirect (2 methods)
+ - Estimated: 4-5 tests, ~200 lines
+ - Complexity: Medium (encrypted payment data)
+
+3. **GooglePayTest.php** (P1)
+ - Methods: pay (similar to ApplePay)
+ - Estimated: 4-5 tests, ~200 lines
+ - Complexity: Medium
+
+4. **KlarnaKPTest.php** (P1)
+ - Methods: pay, authorize, capture, refund, payRemainder (~6 methods)
+ - Estimated: 8-10 tests, ~350 lines
+ - Complexity: High (article/recipient models)
+
+5. **AfterpayTest.php** (P1)
+ - Methods: pay, authorize, capture, cancelAuthorize, refund, payRemainder (6 methods)
+ - Estimated: 8-10 tests, ~350 lines
+ - Complexity: High (complex article/person/recipient models)
+
+6. **AfterpayDigiAcceptTest.php** (P1)
+ - Similar to Afterpay
+ - Estimated: 8-10 tests, ~350 lines
+
+7. **AlipayTest.php** (P2)
+ - Methods: pay, payRemainder (2 methods)
+ - Estimated: 4-5 tests, ~200 lines
+ - Complexity: Low-Medium
+
+8. **Przelewy24Test.php** (P2)
+ - Methods: pay, refund, payRemainder (~4 methods)
+ - Estimated: 6-8 tests, ~280 lines
+ - Complexity: Medium (customer parameter validation)
+
+9. **TrustlyTest.php** (P2)
+ - Methods: pay, refund, payRemainder (~4 methods)
+ - Estimated: 6-8 tests, ~280 lines
+ - Complexity: Medium
+
+10. **GiftCardTest.php** (P2)
+ - Methods: pay, refund, payRedirect, payRemainder, paymentName (~5 methods)
+ - Estimated: 6-8 tests, ~280 lines
+
+#### **Task Group 2: Medium Priority Regional/Specialized (15 files)**
+
+**Estimated Effort:** 3,500-4,500 lines, 80-100 test methods, 2-3 weeks
+
+11-25. **BankTransferTest.php, BelfiusTest.php, BillinkTest.php, BizumTest.php, BlikTest.php, EPSTest.php, In3Test.php, In3OldTest.php, KBCTest.php, KlarnaPayTest.php, KnakenPayTest.php, MBWayTest.php, MultibancoTest.php, PayconiqTest.php, PaymentInitiationTest.php**
+
+- Each: 4-8 tests depending on complexity
+- Estimated per file: ~200-350 lines
+- Follow exact patterns from golden standards
+
+#### **Task Group 3: Low Priority Complex/Specialized (8 files)**
+
+**Estimated Effort:** 3,000-3,500 lines, 70-95 test methods, 2-3 weeks
+
+26. **SubscriptionsTest.php** (P0 business value, P3 complexity)
+ - Methods: create, createCombined, update, stop, resume, info, etc. (10+ methods)
+ - Estimated: 15-20 tests, ~600 lines
+ - **Critical:** Recurring revenue - handle with extreme care
+
+27. **CreditManagementTest.php** (P0 business value, P3 complexity)
+ - Methods: createInvoice, createCreditNote, paymentPlan, addProductLines, etc. (10+ methods)
+ - Estimated: 15-20 tests, ~650 lines
+ - **Complex:** Invoice/debtor management
+
+28. **MarketplacesTest.php**
+ - Methods: split, transfer, refundSupplementary (~3 methods)
+ - Estimated: 10-12 tests, ~450 lines
+
+29. **BuckarooVoucherTest.php**
+ - Methods: create, deactivate, pay, getBalance, refund (5 methods)
+ - Estimated: 7-9 tests, ~300 lines
+
+30. **BuckarooWalletTest.php**
+ - Methods: Multiple wallet operations (~9 methods)
+ - Estimated: 8-10 tests, ~350 lines
+
+31-33. **ClickToPayTest.php, EmandatesTest.php, ExternalPaymentTest.php**
+ - Specialized scenarios
+ - Estimated per file: ~5-8 tests, ~250-300 lines
+
+#### **Task Group 4: Remaining Methods (15+ files)**
+
+**Estimated Effort:** 2,500-3,500 lines, 60-80 test methods, 1-2 weeks
+
+34-48+. All remaining payment methods:
+- PointOfSale, Surepay, Swish, Thunes, Twint, WeChatPay, Wero, iDealProcessing, iDealQR, iDin, PayPerEmail, etc.
+- Each: 4-8 tests
+- Follow golden standard patterns
+
+### 4.3 Total Estimate
+
+| Category | Files | Tests | Lines | Status |
+|----------|-------|-------|-------|--------|
+| **Existing (Golden Standards)** | 4 | 39 | 1,561 | ✅ DONE |
+| **To Create - Group 1** | 10 | 70-85 | 3,000-3,500 | ❌ TODO |
+| **To Create - Group 2** | 15 | 80-100 | 3,500-4,500 | ❌ TODO |
+| **To Create - Group 3** | 8 | 70-95 | 3,000-3,500 | ❌ TODO |
+| **To Create - Group 4** | 15+ | 60-80 | 2,500-3,500 | ❌ TODO |
+| **GRAND TOTAL** | **~52** | **320-400** | **~13,500-16,500** | **8% DONE** |
+
+---
+
+## Section 5: Execution Order
+
+### **Phase 1: High-Value Common Methods (Weeks 1-2)**
+**Goal:** Cover 80% of real-world payment volume
+
+1. ✅ iDeal, CreditCard, PayPal, SEPA (COMPLETE)
+2. Bancontact (Belgium - very high volume)
+3. ApplePay (Digital wallet growth)
+4. GooglePay (Digital wallet growth)
+5. KlarnaKP (BNPL leader)
+6. Afterpay (BNPL leader)
+7. Alipay (Asian markets)
+8. Trustly (Nordic)
+9. Przelewy24 (Poland)
+10. GiftCard (Retail)
+
+**Output:** 10 files, ~3,000-3,500 lines, 70-85 tests
+
+### **Phase 2: Regional & Specialized (Weeks 3-4)**
+**Goal:** Geographic coverage
+
+11-25. AfterpayDigiAccept, In3, In3Old, KlarnaPay, PaymentInitiation, BankTransfer, Billink, iDealProcessing, Belfius, KBC, EPS, Bizum, Blik, MBWay, Multibanco, Payconiq, Swish, Twint, Wero, WeChatPay, KnakenPay, iDealQR, iDin
+
+**Output:** 15 files, ~3,500-4,500 lines, 80-100 tests
+
+### **Phase 3: Complex/Specialized (Weeks 5-6)**
+**Goal:** High business value, complex logic
+
+26. Subscriptions (recurring revenue - CRITICAL)
+27. CreditManagement (invoice management - CRITICAL)
+28. Marketplaces (platform splits)
+29. BuckarooVoucher
+30. BuckarooWallet
+31. ClickToPay
+32. Emandates
+33. ExternalPayment
+
+**Output:** 8 files, ~3,000-3,500 lines, 70-95 tests
+
+### **Phase 4: Remaining Methods (Week 7)**
+**Goal:** Complete coverage
+
+34-48+. All remaining payment methods (PointOfSale, Surepay, Thunes, PayPerEmail, etc.)
+
+**Output:** ~15 files, ~2,500-3,500 lines, 60-80 tests
+
+### **Timeline Summary**
+- **7-8 weeks** for complete coverage
+- **~2-3 payment method tests per day**
+- **Each test file: 1-3 hours** depending on complexity
+
+---
+
+## Section 6: Quality Checklist
+
+Every new test file MUST pass this checklist:
+
+### **Structure**
+- [ ] Filename: `{PaymentMethodName}Test.php` (matches source class name exactly)
+- [ ] Namespace: `Tests\Feature\PaymentMethods`
+- [ ] Extends `Tests\TestCase`
+- [ ] Uses `@runTestsInSeparateProcesses` and `@preserveGlobalState disabled`
+- [ ] Uses `declare(strict_types=1);`
+
+### **Test Methods**
+- [ ] Every test has `/** @test */` annotation
+- [ ] All test names follow `it_[verb]_[context]()` pattern
+- [ ] Tests organized logically (happy path → variants → edge cases → errors)
+- [ ] NO PHP class mocking - ONLY HTTP mocking via `BuckarooMockRequest`
+
+### **Coverage**
+- [ ] Every public method in source class has at least 1 test
+- [ ] Status code data provider included (7 variants: 190, 490, 491, 690, 890, 492, 792)
+- [ ] Variant data providers if applicable (card types, issuers, etc.)
+- [ ] Edge cases tested (missing parameters, validation failures, etc.)
+
+### **Assertions**
+- [ ] Every test asserts `transactionKey`
+- [ ] Every test asserts `invoice`
+- [ ] Every test asserts status (`isSuccess()`, `isPendingProcessing()`, etc.)
+- [ ] Redirect tests assert `hasRedirect()` and `getRedirectUrl()`
+- [ ] Service parameter tests extract and validate response parameters
+- [ ] Uses `assertSame()` for strict comparisons where appropriate
+
+### **Mock Quality**
+- [ ] Mock response structure complete: Key, Status (Code/SubCode/DateTime), RequiredAction, Services, Invoice, Currency, Amount, IsTest
+- [ ] Realistic test data (transaction keys with `uniqid()`, realistic amounts, dates)
+- [ ] HTTP method and URL pattern correct (`POST`, `*/json/Transaction*` or `GET`, `*/json/Transaction/Specification/*`)
+
+### **Code Quality**
+- [ ] No repetitive tests (use data providers instead)
+- [ ] No AI slop (no verbose comments, no generic naming, no over-testing)
+- [ ] Clean Arrange-Act-Assert structure visible in every test
+- [ ] PHP 7.4 compatible syntax only (no PHP 8.x features)
+- [ ] Consistent formatting matching golden standards
+
+---
+
+## Section 7: Implementation Guidelines
+
+### **DO:**
+✅ **ALWAYS** read the source payment method class FIRST before writing tests
+✅ Copy structure from golden standard files (iDealTest, CreditCardTest, PayPalTest, SEPATest)
+✅ Test EVERY public method in the payment method class
+✅ Use data providers for status codes (7 variants) and variants (card types, issuers, etc.)
+✅ Assert transactionKey, invoice, status in EVERY test
+✅ Mock HTTP layer only via `BuckarooMockRequest`
+✅ Keep test method count reasonable (consolidate via data providers)
+✅ Test realistic scenarios (redirects, encrypted data, recurrent flows, complex parameters)
+✅ Validate complex parameters (customer, address, articles, recipients, etc.)
+✅ Use `uniqid()` for transaction keys to ensure uniqueness
+
+### **DON'T:**
+❌ **NEVER** mock PHP classes/methods (use ONLY HTTP mocking)
+❌ Don't create 10 separate tests that differ only in input data (use data providers)
+❌ Don't skip assertions (minimum: transactionKey + invoice + status)
+❌ Don't test trivial getters/setters
+❌ Don't use generic test names (`test_payment_works()` ← BAD)
+❌ Don't write tests without reading source class first
+❌ Don't copy-paste without adapting to specific payment method
+❌ Don't skip edge cases (missing parameters, validation failures, exceptions)
+❌ Don't use PHP 8.x syntax (constructor property promotion, match expressions, named arguments, union types)
+
+---
+
+## Section 8: Success Metrics
+
+### **Coverage Targets**
+- Phase 1 Complete: 14/52 methods (27%)
+- Phase 2 Complete: 29/52 methods (56%)
+- Phase 3 Complete: 37/52 methods (71%)
+- Phase 4 Complete: 52/52 methods (100%)
+
+### **Quality Targets**
+- **Line Coverage:** 95%+ for all payment method classes
+- **Method Coverage:** 100% of public methods tested
+- **Test Quality:** Zero AI slop, all tests match golden standard quality
+- **HTTP Mocking Only:** Zero PHP class mocking across entire test suite
+- **Consistency:** All tests follow exact patterns from golden standards
+
+### **Timeline Targets**
+- Phase 1: Complete in 2 weeks
+- Phase 2: Complete in 4 weeks (cumulative)
+- Phase 3: Complete in 6 weeks (cumulative)
+- Phase 4: Complete in 7-8 weeks (cumulative)
+
+---
+
+## Conclusion
+
+The 4 existing test files are **exemplary golden standards** requiring **ZERO refactoring**. They demonstrate:
+
+✅ Perfect test structure and organization
+✅ Comprehensive, meaningful assertions
+✅ Smart consolidation via data providers
+✅ Real-world scenario coverage
+✅ Clean, maintainable, human-quality code
+✅ Zero AI slop
+
+**The task ahead:** Replicate this quality across 44+ remaining payment methods.
+
+**Success Formula:**
+1. Use `iDealTest.php` and `CreditCardTest.php` as primary templates
+2. Follow the golden standard patterns exactly
+3. Read source payment method class before writing tests
+4. Create tests systematically, one payment method at a time
+5. Run quality checklist before each commit
+6. Never compromise on quality - every test protects real money
+
+**Expected Outcome:** World-class test coverage protecting financial transactions and customer trust across all 48+ payment methods.
+
+**Next Steps:**
+- Start Phase 1 with high-priority common methods
+- Begin with `BancontactTest.php` (highest priority, most complex)
+- Maintain golden standard quality throughout
+
+---
+
+**Last Updated:** 2026-01-14
+**Analysis Depth:** DEEP (Line-by-line review of all existing tests + full payment method inventory)
+**Status:** Planning Complete - Ready for Implementation
diff --git a/tests/Feature/PaymentMethods/SEPATest.php b/tests/Feature/PaymentMethods/SEPATest.php
new file mode 100644
index 00000000..3b113850
--- /dev/null
+++ b/tests/Feature/PaymentMethods/SEPATest.php
@@ -0,0 +1,360 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'SepaDirectDebit',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-SEPA-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('sepadirectdebit')->pay([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-SEPA-001',
+ 'iban' => 'NL13TEST0123456789',
+ 'bic' => 'TESTNL2A',
+ 'mandateReference' => 'MANDATE-001',
+ 'mandateDate' => '2024-01-15',
+ 'collectDate' => '2024-01-20',
+ 'customer' => [
+ 'name' => 'John Doe',
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertSame($transactionKey, $response->getTransactionKey());
+ $this->assertSame('INV-SEPA-001', $response->getInvoice());
+ }
+
+ /** @test */
+ public function it_creates_a_sepa_authorize_transaction(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Authorization successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'SepaDirectDebit',
+ 'Action' => 'Authorize',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AUTH-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('sepadirectdebit')->authorize([
+ 'amountDebit' => 75.00,
+ 'invoice' => 'INV-AUTH-001',
+ 'iban' => 'NL13TEST0123456789',
+ 'bic' => 'TESTNL2A',
+ 'mandateReference' => 'MANDATE-002',
+ 'mandateDate' => '2024-01-15',
+ 'customer' => [
+ 'name' => 'Jane Smith',
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertSame($transactionKey, $response->getTransactionKey());
+ $this->assertSame('INV-AUTH-001', $response->getInvoice());
+ }
+
+ /** @test */
+ public function it_creates_a_sepa_recurrent_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_SEPA_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Recurrent payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'SepaDirectDebit',
+ 'Action' => 'PayRecurrent',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-RECUR-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 29.99,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('sepadirectdebit')->payRecurrent([
+ 'amountDebit' => 29.99,
+ 'invoice' => 'INV-RECUR-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(29.99, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertSame($transactionKey, $response->getTransactionKey());
+ $this->assertSame('INV-RECUR-001', $response->getInvoice());
+ }
+
+ /** @test */
+ public function it_creates_a_sepa_payment_with_extra_info(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'SepaDirectDebit',
+ 'Action' => 'Pay,ExtraInfo',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-EXTRA-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 150.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('sepadirectdebit')->extraInfo([
+ 'amountDebit' => 150.00,
+ 'invoice' => 'INV-EXTRA-001',
+ 'iban' => 'NL13TEST0123456789',
+ 'bic' => 'TESTNL2A',
+ 'mandateReference' => 'MANDATE-003',
+ 'mandateDate' => '2024-01-15',
+ 'customer' => [
+ 'name' => 'Bob Wilson',
+ 'initials' => 'B',
+ 'lastName' => 'Wilson',
+ 'birthDate' => '1985-06-15',
+ ],
+ 'address' => [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'email' => 'bob@example.com',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(150.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertSame($transactionKey, $response->getTransactionKey());
+ $this->assertSame('INV-EXTRA-001', $response->getInvoice());
+ }
+
+ /** @test */
+ public function it_creates_a_sepa_payment_with_emandate(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'E-mandate payment successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'SepaDirectDebit',
+ 'Action' => 'PayWithEmandate',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-EMANDATE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 200.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('sepadirectdebit')->payWithEmandate([
+ 'amountDebit' => 200.00,
+ 'invoice' => 'INV-EMANDATE-001',
+ 'mandateReference' => 'EMANDATE-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(200.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertSame($transactionKey, $response->getTransactionKey());
+ $this->assertSame('INV-EMANDATE-001', $response->getInvoice());
+ }
+
+ /** @test */
+ public function it_creates_a_sepa_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_SEPA_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'SepaDirectDebit',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('sepadirectdebit')->refund([
+ 'amountCredit' => 25.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertSame($transactionKey, $response->getTransactionKey());
+ $this->assertSame('INV-REFUND-001', $response->getInvoice());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status description'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Subcode description'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'SepaDirectDebit',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('sepadirectdebit')->pay([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-STATUS-001',
+ 'iban' => 'NL13TEST0123456789',
+ 'customer' => [
+ 'name' => 'Test Customer',
+ ],
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/SubscriptionsTest.php b/tests/Feature/PaymentMethods/SubscriptionsTest.php
new file mode 100644
index 00000000..6263d0a7
--- /dev/null
+++ b/tests/Feature/PaymentMethods/SubscriptionsTest.php
@@ -0,0 +1,377 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Subscription created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Subscriptions',
+ 'Action' => 'CreateSubscription',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('subscriptions')->create([
+ 'configurationCode' => 'CONFIG-123',
+ 'includeTransaction' => false,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_combined_subscription(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Combined subscription created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Subscriptions',
+ 'Action' => 'CreateCombinedSubscription',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('subscriptions')->createCombined([
+ 'configurationCode' => 'CONFIG-456',
+ 'includeTransaction' => true,
+ 'debtor' => [
+ 'code' => 'DEBTOR-001',
+ ],
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_updates_subscription(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S002', 'Description' => 'Subscription updated'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Subscriptions',
+ 'Action' => 'UpdateSubscription',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('subscriptions')->update([
+ 'subscriptionGuid' => 'SUBSCRIPTION-GUID-123',
+ 'configurationCode' => 'CONFIG-789',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_updates_combined_subscription(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S003', 'Description' => 'Combined subscription updated'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Subscriptions',
+ 'Action' => 'UpdateCombinedSubscription',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('subscriptions')->updateCombined([
+ 'subscriptionGuid' => 'SUBSCRIPTION-GUID-456',
+ 'configurationCode' => 'CONFIG-UPDATED',
+ 'startRecurrent' => true,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_stops_subscription(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S004', 'Description' => 'Subscription stopped'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Subscriptions',
+ 'Action' => 'StopSubscription',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('subscriptions')->stop([
+ 'subscriptionGuid' => 'SUBSCRIPTION-GUID-789',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_retrieves_subscription_info(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S005', 'Description' => 'Subscription info retrieved'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Subscriptions',
+ 'Action' => 'SubscriptionInfo',
+ 'Parameters' => [
+ ['Name' => 'SubscriptionGuid', 'Value' => 'SUBSCRIPTION-GUID-INFO'],
+ ['Name' => 'Status', 'Value' => 'Active'],
+ ],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('subscriptions')->info([
+ 'subscriptionGuid' => 'SUBSCRIPTION-GUID-INFO',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+
+ $params = $response->getServiceParameters();
+ $this->assertEquals('SUBSCRIPTION-GUID-INFO', $params['subscriptionguid']);
+ $this->assertEquals('Active', $params['status']);
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_deletes_payment_configuration(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S006', 'Description' => 'Payment configuration deleted'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Subscriptions',
+ 'Action' => 'DeletePaymentConfiguration',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('subscriptions')->deletePaymentConfig([
+ 'configurationCode' => 'CONFIG-TO-DELETE',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_pauses_subscription(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S007', 'Description' => 'Subscription paused'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Subscriptions',
+ 'Action' => 'PauseSubscription',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('subscriptions')->pause([
+ 'subscriptionGuid' => 'SUBSCRIPTION-GUID-PAUSE',
+ 'pauseDate' => '2026-02-01',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_resumes_subscription(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S008', 'Description' => 'Subscription resumed'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Subscriptions',
+ 'Action' => 'ResumeSubscription',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('subscriptions')->resume([
+ 'subscriptionGuid' => 'SUBSCRIPTION-GUID-RESUME',
+ 'resumeDate' => '2026-03-01',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Subscriptions',
+ 'Action' => 'CreateSubscription',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('subscriptions')->create([
+ 'configurationCode' => 'CONFIG-STATUS-TEST',
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'waiting_on_user_input' => [790, 'isWaitingOnUserInput'],
+ 'pending_processing' => [791, 'isPendingProcessing'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/SurepayTest.php b/tests/Feature/PaymentMethods/SurepayTest.php
new file mode 100644
index 00000000..6b2a6c98
--- /dev/null
+++ b/tests/Feature/PaymentMethods/SurepayTest.php
@@ -0,0 +1,95 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/DataRequest*', [
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Verification successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'surepay',
+ 'Action' => 'verify',
+ 'Parameters' => [
+ ['Name' => 'AccountName', 'Value' => 'John Doe'],
+ ['Name' => 'IBAN', 'Value' => 'NL91ABNA0417164300'],
+ ['Name' => 'Result', 'Value' => 'Match'],
+ ],
+ ],
+ ],
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('surepay')->verify([
+ 'accountName' => 'John Doe',
+ 'iban' => 'NL91ABNA0417164300',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/DataRequest*', [
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'surepay',
+ 'Action' => 'verify',
+ 'Parameters' => [],
+ ],
+ ],
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('surepay')->verify([
+ 'accountName' => 'Test Account',
+ 'iban' => 'NL91ABNA0417164300',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/SwishTest.php b/tests/Feature/PaymentMethods/SwishTest.php
new file mode 100644
index 00000000..844b6064
--- /dev/null
+++ b/tests/Feature/PaymentMethods/SwishTest.php
@@ -0,0 +1,209 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Swish'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Swish',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-SWISH-001',
+ 'Currency' => 'SEK',
+ 'AmountDebit' => 150.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('swish')->pay([
+ 'amountDebit' => 150.00,
+ 'invoice' => 'INV-SWISH-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-SWISH-001', $response->getInvoice());
+ $this->assertEquals('SEK', $response->getCurrency());
+ $this->assertEquals(150.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_swish_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_SWISH_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Swish',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'SEK',
+ 'AmountCredit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('swish')->refund([
+ 'amountCredit' => 75.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('SEK', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_swish_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://swish.com/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Swish'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Swish',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'SEK',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('swish')->payRemainder([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('SEK', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Swish',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'SEK',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('swish')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/ThunesTest.php b/tests/Feature/PaymentMethods/ThunesTest.php
new file mode 100644
index 00000000..ccdf29da
--- /dev/null
+++ b/tests/Feature/PaymentMethods/ThunesTest.php
@@ -0,0 +1,240 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'thunes',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-THUNES-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 40.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('thunes')->pay([
+ 'amountDebit' => 40.00,
+ 'invoice' => 'INV-THUNES-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-THUNES-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(40.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_monizze_meal_voucher_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'monizzemealvoucher',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-MONIZZE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 15.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('monizzemealvoucher')->pay([
+ 'amountDebit' => 15.00,
+ 'invoice' => 'INV-MONIZZE-001',
+ 'name' => 'monizzemealvoucher',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-MONIZZE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(15.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_sodexo_meal_voucher_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Payment created'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'sodexomealvoucher',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-SODEXO-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 20.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('sodexomealvoucher')->pay([
+ 'amountDebit' => 20.00,
+ 'invoice' => 'INV-SODEXO-001',
+ 'name' => 'sodexomealvoucher',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-SODEXO-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(20.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_thunes_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_THUNES_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'thunes',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-THUNES-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('thunes')->refund([
+ 'amountCredit' => 10.00,
+ 'invoice' => 'INV-THUNES-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-THUNES-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(10.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'thunes',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('thunes')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/TrustlyTest.php b/tests/Feature/PaymentMethods/TrustlyTest.php
new file mode 100644
index 00000000..f8a9d0ae
--- /dev/null
+++ b/tests/Feature/PaymentMethods/TrustlyTest.php
@@ -0,0 +1,166 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Trustly'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Trustly',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-TRUSTLY-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 45.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('trustly')->pay([
+ 'amountDebit' => 45.00,
+ 'invoice' => 'INV-TRUSTLY-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-TRUSTLY-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(45.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_trustly_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://trustly.com/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Trustly'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Trustly',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 30.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('trustly')->payRemainder([
+ 'amountDebit' => 30.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(30.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Trustly',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('trustly')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/TwintTest.php b/tests/Feature/PaymentMethods/TwintTest.php
new file mode 100644
index 00000000..7e6d1b6b
--- /dev/null
+++ b/tests/Feature/PaymentMethods/TwintTest.php
@@ -0,0 +1,186 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Twint'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Twint',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-TWINT-001',
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('twint')->pay([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-TWINT-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-TWINT-001', $response->getInvoice());
+ }
+
+ /** @test */
+ public function it_creates_a_twint_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_TWINT_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Twint',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('twint')->refund([
+ 'amountCredit' => 50.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ }
+
+ /** @test */
+ public function it_creates_a_twint_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://twint.ch/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Twint'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Twint',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('twint')->payRemainder([
+ 'amountDebit' => 25.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Twint',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('twint')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/WeChatPayTest.php b/tests/Feature/PaymentMethods/WeChatPayTest.php
new file mode 100644
index 00000000..f533f6f4
--- /dev/null
+++ b/tests/Feature/PaymentMethods/WeChatPayTest.php
@@ -0,0 +1,169 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to WeChat Pay'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'wechatpay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-WECHAT-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 88.88,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('wechatpay')->pay([
+ 'amountDebit' => 88.88,
+ 'invoice' => 'INV-WECHAT-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-WECHAT-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(88.88, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_wechatpay_payment_with_locale(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://qr.wechat.com/pay?token=' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to WeChat Pay'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'wechatpay',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'Locale', 'Value' => 'zh_CN'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-WECHAT-LOCALE-001',
+ 'Currency' => 'CNY',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('wechatpay')->pay([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-WECHAT-LOCALE-001',
+ 'locale' => 'zh_CN',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals('INV-WECHAT-LOCALE-001', $response->getInvoice());
+ $this->assertEquals('CNY', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'wechatpay',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('wechatpay')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/WeroTest.php b/tests/Feature/PaymentMethods/WeroTest.php
new file mode 100644
index 00000000..6451f559
--- /dev/null
+++ b/tests/Feature/PaymentMethods/WeroTest.php
@@ -0,0 +1,342 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Wero'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Wero',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-WERO-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 75.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('wero')->pay([
+ 'amountDebit' => 75.00,
+ 'invoice' => 'INV-WERO-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-WERO-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(75.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_wero_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_WERO_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Wero',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 40.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('wero')->refund([
+ 'amountCredit' => 40.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(40.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_a_wero_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://wero.eu/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Wero'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Wero',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('wero')->payRemainder([
+ 'amountDebit' => 25.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /** @test */
+ public function it_creates_a_wero_authorization(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://wero.eu/redirect/' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to Wero'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'Wero',
+ 'Action' => 'Authorize',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-AUTH-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('wero')->authorize([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-AUTH-001',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-AUTH-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_cancels_a_wero_authorization(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_WERO_AUTH_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Authorization cancelled'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Wero',
+ 'Action' => 'CancelAuthorize',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-CANCEL-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('wero')->cancelAuthorize([
+ 'amountCredit' => 100.00,
+ 'invoice' => 'INV-CANCEL-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-CANCEL-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_captures_a_wero_authorization(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_WERO_AUTH_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Capture successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Wero',
+ 'Action' => 'Capture',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-CAPTURE-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 100.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('wero')->capture([
+ 'amountDebit' => 100.00,
+ 'invoice' => 'INV-CAPTURE-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-CAPTURE-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(100.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'Wero',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('wero')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/iDealProcessingTest.php b/tests/Feature/PaymentMethods/iDealProcessingTest.php
new file mode 100644
index 00000000..f403bd90
--- /dev/null
+++ b/tests/Feature/PaymentMethods/iDealProcessingTest.php
@@ -0,0 +1,294 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to bank'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'idealprocessing',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'consumerIssuer', 'Value' => 'RABONL2U'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-IDPRO-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 85.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('idealprocessing')->pay([
+ 'amountDebit' => 85.00,
+ 'invoice' => 'INV-IDPRO-001',
+ 'issuer' => 'RABONL2U',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IDPRO-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(85.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_ideal_processing_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://ideal.rabobank.nl/secure/login?trx=' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to bank'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'idealprocessing',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IDPRO-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 40.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('idealprocessing')->payRemainder([
+ 'amountDebit' => 40.00,
+ 'invoice' => 'INV-IDPRO-REMAINDER-001',
+ 'issuer' => 'RABONL2U',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IDPRO-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(40.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /** @test */
+ public function it_creates_ideal_processing_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_IDPRO_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'idealprocessing',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-IDPRO-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 20.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('idealprocessing')->refund([
+ 'amountCredit' => 20.00,
+ 'invoice' => 'INV-IDPRO-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IDPRO-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(20.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_fetches_issuers(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('GET', '*/json/Transaction/Specification/idealprocessing*', [
+ 'Actions' => [
+ [
+ 'RequestParameters' => [
+ [
+ 'ListItemDescriptions' => [
+ ['Value' => 'RABONL2U', 'Description' => 'Rabobank'],
+ ['Value' => 'INGBNL2A', 'Description' => 'ING Bank'],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ]),
+ ]);
+
+ $issuers = $this->buckaroo->method('idealprocessing')->issuers();
+
+ $this->assertIsArray($issuers);
+ $this->assertNotEmpty($issuers);
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'idealprocessing',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('idealprocessing')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ 'issuer' => 'RABONL2U',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+
+ /**
+ * @test
+ * @dataProvider issuerProvider
+ */
+ public function it_works_with_different_issuers(string $issuer): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = "https://ideal.bank.nl/secure/login?trx={$transactionKey}";
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to bank'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'idealprocessing',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-ISSUER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('idealprocessing')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-ISSUER-001',
+ 'issuer' => $issuer,
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ public static function issuerProvider(): array
+ {
+ return [
+ ['RABONL2U'],
+ ['INGBNL2A'],
+ ['ABNANL2A'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/iDealQRTest.php b/tests/Feature/PaymentMethods/iDealQRTest.php
new file mode 100644
index 00000000..9c972bb5
--- /dev/null
+++ b/tests/Feature/PaymentMethods/iDealQRTest.php
@@ -0,0 +1,336 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'QR code generated'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'idealqr',
+ 'Action' => 'Generate',
+ 'Parameters' => [
+ ['Name' => 'Amount', 'Value' => 25.00],
+ ['Name' => 'PurchaseId', 'Value' => 'PURCHASE-001'],
+ ['Name' => 'Description', 'Value' => 'QR code payment'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-QR-001',
+ 'Currency' => 'EUR',
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal_qr')->generate([
+ 'amount' => 25.00,
+ 'purchaseId' => 'PURCHASE-001',
+ 'description' => 'QR code payment',
+ 'invoice' => 'INV-QR-001',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-QR-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_generates_qr_code_with_changeable_amount(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'QR code generated'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'idealqr',
+ 'Action' => 'Generate',
+ 'Parameters' => [
+ ['Name' => 'Amount', 'Value' => 50.00],
+ ['Name' => 'AmountIsChangeable', 'Value' => true],
+ ['Name' => 'MinAmount', 'Value' => 10.00],
+ ['Name' => 'MaxAmount', 'Value' => 100.00],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-QR-002',
+ 'Currency' => 'EUR',
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal_qr')->generate([
+ 'amount' => 50.00,
+ 'amountIsChangeable' => true,
+ 'minAmount' => 10.00,
+ 'maxAmount' => 100.00,
+ 'invoice' => 'INV-QR-002',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-QR-002', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_generates_one_off_qr_code(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'One-off QR code generated'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'idealqr',
+ 'Action' => 'Generate',
+ 'Parameters' => [
+ ['Name' => 'Amount', 'Value' => 15.00],
+ ['Name' => 'IsOneOff', 'Value' => true],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-QR-003',
+ 'Currency' => 'EUR',
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal_qr')->generate([
+ 'amount' => 15.00,
+ 'isOneOff' => true,
+ 'invoice' => 'INV-QR-003',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-QR-003', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_generates_qr_code_with_expiration(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $expiration = date('Y-m-d\TH:i:s', strtotime('+1 hour'));
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'QR code with expiration'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'idealqr',
+ 'Action' => 'Generate',
+ 'Parameters' => [
+ ['Name' => 'Amount', 'Value' => 30.00],
+ ['Name' => 'Expiration', 'Value' => $expiration],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-QR-004',
+ 'Currency' => 'EUR',
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal_qr')->generate([
+ 'amount' => 30.00,
+ 'expiration' => $expiration,
+ 'invoice' => 'INV-QR-004',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-QR-004', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_generates_qr_code_with_custom_image_size(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'QR code generated'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'idealqr',
+ 'Action' => 'Generate',
+ 'Parameters' => [
+ ['Name' => 'Amount', 'Value' => 20.00],
+ ['Name' => 'ImageSize', 'Value' => 500],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-QR-005',
+ 'Currency' => 'EUR',
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal_qr')->generate([
+ 'amount' => 20.00,
+ 'imageSize' => 500,
+ 'invoice' => 'INV-QR-005',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-QR-005', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_generates_processing_qr_code(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Processing QR code generated'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'idealqr',
+ 'Action' => 'Generate',
+ 'Parameters' => [
+ ['Name' => 'Amount', 'Value' => 40.00],
+ ['Name' => 'IsProcessing', 'Value' => true],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-QR-006',
+ 'Currency' => 'EUR',
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal_qr')->generate([
+ 'amount' => 40.00,
+ 'isProcessing' => true,
+ 'invoice' => 'INV-QR-006',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-QR-006', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'idealqr',
+ 'Action' => 'Generate',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal_qr')->generate([
+ 'amount' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/iDealTest.php b/tests/Feature/PaymentMethods/iDealTest.php
new file mode 100644
index 00000000..bb3e4048
--- /dev/null
+++ b/tests/Feature/PaymentMethods/iDealTest.php
@@ -0,0 +1,379 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to bank'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'ideal',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'consumerIssuer', 'Value' => 'RABONL2U'],
+ ],
+ ],
+ ],
+ 'Invoice' => 'INV-IDEAL-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 25.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal')->pay([
+ 'amountDebit' => 25.00,
+ 'invoice' => 'INV-IDEAL-001',
+ 'issuer' => 'RABONL2U',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals('INV-IDEAL-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(25.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_creates_an_ideal_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_IDEAL_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'ideal',
+ 'Action' => 'Refund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal')->refund([
+ 'amountCredit' => 10.00,
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(10.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_creates_an_ideal_instant_refund(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $originalTxKey = 'ORIG_IDEAL_TX_KEY';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Instant refund successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'ideal',
+ 'Action' => 'instantRefund',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-INSTANT-REFUND-001',
+ 'Currency' => 'EUR',
+ 'AmountCredit' => 15.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal')->instantRefund([
+ 'amountCredit' => 15.00,
+ 'invoice' => 'INV-INSTANT-REFUND-001',
+ 'originalTransactionKey' => $originalTxKey,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals('INV-INSTANT-REFUND-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(15.00, $response->getAmountCredit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ }
+
+ /** @test */
+ public function it_creates_an_ideal_pay_remainder(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://ideal.rabobank.nl/secure/login?trx=' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to bank'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'ideal',
+ 'Action' => 'PayRemainder',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-REMAINDER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 50.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal')->payRemainder([
+ 'amountDebit' => 50.00,
+ 'invoice' => 'INV-REMAINDER-001',
+ 'issuer' => 'RABONL2U',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals('INV-REMAINDER-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(50.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /** @test */
+ public function it_creates_fast_checkout_payment(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = 'https://ideal.rabobank.nl/secure/login?trx=' . $transactionKey;
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to bank'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'ideal',
+ 'Action' => 'PayFastCheckout',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-FAST-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 35.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal')->payFastCheckout([
+ 'amountDebit' => 35.00,
+ 'invoice' => 'INV-FAST-001',
+ 'issuer' => 'RABONL2U',
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertTrue($response->hasRedirect());
+ $this->assertEquals('INV-FAST-001', $response->getInvoice());
+ $this->assertEquals('EUR', $response->getCurrency());
+ $this->assertEquals(35.00, $response->getAmountDebit());
+ $this->assertTrue($response->get('IsTest'));
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+ $this->assertEquals($redirectUrl, $response->getRedirectUrl());
+ }
+
+ /** @test */
+ public function it_fetches_issuers(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('GET', '*/json/Transaction/Specification/ideal*', [
+ 'Services' => [
+ [
+ 'Name' => 'ideal',
+ 'Parameters' => [
+ ['Name' => 'issuer', 'ListItemDescription' => 'Rabobank', 'ListItemID' => 'RABONL2U'],
+ ],
+ ],
+ ],
+ ]),
+ ]);
+
+ $issuers = $this->buckaroo->method('ideal')->issuers();
+
+ $this->assertIsArray($issuers);
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [
+ [
+ 'Name' => 'ideal',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-STATUS-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-STATUS-001',
+ 'issuer' => 'RABONL2U',
+ ]);
+
+ if ($assertMethod === 'getStatusCode') {
+ $this->assertEquals($statusCode, $response->getStatusCode());
+ } else {
+ $this->assertTrue($response->$assertMethod());
+ }
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'cancelled' => [890, 'isCanceled'],
+ 'technical_error' => [492, 'getStatusCode'],
+ 'waiting_on_consumer' => [792, 'getStatusCode'],
+ ];
+ }
+
+ /**
+ * @test
+ * @dataProvider issuerProvider
+ */
+ public function it_works_with_different_issuers(string $issuer): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $redirectUrl = "https://ideal.bank.nl/secure/login?trx={$transactionKey}";
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/Transaction*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 791, 'Description' => 'Pending processing'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Redirecting to bank'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => $redirectUrl,
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'ideal',
+ 'Action' => 'Pay',
+ 'Parameters' => [],
+ ],
+ ],
+ 'Invoice' => 'INV-ISSUER-001',
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('ideal')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'INV-ISSUER-001',
+ 'issuer' => $issuer,
+ ]);
+
+ $this->assertTrue($response->isPendingProcessing());
+ }
+
+ public static function issuerProvider(): array
+ {
+ return [
+ ['RABONL2U'],
+ ['INGBNL2A'],
+ ['ABNANL2A'],
+ ];
+ }
+}
diff --git a/tests/Feature/PaymentMethods/iDinTest.php b/tests/Feature/PaymentMethods/iDinTest.php
new file mode 100644
index 00000000..23396c86
--- /dev/null
+++ b/tests/Feature/PaymentMethods/iDinTest.php
@@ -0,0 +1,189 @@
+mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Identity verified'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'idin',
+ 'Action' => 'Identify',
+ 'Parameters' => [
+ ['Name' => 'Issuer', 'Value' => 'ABNANL2A'],
+ ['Name' => 'ConsumerBin', 'Value' => 'ABC123XYZ'],
+ ['Name' => 'ConsumerName', 'Value' => 'J. Doe'],
+ ],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('idin')->identify([
+ 'issuer' => 'ABNANL2A',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+
+ $params = $response->getServiceParameters();
+ $this->assertEquals('ABNANL2A', $params['issuer']);
+ $this->assertEquals('ABC123XYZ', $params['consumerbin']);
+ $this->assertEquals('J. Doe', $params['consumername']);
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_performs_verify_verification(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S002', 'Description' => 'Verification completed'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'idin',
+ 'Action' => 'Verify',
+ 'Parameters' => [
+ ['Name' => 'Issuer', 'Value' => 'INGBNL2A'],
+ ['Name' => 'ConsumerBin', 'Value' => 'XYZ789ABC'],
+ ['Name' => 'IsEighteenOrOlder', 'Value' => 'true'],
+ ],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('idin')->verify([
+ 'issuer' => 'INGBNL2A',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+
+ $params = $response->getServiceParameters();
+ $this->assertEquals('INGBNL2A', $params['issuer']);
+ $this->assertEquals('XYZ789ABC', $params['consumerbin']);
+ $this->assertEquals('true', $params['iseighteenorolder']);
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /** @test */
+ public function it_performs_login_verification(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S003', 'Description' => 'Login completed'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'idin',
+ 'Action' => 'Login',
+ 'Parameters' => [
+ ['Name' => 'Issuer', 'Value' => 'RABONL2U'],
+ ['Name' => 'ConsumerBin', 'Value' => 'LOGIN123BIN'],
+ ['Name' => 'SessionId', 'Value' => 'SESSION-456-XYZ'],
+ ],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('idin')->login([
+ 'issuer' => 'RABONL2U',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertEquals($transactionKey, $response->getTransactionKey());
+
+ $params = $response->getServiceParameters();
+ $this->assertEquals('RABONL2U', $params['issuer']);
+ $this->assertEquals('LOGIN123BIN', $params['consumerbin']);
+ $this->assertEquals('SESSION-456-XYZ', $params['sessionid']);
+ $this->assertTrue($response->get('IsTest'));
+ }
+
+ /**
+ * @test
+ * @dataProvider statusCodeProvider
+ */
+ public function it_handles_various_status_codes(int $statusCode, string $assertMethod): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/json/DataRequest*', [
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => $statusCode, 'Description' => 'Status'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Sub status'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'idin',
+ 'Action' => 'Identify',
+ 'Parameters' => [],
+ ],
+ ],
+ 'IsTest' => true,
+ ]),
+ ]);
+
+ $response = $this->buckaroo->method('idin')->identify([
+ 'issuer' => 'ABNANL2A',
+ ]);
+
+ $this->assertTrue($response->$assertMethod());
+ }
+
+ public static function statusCodeProvider(): array
+ {
+ return [
+ 'success' => [190, 'isSuccess'],
+ 'failed' => [490, 'isFailed'],
+ 'validation_failure' => [491, 'isValidationFailure'],
+ 'rejected' => [690, 'isRejected'],
+ 'waiting_on_user_input' => [790, 'isWaitingOnUserInput'],
+ 'pending_processing' => [791, 'isPendingProcessing'],
+ 'cancelled' => [890, 'isCanceled'],
+ ];
+ }
+}
diff --git a/tests/FeatureTestCase.php b/tests/FeatureTestCase.php
new file mode 100644
index 00000000..1aee7531
--- /dev/null
+++ b/tests/FeatureTestCase.php
@@ -0,0 +1,20 @@
+useMock();
+ }
+}
diff --git a/tests/Support/BuckarooMockRequest.php b/tests/Support/BuckarooMockRequest.php
new file mode 100755
index 00000000..f716289f
--- /dev/null
+++ b/tests/Support/BuckarooMockRequest.php
@@ -0,0 +1,76 @@
+method = strtoupper($method);
+ $this->url = $url;
+ }
+
+ /** factory */
+ public static function json(string $method, string $url, array $payload, int $status = 200, array $headers = []): self
+ {
+ $i = new self($method, $url);
+ $i->isJson = true;
+ $i->responseBody = $payload;
+ $i->status = $status;
+ $i->responseHeaders = $headers;
+
+ return $i;
+ }
+
+ /** internal api */
+ public function matches(string $method, string $url, array $headers, ?string $raw): bool
+ {
+ if (strtoupper($method) !== $this->method) {
+ return false;
+ }
+
+ $isRegex = strlen($this->url) > 2 && $this->url[0] === '/' && substr($this->url, -1) === '/';
+ $hasWildcard = !$isRegex && strpos($this->url, '*') !== false;
+
+ if ($hasWildcard) {
+ $urlMatches = preg_match('/^' . str_replace('\*', '.*', preg_quote($this->url, '/')) . '$/', $url);
+ } elseif ($isRegex) {
+ $urlMatches = preg_match($this->url, $url);
+ } else {
+ $urlMatches = $url === $this->url;
+ }
+
+ return (bool) $urlMatches;
+ }
+
+ public function mismatchMessage(string $method, string $url, array $headers, ?string $raw): string
+ {
+ $expected = strtoupper($this->method) . ' ' . $this->url;
+ $actual = strtoupper($method) . ' ' . $url;
+
+ return "Buckaroo request mismatch\nexpected: {$expected}\nactual: {$actual}\nbody: " . ($raw ?? '');
+ }
+
+ /** @return array{0:int,1:array,2:array|string,3:bool} */
+ public function responseSpec(): array
+ {
+ return [$this->status, $this->responseHeaders, $this->responseBody, $this->isJson];
+ }
+}
diff --git a/tests/Support/MockBuckaroo.php b/tests/Support/MockBuckaroo.php
new file mode 100755
index 00000000..c3ea0b70
--- /dev/null
+++ b/tests/Support/MockBuckaroo.php
@@ -0,0 +1,131 @@
+queue = array_values($requests);
+ $this->index = 0;
+ }
+
+ public function assertAllConsumed(): void
+ {
+ $left = count($this->queue) - $this->index;
+ if ($left > 0) {
+ throw new LogicException("{$left} Buckaroo request(s) were not called");
+ }
+ }
+
+ public function resetQueue(): void
+ {
+ $this->queue = [];
+ $this->index = 0;
+ }
+
+ /** internal dispatch called by the fake client */
+ public function dispatch(string $method, string $url, array $headers, ?string $raw): array
+ {
+ if ($this->index >= count($this->queue)) {
+ throw new LogicException("Unexpected Buckaroo call with no mocks left: {$method} {$url}");
+ }
+
+ $req = $this->queue[$this->index];
+
+ if (!$req->matches($method, $url, $headers, $raw)) {
+ throw new LogicException($req->mismatchMessage($method, $url, $headers, $raw));
+ }
+
+ $this->index++;
+
+ [$status, $respHeaders, $body, $isJson] = $req->responseSpec();
+ if ($isJson) {
+ $respHeaders = ['Content-Type' => 'application/json'] + $respHeaders;
+ $encoded = json_encode($body, JSON_UNESCAPED_SLASHES);
+ $response = new Response($status, $respHeaders, $encoded);
+
+ return [$response, $body];
+ }
+
+ $response = new Response($status, $respHeaders, (string) $body);
+
+ return [$response, (string) $body];
+ }
+
+ public function install(): void
+ {
+ self::$instance = $this;
+
+ // Only install the overload mock once per PHP process
+ // The class can only be mocked before it's loaded
+ if (self::$mockInstalled) {
+ return;
+ }
+
+ self::$mockInstalled = true;
+
+ /** Create a fresh overload mock for HttpClientFactory used by Client */
+ $factoryOverload = Mockery::mock('overload:' . HttpClientFactory::class);
+
+ /** Return a fake HttpClientInterface that dispatches to our queue-based mock */
+ $factoryOverload
+ ->shouldReceive('createClient')
+ ->zeroOrMoreTimes()
+ ->andReturnUsing(static function ($config) {
+ return new class() implements HttpClientInterface {
+ public function call(string $url, array $headers, string $method, ?string $data = null)
+ {
+ $mock = MockBuckaroo::getInstance();
+
+ if (!$mock) {
+ throw new LogicException(
+ 'No MockBuckaroo registered. Call useMock() in your test.'
+ );
+ }
+
+ return $mock->dispatch($method, $url, $headers, $data);
+ }
+ };
+ });
+ }
+
+ public static function getInstance(): ?MockBuckaroo
+ {
+ return self::$instance;
+ }
+
+ public static function clearInstance(): void
+ {
+ if (self::$instance) {
+ self::$instance->resetQueue();
+ }
+ self::$instance = null;
+ }
+}
diff --git a/tests/Support/TestHelpers.php b/tests/Support/TestHelpers.php
new file mode 100755
index 00000000..a709350e
--- /dev/null
+++ b/tests/Support/TestHelpers.php
@@ -0,0 +1,97 @@
+ $value) {
+ $dataString .= $key . '=' . html_entity_decode((string) $value);
+ }
+
+ return sha1($dataString . $secretKey);
+ }
+
+ /**
+ * Generate a realistic Buckaroo transaction key
+ * Format: 32-character alphanumeric string (uppercase)
+ *
+ * @param string|null $suffix Optional suffix for specific test scenarios
+ * @return string Transaction key in Buckaroo format
+ */
+ public static function generateTransactionKey(?string $suffix = null): string
+ {
+ return strtoupper(bin2hex(random_bytes(16)));
+ }
+
+ /**
+ * Create a successful transaction response fixture
+ *
+ * @param array $overrides Override specific fields
+ * @return array
+ */
+ public static function successResponse(array $overrides = []): array
+ {
+ return array_merge([
+ 'Key' => self::generateTransactionKey(),
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'Transaction successful'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'RequiredAction' => null,
+ 'Services' => [],
+ 'Invoice' => 'INV-' . uniqid(),
+ 'ServiceCode' => 'creditcard',
+ 'IsTest' => true,
+ 'Currency' => 'EUR',
+ 'AmountDebit' => 10.00,
+ ], $overrides);
+ }
+
+ /**
+ * Create a failed response fixture
+ *
+ * @param string $error Error message
+ * @param array $overrides Override specific fields
+ * @return array
+ */
+ public static function failedResponse(string $error = 'Transaction failed', array $overrides = []): array
+ {
+ return self::successResponse(array_merge([
+ 'Status' => [
+ 'Code' => ['Code' => 490, 'Description' => 'Failed'],
+ 'SubCode' => ['Code' => 'F001', 'Description' => $error],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ ], $overrides));
+ }
+}
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 00000000..1e5ee59e
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,54 @@
+mockBuckaroo = new MockBuckaroo();
+ }
+
+ /**
+ * Enable HTTP mocking for this test.
+ * Call this method before using mockTransportRequests().
+ * IMPORTANT: Must be called before createBuckarooClient().
+ */
+ protected function useMock(): void
+ {
+ $this->mockEnabled = true;
+ $this->mockBuckaroo->install();
+ $this->buckaroo = $this->createBuckarooClient();
+ }
+
+ protected function tearDown(): void
+ {
+ if ($this->mockEnabled) {
+ $this->mockBuckaroo->assertAllConsumed();
+ }
+ MockBuckaroo::clearInstance();
+
+ parent::tearDown();
+ }
+
+ protected function createBuckarooClient(array $overrides = []): BuckarooClient
+ {
+ return new BuckarooClient(
+ $overrides['websiteKey'] ?? $_ENV['BPE_WEBSITE_KEY'],
+ $overrides['secretKey'] ?? $_ENV['BPE_SECRET_KEY'],
+ $overrides['mode'] ?? 'test'
+ );
+ }
+}
diff --git a/tests/Unit/BuckarooClientTest.php b/tests/Unit/BuckarooClientTest.php
new file mode 100644
index 00000000..5eba642f
--- /dev/null
+++ b/tests/Unit/BuckarooClientTest.php
@@ -0,0 +1,324 @@
+useMock();
+ }
+
+ public function test_creates_client_with_string_credentials(): void
+ {
+ $client = new BuckarooClient('test-key', 'test-secret', 'test');
+
+ $internalClient = $client->client();
+ $config = $internalClient->config();
+
+ $this->assertInstanceOf(Client::class, $internalClient);
+ $this->assertInstanceOf(Config::class, $config);
+ $this->assertSame('test-key', $config->websiteKey());
+ $this->assertSame('test-secret', $config->secretKey());
+ $this->assertSame('test', $config->mode());
+ }
+
+ public function test_creates_client_with_config_object(): void
+ {
+ $config = new DefaultConfig('config-key', 'config-secret', 'live');
+
+ $client = new BuckarooClient($config);
+
+ $internalClient = $client->client();
+ $retrievedConfig = $internalClient->config();
+
+ $this->assertSame($config, $retrievedConfig);
+ $this->assertSame('config-key', $retrievedConfig->websiteKey());
+ $this->assertSame('config-secret', $retrievedConfig->secretKey());
+ $this->assertSame('live', $retrievedConfig->mode());
+ }
+
+ public function test_creates_client_with_different_modes(): void
+ {
+ $defaultClient = new BuckarooClient('test-key', 'test-secret');
+ $this->assertSame('test', $defaultClient->client()->config()->mode());
+ $this->assertFalse($defaultClient->client()->config()->isLiveMode());
+
+ $testClient = new BuckarooClient('test-key', 'test-secret', 'test');
+ $this->assertSame('test', $testClient->client()->config()->mode());
+ $this->assertFalse($testClient->client()->config()->isLiveMode());
+
+ $liveClient = new BuckarooClient('live-key', 'live-secret', 'live');
+ $this->assertSame('live', $liveClient->client()->config()->mode());
+ $this->assertTrue($liveClient->client()->config()->isLiveMode());
+ }
+
+ public function test_throws_exception_when_credentials_missing(): void
+ {
+ $this->expectException(BuckarooException::class);
+ $this->expectExceptionMessage('Config is missing');
+
+ new BuckarooClient('', '');
+ }
+
+ public function test_creates_payment_methods(): void
+ {
+ $client = new BuckarooClient('test-key', 'test-secret');
+
+ $ideal = $client->method('ideal');
+ $this->assertInstanceOf(PaymentFacade::class, $ideal);
+ $this->assertSame('ideal', $ideal->paymentMethod()->paymentName());
+
+ $creditcard = $client->method('creditcard');
+ $this->assertInstanceOf(PaymentFacade::class, $creditcard);
+
+ $paypal = $client->method('paypal');
+ $this->assertInstanceOf(PaymentFacade::class, $paypal);
+ $this->assertSame('paypal', $paypal->paymentMethod()->paymentName());
+
+ $generic = $client->method(null);
+ $this->assertInstanceOf(PaymentFacade::class, $generic);
+ }
+
+ public function test_creates_transaction_service_with_transaction_key(): void
+ {
+ $client = new BuckarooClient('test-key', 'test-secret');
+
+ $service = $client->transaction('TX-12345');
+
+ $this->assertInstanceOf(TransactionService::class, $service);
+ }
+
+ public function test_passes_client_to_transaction_service(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Transaction/Status/TX-12345',
+ TestHelpers::successResponse([
+ 'Key' => 'TX-12345',
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ ],
+ ])
+ ),
+ ]);
+
+ $client = new BuckarooClient($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $service = $client->transaction('TX-12345');
+
+ $response = $service->status();
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertSame('TX-12345', $response->getTransactionKey());
+ }
+
+ public function test_returns_internal_client(): void
+ {
+ $client = new BuckarooClient('test-key', 'test-secret');
+
+ $internalClient = $client->client();
+
+ $this->assertInstanceOf(Client::class, $internalClient);
+ }
+
+ public function test_updates_config_on_client(): void
+ {
+ $client = new BuckarooClient('original-key', 'original-secret', 'test');
+
+ $newConfig = new DefaultConfig('new-key', 'new-secret', 'live');
+ $result = $client->setConfig($newConfig);
+
+ $this->assertSame($client, $result);
+
+ $retrievedConfig = $client->client()->config();
+ $this->assertSame('new-key', $retrievedConfig->websiteKey());
+ $this->assertSame('new-secret', $retrievedConfig->secretKey());
+ $this->assertSame('live', $retrievedConfig->mode());
+ }
+
+ public function test_preserves_config_across_operations(): void
+ {
+ $config = new DefaultConfig('persistent-key', 'persistent-secret', 'test');
+ $client = new BuckarooClient($config);
+
+ $client->method('ideal');
+ $client->transaction('TX-123');
+
+ $retrievedConfig = $client->client()->config();
+
+ $this->assertSame($config, $retrievedConfig);
+ $this->assertSame('persistent-key', $retrievedConfig->websiteKey());
+ $this->assertSame('persistent-secret', $retrievedConfig->secretKey());
+ }
+
+ public function test_validates_credentials_successfully(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Specification/ideal*',
+ [
+ 'Services' => [
+ [
+ 'Name' => 'ideal',
+ 'Version' => 2,
+ ],
+ ],
+ ],
+ 200
+ ),
+ ]);
+
+ $client = new BuckarooClient($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ $result = $client->confirmCredential();
+
+ $this->assertTrue($result);
+ }
+
+ public function test_returns_false_for_invalid_credentials(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Specification/ideal*',
+ [
+ 'error' => 'Invalid credentials',
+ ],
+ 401
+ ),
+ ]);
+
+ $client = new BuckarooClient('invalid-key', 'invalid-secret');
+
+ $result = $client->confirmCredential();
+
+ $this->assertFalse($result);
+ }
+
+ public function test_attaches_logger_observer(): void
+ {
+ $client = new BuckarooClient('test-key', 'test-secret');
+
+ $observer = new class implements Observer {
+ public function handle(string $method, string $message, array $context = []): void
+ {
+ }
+ };
+
+ $result = $client->attachLogger($observer);
+
+ $this->assertSame($client, $result);
+
+ $config = $client->client()->config();
+ $logger = $config->getLogger();
+
+ $this->assertNotNull($logger);
+ }
+
+ public function test_creates_batch_transactions(): void
+ {
+ $client = new BuckarooClient('test-key', 'test-secret');
+
+ $transactions = [
+ ['AmountDebit' => 10.00, 'Invoice' => 'INV-001'],
+ ['AmountDebit' => 20.00, 'Invoice' => 'INV-002'],
+ ];
+
+ $batch = $client->batch($transactions);
+
+ $this->assertInstanceOf(BatchTransactions::class, $batch);
+ }
+
+ public function test_retrieves_active_subscriptions_list(): void
+ {
+ $xmlResponse = '
+
+
+ PLAN-001
+ SUB-001
+
+ EUR
+ USD
+
+
+';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/DataRequest*',
+ [
+ 'Key' => 'DATARESPONSE-001',
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ 'SubCode' => ['Code' => 'S001'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'GetActiveSubscriptions',
+ 'Action' => 'GetActiveSubscriptions',
+ 'Parameters' => [
+ [
+ 'Name' => 'activesubscriptions',
+ 'Value' => $xmlResponse,
+ ],
+ ],
+ ],
+ ],
+ ]
+ ),
+ ]);
+
+ $client = new BuckarooClient($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ $subscriptions = $client->getActiveSubscriptions();
+
+ $this->assertIsArray($subscriptions);
+ $this->assertNotEmpty($subscriptions);
+ $this->assertArrayHasKey('ratePlanCode', $subscriptions[0]);
+ $this->assertSame('PLAN-001', $subscriptions[0]['ratePlanCode']);
+ }
+
+ public function test_handles_empty_subscriptions_response(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/DataRequest/',
+ [
+ 'ServiceParameters' => [],
+ ]
+ ),
+ ]);
+
+ $client = new BuckarooClient($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ $subscriptions = $client->getActiveSubscriptions();
+
+ $this->assertIsArray($subscriptions);
+ $this->assertEmpty($subscriptions);
+ }
+}
diff --git a/tests/Unit/Config/ConfigTest.php b/tests/Unit/Config/ConfigTest.php
new file mode 100644
index 00000000..3a96986c
--- /dev/null
+++ b/tests/Unit/Config/ConfigTest.php
@@ -0,0 +1,597 @@
+assertSame('websiteKey', $config->websiteKey());
+ $this->assertSame('secretKey', $config->secretKey());
+ }
+
+ public function test_defaults_to_test_mode(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $this->assertSame('test', $config->mode());
+ $this->assertFalse($config->isLiveMode());
+ }
+
+ public function test_can_set_live_mode(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey', 'live');
+
+ $this->assertSame('live', $config->mode());
+ $this->assertTrue($config->isLiveMode());
+ }
+
+ public function test_defaults_currency_to_eur(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $this->assertSame('EUR', $config->currency());
+ }
+
+ public function test_sets_optional_url_parameters(): void
+ {
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'test',
+ 'EUR',
+ 'https://example.com/return',
+ 'https://example.com/cancel',
+ 'https://example.com/push'
+ );
+
+ $this->assertSame('https://example.com/return', $config->returnURL());
+ $this->assertSame('https://example.com/cancel', $config->returnURLCancel());
+ $this->assertSame('https://example.com/push', $config->pushURL());
+ }
+
+ public function test_merges_additional_config(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $config->merge([
+ 'currency' => 'USD',
+ 'returnURL' => 'https://merged.com/return',
+ ]);
+
+ $this->assertSame('USD', $config->currency());
+ $this->assertSame('https://merged.com/return', $config->returnURL());
+ }
+
+ public function test_prevents_merging_credentials(): void
+ {
+ $config = new DefaultConfig('originalWebsiteKey', 'originalSecretKey');
+
+ $config->merge([
+ 'websiteKey' => 'hackedWebsiteKey',
+ 'secretKey' => 'hackedSecretKey',
+ 'currency' => 'GBP',
+ ]);
+
+ // Credentials should NOT be changed
+ $this->assertSame('originalWebsiteKey', $config->websiteKey());
+ $this->assertSame('originalSecretKey', $config->secretKey());
+ // Other fields should be merged
+ $this->assertSame('GBP', $config->currency());
+ }
+
+ public function test_returns_default_culture(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $this->assertSame('en-GB', $config->culture());
+ }
+
+ public function test_returns_default_channel(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $this->assertSame('Web', $config->channel());
+ }
+
+ public function test_can_change_mode_via_mode_method(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey', 'test');
+
+ $this->assertSame('test', $config->mode());
+
+ // Change to live
+ $config->mode('live');
+ $this->assertSame('live', $config->mode());
+ $this->assertTrue($config->isLiveMode());
+
+ // Change back to test
+ $config->mode('test');
+ $this->assertSame('test', $config->mode());
+ $this->assertFalse($config->isLiveMode());
+ }
+
+ public function test_ignores_invalid_mode_values(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey', 'test');
+
+ // Try to set invalid mode
+ $config->mode('invalid');
+
+ // Should remain unchanged
+ $this->assertSame('test', $config->mode());
+ }
+
+ public function test_gets_multiple_properties(): void
+ {
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'test',
+ 'EUR',
+ 'https://example.com/return'
+ );
+
+ $properties = $config->get(['websiteKey', 'secretKey', 'mode', 'currency']);
+
+ $this->assertArrayHasKey('websiteKey', $properties);
+ $this->assertArrayHasKey('secretKey', $properties);
+ $this->assertArrayHasKey('mode', $properties);
+ $this->assertArrayHasKey('currency', $properties);
+ $this->assertSame('websiteKey', $properties['websiteKey']);
+ $this->assertSame('test', $properties['mode']);
+ }
+
+ public function test_sets_platform_information(): void
+ {
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'test',
+ 'EUR',
+ null,
+ null,
+ null,
+ 'MyPlatform',
+ '2.0.0',
+ 'MyCompany',
+ 'MyModule',
+ '1.5.0'
+ );
+
+ $this->assertSame('MyPlatform', $config->platformName());
+ $this->assertSame('2.0.0', $config->platformVersion());
+ $this->assertSame('MyCompany', $config->moduleSupplier());
+ $this->assertSame('MyModule', $config->moduleName());
+ $this->assertSame('1.5.0', $config->moduleVersion());
+ }
+
+ public function test_returns_null_timeout_by_default(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $this->assertNull($config->getTimeout());
+ $this->assertNull($config->getConnectTimeout());
+ }
+
+ public function test_sets_timeout_values(): void
+ {
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'test',
+ 'EUR',
+ null, // returnURL
+ null, // returnURLCancel
+ null, // pushURL
+ null, // platformName
+ null, // platformVersion
+ null, // moduleSupplier
+ null, // moduleName
+ null, // moduleVersion
+ null, // culture
+ null, // channel
+ null, // logger
+ 30, // timeout
+ 10 // connectTimeout
+ );
+
+ $this->assertSame(30, $config->getTimeout());
+ $this->assertSame(10, $config->getConnectTimeout());
+ }
+
+ public function test_creates_default_logger_automatically(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $logger = $config->getLogger();
+
+ $this->assertNotNull($logger);
+ $this->assertInstanceOf(Subject::class, $logger);
+ $this->assertInstanceOf(DefaultLogger::class, $logger);
+ }
+
+ public function test_accepts_custom_logger(): void
+ {
+ $customLogger = new DefaultLogger();
+
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'test',
+ 'EUR',
+ null, // returnURL
+ null, // returnURLCancel
+ null, // pushURL
+ null, // platformName
+ null, // platformVersion
+ null, // moduleSupplier
+ null, // moduleName
+ null, // moduleVersion
+ null, // culture
+ null, // channel
+ $customLogger
+ );
+
+ $this->assertSame($customLogger, $config->getLogger());
+ }
+
+ public function test_can_change_logger_via_setter(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $originalLogger = $config->getLogger();
+
+ $newLogger = new DefaultLogger();
+ $config->setLogger($newLogger);
+
+ $this->assertNotSame($originalLogger, $config->getLogger());
+ $this->assertSame($newLogger, $config->getLogger());
+ }
+
+ public function test_returns_self_from_set_logger(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $newLogger = new DefaultLogger();
+
+ $result = $config->setLogger($newLogger);
+
+ $this->assertSame($config, $result);
+ }
+
+ public function test_allows_logger_to_attach_observers(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $logger = $config->getLogger();
+
+ // Create a mock observer
+ $observer = new class implements Observer {
+ public array $messages = [];
+
+ public function handle(string $method, string $message, array $context = []): void
+ {
+ $this->messages[] = ['method' => $method, 'message' => $message, 'context' => $context];
+ }
+ };
+
+ $logger->attach($observer);
+ $logger->info('Test message', ['key' => 'value']);
+
+ $this->assertCount(1, $observer->messages);
+ $this->assertSame('info', $observer->messages[0]['method']);
+ $this->assertSame('Test message', $observer->messages[0]['message']);
+ $this->assertSame(['key' => 'value'], $observer->messages[0]['context']);
+ }
+
+ public function test_allows_logger_to_detach_observers(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $logger = $config->getLogger();
+
+ $observer = new class implements Observer {
+ public array $messages = [];
+
+ public function handle(string $method, string $message, array $context = []): void
+ {
+ $this->messages[] = $message;
+ }
+ };
+
+ $logger->attach($observer);
+ $logger->info('First message');
+
+ $this->assertCount(1, $observer->messages);
+
+ $logger->detach($observer);
+ $logger->info('Second message');
+
+ // Should still be 1 since observer was detached
+ $this->assertCount(1, $observer->messages);
+ }
+
+ public function test_defines_mode_constants(): void
+ {
+ $this->assertSame('live', DefaultConfig::LIVE_MODE);
+ $this->assertSame('test', DefaultConfig::TEST_MODE);
+ }
+
+ public function test_uses_environment_variable_for_mode(): void
+ {
+ $_ENV['BPE_MODE'] = 'live';
+
+ $config = new DefaultConfig('websiteKey', 'secretKey', 'test');
+
+ // Environment variable should override constructor parameter
+ $this->assertSame('live', $config->mode());
+ $this->assertTrue($config->isLiveMode());
+ }
+
+ public function test_uses_environment_variable_for_currency(): void
+ {
+ $_ENV['BPE_CURRENCY_CODE'] = 'USD';
+
+ $config = new DefaultConfig('websiteKey', 'secretKey', 'test', 'EUR');
+
+ // Environment variable should override constructor parameter
+ $this->assertSame('USD', $config->currency());
+ }
+
+ public function test_uses_environment_variables_for_urls(): void
+ {
+ $_ENV['BPE_RETURN_URL'] = 'https://env.example.com/return';
+ $_ENV['BPE_RETURN_URL_CANCEL'] = 'https://env.example.com/cancel';
+ $_ENV['BPE_PUSH_URL'] = 'https://env.example.com/push';
+
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'test',
+ 'EUR',
+ 'https://constructor.example.com/return',
+ 'https://constructor.example.com/cancel',
+ 'https://constructor.example.com/push'
+ );
+
+ // Environment variables should override constructor parameters
+ $this->assertSame('https://env.example.com/return', $config->returnURL());
+ $this->assertSame('https://env.example.com/cancel', $config->returnURLCancel());
+ $this->assertSame('https://env.example.com/push', $config->pushURL());
+ }
+
+ public function test_uses_environment_variables_for_platform_information(): void
+ {
+ $_ENV['PlatformName'] = 'EnvPlatform';
+ $_ENV['PlatformVersion'] = '3.0.0';
+ $_ENV['ModuleSupplier'] = 'EnvSupplier';
+ $_ENV['ModuleName'] = 'EnvModule';
+ $_ENV['ModuleVersion'] = '2.5.0';
+
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'test',
+ 'EUR',
+ null,
+ null,
+ null,
+ 'ConstructorPlatform',
+ '1.0.0',
+ 'ConstructorSupplier',
+ 'ConstructorModule',
+ '1.0.0'
+ );
+
+ // Environment variables should override constructor parameters
+ $this->assertSame('EnvPlatform', $config->platformName());
+ $this->assertSame('3.0.0', $config->platformVersion());
+ $this->assertSame('EnvSupplier', $config->moduleSupplier());
+ $this->assertSame('EnvModule', $config->moduleName());
+ $this->assertSame('2.5.0', $config->moduleVersion());
+ }
+
+ public function test_uses_environment_variables_for_culture_and_channel(): void
+ {
+ $_ENV['Culture'] = 'nl-NL';
+ $_ENV['Channel'] = 'Mobile';
+
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'test',
+ 'EUR',
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 'en-GB',
+ 'Web'
+ );
+
+ // Environment variables should override constructor parameters
+ $this->assertSame('nl-NL', $config->culture());
+ $this->assertSame('Mobile', $config->channel());
+ }
+
+ public function test_falls_back_to_constructor_when_no_environment_variable(): void
+ {
+ // Ensure no environment variables are set
+ unset(
+ $_ENV['BPE_MODE'],
+ $_ENV['BPE_CURRENCY_CODE'],
+ $_ENV['BPE_RETURN_URL']
+ );
+
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'live',
+ 'USD',
+ 'https://example.com/return'
+ );
+
+ $this->assertSame('live', $config->mode());
+ $this->assertSame('USD', $config->currency());
+ $this->assertSame('https://example.com/return', $config->returnURL());
+ }
+
+ public function test_sets_custom_culture_via_constructor(): void
+ {
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'test',
+ 'EUR',
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 'nl-NL'
+ );
+
+ $this->assertSame('nl-NL', $config->culture());
+ }
+
+ public function test_sets_custom_channel_via_constructor(): void
+ {
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'test',
+ 'EUR',
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 'Mobile'
+ );
+
+ $this->assertSame('Mobile', $config->channel());
+ }
+
+ public function test_returns_self_from_merge(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $result = $config->merge(['currency' => 'GBP']);
+
+ $this->assertSame($config, $result);
+ }
+
+ public function test_ignores_non_existent_properties_in_merge(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $config->merge([
+ 'currency' => 'GBP',
+ 'nonExistentProperty' => 'value',
+ 'anotherFake' => 'ignored',
+ ]);
+
+ $this->assertSame('GBP', $config->currency());
+ $this->assertFalse(property_exists($config, 'nonExistentProperty'));
+ $this->assertFalse(property_exists($config, 'anotherFake'));
+ }
+
+ public function test_merges_empty_array_without_errors(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $originalMode = $config->mode();
+
+ $result = $config->merge([]);
+
+ $this->assertSame($config, $result);
+ $this->assertSame($originalMode, $config->mode());
+ }
+
+ public function test_accumulates_multiple_merge_operations(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $config->merge(['currency' => 'USD']);
+ $config->merge(['returnURL' => 'https://example.com']);
+ $config->merge(['currency' => 'GBP']); // Override previous merge
+
+ $this->assertSame('GBP', $config->currency());
+ $this->assertSame('https://example.com', $config->returnURL());
+ }
+
+ public function test_merges_culture_and_channel_values(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $config->merge([
+ 'culture' => 'de-DE',
+ 'channel' => 'API',
+ ]);
+
+ $this->assertSame('de-DE', $config->culture());
+ $this->assertSame('API', $config->channel());
+ }
+
+ public function test_gets_empty_array_when_called_with_empty_array(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $properties = $config->get([]);
+
+ $this->assertIsArray($properties);
+ $this->assertEmpty($properties);
+ }
+
+ public function test_ignores_non_existent_method_names_in_get(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $properties = $config->get(['websiteKey', 'nonExistentMethod', 'mode']);
+
+ $this->assertArrayHasKey('websiteKey', $properties);
+ $this->assertArrayHasKey('mode', $properties);
+ $this->assertArrayNotHasKey('nonExistentMethod', $properties);
+ $this->assertCount(2, $properties);
+ }
+
+ public function test_gets_only_valid_methods_from_mixed_input(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+
+ $properties = $config->get([
+ 'websiteKey',
+ 'invalidMethod1',
+ 'mode',
+ 'invalidMethod2',
+ 'currency',
+ ]);
+
+ $this->assertArrayHasKey('websiteKey', $properties);
+ $this->assertArrayHasKey('mode', $properties);
+ $this->assertArrayHasKey('currency', $properties);
+ $this->assertArrayNotHasKey('invalidMethod1', $properties);
+ $this->assertArrayNotHasKey('invalidMethod2', $properties);
+ $this->assertCount(3, $properties);
+ $this->assertSame('websiteKey', $properties['websiteKey']);
+ $this->assertSame('test', $properties['mode']);
+ $this->assertSame('EUR', $properties['currency']);
+ }
+}
diff --git a/tests/Unit/Exceptions/BuckarooExceptionTest.php b/tests/Unit/Exceptions/BuckarooExceptionTest.php
new file mode 100644
index 00000000..2c8359e7
--- /dev/null
+++ b/tests/Unit/Exceptions/BuckarooExceptionTest.php
@@ -0,0 +1,137 @@
+assertInstanceOf(Exception::class, $exception);
+ $this->assertInstanceOf(Throwable::class, $exception);
+ }
+
+ public function test_it_creates_exception_with_message(): void
+ {
+ $exception = new BuckarooException(null, 'Test error message');
+
+ $this->assertStringContainsString('Test error message', $exception->getMessage());
+ }
+
+ public function test_it_prepends_sdk_exception_prefix_to_message(): void
+ {
+ $exception = new BuckarooException(null, 'Original message');
+
+ $this->assertStringStartsWith('Buckaroo SDKExeption:', $exception->getMessage());
+ $this->assertSame('Buckaroo SDKExeption: Original message', $exception->getMessage());
+ }
+
+ public function test_it_creates_exception_with_code(): void
+ {
+ $exception = new BuckarooException(null, 'Test message', 500);
+
+ $this->assertSame(500, $exception->getCode());
+ }
+
+ public function test_it_creates_exception_with_previous_exception(): void
+ {
+ $previous = new Exception('Previous exception');
+ $exception = new BuckarooException(null, 'Current message', 0, $previous);
+
+ $this->assertSame($previous, $exception->getPrevious());
+ }
+
+ public function test_it_is_throwable(): void
+ {
+ $this->expectException(BuckarooException::class);
+ $this->expectExceptionMessage('Buckaroo SDKExeption: Thrown exception');
+
+ throw new BuckarooException(null, 'Thrown exception');
+ }
+
+ public function test_it_can_be_caught(): void
+ {
+ $caught = false;
+
+ try {
+ throw new BuckarooException(null, 'Caught exception');
+ } catch (BuckarooException $e) {
+ $caught = true;
+ $this->assertStringContainsString('Caught exception', $e->getMessage());
+ }
+
+ $this->assertTrue($caught);
+ }
+
+ public function test_it_logs_error_when_logger_provided(): void
+ {
+ $loggedMessage = null;
+
+ $observer = new class($loggedMessage) implements Observer {
+ private $loggedMessage;
+
+ public function __construct(&$loggedMessage)
+ {
+ $this->loggedMessage = &$loggedMessage;
+ }
+
+ public function handle(string $method, string $message, array $context = []): void
+ {
+ if ($method === 'error') {
+ $this->loggedMessage = $message;
+ }
+ }
+ };
+
+ $logger = new DefaultLogger();
+ $logger->attach($observer);
+
+ new BuckarooException($logger, 'Logged error message');
+
+ $this->assertSame('Buckaroo SDKExeption: Logged error message', $loggedMessage);
+ }
+
+ public function test_it_does_not_log_when_logger_is_null(): void
+ {
+ // Should not throw any errors when logger is null
+ $exception = new BuckarooException(null, 'No logger message');
+
+ $this->assertInstanceOf(BuckarooException::class, $exception);
+ }
+
+ public function test_it_handles_empty_message(): void
+ {
+ $exception = new BuckarooException(null, '');
+
+ $this->assertSame('Buckaroo SDKExeption: ', $exception->getMessage());
+ }
+
+ public function test_it_handles_special_characters_in_message(): void
+ {
+ $message = 'Error with special chars: "quoted" & and unicode: é';
+
+ $exception = new BuckarooException(null, $message);
+
+ $this->assertStringContainsString($message, $exception->getMessage());
+ }
+
+ public function test_exception_chaining(): void
+ {
+ $root = new Exception('Root cause');
+ $middle = new BuckarooException(null, 'Middle exception', 0, $root);
+ $top = new BuckarooException(null, 'Top exception', 0, $middle);
+
+ $this->assertSame($middle, $top->getPrevious());
+ $this->assertSame($root, $top->getPrevious()->getPrevious());
+ }
+}
diff --git a/tests/Unit/Exceptions/TransferExceptionTest.php b/tests/Unit/Exceptions/TransferExceptionTest.php
new file mode 100644
index 00000000..28f7fba2
--- /dev/null
+++ b/tests/Unit/Exceptions/TransferExceptionTest.php
@@ -0,0 +1,86 @@
+assertInstanceOf(BuckarooException::class, $exception);
+ $this->assertInstanceOf(Exception::class, $exception);
+ }
+
+ public function test_it_prepends_transfer_exception_prefix(): void
+ {
+ $exception = new TransferException(null, 'Transfer failed');
+
+ $this->assertStringStartsWith('Buckaroo TransferException', $exception->getMessage());
+ $this->assertSame('Buckaroo TransferException Transfer failed', $exception->getMessage());
+ }
+
+ public function test_it_creates_exception_with_code(): void
+ {
+ $exception = new TransferException(null, 'Transfer error', 503);
+
+ $this->assertSame(503, $exception->getCode());
+ }
+
+ public function test_it_creates_exception_with_previous_exception(): void
+ {
+ $guzzleException = new Exception('Guzzle HTTP error');
+ $exception = new TransferException(null, 'Transfer failed', 0, $guzzleException);
+
+ $this->assertSame($guzzleException, $exception->getPrevious());
+ }
+
+ public function test_it_is_catchable_as_buckaroo_exception(): void
+ {
+ $caught = false;
+
+ try {
+ throw new TransferException(null, 'Network error');
+ } catch (BuckarooException $e) {
+ $caught = true;
+ $this->assertInstanceOf(TransferException::class, $e);
+ }
+
+ $this->assertTrue($caught);
+ }
+
+ public function test_it_logs_when_logger_provided(): void
+ {
+ $logger = new DefaultLogger();
+
+ // Should not throw errors when logging
+ $exception = new TransferException($logger, 'Logged transfer error');
+
+ $this->assertInstanceOf(TransferException::class, $exception);
+ }
+
+ public function test_it_can_be_thrown_and_caught(): void
+ {
+ $this->expectException(TransferException::class);
+ $this->expectExceptionMessage('Buckaroo TransferException Connection timeout');
+
+ throw new TransferException(null, 'Connection timeout');
+ }
+
+ public function test_message_format_differs_from_base_exception(): void
+ {
+ $buckarooException = new BuckarooException(null, 'Test');
+ $transferException = new TransferException(null, 'Test');
+
+ $this->assertSame('Buckaroo SDKExeption: Test', $buckarooException->getMessage());
+ $this->assertSame('Buckaroo TransferException Test', $transferException->getMessage());
+ }
+}
diff --git a/tests/Unit/Handlers/CredentialsTest.php b/tests/Unit/Handlers/CredentialsTest.php
new file mode 100644
index 00000000..73743628
--- /dev/null
+++ b/tests/Unit/Handlers/CredentialsTest.php
@@ -0,0 +1,69 @@
+useMock();
+ }
+
+ /** @test */
+ public function it_confirms_valid_credentials(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Specification/ideal*',
+ [
+ 'Services' => [
+ ['Name' => 'ideal', 'Version' => 2],
+ ],
+ ]
+ ),
+ ]);
+
+ $credentials = new Credentials(
+ $this->buckaroo->client(),
+ $this->buckaroo->client()->config()
+ );
+
+ $result = $credentials->confirm();
+
+ $this->assertTrue($result);
+ }
+
+ /** @test */
+ public function it_returns_false_for_invalid_credentials(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Specification/ideal*',
+ ['error' => 'Unauthorized'],
+ 401
+ ),
+ ]);
+
+ $credentials = new Credentials(
+ $this->buckaroo->client(),
+ $this->buckaroo->client()->config()
+ );
+
+ $result = $credentials->confirm();
+
+ $this->assertFalse($result);
+ }
+}
diff --git a/tests/Unit/Handlers/HMAC/GeneratorTest.php b/tests/Unit/Handlers/HMAC/GeneratorTest.php
new file mode 100644
index 00000000..d7023b72
--- /dev/null
+++ b/tests/Unit/Handlers/HMAC/GeneratorTest.php
@@ -0,0 +1,318 @@
+ 10.00, 'currency' => 'EUR'];
+ $uri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+
+ $generator = new Generator($config, $data, $uri);
+ $header = $generator->generate();
+
+ // Header should be: websiteKey:hmac:nonce:timestamp
+ $parts = explode(':', $header);
+
+ $this->assertCount(4, $parts, 'Header should have 4 parts');
+ $this->assertSame($_ENV['BPE_WEBSITE_KEY'], $parts[0], 'First part should be website key');
+ $this->assertNotEmpty($parts[1], 'HMAC hash should not be empty');
+ $this->assertNotEmpty($parts[2], 'Nonce should not be empty');
+ $this->assertNotEmpty($parts[3], 'Timestamp should not be empty');
+ }
+
+ /** @test */
+ public function test_generates_valid_uuid_nonce(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $generator = new Generator($config, [], 'https://example.com/api');
+
+ $header = $generator->generate();
+ $parts = explode(':', $header);
+ $nonce = $parts[2];
+
+ // UUID v4 format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
+ $uuidPattern = '/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i';
+
+ $this->assertMatchesRegularExpression($uuidPattern, $nonce, 'Nonce should be a valid UUID v4');
+ }
+
+ /** @test */
+ public function test_normalizes_uri_correctly(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ // Test with full URL
+ $generator1 = new Generator($config, [], 'https://testcheckout.buckaroo.nl/json/Transaction');
+ $uri1 = $generator1->uri();
+ $this->assertSame('testcheckout.buckaroo.nl%2fjson%2ftransaction', $uri1, 'URI should be normalized (protocol stripped, lowercased, URL-encoded)');
+
+ // Test with http protocol
+ $generator2 = new Generator($config, [], 'http://API.Example.COM/Path');
+ $uri2 = $generator2->uri();
+ $this->assertSame('api.example.com%2fpath', $uri2, 'URI should lowercase and URL-encode');
+ }
+
+ /** @test */
+ public function test_handles_empty_data(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $generator = new Generator($config, null, 'https://example.com/api');
+
+ $header = $generator->generate();
+ $parts = explode(':', $header);
+
+ $this->assertCount(4, $parts, 'Header should still have 4 parts with empty data');
+ $this->assertNotEmpty($parts[1], 'HMAC should be generated even with empty data');
+ }
+
+ /** @test */
+ public function test_encodes_data_with_correct_flags(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ // Data with special characters and decimal values
+ $data = [
+ 'amount' => 10.50,
+ 'url' => 'https://example.com/callback',
+ 'description' => 'Test/Payment',
+ ];
+
+ $generator = new Generator($config, $data, 'https://example.com/api');
+ $base64Data = $generator->base64Data($data);
+
+ // The data should be JSON encoded with JSON_UNESCAPED_SLASHES and JSON_PRESERVE_ZERO_FRACTION
+ // Then MD5 hashed, then base64 encoded
+ $expectedJson = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION);
+ $expectedBase64 = base64_encode(md5(mb_convert_encoding($expectedJson, 'UTF-8', 'auto'), true));
+
+ $this->assertSame($expectedBase64, $base64Data, 'base64Data should use correct JSON encoding flags');
+ }
+
+ /** @test */
+ public function test_generates_consistent_hmac_for_same_input(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['amount' => 15.00, 'currency' => 'USD'];
+ $uri = 'https://example.com/transaction';
+
+ // Create two generators with same input but different nonce/timestamp
+ $generator1 = new Generator($config, $data, $uri);
+ $generator2 = new Generator($config, $data, $uri);
+
+ // Extract just the hash part (second element)
+ $header1Parts = explode(':', $generator1->generate());
+ $header2Parts = explode(':', $generator2->generate());
+
+ // The HMAC hashes will be different because nonce and timestamp are different
+ // But if we set the same nonce and timestamp, they should match
+ $this->assertNotEquals($header1Parts[1], $header2Parts[1], 'Different nonce/timestamp should produce different HMACs');
+ }
+
+ /** @test */
+ public function test_generates_different_hmac_for_different_secret_keys(): void
+ {
+ $data = ['amount' => 10.00];
+ $uri = 'https://example.com/api';
+
+ $config1 = new DefaultConfig('test_website_key', 'secret_key_1');
+ $config2 = new DefaultConfig('test_website_key', 'secret_key_2');
+
+ $generator1 = new Generator($config1, $data, $uri);
+ $generator2 = new Generator($config2, $data, $uri);
+
+ $header1Parts = explode(':', $generator1->generate());
+ $header2Parts = explode(':', $generator2->generate());
+
+ $this->assertNotEquals($header1Parts[1], $header2Parts[1], 'Different secret keys should produce different HMACs');
+ }
+
+ /** @test */
+ public function test_generates_deterministic_hmac_with_fixed_nonce_and_timestamp(): void
+ {
+ $config = new DefaultConfig('test_website_key', 'secret_key_123');
+ $data = ['amount' => 10.50, 'currency' => 'EUR'];
+ $uri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+
+ $generator = new Generator($config, $data, $uri);
+
+ $fixedNonce = 'test-nonce-12345';
+ $fixedTime = '1234567890';
+ $generator->nonce($fixedNonce);
+ $generator->time($fixedTime);
+
+ $header = $generator->generate();
+ $parts = explode(':', $header);
+
+ $this->assertSame('test_website_key', $parts[0]);
+ $this->assertSame($fixedNonce, $parts[2]);
+ $this->assertSame($fixedTime, $parts[3]);
+
+ $expectedBase64Data = $generator->base64Data($data);
+ $expectedUri = $generator->uri($uri);
+ $hashString = 'test_website_key' . 'POST' . $expectedUri . $fixedTime . $fixedNonce . $expectedBase64Data;
+ $expectedHmac = base64_encode(hash_hmac('sha256', $hashString, 'secret_key_123', true));
+
+ $this->assertSame($expectedHmac, $parts[1]);
+
+ $secondHeader = $generator->generate();
+ $this->assertSame($header, $secondHeader);
+ }
+
+ /** @test */
+ public function test_uses_different_http_methods_in_hmac_calculation(): void
+ {
+ $config = new DefaultConfig('test_key', 'test_secret');
+ $data = ['test' => 'data'];
+ $uri = 'https://example.com/api';
+
+ $generatorPost = new Generator($config, $data, $uri, 'POST');
+ $generatorGet = new Generator($config, $data, $uri, 'GET');
+ $generatorPut = new Generator($config, $data, $uri, 'PUT');
+ $generatorDelete = new Generator($config, $data, $uri, 'DELETE');
+
+ $fixedNonce = 'same-nonce';
+ $fixedTime = '1000000000';
+ foreach ([$generatorPost, $generatorGet, $generatorPut, $generatorDelete] as $gen) {
+ $gen->nonce($fixedNonce);
+ $gen->time($fixedTime);
+ }
+
+ $hmacPost = explode(':', $generatorPost->generate())[1];
+ $hmacGet = explode(':', $generatorGet->generate())[1];
+ $hmacPut = explode(':', $generatorPut->generate())[1];
+ $hmacDelete = explode(':', $generatorDelete->generate())[1];
+
+ $this->assertNotSame($hmacPost, $hmacGet);
+ $this->assertNotSame($hmacPost, $hmacPut);
+ $this->assertNotSame($hmacPost, $hmacDelete);
+ $this->assertNotSame($hmacGet, $hmacPut);
+ }
+
+ /** @test */
+ public function test_handles_string_data_without_json_encoding(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $stringData = 'raw-string-data';
+ $generator = new Generator($config, $stringData, 'https://example.com/api');
+
+ $base64Data = $generator->base64Data($stringData);
+ $expectedBase64 = base64_encode(md5($stringData, true));
+
+ $this->assertSame($expectedBase64, $base64Data);
+
+ $header = $generator->generate();
+ $parts = explode(':', $header);
+ $this->assertCount(4, $parts);
+ $this->assertNotEmpty($parts[1]);
+ }
+
+ /** @test */
+ public function test_distinguishes_empty_string_from_null_and_empty_array(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $uri = 'https://example.com/api';
+
+ $generatorNull = new Generator($config, null, $uri);
+ $generatorEmptyString = new Generator($config, '', $uri);
+ $generatorEmptyArray = new Generator($config, [], $uri);
+
+ $this->assertSame('', $generatorNull->base64Data(null));
+ $this->assertSame('', $generatorEmptyString->base64Data(''));
+ $this->assertSame('', $generatorEmptyArray->base64Data([]));
+
+ $generatorNull->nonce('same');
+ $generatorNull->time('1000000000');
+ $generatorEmptyString->nonce('same');
+ $generatorEmptyString->time('1000000000');
+ $generatorEmptyArray->nonce('same');
+ $generatorEmptyArray->time('1000000000');
+
+ $hmacNull = explode(':', $generatorNull->generate())[1];
+ $hmacEmptyString = explode(':', $generatorEmptyString->generate())[1];
+ $hmacEmptyArray = explode(':', $generatorEmptyArray->generate())[1];
+
+ $this->assertSame($hmacNull, $hmacEmptyString);
+ $this->assertSame($hmacNull, $hmacEmptyArray);
+ }
+
+ /** @test */
+ public function test_normalizes_uri_with_query_parameters(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $generator = new Generator($config, [], 'https://api.buckaroo.nl/json/Transaction?serviceVersion=2&test=true');
+ $normalizedUri = $generator->uri();
+
+ $this->assertStringContainsString('%3f', $normalizedUri);
+ $this->assertStringContainsString('serviceversion', strtolower($normalizedUri));
+ $this->assertSame('api.buckaroo.nl%2fjson%2ftransaction%3fserviceversion%3d2%26test%3dtrue', $normalizedUri);
+ }
+
+ /** @test */
+ public function test_normalizes_uri_with_special_characters(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $generator = new Generator($config, [], 'https://example.com/path with spaces/transaction');
+ $normalizedUri = $generator->uri();
+
+ $this->assertStringContainsString('+', $normalizedUri);
+ $this->assertSame('example.com%2fpath+with+spaces%2ftransaction', $normalizedUri);
+ }
+
+ /** @test */
+ public function test_handles_unicode_data_correctly(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $unicodeData = [
+ 'description' => 'Payment with emoji 💳 and Chinese 支付',
+ 'customer' => 'محمد علي',
+ 'amount' => 10.50,
+ ];
+
+ $generator = new Generator($config, $unicodeData, 'https://example.com/api');
+ $base64Data = $generator->base64Data($unicodeData);
+
+ $jsonData = json_encode($unicodeData, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION);
+ $expectedBase64 = base64_encode(md5(mb_convert_encoding($jsonData, 'UTF-8', 'auto'), true));
+
+ $this->assertSame($expectedBase64, $base64Data);
+
+ $header = $generator->generate();
+ $parts = explode(':', $header);
+ $this->assertCount(4, $parts);
+ $this->assertNotEmpty($parts[1]);
+ }
+
+ /** @test */
+ public function test_preserves_zero_fraction_in_various_float_formats(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ $testCases = [
+ ['amount' => 10.00],
+ ['amount' => 10.10],
+ ['amount' => 0.01],
+ ['amount' => 999999.99],
+ ];
+
+ foreach ($testCases as $data) {
+ $generator = new Generator($config, $data, 'https://example.com/api');
+ $base64Data = $generator->base64Data($data);
+
+ $jsonData = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION);
+ $this->assertStringContainsString('.', $jsonData);
+
+ $expectedBase64 = base64_encode(md5(mb_convert_encoding($jsonData, 'UTF-8', 'auto'), true));
+ $this->assertSame($expectedBase64, $base64Data);
+ }
+ }
+}
diff --git a/tests/Unit/Handlers/HMAC/ValidatorTest.php b/tests/Unit/Handlers/HMAC/ValidatorTest.php
new file mode 100644
index 00000000..98dcfc0c
--- /dev/null
+++ b/tests/Unit/Handlers/HMAC/ValidatorTest.php
@@ -0,0 +1,298 @@
+ 10.00, 'currency' => 'EUR'];
+ $uri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+ $method = 'POST';
+
+ $generator = new Generator($config, $data, $uri, $method);
+ $header = $generator->generate();
+
+ $validator = new Validator($config);
+ $isValid = $validator->validate($header, $uri, $method, $data);
+
+ $this->assertTrue($isValid);
+ }
+
+ /** @test */
+ public function test_rejects_invalid_hmac_signature(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['amount' => 10.00, 'currency' => 'EUR'];
+ $uri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+ $method = 'POST';
+
+ $invalidHeader = 'test_website_key:invalid_hash_value:some-nonce:1234567890';
+
+ $validator = new Validator($config);
+ $isValid = $validator->validate($invalidHeader, $uri, $method, $data);
+
+ $this->assertFalse($isValid);
+ }
+
+ /** @test */
+ public function test_rejects_tampered_data(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $originalData = ['amount' => 10.00, 'currency' => 'EUR'];
+ $uri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+ $method = 'POST';
+
+ $generator = new Generator($config, $originalData, $uri, $method);
+ $header = $generator->generate();
+
+ $tamperedData = ['amount' => 100.00, 'currency' => 'EUR'];
+
+ $validator = new Validator($config);
+ $isValid = $validator->validate($header, $uri, $method, $tamperedData);
+
+ $this->assertFalse($isValid);
+ }
+
+ /** @test */
+ public function test_throws_on_validate_or_fail_with_invalid_signature(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $invalidHeader = 'test_website_key:bad_hash:nonce:timestamp';
+
+ $validator = new Validator($config);
+
+ $this->expectException(BuckarooException::class);
+ $this->expectExceptionMessage('HMAC validation failed.');
+
+ $validator->validateOrFail($invalidHeader, 'https://example.com', 'POST', []);
+ }
+
+ /** @test */
+ public function test_returns_calculated_hash(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['amount' => 10.00];
+ $uri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+ $method = 'POST';
+
+ $generator = new Generator($config, $data, $uri, $method);
+ $header = $generator->generate();
+
+ $validator = new Validator($config);
+ $validator->validate($header, $uri, $method, $data);
+
+ $calculatedHash = $validator->getHash();
+
+ $this->assertNotEmpty($calculatedHash);
+ $this->assertStringContainsString('=', $calculatedHash);
+ }
+
+ /** @test */
+ public function test_returns_true_on_validate_or_fail_with_valid_signature(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['amount' => 25.00, 'currency' => 'USD'];
+ $uri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+ $method = 'POST';
+
+ $generator = new Generator($config, $data, $uri, $method);
+ $header = $generator->generate();
+
+ $validator = new Validator($config);
+ $result = $validator->validateOrFail($header, $uri, $method, $data);
+
+ $this->assertTrue($result);
+ }
+
+ /** @test */
+ public function test_rejects_signature_with_wrong_secret_key(): void
+ {
+ $data = ['amount' => 10.00];
+ $uri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+ $method = 'POST';
+
+ $configGen = new DefaultConfig('test_website_key', 'correct_secret_key');
+ $generator = new Generator($configGen, $data, $uri, $method);
+ $header = $generator->generate();
+
+ $configVal = new DefaultConfig('test_website_key', 'wrong_secret_key');
+ $validator = new Validator($configVal);
+ $isValid = $validator->validate($header, $uri, $method, $data);
+
+ $this->assertFalse($isValid);
+ }
+
+ /** @test */
+ public function test_validates_with_empty_data(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $uri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+ $method = 'POST';
+
+ $generator = new Generator($config, null, $uri, $method);
+ $header = $generator->generate();
+
+ $validator = new Validator($config);
+ $isValid = $validator->validate($header, $uri, $method, null);
+
+ $this->assertTrue($isValid);
+ }
+
+ /** @test */
+ public function test_rejects_header_with_empty_segments(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $validator = new Validator($config);
+
+ $invalidHeader = 'key::nonce:time';
+ $isValid = $validator->validate($invalidHeader, 'https://example.com', 'POST', []);
+
+ $this->assertFalse($isValid);
+ }
+
+ /** @test */
+ public function test_rejects_signature_with_wrong_website_key(): void
+ {
+ $data = ['amount' => 10.00];
+ $uri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+ $method = 'POST';
+
+ $configGen = new DefaultConfig('website_key_A', 'shared_secret');
+ $generator = new Generator($configGen, $data, $uri, $method);
+ $header = $generator->generate();
+
+ $configVal = new DefaultConfig('website_key_B', 'shared_secret');
+ $validator = new Validator($configVal);
+ $isValid = $validator->validate($header, $uri, $method, $data);
+
+ $this->assertFalse($isValid);
+ }
+
+ /** @test */
+ public function test_validates_multiple_http_methods(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $validator = new Validator($config);
+
+ // Test GET request
+ $getData = ['page' => 1];
+ $getUri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+ $getGenerator = new Generator($config, $getData, $getUri, 'GET');
+ $getHeader = $getGenerator->generate();
+ $this->assertTrue($validator->validate($getHeader, $getUri, 'GET', $getData));
+
+ // Test PUT request
+ $putData = ['status' => 'updated'];
+ $putUri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+ $putGenerator = new Generator($config, $putData, $putUri, 'PUT');
+ $putHeader = $putGenerator->generate();
+ $this->assertTrue($validator->validate($putHeader, $putUri, 'PUT', $putData));
+
+ // Test DELETE request
+ $deleteData = [];
+ $deleteUri = 'https://testcheckout.buckaroo.nl/json/Transaction/123';
+ $deleteGenerator = new Generator($config, $deleteData, $deleteUri, 'DELETE');
+ $deleteHeader = $deleteGenerator->generate();
+ $this->assertTrue($validator->validate($deleteHeader, $deleteUri, 'DELETE', $deleteData));
+ }
+
+ /** @test */
+ public function test_rejects_on_method_mismatch(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['amount' => 10.00];
+ $uri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+
+ $generator = new Generator($config, $data, $uri, 'POST');
+ $header = $generator->generate();
+
+ $validator = new Validator($config);
+ $isValid = $validator->validate($header, $uri, 'GET', $data);
+
+ $this->assertFalse($isValid);
+ }
+
+ /** @test */
+ public function test_validates_various_uri_formats(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $validator = new Validator($config);
+ $data = ['amount' => 10.00];
+ $method = 'POST';
+
+ // Test URI with query parameters
+ $queryUri = 'https://testcheckout.buckaroo.nl/json/Transaction?serviceVersion=2&test=true';
+ $queryGenerator = new Generator($config, $data, $queryUri, $method);
+ $queryHeader = $queryGenerator->generate();
+ $this->assertTrue($validator->validate($queryHeader, $queryUri, $method, $data));
+
+ // Test URI with special characters
+ $specialUri = 'https://testcheckout.buckaroo.nl/json/Transaction/Order 123';
+ $specialGenerator = new Generator($config, $data, $specialUri, $method);
+ $specialHeader = $specialGenerator->generate();
+ $this->assertTrue($validator->validate($specialHeader, $specialUri, $method, $data));
+
+ // Test HTTP protocol (vs HTTPS)
+ $httpUri = 'http://testcheckout.buckaroo.nl/json/Transaction';
+ $httpGenerator = new Generator($config, $data, $httpUri, $method);
+ $httpHeader = $httpGenerator->generate();
+ $this->assertTrue($validator->validate($httpHeader, $httpUri, $method, $data));
+
+ // Test URI mismatch rejection
+ $originalUri = 'https://api.buckaroo.nl/json/Transaction';
+ $mismatchGenerator = new Generator($config, $data, $originalUri, $method);
+ $mismatchHeader = $mismatchGenerator->generate();
+ $this->assertFalse($validator->validate($mismatchHeader, 'https://api.buckaroo.nl/json/Different', $method, $data));
+ }
+
+ /** @test */
+ public function test_validates_various_data_types(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $validator = new Validator($config);
+ $uri = 'https://testcheckout.buckaroo.nl/json/Transaction';
+ $method = 'POST';
+
+ // Test string data
+ $stringData = 'raw-string-payload';
+ $stringGenerator = new Generator($config, $stringData, $uri, $method);
+ $stringHeader = $stringGenerator->generate();
+ $this->assertTrue($validator->validate($stringHeader, $uri, $method, $stringData));
+
+ // Test nested array data
+ $nestedData = [
+ 'transaction' => [
+ 'amount' => 10.00,
+ 'currency' => 'EUR',
+ 'items' => [
+ ['name' => 'Product A', 'price' => 5.00],
+ ['name' => 'Product B', 'price' => 5.00],
+ ],
+ ],
+ ];
+ $nestedGenerator = new Generator($config, $nestedData, $uri, $method);
+ $nestedHeader = $nestedGenerator->generate();
+ $this->assertTrue($validator->validate($nestedHeader, $uri, $method, $nestedData));
+
+ // Test Unicode data
+ $unicodeData = [
+ 'description' => 'Payment 💳 支付',
+ 'customer' => 'محمد',
+ 'amount' => 10.50,
+ ];
+ $unicodeGenerator = new Generator($config, $unicodeData, $uri, $method);
+ $unicodeHeader = $unicodeGenerator->generate();
+ $this->assertTrue($validator->validate($unicodeHeader, $uri, $method, $unicodeData));
+ }
+}
diff --git a/tests/Unit/Handlers/Logging/DefaultLoggerTest.php b/tests/Unit/Handlers/Logging/DefaultLoggerTest.php
new file mode 100644
index 00000000..45776d7b
--- /dev/null
+++ b/tests/Unit/Handlers/Logging/DefaultLoggerTest.php
@@ -0,0 +1,182 @@
+createMockObserver();
+
+ $result = $logger->attach($observer);
+
+ $this->assertSame($logger, $result);
+ }
+
+ public function test_attaches_multiple_observers_as_array(): void
+ {
+ $logger = new DefaultLogger();
+ $observer1 = $this->createMockObserver();
+ $observer2 = $this->createMockObserver();
+
+ $result = $logger->attach([$observer1, $observer2]);
+
+ $this->assertSame($logger, $result);
+ }
+
+ public function test_detaches_observer(): void
+ {
+ $logger = new DefaultLogger();
+ $observer = $this->createMockObserver();
+
+ $logger->attach($observer);
+ $result = $logger->detach($observer);
+
+ $this->assertSame($logger, $result);
+ }
+
+ public function test_notifies_observers_for_all_psr3_log_levels(): void
+ {
+ $levels = ['emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info'];
+
+ foreach ($levels as $level) {
+ $logger = new DefaultLogger();
+ $observer = $this->createMockObserver();
+
+ $observer->expects($this->once())
+ ->method('handle')
+ ->with($level, "Test {$level} message", ['key' => 'value']);
+
+ $logger->attach($observer);
+ $logger->$level("Test {$level} message", ['key' => 'value']);
+ }
+ }
+
+ public function test_log_method_uses_log_level_in_notification(): void
+ {
+ $logger = new DefaultLogger();
+ $observer = $this->createMockObserver();
+
+ $observer->expects($this->once())
+ ->method('handle')
+ ->with('log', 'Generic log message', ['context' => 'data']);
+
+ $logger->attach($observer);
+ $logger->log('info', 'Generic log message', ['context' => 'data']);
+ }
+
+ public function test_debug_logs_when_bpe_debug_enabled(): void
+ {
+ $_ENV['BPE_DEBUG'] = true;
+
+ $logger = new DefaultLogger();
+ $observer = $this->createMockObserver();
+ $observer->expects($this->once())
+ ->method('handle')
+ ->with('debug', 'Debug message', []);
+
+ $logger->attach($observer);
+ $logger->debug('Debug message');
+ }
+
+ public function test_debug_does_not_log_when_bpe_debug_disabled(): void
+ {
+ unset($_ENV['BPE_DEBUG']);
+
+ $logger = new DefaultLogger();
+ $observer = $this->createMockObserver();
+ $observer->expects($this->never())
+ ->method('handle');
+
+ $logger->attach($observer);
+ $logger->debug('Debug message');
+ }
+
+ public function test_notifies_multiple_observers(): void
+ {
+ $logger = new DefaultLogger();
+ $observer1 = $this->createMockObserver();
+ $observer2 = $this->createMockObserver();
+
+ $observer1->expects($this->once())
+ ->method('handle')
+ ->with('info', 'Test message', []);
+ $observer2->expects($this->once())
+ ->method('handle')
+ ->with('info', 'Test message', []);
+
+ $logger->attach($observer1);
+ $logger->attach($observer2);
+ $logger->info('Test message');
+ }
+
+ public function test_detached_observer_does_not_receive_notifications(): void
+ {
+ $logger = new DefaultLogger();
+ $observer = $this->createMockObserver();
+
+ $observer->expects($this->never())
+ ->method('handle');
+
+ $logger->attach($observer);
+ $logger->detach($observer);
+ $logger->info('Test message');
+ }
+
+ public function test_ignores_non_observer_attachments(): void
+ {
+ $logger = new DefaultLogger();
+ $notAnObserver = new \stdClass();
+
+ $result = $logger->attach($notAnObserver);
+
+ $this->assertSame($logger, $result);
+ }
+
+ public function test_constructor_attaches_monolog_when_bpe_log_enabled(): void
+ {
+ $_ENV['BPE_LOG'] = 'true';
+
+ $logger = new DefaultLogger();
+
+ // Monolog observer is attached by constructor
+ // We can verify by checking that logging doesn't throw
+ $this->assertInstanceOf(DefaultLogger::class, $logger);
+ }
+
+ public function test_constructor_attaches_error_reporter_when_bpe_report_error_enabled(): void
+ {
+ $_ENV['BPE_REPORT_ERROR'] = 'true';
+
+ $logger = new DefaultLogger();
+
+ // ErrorReporter observer is attached by constructor
+ // We can verify by checking that logging doesn't throw
+ $this->assertInstanceOf(DefaultLogger::class, $logger);
+ }
+
+ /**
+ * Creates a mock Observer for testing notification behavior.
+ *
+ * Note: While we generally avoid PHPUnit mocking, mocking interfaces
+ * (contracts with no implementation) is acceptable when testing
+ * observer/listener patterns.
+ */
+ private function createMockObserver(): Observer
+ {
+ return $this->createMock(Observer::class);
+ }
+}
diff --git a/tests/Unit/Handlers/Logging/Observers/ErrorReporterTest.php b/tests/Unit/Handlers/Logging/Observers/ErrorReporterTest.php
new file mode 100644
index 00000000..052e02b7
--- /dev/null
+++ b/tests/Unit/Handlers/Logging/Observers/ErrorReporterTest.php
@@ -0,0 +1,164 @@
+assertInstanceOf(Observer::class, $errorReporter);
+ }
+
+ public function test_has_reportable_methods(): void
+ {
+ $errorReporter = new ErrorReporter();
+
+ $reflection = new ReflectionClass($errorReporter);
+ $property = $reflection->getProperty('reportables');
+ $property->setAccessible(true);
+
+ $reportables = $property->getValue($errorReporter);
+
+ $this->assertContains('error', $reportables);
+ $this->assertContains('critical', $reportables);
+ $this->assertContains('emergency', $reportables);
+ }
+
+ public function test_handle_returns_self_for_error_level(): void
+ {
+ $errorReporter = new ErrorReporter();
+
+ $result = $errorReporter->handle('error', 'Error message', ['key' => 'value']);
+
+ $this->assertSame($errorReporter, $result);
+ }
+
+ public function test_handle_returns_self_for_critical_level(): void
+ {
+ $errorReporter = new ErrorReporter();
+
+ $result = $errorReporter->handle('critical', 'Critical message', []);
+
+ $this->assertSame($errorReporter, $result);
+ }
+
+ public function test_handle_returns_self_for_emergency_level(): void
+ {
+ $errorReporter = new ErrorReporter();
+
+ $result = $errorReporter->handle('emergency', 'Emergency message', []);
+
+ $this->assertSame($errorReporter, $result);
+ }
+
+ public function test_handle_returns_self_for_info_level(): void
+ {
+ $errorReporter = new ErrorReporter();
+
+ $result = $errorReporter->handle('info', 'Info message', []);
+
+ $this->assertSame($errorReporter, $result);
+ }
+
+ public function test_handle_returns_self_for_warning_level(): void
+ {
+ $errorReporter = new ErrorReporter();
+
+ $result = $errorReporter->handle('warning', 'Warning message', []);
+
+ $this->assertSame($errorReporter, $result);
+ }
+
+ public function test_handle_returns_self_for_debug_level(): void
+ {
+ $errorReporter = new ErrorReporter();
+
+ $result = $errorReporter->handle('debug', 'Debug message', []);
+
+ $this->assertSame($errorReporter, $result);
+ }
+
+ public function test_handle_returns_self_for_notice_level(): void
+ {
+ $errorReporter = new ErrorReporter();
+
+ $result = $errorReporter->handle('notice', 'Notice message', []);
+
+ $this->assertSame($errorReporter, $result);
+ }
+
+ public function test_handle_returns_self_for_alert_level(): void
+ {
+ $errorReporter = new ErrorReporter();
+
+ $result = $errorReporter->handle('alert', 'Alert message', []);
+
+ $this->assertSame($errorReporter, $result);
+ }
+
+ public function test_handle_accepts_empty_context(): void
+ {
+ $errorReporter = new ErrorReporter();
+
+ $result = $errorReporter->handle('error', 'Error with empty context', []);
+
+ $this->assertSame($errorReporter, $result);
+ }
+
+ public function test_handle_accepts_complex_context(): void
+ {
+ $errorReporter = new ErrorReporter();
+
+ $context = [
+ 'transaction_id' => 'TX12345',
+ 'error_code' => 500,
+ 'stack_trace' => ['line1', 'line2', 'line3'],
+ 'metadata' => [
+ 'user_id' => 123,
+ 'timestamp' => '2024-01-01T00:00:00Z',
+ ],
+ ];
+
+ $result = $errorReporter->handle('error', 'Complex error', $context);
+
+ $this->assertSame($errorReporter, $result);
+ }
+
+ public function test_non_reportable_methods_are_not_in_list(): void
+ {
+ $errorReporter = new ErrorReporter();
+
+ $reflection = new ReflectionClass($errorReporter);
+ $property = $reflection->getProperty('reportables');
+ $property->setAccessible(true);
+
+ $reportables = $property->getValue($errorReporter);
+
+ $this->assertNotContains('info', $reportables);
+ $this->assertNotContains('warning', $reportables);
+ $this->assertNotContains('debug', $reportables);
+ $this->assertNotContains('notice', $reportables);
+ $this->assertNotContains('alert', $reportables);
+ }
+
+ public function test_handle_is_chainable(): void
+ {
+ $errorReporter = new ErrorReporter();
+
+ $result = $errorReporter
+ ->handle('error', 'First error', [])
+ ->handle('critical', 'Second error', [])
+ ->handle('info', 'Info message', []);
+
+ $this->assertSame($errorReporter, $result);
+ }
+}
diff --git a/tests/Unit/Handlers/Logging/Observers/MonologTest.php b/tests/Unit/Handlers/Logging/Observers/MonologTest.php
new file mode 100644
index 00000000..55891adc
--- /dev/null
+++ b/tests/Unit/Handlers/Logging/Observers/MonologTest.php
@@ -0,0 +1,220 @@
+assertInstanceOf(Observer::class, $monolog);
+ }
+
+ public function test_creates_logger_with_buckaroo_name(): void
+ {
+ $monolog = new Monolog();
+
+ $reflection = new ReflectionClass($monolog);
+ $property = $reflection->getProperty('log');
+ $property->setAccessible(true);
+
+ $logger = $property->getValue($monolog);
+
+ $this->assertInstanceOf(Logger::class, $logger);
+ $this->assertSame('Buckaroo log', $logger->getName());
+ }
+
+ public function test_handle_method_logs_info_level(): void
+ {
+ $monolog = new Monolog();
+
+ // Replace the internal logger with a test handler
+ $testHandler = new TestHandler();
+ $testLogger = new Logger('test', [$testHandler]);
+
+ $reflection = new ReflectionClass($monolog);
+ $property = $reflection->getProperty('log');
+ $property->setAccessible(true);
+ $property->setValue($monolog, $testLogger);
+
+ $monolog->handle('info', 'Test info message', ['key' => 'value']);
+
+ $this->assertTrue($testHandler->hasInfoRecords());
+ $this->assertTrue($testHandler->hasInfo('Test info message'));
+ }
+
+ public function test_handle_method_logs_error_level(): void
+ {
+ $monolog = new Monolog();
+
+ $testHandler = new TestHandler();
+ $testLogger = new Logger('test', [$testHandler]);
+
+ $reflection = new ReflectionClass($monolog);
+ $property = $reflection->getProperty('log');
+ $property->setAccessible(true);
+ $property->setValue($monolog, $testLogger);
+
+ $monolog->handle('error', 'Test error message', ['error' => 'details']);
+
+ $this->assertTrue($testHandler->hasErrorRecords());
+ $this->assertTrue($testHandler->hasError('Test error message'));
+ }
+
+ public function test_handle_method_logs_warning_level(): void
+ {
+ $monolog = new Monolog();
+
+ $testHandler = new TestHandler();
+ $testLogger = new Logger('test', [$testHandler]);
+
+ $reflection = new ReflectionClass($monolog);
+ $property = $reflection->getProperty('log');
+ $property->setAccessible(true);
+ $property->setValue($monolog, $testLogger);
+
+ $monolog->handle('warning', 'Test warning message', []);
+
+ $this->assertTrue($testHandler->hasWarningRecords());
+ $this->assertTrue($testHandler->hasWarning('Test warning message'));
+ }
+
+ public function test_handle_method_logs_debug_level(): void
+ {
+ $monolog = new Monolog();
+
+ $testHandler = new TestHandler();
+ $testLogger = new Logger('test', [$testHandler]);
+
+ $reflection = new ReflectionClass($monolog);
+ $property = $reflection->getProperty('log');
+ $property->setAccessible(true);
+ $property->setValue($monolog, $testLogger);
+
+ $monolog->handle('debug', 'Test debug message', []);
+
+ $this->assertTrue($testHandler->hasDebugRecords());
+ $this->assertTrue($testHandler->hasDebug('Test debug message'));
+ }
+
+ public function test_handle_method_passes_context_to_logger(): void
+ {
+ $monolog = new Monolog();
+
+ $testHandler = new TestHandler();
+ $testLogger = new Logger('test', [$testHandler]);
+
+ $reflection = new ReflectionClass($monolog);
+ $property = $reflection->getProperty('log');
+ $property->setAccessible(true);
+ $property->setValue($monolog, $testLogger);
+
+ $context = ['transaction_id' => '12345', 'amount' => 10.50];
+ $monolog->handle('info', 'Transaction processed', $context);
+
+ $records = $testHandler->getRecords();
+ $this->assertCount(1, $records);
+ $this->assertSame($context, $records[0]['context']);
+ }
+
+ public function test_handle_method_logs_critical_level(): void
+ {
+ $monolog = new Monolog();
+
+ $testHandler = new TestHandler();
+ $testLogger = new Logger('test', [$testHandler]);
+
+ $reflection = new ReflectionClass($monolog);
+ $property = $reflection->getProperty('log');
+ $property->setAccessible(true);
+ $property->setValue($monolog, $testLogger);
+
+ $monolog->handle('critical', 'Critical error occurred', []);
+
+ $this->assertTrue($testHandler->hasCriticalRecords());
+ $this->assertTrue($testHandler->hasCritical('Critical error occurred'));
+ }
+
+ public function test_handle_method_logs_alert_level(): void
+ {
+ $monolog = new Monolog();
+
+ $testHandler = new TestHandler();
+ $testLogger = new Logger('test', [$testHandler]);
+
+ $reflection = new ReflectionClass($monolog);
+ $property = $reflection->getProperty('log');
+ $property->setAccessible(true);
+ $property->setValue($monolog, $testLogger);
+
+ $monolog->handle('alert', 'Alert message', []);
+
+ $this->assertTrue($testHandler->hasAlertRecords());
+ $this->assertTrue($testHandler->hasAlert('Alert message'));
+ }
+
+ public function test_handle_method_logs_emergency_level(): void
+ {
+ $monolog = new Monolog();
+
+ $testHandler = new TestHandler();
+ $testLogger = new Logger('test', [$testHandler]);
+
+ $reflection = new ReflectionClass($monolog);
+ $property = $reflection->getProperty('log');
+ $property->setAccessible(true);
+ $property->setValue($monolog, $testLogger);
+
+ $monolog->handle('emergency', 'Emergency message', []);
+
+ $this->assertTrue($testHandler->hasEmergencyRecords());
+ $this->assertTrue($testHandler->hasEmergency('Emergency message'));
+ }
+
+ public function test_handle_method_logs_notice_level(): void
+ {
+ $monolog = new Monolog();
+
+ $testHandler = new TestHandler();
+ $testLogger = new Logger('test', [$testHandler]);
+
+ $reflection = new ReflectionClass($monolog);
+ $property = $reflection->getProperty('log');
+ $property->setAccessible(true);
+ $property->setValue($monolog, $testLogger);
+
+ $monolog->handle('notice', 'Notice message', []);
+
+ $this->assertTrue($testHandler->hasNoticeRecords());
+ $this->assertTrue($testHandler->hasNotice('Notice message'));
+ }
+
+ public function test_handle_with_empty_context(): void
+ {
+ $monolog = new Monolog();
+
+ $testHandler = new TestHandler();
+ $testLogger = new Logger('test', [$testHandler]);
+
+ $reflection = new ReflectionClass($monolog);
+ $property = $reflection->getProperty('log');
+ $property->setAccessible(true);
+ $property->setValue($monolog, $testLogger);
+
+ $monolog->handle('info', 'Message without context', []);
+
+ $records = $testHandler->getRecords();
+ $this->assertCount(1, $records);
+ $this->assertSame([], $records[0]['context']);
+ }
+}
diff --git a/tests/Unit/Handlers/Reply/HttpPostTest.php b/tests/Unit/Handlers/Reply/HttpPostTest.php
new file mode 100644
index 00000000..ad76e720
--- /dev/null
+++ b/tests/Unit/Handlers/Reply/HttpPostTest.php
@@ -0,0 +1,393 @@
+ '10.00',
+ 'brq_currency' => 'EUR',
+ 'brq_invoicenumber' => 'INV-001',
+ ];
+
+ // Generate valid signature
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['brq_signature'] = $signature;
+
+ $handler = new HttpPost($config, $data);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Valid brq_ signature should be accepted');
+ }
+
+ public function test_includes_add_and_cust_prefixes(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = [
+ 'brq_amount' => '15.00',
+ 'add_custom_field' => 'custom_value',
+ 'cust_customer_id' => '12345',
+ ];
+
+ // Generate signature that includes add_ and cust_ prefixes
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['brq_signature'] = $signature;
+
+ $handler = new HttpPost($config, $data);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Signature should include add_ and cust_ prefixed fields');
+ }
+
+ public function test_decodes_all_html_entity_types(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ // Test named HTML entities
+ $namedData = [
+ 'brq_description' => 'Test & Payment',
+ 'brq_amount' => '10.00',
+ ];
+ $namedSignature = TestHelpers::generateHttpPostSignature($namedData);
+ $namedData['brq_signature'] = $namedSignature;
+ $namedHandler = new HttpPost($config, $namedData);
+ $this->assertTrue($namedHandler->validate(), 'Named HTML entities should be decoded');
+
+ // Test numeric HTML entities
+ $numericData = [
+ 'brq_description' => 'Less than: < Greater than: >',
+ 'brq_amount' => '10.00',
+ ];
+ $numericSignature = TestHelpers::generateHttpPostSignature($numericData);
+ $numericData['brq_signature'] = $numericSignature;
+ $numericHandler = new HttpPost($config, $numericData);
+ $this->assertTrue($numericHandler->validate(), 'Numeric HTML entities should be decoded');
+
+ // Test hexadecimal HTML entities
+ $hexData = [
+ 'brq_description' => 'Less than: < Greater than: > Ampersand: &',
+ 'brq_amount' => '10.00',
+ ];
+ $hexSignature = TestHelpers::generateHttpPostSignature($hexData);
+ $hexData['brq_signature'] = $hexSignature;
+ $hexHandler = new HttpPost($config, $hexData);
+ $this->assertTrue($hexHandler->validate(), 'Hexadecimal HTML entities should be decoded');
+
+ // Test mixed HTML entity types
+ $mixedData = [
+ 'brq_description' => '< > & " < <',
+ 'brq_amount' => '10.00',
+ ];
+ $mixedSignature = TestHelpers::generateHttpPostSignature($mixedData);
+ $mixedData['brq_signature'] = $mixedSignature;
+ $mixedHandler = new HttpPost($config, $mixedData);
+ $this->assertTrue($mixedHandler->validate(), 'Mixed HTML entity types should all be decoded');
+ }
+
+ public function test_uses_case_insensitive_sorting(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ // Data with mixed case keys - sorting should be case-insensitive
+ $data = [
+ 'brq_Zebra' => 'last',
+ 'brq_apple' => 'first',
+ 'brq_Banana' => 'second',
+ ];
+
+ // Generate signature
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['brq_signature'] = $signature;
+
+ $handler = new HttpPost($config, $data);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Keys should be sorted case-insensitively');
+ }
+
+ public function test_rejects_invalid_signature(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = [
+ 'brq_amount' => '10.00',
+ 'brq_currency' => 'EUR',
+ 'brq_signature' => 'invalid_signature_that_wont_match',
+ ];
+
+ $handler = new HttpPost($config, $data);
+ $isValid = $handler->validate();
+
+ $this->assertFalse($isValid, 'Invalid signature should be rejected');
+ }
+
+ public function test_handles_mixed_case_prefixes(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ // Mix of lowercase and uppercase prefixes
+ $data = [
+ 'brq_amount' => '30.00',
+ 'BRQ_CURRENCY' => 'EUR',
+ 'add_field' => 'value1',
+ 'ADD_FIELD2' => 'value2',
+ 'cust_id' => '123',
+ 'CUST_NAME' => 'John',
+ ];
+
+ // Generate signature
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['brq_signature'] = $signature;
+
+ $handler = new HttpPost($config, $data);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Mixed case prefixes should all be included');
+ }
+
+ public function test_ignores_unknown_prefixes(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ // Data with unknown prefix
+ $data = [
+ 'brq_amount' => '10.00',
+ 'unknown_field' => 'should_be_ignored',
+ 'random_data' => 'also_ignored',
+ ];
+
+ // Generate signature - should only include brq_ prefixed fields
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['brq_signature'] = $signature;
+
+ $handler = new HttpPost($config, $data);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Unknown prefixes should be ignored in signature calculation');
+ }
+
+ public function test_handles_uppercase_signature_field(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = [
+ 'BRQ_AMOUNT' => '50.00',
+ 'BRQ_CURRENCY' => 'USD',
+ ];
+
+ // Generate signature and add as uppercase
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['BRQ_SIGNATURE'] = $signature;
+
+ $handler = new HttpPost($config, $data);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Uppercase BRQ_SIGNATURE should be recognized');
+ }
+
+ public function test_rejects_tampered_data(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ // Original data
+ $originalData = [
+ 'brq_amount' => '10.00',
+ 'brq_currency' => 'EUR',
+ ];
+
+ // Generate signature with original data
+ $signature = TestHelpers::generateHttpPostSignature($originalData);
+
+ // Tamper with the data (change amount)
+ $tamperedData = [
+ 'brq_amount' => '1000.00', // Changed!
+ 'brq_currency' => 'EUR',
+ 'brq_signature' => $signature,
+ ];
+
+ $handler = new HttpPost($config, $tamperedData);
+ $isValid = $handler->validate();
+
+ $this->assertFalse($isValid, 'Tampered data should fail validation');
+ }
+
+ public function test_rejects_signature_with_wrong_secret_key(): void
+ {
+ // Generate signature with one secret key
+ $data = [
+ 'brq_amount' => '10.00',
+ 'brq_currency' => 'EUR',
+ ];
+
+ $signature = TestHelpers::generateHttpPostSignature($data, 'correct_secret_key');
+ $data['brq_signature'] = $signature;
+
+ // Validate with different secret key
+ $config = new DefaultConfig('test_website_key', 'wrong_secret_key');
+ $handler = new HttpPost($config, $data);
+ $isValid = $handler->validate();
+
+ $this->assertFalse($isValid, 'Signature generated with different secret key should be rejected');
+ }
+
+ public function test_handles_numeric_values(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ // Values that might be numeric
+ $data = [
+ 'brq_amount' => '99.99',
+ 'brq_statuscode' => '190',
+ 'brq_quantity' => '5',
+ ];
+
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['brq_signature'] = $signature;
+
+ $handler = new HttpPost($config, $data);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Numeric string values should be handled correctly');
+ }
+
+ public function test_handles_special_characters_in_values(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ $data = [
+ 'brq_description' => 'Order #123 - Special chars: <>"\'/\\',
+ 'brq_amount' => '10.00',
+ ];
+
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['brq_signature'] = $signature;
+
+ $handler = new HttpPost($config, $data);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Special characters in values should be handled correctly');
+ }
+
+ public function test_rejects_invalid_or_missing_signatures(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ // Test missing signature field
+ $missingData = [
+ 'brq_amount' => '10.00',
+ 'brq_currency' => 'EUR',
+ 'brq_invoicenumber' => 'INV-001',
+ ];
+ $missingHandler = new HttpPost($config, $missingData);
+ $this->assertFalse($missingHandler->validate(), 'Missing signature field should fail validation');
+
+ // Test empty signature
+ $emptyData = [
+ 'brq_amount' => '10.00',
+ 'brq_currency' => 'EUR',
+ 'brq_signature' => '',
+ ];
+ $emptyHandler = new HttpPost($config, $emptyData);
+ $this->assertFalse($emptyHandler->validate(), 'Empty signature should fail validation');
+
+ // Test whitespace-only signature
+ $whitespaceData = [
+ 'brq_amount' => '10.00',
+ 'brq_currency' => 'EUR',
+ 'brq_signature' => ' ',
+ ];
+ $whitespaceHandler = new HttpPost($config, $whitespaceData);
+ $this->assertFalse($whitespaceHandler->validate(), 'Whitespace-only signature should fail validation');
+ }
+
+ public function test_handles_payload_with_no_valid_fields(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ // Only signature and unknown fields (no brq_/add_/cust_ fields)
+ $data = [
+ 'unknown_field' => 'value',
+ 'random_data' => 'test',
+ ];
+
+ // Generate signature for empty filtered data (just secret key)
+ $signature = sha1($_ENV['BPE_SECRET_KEY']);
+ $data['brq_signature'] = $signature;
+
+ $handler = new HttpPost($config, $data);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Payload with no valid fields should validate if signature matches');
+ }
+
+ public function test_handles_unicode_and_multibyte_characters(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ // Test common Unicode characters (accents, symbols)
+ $unicodeData = [
+ 'brq_description' => 'Café ☕ Payment',
+ 'brq_customer' => 'José García',
+ 'brq_amount' => '10.00',
+ ];
+ $unicodeSignature = TestHelpers::generateHttpPostSignature($unicodeData);
+ $unicodeData['brq_signature'] = $unicodeSignature;
+ $unicodeHandler = new HttpPost($config, $unicodeData);
+ $this->assertTrue($unicodeHandler->validate(), 'Unicode characters should be handled correctly');
+
+ // Test multibyte characters (CJK, Arabic)
+ $multibyteData = [
+ 'brq_description' => '日本語 中文 العربية',
+ 'brq_amount' => '25.00',
+ ];
+ $multibyteSignature = TestHelpers::generateHttpPostSignature($multibyteData);
+ $multibyteData['brq_signature'] = $multibyteSignature;
+ $multibyteHandler = new HttpPost($config, $multibyteData);
+ $this->assertTrue($multibyteHandler->validate(), 'Multibyte characters should be handled correctly');
+ }
+
+ public function test_includes_fields_with_empty_values(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ $data = [
+ 'brq_amount' => '10.00',
+ 'brq_description' => '',
+ 'brq_currency' => 'EUR',
+ ];
+
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['brq_signature'] = $signature;
+
+ $handler = new HttpPost($config, $data);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Fields with empty values should be included in signature');
+ }
+
+ public function test_handles_field_names_with_multiple_underscores(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ $data = [
+ 'brq_service_some_long_field_name' => 'value',
+ 'brq_amount' => '10.00',
+ ];
+
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['brq_signature'] = $signature;
+
+ $handler = new HttpPost($config, $data);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Field names with multiple underscores should be handled');
+ }
+}
diff --git a/tests/Unit/Handlers/Reply/JsonTest.php b/tests/Unit/Handlers/Reply/JsonTest.php
new file mode 100644
index 00000000..4c983107
--- /dev/null
+++ b/tests/Unit/Handlers/Reply/JsonTest.php
@@ -0,0 +1,332 @@
+ ['Key' => 'ABC123'],
+ 'Key' => 'ABC123',
+ ];
+ $uri = 'https://example.com/push';
+ $method = 'POST';
+
+ // Generate valid HMAC header
+ $generator = new Generator($config, $data, $uri, $method);
+ $authHeader = $generator->generate();
+
+ $handler = new Json($config, $data, $authHeader, $uri, $method);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Valid HMAC signature should be accepted');
+ }
+
+ public function test_rejects_invalid_hmac_signature(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = [
+ 'Transaction' => ['Key' => 'ABC123'],
+ 'Key' => 'ABC123',
+ ];
+ $uri = 'https://example.com/push';
+
+ // Use invalid auth header
+ $invalidAuthHeader = $_ENV['BPE_WEBSITE_KEY'] . ':invalid_hash:nonce:12345';
+
+ $handler = new Json($config, $data, $invalidAuthHeader, $uri);
+ $isValid = $handler->validate();
+
+ $this->assertFalse($isValid, 'Invalid HMAC signature should be rejected');
+ }
+
+ public function test_rejects_tampered_data(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $originalData = [
+ 'Transaction' => ['Key' => 'ABC123', 'Amount' => 10.00],
+ 'Key' => 'ABC123',
+ ];
+ $uri = 'https://example.com/push';
+ $method = 'POST';
+
+ // Generate header with original data
+ $generator = new Generator($config, $originalData, $uri, $method);
+ $authHeader = $generator->generate();
+
+ // Try to validate with tampered data
+ $tamperedData = [
+ 'Transaction' => ['Key' => 'ABC123', 'Amount' => 1000.00], // Amount changed!
+ 'Key' => 'ABC123',
+ ];
+
+ $handler = new Json($config, $tamperedData, $authHeader, $uri, $method);
+ $isValid = $handler->validate();
+
+ $this->assertFalse($isValid, 'Tampered data should fail validation');
+ }
+
+ public function test_validates_with_get_method(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['Key' => 'ABC123'];
+ $uri = 'https://example.com/callback';
+ $method = 'GET';
+
+ // Generate header with GET method
+ $generator = new Generator($config, $data, $uri, $method);
+ $authHeader = $generator->generate();
+
+ $handler = new Json($config, $data, $authHeader, $uri, $method);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'GET method should be supported');
+ }
+
+ public function test_defaults_to_post_method(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['Key' => 'ABC123'];
+ $uri = 'https://example.com/push';
+
+ // Generate header with default POST method
+ $generator = new Generator($config, $data, $uri, 'POST');
+ $authHeader = $generator->generate();
+
+ // Create handler without specifying method (should default to POST)
+ $handler = new Json($config, $data, $authHeader, $uri);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Should default to POST method');
+ }
+
+ public function test_rejects_signature_with_wrong_secret_key(): void
+ {
+ $data = ['Key' => 'ABC123'];
+ $uri = 'https://example.com/push';
+ $method = 'POST';
+
+ // Generate header with one secret key
+ $configGen = new DefaultConfig('website_key', 'correct_secret_key');
+ $generator = new Generator($configGen, $data, $uri, $method);
+ $authHeader = $generator->generate();
+
+ // Try to validate with different secret key
+ $configVal = new DefaultConfig('website_key', 'wrong_secret_key');
+ $handler = new Json($configVal, $data, $authHeader, $uri, $method);
+ $isValid = $handler->validate();
+
+ $this->assertFalse($isValid, 'Wrong secret key should fail validation');
+ }
+
+ public function test_validates_complex_nested_data(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = [
+ 'Transaction' => [
+ 'Key' => 'TX123456',
+ 'Status' => [
+ 'Code' => ['Code' => 190, 'Description' => 'Success'],
+ 'SubCode' => ['Code' => 'S001', 'Description' => 'OK'],
+ ],
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'creditcard',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'CardNumber', 'Value' => '****1234'],
+ ],
+ ],
+ ],
+ 'Key' => 'TX123456',
+ 'Invoice' => 'INV-001',
+ 'AmountDebit' => 100.50,
+ ];
+ $uri = 'https://example.com/push';
+ $method = 'POST';
+
+ // Generate valid HMAC header
+ $generator = new Generator($config, $data, $uri, $method);
+ $authHeader = $generator->generate();
+
+ $handler = new Json($config, $data, $authHeader, $uri, $method);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Complex nested data should validate correctly');
+ }
+
+ public function test_rejects_signature_with_different_uri(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['Key' => 'TX123'];
+ $method = 'POST';
+
+ $generator = new Generator($config, $data, 'https://example.com/push', $method);
+ $authHeader = $generator->generate();
+
+ $handler = new Json($config, $data, $authHeader, 'https://example.com/different', $method);
+ $isValid = $handler->validate();
+
+ $this->assertFalse($isValid, 'URI tampering should be rejected');
+ }
+
+ public function test_rejects_signature_with_uri_query_parameter_changes(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['Key' => 'TX123'];
+ $method = 'POST';
+
+ $generator = new Generator($config, $data, 'https://example.com/push?v=1', $method);
+ $authHeader = $generator->generate();
+
+ $handler = new Json($config, $data, $authHeader, 'https://example.com/push?v=2', $method);
+ $isValid = $handler->validate();
+
+ $this->assertFalse($isValid, 'URI query parameter changes should be rejected');
+ }
+
+ public function test_rejects_method_mismatch(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['Key' => 'TX123'];
+ $uri = 'https://example.com/push';
+
+ $generator = new Generator($config, $data, $uri, 'POST');
+ $authHeader = $generator->generate();
+
+ $handler = new Json($config, $data, $authHeader, $uri, 'GET');
+ $isValid = $handler->validate();
+
+ $this->assertFalse($isValid, 'HTTP method mismatch should be rejected');
+ }
+
+ public function test_rejects_signature_with_wrong_website_key(): void
+ {
+ $data = ['Key' => 'TX123'];
+ $uri = 'https://example.com/push';
+ $method = 'POST';
+
+ $configGen = new DefaultConfig('website_key_A', 'shared_secret');
+ $generator = new Generator($configGen, $data, $uri, $method);
+ $authHeader = $generator->generate();
+
+ $configVal = new DefaultConfig('website_key_B', 'shared_secret');
+ $handler = new Json($configVal, $data, $authHeader, $uri, $method);
+ $isValid = $handler->validate();
+
+ $this->assertFalse($isValid, 'Wrong website key should be rejected');
+ }
+
+ public function test_validates_unicode_data(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = [
+ 'Description' => 'Payment 💳 支付',
+ 'Customer' => 'محمد',
+ 'Amount' => 10.50,
+ 'Key' => 'TX123',
+ ];
+ $uri = 'https://example.com/push';
+ $method = 'POST';
+
+ $generator = new Generator($config, $data, $uri, $method);
+ $authHeader = $generator->generate();
+
+ $handler = new Json($config, $data, $authHeader, $uri, $method);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Unicode characters should be handled correctly');
+ }
+
+ public function test_validates_data_with_special_characters(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = [
+ 'Description' => 'Order #123 - Special chars: <>"\'/\\',
+ 'Invoice' => 'INV-001 & Co.',
+ 'Key' => 'TX123',
+ ];
+ $uri = 'https://example.com/push';
+ $method = 'POST';
+
+ $generator = new Generator($config, $data, $uri, $method);
+ $authHeader = $generator->generate();
+
+ $handler = new Json($config, $data, $authHeader, $uri, $method);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'Special characters should be handled correctly');
+ }
+
+ public function test_validates_put_method(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['Status' => 'updated', 'Key' => 'TX123'];
+ $uri = 'https://example.com/push';
+ $method = 'PUT';
+
+ $generator = new Generator($config, $data, $uri, $method);
+ $authHeader = $generator->generate();
+
+ $handler = new Json($config, $data, $authHeader, $uri, $method);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'PUT method should be supported');
+ }
+
+ public function test_validates_delete_method(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = [];
+ $uri = 'https://example.com/push/TX123';
+ $method = 'DELETE';
+
+ $generator = new Generator($config, $data, $uri, $method);
+ $authHeader = $generator->generate();
+
+ $handler = new Json($config, $data, $authHeader, $uri, $method);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'DELETE method should be supported');
+ }
+
+ public function test_validates_uri_with_query_parameters(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['Key' => 'TX123', 'Amount' => 10.00];
+ $uri = 'https://example.com/push?serviceVersion=2&test=true';
+ $method = 'POST';
+
+ $generator = new Generator($config, $data, $uri, $method);
+ $authHeader = $generator->generate();
+
+ $handler = new Json($config, $data, $authHeader, $uri, $method);
+ $isValid = $handler->validate();
+
+ $this->assertTrue($isValid, 'URI with query parameters should be supported');
+ }
+
+ public function test_rejects_header_with_empty_segments(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['Key' => 'TX123'];
+ $uri = 'https://example.com/push';
+
+ $invalidHeader = 'key::nonce:timestamp';
+
+ $handler = new Json($config, $data, $invalidHeader, $uri);
+ $isValid = $handler->validate();
+
+ $this->assertFalse($isValid, 'Header with empty segments should be rejected');
+ }
+}
diff --git a/tests/Unit/Handlers/Reply/ReplyHandlerTest.php b/tests/Unit/Handlers/Reply/ReplyHandlerTest.php
new file mode 100644
index 00000000..272c66f3
--- /dev/null
+++ b/tests/Unit/Handlers/Reply/ReplyHandlerTest.php
@@ -0,0 +1,285 @@
+ ['Key' => 'ABC123'], 'Key' => 'ABC123'];
+ $uri = 'https://example.com/push';
+
+ $generator = new Generator($config, $data, $uri, 'POST');
+ $authHeader = $generator->generate();
+
+ $handler = new ReplyHandler($config, $data, $authHeader, $uri);
+ $handler->validate();
+
+ $this->assertTrue($handler->isValid());
+ }
+
+ public function test_validates_json_push_notifications_with_datarequest_key(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['DataRequest' => ['Key' => 'XYZ789'], 'Key' => 'XYZ789'];
+ $uri = 'https://example.com/push';
+
+ $generator = new Generator($config, $data, $uri, 'POST');
+ $authHeader = $generator->generate();
+
+ $handler = new ReplyHandler($config, $data, $authHeader, $uri);
+ $handler->validate();
+
+ $this->assertTrue($handler->isValid());
+ }
+
+ public function test_validates_json_push_notifications_from_json_string(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['Transaction' => ['Key' => 'ABC123'], 'Key' => 'ABC123'];
+ $uri = 'https://example.com/push';
+
+ $generator = new Generator($config, $data, $uri, 'POST');
+ $authHeader = $generator->generate();
+
+ $jsonString = json_encode($data);
+
+ $handler = new ReplyHandler($config, $jsonString, $authHeader, $uri);
+ $handler->validate();
+
+ $this->assertTrue($handler->isValid());
+ }
+
+ public function test_validates_http_post_webhooks_with_brq_prefix(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['brq_amount' => '10.00'];
+
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['brq_signature'] = $signature;
+
+ $handler = new ReplyHandler($config, $data);
+ $handler->validate();
+
+ $this->assertTrue($handler->isValid());
+ }
+
+ public function test_validates_http_post_webhooks_with_uppercase_brq_prefix(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['BRQ_AMOUNT' => '25.50'];
+
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['BRQ_SIGNATURE'] = $signature;
+
+ $handler = new ReplyHandler($config, $data);
+ $handler->validate();
+
+ $this->assertTrue($handler->isValid());
+ }
+
+ public function test_json_format_takes_precedence_over_http_post(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = [
+ 'Transaction' => ['Key' => 'ABC123'],
+ 'Key' => 'ABC123',
+ 'brq_amount' => '10.00',
+ ];
+ $uri = 'https://example.com/push';
+
+ $generator = new Generator($config, $data, $uri, 'POST');
+ $authHeader = $generator->generate();
+
+ $handler = new ReplyHandler($config, $data, $authHeader, $uri);
+ $handler->validate();
+
+ $this->assertTrue($handler->isValid());
+ }
+
+ public function test_falls_back_to_http_post_when_json_keys_present_but_auth_missing(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = [
+ 'Transaction' => ['Key' => 'ABC123'],
+ 'brq_amount' => '10.00',
+ ];
+
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['brq_signature'] = $signature;
+
+ $handler = new ReplyHandler($config, $data);
+ $handler->validate();
+
+ $this->assertTrue($handler->isValid());
+ }
+
+ public function test_throws_for_unknown_format(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['unknown_field' => 'value', 'another_field' => 'data'];
+
+ $handler = new ReplyHandler($config, $data);
+
+ $this->expectException(Exception::class);
+ $this->expectExceptionMessage('No reply handler strategy applied.');
+
+ $handler->validate();
+ }
+
+ public function test_throws_for_empty_data_array(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+
+ $handler = new ReplyHandler($config, []);
+
+ $this->expectException(Exception::class);
+ $this->expectExceptionMessage('No reply handler strategy applied.');
+
+ $handler->validate();
+ }
+
+ public function test_throws_for_invalid_json_string(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $invalidJson = '{invalid json}';
+
+ $handler = new ReplyHandler($config, $invalidJson, 'auth:header', 'https://example.com');
+
+ $this->expectException(TypeError::class);
+
+ $handler->validate();
+ }
+
+ public function test_throws_when_json_data_missing_auth_header(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['Transaction' => ['Key' => 'ABC123'], 'Key' => 'ABC123'];
+
+ $handler = new ReplyHandler($config, $data, null, 'https://example.com/push');
+
+ $this->expectException(Exception::class);
+ $this->expectExceptionMessage('No reply handler strategy applied.');
+
+ $handler->validate();
+ }
+
+ public function test_throws_when_json_data_missing_uri(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['Transaction' => ['Key' => 'ABC123'], 'Key' => 'ABC123'];
+
+ $generator = new Generator($config, $data, 'https://example.com/push', 'POST');
+ $authHeader = $generator->generate();
+
+ $handler = new ReplyHandler($config, $data, $authHeader, null);
+
+ $this->expectException(Exception::class);
+ $this->expectExceptionMessage('No reply handler strategy applied.');
+
+ $handler->validate();
+ }
+
+ public function test_isValid_returns_false_before_validation(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['brq_amount' => '10.00'];
+
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['brq_signature'] = $signature;
+
+ $handler = new ReplyHandler($config, $data);
+
+ $this->assertFalse($handler->isValid());
+ }
+
+ public function test_isValid_updates_after_successful_validation(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['brq_amount' => '10.00'];
+
+ $signature = TestHelpers::generateHttpPostSignature($data);
+ $data['brq_signature'] = $signature;
+
+ $handler = new ReplyHandler($config, $data);
+
+ $this->assertFalse($handler->isValid());
+ $handler->validate();
+ $this->assertTrue($handler->isValid());
+ }
+
+ public function test_provides_case_insensitive_data_access(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['brq_amount' => '10.00', 'BRQ_CURRENCY' => 'EUR'];
+
+ $handler = new ReplyHandler($config, $data);
+
+ $this->assertSame('10.00', $handler->data('brq_amount'));
+ $this->assertSame('10.00', $handler->data('BRQ_AMOUNT'));
+ $this->assertSame('EUR', $handler->data('BRQ_CURRENCY'));
+ $this->assertSame('EUR', $handler->data('brq_currency'));
+ }
+
+ public function test_returns_all_data_when_no_key_specified(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['brq_amount' => '10.00', 'brq_currency' => 'EUR'];
+
+ $handler = new ReplyHandler($config, $data);
+
+ $allData = $handler->data();
+
+ $this->assertIsArray($allData);
+ $this->assertArrayHasKey('brq_amount', $allData);
+ $this->assertArrayHasKey('brq_currency', $allData);
+ }
+
+ public function test_returns_null_for_missing_key(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['brq_amount' => '10.00'];
+
+ $handler = new ReplyHandler($config, $data);
+
+ $this->assertNull($handler->data('nonexistent_key'));
+ }
+
+ public function test_allows_data_access_before_validation(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['brq_amount' => '10.00', 'brq_currency' => 'EUR'];
+
+ $handler = new ReplyHandler($config, $data);
+
+ $this->assertSame('10.00', $handler->data('brq_amount'));
+ $this->assertIsArray($handler->data());
+ }
+
+ public function test_data_returns_original_json_string_when_string_input(): void
+ {
+ $config = new DefaultConfig($_ENV['BPE_WEBSITE_KEY'], $_ENV['BPE_SECRET_KEY']);
+ $data = ['Transaction' => ['Key' => 'ABC123'], 'Key' => 'ABC123'];
+ $uri = 'https://example.com/push';
+
+ $generator = new Generator($config, $data, $uri, 'POST');
+ $authHeader = $generator->generate();
+
+ $jsonString = json_encode($data);
+
+ $handler = new ReplyHandler($config, $jsonString, $authHeader, $uri);
+
+ $this->assertSame($jsonString, $handler->data());
+ }
+}
diff --git a/tests/Unit/Models/Adapters/ServiceParametersKeysAdapterTest.php b/tests/Unit/Models/Adapters/ServiceParametersKeysAdapterTest.php
new file mode 100644
index 00000000..58eec9e3
--- /dev/null
+++ b/tests/Unit/Models/Adapters/ServiceParametersKeysAdapterTest.php
@@ -0,0 +1,208 @@
+ 'Test Street']);
+ $adapter = new BillinkAddressAdapter($address);
+
+ $this->assertInstanceOf(ServiceParameter::class, $adapter);
+ }
+
+ public function test_proxies_property_access_to_wrapped_model(): void
+ {
+ $address = new Address([
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'city' => 'Amsterdam',
+ ]);
+
+ $adapter = new BillinkAddressAdapter($address);
+
+ $this->assertEquals('Main Street', $adapter->street);
+ $this->assertEquals('123', $adapter->houseNumber);
+ $this->assertEquals('Amsterdam', $adapter->city);
+ }
+
+ public function test_returns_null_for_non_existent_property(): void
+ {
+ $address = new Address(['street' => 'Test Street']);
+ $adapter = new BillinkAddressAdapter($address);
+
+ $this->assertNull($adapter->nonExistentProperty);
+ }
+
+ public function test_returns_null_for_unset_property(): void
+ {
+ $address = new Address(['street' => 'Test Street']);
+ $adapter = new BillinkAddressAdapter($address);
+
+ // city was not set
+ $this->assertNull($adapter->city);
+ }
+
+ public function test_transforms_property_key_using_custom_mapping(): void
+ {
+ $address = new Address(['houseNumber' => '456']);
+
+ // Billink adapter maps houseNumber -> StreetNumber
+ $billinkAdapter = new BillinkAddressAdapter($address);
+ $this->assertEquals('StreetNumber', $billinkAdapter->serviceParameterKeyOf('houseNumber'));
+
+ // In3Old adapter maps houseNumber -> HouseNumber (no change) but uses default ucfirst
+ $in3OldAdapter = new In3OldAddressAdapter($address);
+ $this->assertEquals('HouseNumber', $in3OldAdapter->serviceParameterKeyOf('houseNumber'));
+ }
+
+ public function test_uses_ucfirst_for_unmapped_keys(): void
+ {
+ $address = new Address(['street' => 'Test Street']);
+ $adapter = new BillinkAddressAdapter($address);
+
+ // street is not in the keys array, so it should use ucfirst
+ $this->assertEquals('Street', $adapter->serviceParameterKeyOf('street'));
+ $this->assertEquals('City', $adapter->serviceParameterKeyOf('city'));
+ $this->assertEquals('Country', $adapter->serviceParameterKeyOf('country'));
+ }
+
+ public function test_billink_adapter_key_mappings(): void
+ {
+ $address = new Address([
+ 'houseNumber' => '123',
+ 'houseNumberAdditional' => 'A',
+ 'zipcode' => '1234AB',
+ ]);
+
+ $adapter = new BillinkAddressAdapter($address);
+
+ // Test Billink-specific mappings
+ $this->assertEquals('StreetNumber', $adapter->serviceParameterKeyOf('houseNumber'));
+ $this->assertEquals('StreetNumberAdditional', $adapter->serviceParameterKeyOf('houseNumberAdditional'));
+ $this->assertEquals('PostalCode', $adapter->serviceParameterKeyOf('zipcode'));
+ }
+
+ public function test_in3old_adapter_key_mappings(): void
+ {
+ $address = new Address([
+ 'houseNumberAdditional' => 'B',
+ 'zipcode' => '5678CD',
+ ]);
+
+ $adapter = new In3OldAddressAdapter($address);
+
+ // Test In3Old-specific mappings
+ $this->assertEquals('HouseNumberSuffix', $adapter->serviceParameterKeyOf('houseNumberAdditional'));
+ $this->assertEquals('ZipCode', $adapter->serviceParameterKeyOf('zipcode'));
+ }
+
+ public function test_delegates_to_array_to_wrapped_model(): void
+ {
+ $address = new Address([
+ 'street' => 'Test Street',
+ 'houseNumber' => '789',
+ 'city' => 'Utrecht',
+ ]);
+
+ $adapter = new BillinkAddressAdapter($address);
+ $array = $adapter->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertEquals('Test Street', $array['street']);
+ $this->assertEquals('789', $array['houseNumber']);
+ $this->assertEquals('Utrecht', $array['city']);
+ }
+
+ public function test_delegates_get_object_vars_to_wrapped_model(): void
+ {
+ $address = new Address([
+ 'street' => 'Object Street',
+ 'country' => 'NL',
+ ]);
+
+ $adapter = new BillinkAddressAdapter($address);
+ $vars = $adapter->getObjectVars();
+
+ $this->assertIsArray($vars);
+ $this->assertArrayHasKey('street', $vars);
+ $this->assertArrayHasKey('country', $vars);
+ $this->assertEquals('Object Street', $vars['street']);
+ $this->assertEquals('NL', $vars['country']);
+ }
+
+ public function test_works_with_empty_model(): void
+ {
+ $address = new Address();
+ $adapter = new BillinkAddressAdapter($address);
+
+ $this->assertNull($adapter->street);
+ $this->assertEmpty($adapter->toArray());
+ }
+
+ public function test_preserves_original_model_data(): void
+ {
+ $originalData = [
+ 'street' => 'Original Street',
+ 'houseNumber' => '100',
+ 'zipcode' => '9999ZZ',
+ 'city' => 'Den Haag',
+ 'country' => 'NL',
+ ];
+
+ $address = new Address($originalData);
+ $adapter = new BillinkAddressAdapter($address);
+
+ // Verify all data is preserved
+ foreach ($originalData as $key => $value) {
+ $this->assertEquals($value, $adapter->$key, "Property {$key} should match");
+ }
+ }
+
+ public function test_handles_special_characters_in_values(): void
+ {
+ $address = new Address([
+ 'street' => "Strasse mit Umlauten \u00e4\u00f6\u00fc",
+ 'houseNumber' => '1-3',
+ 'city' => "Saint-\u00c9tienne",
+ ]);
+
+ $adapter = new BillinkAddressAdapter($address);
+
+ $this->assertEquals("Strasse mit Umlauten \u00e4\u00f6\u00fc", $adapter->street);
+ $this->assertEquals('1-3', $adapter->houseNumber);
+ $this->assertEquals("Saint-\u00c9tienne", $adapter->city);
+ }
+
+ public function test_multiple_adapters_can_wrap_same_model(): void
+ {
+ $address = new Address([
+ 'street' => 'Shared Street',
+ 'houseNumber' => '50',
+ 'zipcode' => '1000AA',
+ ]);
+
+ $billinkAdapter = new BillinkAddressAdapter($address);
+ $in3OldAdapter = new In3OldAddressAdapter($address);
+
+ // Both should access the same underlying data
+ $this->assertEquals($billinkAdapter->street, $in3OldAdapter->street);
+ $this->assertEquals($billinkAdapter->houseNumber, $in3OldAdapter->houseNumber);
+ $this->assertEquals($billinkAdapter->zipcode, $in3OldAdapter->zipcode);
+
+ // But key mappings should differ
+ $this->assertNotEquals(
+ $billinkAdapter->serviceParameterKeyOf('zipcode'),
+ $in3OldAdapter->serviceParameterKeyOf('zipcode')
+ );
+ }
+}
diff --git a/tests/Unit/Models/AdditionalParametersTest.php b/tests/Unit/Models/AdditionalParametersTest.php
new file mode 100644
index 00000000..408456df
--- /dev/null
+++ b/tests/Unit/Models/AdditionalParametersTest.php
@@ -0,0 +1,487 @@
+ 'value1',
+ 'key2' => 'value2',
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertArrayHasKey('AdditionalParameter', $array);
+ $this->assertCount(2, $array['AdditionalParameter']);
+ $this->assertEmpty($array['List']);
+ $this->assertSame('key1', $array['AdditionalParameter'][0]['Name']);
+ $this->assertSame('value1', $array['AdditionalParameter'][0]['Value']);
+ $this->assertSame('key2', $array['AdditionalParameter'][1]['Name']);
+ $this->assertSame('value2', $array['AdditionalParameter'][1]['Value']);
+ }
+
+ public function test_data_request_mode_uses_list_array(): void
+ {
+ $params = new AdditionalParameters([
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ ], true);
+
+ $array = $params->toArray();
+
+ $this->assertArrayHasKey('List', $array);
+ $this->assertCount(2, $array['List']);
+ $this->assertEmpty($array['AdditionalParameter']);
+ $this->assertSame('key1', $array['List'][0]['Name']);
+ $this->assertSame('value1', $array['List'][0]['Value']);
+ $this->assertSame('key2', $array['List'][1]['Name']);
+ $this->assertSame('value2', $array['List'][1]['Value']);
+ }
+
+ public function test_constructor_flag_controls_array_selection(): void
+ {
+ $defaultMode = new AdditionalParameters(['key' => 'value'], false);
+ $dataRequestMode = new AdditionalParameters(['key' => 'value'], true);
+
+ $defaultArray = $defaultMode->toArray();
+ $dataRequestArray = $dataRequestMode->toArray();
+
+ $this->assertCount(1, $defaultArray['AdditionalParameter']);
+ $this->assertEmpty($defaultArray['List']);
+
+ $this->assertCount(1, $dataRequestArray['List']);
+ $this->assertEmpty($dataRequestArray['AdditionalParameter']);
+ }
+
+ public function test_set_properties_respects_mode_across_multiple_calls(): void
+ {
+ $defaultParams = new AdditionalParameters(['key1' => 'value1'], false);
+ $defaultParams->setProperties(['key2' => 'value2']);
+
+ $dataRequestParams = new AdditionalParameters(['key1' => 'value1'], true);
+ $dataRequestParams->setProperties(['key2' => 'value2']);
+
+ $defaultArray = $defaultParams->toArray();
+ $dataRequestArray = $dataRequestParams->toArray();
+
+ $this->assertCount(2, $defaultArray['AdditionalParameter']);
+ $this->assertEmpty($defaultArray['List']);
+
+ $this->assertCount(2, $dataRequestArray['List']);
+ $this->assertEmpty($dataRequestArray['AdditionalParameter']);
+ }
+
+ public function test_handles_empty_parameters_in_both_modes(): void
+ {
+ $defaultMode = new AdditionalParameters([], false);
+ $dataRequestMode = new AdditionalParameters([], true);
+
+ $defaultArray = $defaultMode->toArray();
+ $dataRequestArray = $dataRequestMode->toArray();
+
+ $this->assertTrue(
+ !isset($defaultArray['AdditionalParameter']) || empty($defaultArray['AdditionalParameter']),
+ 'Empty parameters should result in empty or missing AdditionalParameter'
+ );
+
+ $this->assertTrue(
+ !isset($dataRequestArray['List']) || empty($dataRequestArray['List']),
+ 'Empty parameters should result in empty or missing List'
+ );
+ }
+
+ public function test_handles_null_parameters_in_both_modes(): void
+ {
+ $defaultMode = new AdditionalParameters(null, false);
+ $dataRequestMode = new AdditionalParameters(null, true);
+
+ $defaultArray = $defaultMode->toArray();
+ $dataRequestArray = $dataRequestMode->toArray();
+
+ $this->assertTrue(
+ !isset($defaultArray['AdditionalParameter']) || empty($defaultArray['AdditionalParameter']),
+ 'Null parameters should result in empty or missing AdditionalParameter'
+ );
+
+ $this->assertTrue(
+ !isset($dataRequestArray['List']) || empty($dataRequestArray['List']),
+ 'Null parameters should result in empty or missing List'
+ );
+ }
+
+ public function test_multiple_set_properties_calls_accumulate_in_default_mode(): void
+ {
+ $params = new AdditionalParameters(['key1' => 'value1'], false);
+ $params->setProperties(['key2' => 'value2']);
+ $params->setProperties(['key3' => 'value3']);
+
+ $array = $params->toArray();
+
+ $this->assertCount(3, $array['AdditionalParameter']);
+ $this->assertSame('key1', $array['AdditionalParameter'][0]['Name']);
+ $this->assertSame('value1', $array['AdditionalParameter'][0]['Value']);
+ $this->assertSame('key2', $array['AdditionalParameter'][1]['Name']);
+ $this->assertSame('value2', $array['AdditionalParameter'][1]['Value']);
+ $this->assertSame('key3', $array['AdditionalParameter'][2]['Name']);
+ $this->assertSame('value3', $array['AdditionalParameter'][2]['Value']);
+ }
+
+ public function test_multiple_set_properties_calls_accumulate_in_data_request_mode(): void
+ {
+ $params = new AdditionalParameters(['key1' => 'value1'], true);
+ $params->setProperties(['key2' => 'value2']);
+ $params->setProperties(['key3' => 'value3']);
+
+ $array = $params->toArray();
+
+ $this->assertCount(3, $array['List']);
+ $this->assertSame('key1', $array['List'][0]['Name']);
+ $this->assertSame('value1', $array['List'][0]['Value']);
+ $this->assertSame('key2', $array['List'][1]['Name']);
+ $this->assertSame('value2', $array['List'][1]['Value']);
+ $this->assertSame('key3', $array['List'][2]['Name']);
+ $this->assertSame('value3', $array['List'][2]['Value']);
+ }
+
+ public function test_preserves_boolean_types_in_both_modes(): void
+ {
+ $defaultParams = new AdditionalParameters(['isActive' => true, 'isDeleted' => false], false);
+ $dataRequestParams = new AdditionalParameters(['isActive' => true, 'isDeleted' => false], true);
+
+ $defaultArray = $defaultParams->toArray();
+ $dataRequestArray = $dataRequestParams->toArray();
+
+ $this->assertSame(true, $defaultArray['AdditionalParameter'][0]['Value']);
+ $this->assertSame(false, $defaultArray['AdditionalParameter'][1]['Value']);
+
+ $this->assertSame(true, $dataRequestArray['List'][0]['Value']);
+ $this->assertSame(false, $dataRequestArray['List'][1]['Value']);
+ }
+
+ public function test_preserves_integer_types_in_both_modes(): void
+ {
+ $defaultParams = new AdditionalParameters(['count' => 42, 'negative' => -100, 'zero' => 0], false);
+ $dataRequestParams = new AdditionalParameters(['count' => 42, 'negative' => -100, 'zero' => 0], true);
+
+ $defaultArray = $defaultParams->toArray();
+ $dataRequestArray = $dataRequestParams->toArray();
+
+ $this->assertSame(42, $defaultArray['AdditionalParameter'][0]['Value']);
+ $this->assertSame(-100, $defaultArray['AdditionalParameter'][1]['Value']);
+ $this->assertSame(0, $defaultArray['AdditionalParameter'][2]['Value']);
+
+ $this->assertSame(42, $dataRequestArray['List'][0]['Value']);
+ $this->assertSame(-100, $dataRequestArray['List'][1]['Value']);
+ $this->assertSame(0, $dataRequestArray['List'][2]['Value']);
+ }
+
+ public function test_preserves_float_types_in_both_modes(): void
+ {
+ $defaultParams = new AdditionalParameters(['price' => 99.99, 'rate' => 0.15, 'zero' => 0.0], false);
+ $dataRequestParams = new AdditionalParameters(['price' => 99.99, 'rate' => 0.15, 'zero' => 0.0], true);
+
+ $defaultArray = $defaultParams->toArray();
+ $dataRequestArray = $dataRequestParams->toArray();
+
+ $this->assertSame(99.99, $defaultArray['AdditionalParameter'][0]['Value']);
+ $this->assertSame(0.15, $defaultArray['AdditionalParameter'][1]['Value']);
+ $this->assertSame(0.0, $defaultArray['AdditionalParameter'][2]['Value']);
+ $this->assertIsFloat($defaultArray['AdditionalParameter'][2]['Value']);
+
+ $this->assertSame(99.99, $dataRequestArray['List'][0]['Value']);
+ $this->assertSame(0.15, $dataRequestArray['List'][1]['Value']);
+ $this->assertSame(0.0, $dataRequestArray['List'][2]['Value']);
+ $this->assertIsFloat($dataRequestArray['List'][2]['Value']);
+ }
+
+ public function test_preserves_string_types_in_both_modes(): void
+ {
+ $defaultParams = new AdditionalParameters(['text' => 'hello', 'number' => '123'], false);
+ $dataRequestParams = new AdditionalParameters(['text' => 'hello', 'number' => '123'], true);
+
+ $defaultArray = $defaultParams->toArray();
+ $dataRequestArray = $dataRequestParams->toArray();
+
+ $this->assertSame('hello', $defaultArray['AdditionalParameter'][0]['Value']);
+ $this->assertSame('123', $defaultArray['AdditionalParameter'][1]['Value']);
+
+ $this->assertSame('hello', $dataRequestArray['List'][0]['Value']);
+ $this->assertSame('123', $dataRequestArray['List'][1]['Value']);
+ }
+
+ public function test_preserves_null_values_in_both_modes(): void
+ {
+ $defaultParams = new AdditionalParameters(['optional' => null, 'another' => null], false);
+ $dataRequestParams = new AdditionalParameters(['optional' => null, 'another' => null], true);
+
+ $defaultArray = $defaultParams->toArray();
+ $dataRequestArray = $dataRequestParams->toArray();
+
+ $this->assertCount(2, $defaultArray['AdditionalParameter']);
+ $this->assertNull($defaultArray['AdditionalParameter'][0]['Value']);
+ $this->assertNull($defaultArray['AdditionalParameter'][1]['Value']);
+ $this->assertSame('optional', $defaultArray['AdditionalParameter'][0]['Name']);
+ $this->assertSame('another', $defaultArray['AdditionalParameter'][1]['Name']);
+
+ $this->assertCount(2, $dataRequestArray['List']);
+ $this->assertNull($dataRequestArray['List'][0]['Value']);
+ $this->assertNull($dataRequestArray['List'][1]['Value']);
+ $this->assertSame('optional', $dataRequestArray['List'][0]['Name']);
+ $this->assertSame('another', $dataRequestArray['List'][1]['Name']);
+ }
+
+ public function test_preserves_zero_values(): void
+ {
+ $params = new AdditionalParameters([
+ 'intZero' => 0,
+ 'floatZero' => 0.0,
+ 'stringZero' => '0',
+ 'emptyString' => '',
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertCount(4, $array['AdditionalParameter']);
+ $this->assertSame(0, $array['AdditionalParameter'][0]['Value']);
+ $this->assertSame(0.0, $array['AdditionalParameter'][1]['Value']);
+ $this->assertSame('0', $array['AdditionalParameter'][2]['Value']);
+ $this->assertSame('', $array['AdditionalParameter'][3]['Value']);
+ }
+
+ public function test_preserves_empty_string_values(): void
+ {
+ $params = new AdditionalParameters([
+ 'empty' => '',
+ 'whitespace' => ' ',
+ 'tab' => "\t",
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertSame('', $array['AdditionalParameter'][0]['Value']);
+ $this->assertSame(' ', $array['AdditionalParameter'][1]['Value']);
+ $this->assertSame("\t", $array['AdditionalParameter'][2]['Value']);
+ }
+
+ public function test_handles_single_parameter(): void
+ {
+ $defaultParams = new AdditionalParameters(['singleKey' => 'singleValue'], false);
+ $dataRequestParams = new AdditionalParameters(['singleKey' => 'singleValue'], true);
+
+ $defaultArray = $defaultParams->toArray();
+ $dataRequestArray = $dataRequestParams->toArray();
+
+ $this->assertCount(1, $defaultArray['AdditionalParameter']);
+ $this->assertSame('singleKey', $defaultArray['AdditionalParameter'][0]['Name']);
+ $this->assertSame('singleValue', $defaultArray['AdditionalParameter'][0]['Value']);
+
+ $this->assertCount(1, $dataRequestArray['List']);
+ $this->assertSame('singleKey', $dataRequestArray['List'][0]['Name']);
+ $this->assertSame('singleValue', $dataRequestArray['List'][0]['Value']);
+ }
+
+ public function test_handles_large_parameter_sets(): void
+ {
+ $largeSet = [];
+ for ($i = 0; $i < 100; $i++) {
+ $largeSet['param' . $i] = 'value' . $i;
+ }
+
+ $params = new AdditionalParameters($largeSet);
+ $array = $params->toArray();
+
+ $this->assertCount(100, $array['AdditionalParameter']);
+ $this->assertSame('param0', $array['AdditionalParameter'][0]['Name']);
+ $this->assertSame('value0', $array['AdditionalParameter'][0]['Value']);
+ $this->assertSame('param99', $array['AdditionalParameter'][99]['Name']);
+ $this->assertSame('value99', $array['AdditionalParameter'][99]['Value']);
+ }
+
+ public function test_handles_very_long_string_values(): void
+ {
+ $longString = str_repeat('A', 10000);
+ $params = new AdditionalParameters(['longKey' => $longString]);
+
+ $array = $params->toArray();
+
+ $this->assertSame($longString, $array['AdditionalParameter'][0]['Value']);
+ $this->assertSame(10000, strlen($array['AdditionalParameter'][0]['Value']));
+ }
+
+ public function test_duplicate_keys_create_duplicate_entries(): void
+ {
+ $defaultParams = new AdditionalParameters(['key' => 'value1'], false);
+ $defaultParams->setProperties(['key' => 'value2']);
+
+ $dataRequestParams = new AdditionalParameters(['key' => 'value1'], true);
+ $dataRequestParams->setProperties(['key' => 'value2']);
+
+ $defaultArray = $defaultParams->toArray();
+ $dataRequestArray = $dataRequestParams->toArray();
+
+ $this->assertCount(2, $defaultArray['AdditionalParameter']);
+ $this->assertSame('key', $defaultArray['AdditionalParameter'][0]['Name']);
+ $this->assertSame('value1', $defaultArray['AdditionalParameter'][0]['Value']);
+ $this->assertSame('key', $defaultArray['AdditionalParameter'][1]['Name']);
+ $this->assertSame('value2', $defaultArray['AdditionalParameter'][1]['Value']);
+
+ $this->assertCount(2, $dataRequestArray['List']);
+ $this->assertSame('key', $dataRequestArray['List'][0]['Name']);
+ $this->assertSame('value1', $dataRequestArray['List'][0]['Value']);
+ $this->assertSame('key', $dataRequestArray['List'][1]['Name']);
+ $this->assertSame('value2', $dataRequestArray['List'][1]['Value']);
+ }
+
+ public function test_handles_special_characters_in_values(): void
+ {
+ $params = new AdditionalParameters([
+ 'description' => 'Test & Payment ',
+ 'reference' => 'REF/123/456',
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertSame('Test & Payment ', $array['AdditionalParameter'][0]['Value']);
+ $this->assertSame('REF/123/456', $array['AdditionalParameter'][1]['Value']);
+ }
+
+ public function test_handles_unicode_values(): void
+ {
+ $params = new AdditionalParameters([
+ 'emoji' => '🎉💳',
+ 'chinese' => '支付宝',
+ 'arabic' => 'الدفع',
+ 'currency' => '€ £ ¥',
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertSame('🎉💳', $array['AdditionalParameter'][0]['Value']);
+ $this->assertSame('支付宝', $array['AdditionalParameter'][1]['Value']);
+ $this->assertSame('الدفع', $array['AdditionalParameter'][2]['Value']);
+ $this->assertSame('€ £ ¥', $array['AdditionalParameter'][3]['Value']);
+ }
+
+ public function test_handles_special_characters_in_keys(): void
+ {
+ $params = new AdditionalParameters([
+ 'key-with-dash' => 'value1',
+ 'key.with.dot' => 'value2',
+ 'key_with_underscore' => 'value3',
+ 'key:with:colon' => 'value4',
+ ]);
+
+ $array = $params->toArray();
+
+ $keys = array_column($array['AdditionalParameter'], 'Name');
+
+ $this->assertContains('key-with-dash', $keys);
+ $this->assertContains('key.with.dot', $keys);
+ $this->assertContains('key_with_underscore', $keys);
+ $this->assertContains('key:with:colon', $keys);
+ }
+
+ public function test_preserves_original_key_names(): void
+ {
+ $params = new AdditionalParameters([
+ 'CamelCaseKey' => 'value1',
+ 'snake_case_key' => 'value2',
+ 'mixedCase_Key' => 'value3',
+ ]);
+
+ $array = $params->toArray();
+
+ $keys = array_column($array['AdditionalParameter'], 'Name');
+
+ $this->assertContains('CamelCaseKey', $keys);
+ $this->assertContains('snake_case_key', $keys);
+ $this->assertContains('mixedCase_Key', $keys);
+ }
+
+ public function test_magic_get_access_to_arrays(): void
+ {
+ $defaultParams = new AdditionalParameters(['key1' => 'value1', 'key2' => 'value2'], false);
+ $dataRequestParams = new AdditionalParameters(['key1' => 'value1', 'key2' => 'value2'], true);
+
+ $additionalParameter = $defaultParams->AdditionalParameter;
+ $list = $dataRequestParams->List;
+
+ $this->assertIsArray($additionalParameter);
+ $this->assertCount(2, $additionalParameter);
+ $this->assertSame('key1', $additionalParameter[0]['Name']);
+ $this->assertSame('value1', $additionalParameter[0]['Value']);
+
+ $this->assertIsArray($list);
+ $this->assertCount(2, $list);
+ $this->assertSame('key1', $list[0]['Name']);
+ $this->assertSame('value1', $list[0]['Value']);
+ }
+
+ public function test_get_object_vars_includes_correct_array(): void
+ {
+ $defaultParams = new AdditionalParameters(['key1' => 'value1'], false);
+ $dataRequestParams = new AdditionalParameters(['key1' => 'value1'], true);
+
+ $defaultVars = $defaultParams->getObjectVars();
+ $dataRequestVars = $dataRequestParams->getObjectVars();
+
+ $this->assertArrayHasKey('AdditionalParameter', $defaultVars);
+ $this->assertIsArray($defaultVars['AdditionalParameter']);
+ $this->assertCount(1, $defaultVars['AdditionalParameter']);
+
+ $this->assertArrayHasKey('List', $dataRequestVars);
+ $this->assertIsArray($dataRequestVars['List']);
+ $this->assertCount(1, $dataRequestVars['List']);
+ }
+
+ public function test_to_array_structure_in_default_mode(): void
+ {
+ $params = new AdditionalParameters([
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ ], false);
+
+ $array = $params->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertArrayHasKey('AdditionalParameter', $array);
+ $this->assertIsArray($array['AdditionalParameter']);
+ $this->assertCount(2, $array['AdditionalParameter']);
+ $this->assertEmpty($array['List']);
+
+ foreach ($array['AdditionalParameter'] as $item) {
+ $this->assertArrayHasKey('Name', $item);
+ $this->assertArrayHasKey('Value', $item);
+ $this->assertCount(2, $item);
+ }
+ }
+
+ public function test_to_array_structure_in_data_request_mode(): void
+ {
+ $params = new AdditionalParameters([
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ ], true);
+
+ $array = $params->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertArrayHasKey('List', $array);
+ $this->assertIsArray($array['List']);
+ $this->assertCount(2, $array['List']);
+ $this->assertEmpty($array['AdditionalParameter']);
+
+ foreach ($array['List'] as $item) {
+ $this->assertArrayHasKey('Name', $item);
+ $this->assertArrayHasKey('Value', $item);
+ $this->assertCount(2, $item);
+ }
+ }
+}
diff --git a/tests/Unit/Models/AddressTest.php b/tests/Unit/Models/AddressTest.php
new file mode 100644
index 00000000..5144002f
--- /dev/null
+++ b/tests/Unit/Models/AddressTest.php
@@ -0,0 +1,212 @@
+ 'Main Street',
+ 'houseNumber' => '123',
+ 'houseNumberAdditional' => 'A',
+ 'zipcode' => '1234 AB',
+ 'city' => 'Amsterdam',
+ 'state' => 'North Holland',
+ 'country' => 'NL',
+ ]);
+
+ $this->assertSame('Main Street', $address->street);
+ $this->assertSame('123', $address->houseNumber);
+ $this->assertSame('A', $address->houseNumberAdditional);
+ $this->assertSame('1234 AB', $address->zipcode);
+ $this->assertSame('Amsterdam', $address->city);
+ $this->assertSame('North Holland', $address->state);
+ $this->assertSame('NL', $address->country);
+ }
+
+ public function test_handles_partial_initialization(): void
+ {
+ $minimalAddress = new Address([
+ 'street' => 'Baker Street',
+ 'houseNumber' => '221',
+ 'city' => 'London',
+ 'country' => 'GB',
+ ]);
+
+ $this->assertSame('Baker Street', $minimalAddress->street);
+ $this->assertSame('221', $minimalAddress->houseNumber);
+ $this->assertSame('London', $minimalAddress->city);
+ $this->assertSame('GB', $minimalAddress->country);
+ $this->assertNull($minimalAddress->zipcode);
+
+ $fullAddress = new Address([
+ 'street' => 'Kalverstraat',
+ 'houseNumber' => '92',
+ 'houseNumberAdditional' => 'III',
+ 'zipcode' => '1012 PH',
+ 'city' => 'Amsterdam',
+ 'state' => 'Noord-Holland',
+ 'country' => 'NL',
+ ]);
+
+ $this->assertSame('Kalverstraat', $fullAddress->street);
+ $this->assertSame('92', $fullAddress->houseNumber);
+ $this->assertSame('III', $fullAddress->houseNumberAdditional);
+ $this->assertSame('1012 PH', $fullAddress->zipcode);
+ $this->assertSame('Amsterdam', $fullAddress->city);
+ $this->assertSame('Noord-Holland', $fullAddress->state);
+ $this->assertSame('NL', $fullAddress->country);
+ }
+
+ public function test_to_array_preserves_all_values(): void
+ {
+ $address = new Address([
+ 'street' => 'Herengracht',
+ 'houseNumber' => '501',
+ 'houseNumberAdditional' => 'B',
+ 'zipcode' => '1017 BV',
+ 'city' => 'Amsterdam',
+ 'state' => 'Noord-Holland',
+ 'country' => 'NL',
+ ]);
+
+ $array = $address->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertSame('Herengracht', $array['street']);
+ $this->assertSame('501', $array['houseNumber']);
+ $this->assertSame('B', $array['houseNumberAdditional']);
+ $this->assertSame('1017 BV', $array['zipcode']);
+ $this->assertSame('Amsterdam', $array['city']);
+ $this->assertSame('Noord-Holland', $array['state']);
+ $this->assertSame('NL', $array['country']);
+ }
+
+ public function test_handles_international_address_formats(): void
+ {
+ $ukAddress = new Address([
+ 'street' => 'High Street',
+ 'houseNumber' => '10',
+ 'zipcode' => 'SW1A 1AA',
+ 'city' => 'London',
+ 'country' => 'GB',
+ ]);
+
+ $this->assertSame('High Street', $ukAddress->street);
+ $this->assertSame('SW1A 1AA', $ukAddress->zipcode);
+ $this->assertSame('London', $ukAddress->city);
+ $this->assertSame('GB', $ukAddress->country);
+
+ $germanAddress = new Address([
+ 'street' => 'Hauptstraße',
+ 'houseNumber' => '15',
+ 'zipcode' => '10115',
+ 'city' => 'Berlin',
+ 'state' => 'Berlin',
+ 'country' => 'DE',
+ ]);
+
+ $this->assertSame('Hauptstraße', $germanAddress->street);
+ $this->assertSame('10115', $germanAddress->zipcode);
+ $this->assertSame('Berlin', $germanAddress->city);
+ $this->assertSame('Berlin', $germanAddress->state);
+
+ $usAddress = new Address([
+ 'street' => 'Fifth Avenue',
+ 'houseNumber' => '350',
+ 'zipcode' => '10118',
+ 'city' => 'New York',
+ 'state' => 'NY',
+ 'country' => 'US',
+ ]);
+
+ $this->assertSame('Fifth Avenue', $usAddress->street);
+ $this->assertSame('NY', $usAddress->state);
+ $this->assertSame('US', $usAddress->country);
+
+ $dutchAddress = new Address([
+ 'street' => 'Keizersgracht',
+ 'houseNumber' => '555',
+ 'houseNumberAdditional' => 'H',
+ 'zipcode' => '1017 DR',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ]);
+
+ $this->assertSame('Keizersgracht', $dutchAddress->street);
+ $this->assertSame('H', $dutchAddress->houseNumberAdditional);
+ $this->assertSame('1017 DR', $dutchAddress->zipcode);
+ }
+
+ public function test_handles_unicode_addresses(): void
+ {
+ $arabicAddress = new Address([
+ 'street' => 'شارع الملك فهد',
+ 'houseNumber' => '123',
+ 'city' => 'الرياض',
+ 'country' => 'SA',
+ ]);
+
+ $this->assertSame('شارع الملك فهد', $arabicAddress->street);
+ $this->assertSame('الرياض', $arabicAddress->city);
+
+ $chineseAddress = new Address([
+ 'street' => '南京东路',
+ 'houseNumber' => '100',
+ 'city' => '上海',
+ 'country' => 'CN',
+ ]);
+
+ $this->assertSame('南京东路', $chineseAddress->street);
+ $this->assertSame('上海', $chineseAddress->city);
+
+ $cyrillicAddress = new Address([
+ 'street' => 'Тверская улица',
+ 'houseNumber' => '7',
+ 'zipcode' => '125009',
+ 'city' => 'Москва',
+ 'country' => 'RU',
+ ]);
+
+ $this->assertSame('Тверская улица', $cyrillicAddress->street);
+ $this->assertSame('Москва', $cyrillicAddress->city);
+
+ $greekAddress = new Address([
+ 'street' => 'Πανεπιστημίου',
+ 'houseNumber' => '34',
+ 'city' => 'Αθήνα',
+ 'country' => 'GR',
+ ]);
+
+ $this->assertSame('Πανεπιστημίου', $greekAddress->street);
+ $this->assertSame('Αθήνα', $greekAddress->city);
+
+ $array = $arabicAddress->toArray();
+ $this->assertSame('شارع الملك فهد', $array['street']);
+ $this->assertSame('الرياض', $array['city']);
+ }
+
+ public function test_house_number_additional_variations(): void
+ {
+ $variations = ['A', 'B2', 'III', 'bis', '1/2', 'Apt 5', 'Unit 12', 'rear', 'top floor'];
+
+ foreach ($variations as $suffix) {
+ $address = new Address([
+ 'street' => 'Test Street',
+ 'houseNumber' => '100',
+ 'houseNumberAdditional' => $suffix,
+ 'city' => 'Test City',
+ 'country' => 'NL',
+ ]);
+
+ $this->assertSame($suffix, $address->houseNumberAdditional);
+ $this->assertSame($suffix, $address->toArray()['houseNumberAdditional']);
+ }
+ }
+}
diff --git a/tests/Unit/Models/ArticleTest.php b/tests/Unit/Models/ArticleTest.php
new file mode 100644
index 00000000..0ae10ef0
--- /dev/null
+++ b/tests/Unit/Models/ArticleTest.php
@@ -0,0 +1,158 @@
+ 'SKU-12345',
+ 'type' => 'physical',
+ 'brand' => 'Acme Corp',
+ 'manufacturer' => 'Acme Manufacturing',
+ 'unitCode' => 'PCS',
+ 'price' => 99.99,
+ 'quantity' => 5,
+ 'vatPercentage' => 21.0,
+ 'vatCategory' => 'high',
+ 'description' => 'Premium product with warranty',
+ ]);
+
+ $this->assertSame('SKU-12345', $article->identifier);
+ $this->assertSame('physical', $article->type);
+ $this->assertSame('Acme Corp', $article->brand);
+ $this->assertSame('Acme Manufacturing', $article->manufacturer);
+ $this->assertSame('PCS', $article->unitCode);
+ $this->assertSame(99.99, $article->price);
+ $this->assertSame(5, $article->quantity);
+ $this->assertSame(21.0, $article->vatPercentage);
+ $this->assertSame('high', $article->vatCategory);
+ $this->assertSame('Premium product with warranty', $article->description);
+ }
+
+ public function test_handles_partial_initialization(): void
+ {
+ $article = new Article([
+ 'identifier' => 'SKU-001',
+ 'price' => 49.50,
+ 'quantity' => 1,
+ ]);
+
+ $this->assertSame('SKU-001', $article->identifier);
+ $this->assertSame(49.50, $article->price);
+ $this->assertSame(1, $article->quantity);
+ $this->assertNull($article->type);
+ $this->assertNull($article->brand);
+ $this->assertNull($article->vatCategory);
+ }
+
+ public function test_to_array_preserves_type_integrity(): void
+ {
+ $article = new Article([
+ 'identifier' => 'TEST-123',
+ 'type' => 'digital',
+ 'price' => 19.99,
+ 'quantity' => 3,
+ 'vatPercentage' => 9.0,
+ ]);
+
+ $array = $article->toArray();
+
+ $this->assertIsString($array['identifier']);
+ $this->assertIsString($array['type']);
+ $this->assertIsFloat($array['price']);
+ $this->assertIsInt($array['quantity']);
+ $this->assertIsFloat($array['vatPercentage']);
+
+ $this->assertSame('TEST-123', $array['identifier']);
+ $this->assertSame('digital', $array['type']);
+ $this->assertSame(19.99, $array['price']);
+ $this->assertSame(3, $array['quantity']);
+ $this->assertSame(9.0, $array['vatPercentage']);
+ }
+
+ public function test_float_properties_preserve_precision(): void
+ {
+ $article = new Article([
+ 'identifier' => 'FLOAT-TEST',
+ 'price' => 99.99,
+ 'vatPercentage' => 21.5,
+ ]);
+
+ $this->assertSame(99.99, $article->price);
+ $this->assertSame(21.5, $article->vatPercentage);
+
+ $array = $article->toArray();
+ $this->assertSame(99.99, $array['price']);
+ $this->assertSame(21.5, $array['vatPercentage']);
+ }
+
+ public function test_float_properties_handle_zero_and_edge_values(): void
+ {
+ $article = new Article([
+ 'identifier' => 'EDGE-001',
+ 'price' => 0.0,
+ 'quantity' => 0,
+ 'vatPercentage' => 0.0,
+ ]);
+
+ $this->assertSame(0.0, $article->price);
+ $this->assertSame(0, $article->quantity);
+ $this->assertSame(0.0, $article->vatPercentage);
+
+ $array = $article->toArray();
+ $this->assertIsFloat($array['price']);
+ $this->assertIsInt($array['quantity']);
+ $this->assertIsFloat($array['vatPercentage']);
+ $this->assertSame(0.0, $array['price']);
+ $this->assertSame(0, $array['quantity']);
+ }
+
+ public function test_integer_quantity_handles_edge_cases(): void
+ {
+ $negativeQuantity = new Article([
+ 'identifier' => 'NEG-001',
+ 'quantity' => -5,
+ ]);
+ $this->assertSame(-5, $negativeQuantity->quantity);
+ $this->assertIsInt($negativeQuantity->toArray()['quantity']);
+
+ $largeQuantity = new Article([
+ 'identifier' => 'LARGE-001',
+ 'quantity' => 999999,
+ ]);
+ $this->assertSame(999999, $largeQuantity->quantity);
+ $this->assertIsInt($largeQuantity->toArray()['quantity']);
+
+ $zeroQuantity = new Article([
+ 'identifier' => 'ZERO-001',
+ 'quantity' => 0,
+ ]);
+ $this->assertSame(0, $zeroQuantity->quantity);
+ }
+
+ public function test_string_properties_handle_empty_and_special_characters(): void
+ {
+ $article = new Article([
+ 'identifier' => '',
+ 'type' => 'digital',
+ 'brand' => '',
+ 'description' => 'Special chars: €100, 50% off, , "quoted", \'single\'',
+ ]);
+
+ $this->assertSame('', $article->identifier);
+ $this->assertSame('', $article->brand);
+ $this->assertSame('Special chars: €100, 50% off, , "quoted", \'single\'', $article->description);
+
+ $array = $article->toArray();
+ $this->assertSame('', $array['identifier']);
+ $this->assertSame('', $array['brand']);
+ $this->assertSame('Special chars: €100, 50% off, , "quoted", \'single\'', $array['description']);
+ }
+}
diff --git a/tests/Unit/Models/BankAccountTest.php b/tests/Unit/Models/BankAccountTest.php
new file mode 100644
index 00000000..f6ea01be
--- /dev/null
+++ b/tests/Unit/Models/BankAccountTest.php
@@ -0,0 +1,101 @@
+ 'NL91ABNA0417164300',
+ 'accountName' => 'John Doe',
+ 'bic' => 'ABNANL2A',
+ ]);
+
+ $this->assertSame('NL91ABNA0417164300', $bankAccount->iban);
+ $this->assertSame('John Doe', $bankAccount->accountName);
+ $this->assertSame('ABNANL2A', $bankAccount->bic);
+ }
+
+ public function test_handles_partial_initialization(): void
+ {
+ $bankAccount = new BankAccount([
+ 'iban' => 'DE89370400440532013000',
+ ]);
+
+ $this->assertSame('DE89370400440532013000', $bankAccount->iban);
+ }
+
+ public function test_to_array_preserves_string_types(): void
+ {
+ $bankAccount = new BankAccount([
+ 'iban' => 'BE68539007547034',
+ 'accountName' => 'Jane Smith',
+ 'bic' => 'KREDBEBB',
+ ]);
+
+ $array = $bankAccount->toArray();
+
+ $this->assertIsString($array['iban']);
+ $this->assertIsString($array['accountName']);
+ $this->assertIsString($array['bic']);
+
+ $this->assertSame('BE68539007547034', $array['iban']);
+ $this->assertSame('Jane Smith', $array['accountName']);
+ $this->assertSame('KREDBEBB', $array['bic']);
+ }
+
+ public function test_handles_empty_strings(): void
+ {
+ $bankAccount = new BankAccount([
+ 'iban' => '',
+ 'accountName' => '',
+ 'bic' => '',
+ ]);
+
+ $this->assertSame('', $bankAccount->iban);
+ $this->assertSame('', $bankAccount->accountName);
+ $this->assertSame('', $bankAccount->bic);
+
+ $array = $bankAccount->toArray();
+ $this->assertSame('', $array['iban']);
+ $this->assertSame('', $array['accountName']);
+ $this->assertSame('', $array['bic']);
+ }
+
+ public function test_handles_special_characters_in_account_name(): void
+ {
+ $bankAccount = new BankAccount([
+ 'iban' => 'FR1420041010050500013M02606',
+ 'accountName' => 'François O\'Brien-Müller & Co., "Trading" Ltd.',
+ 'bic' => 'BNPAFRPP',
+ ]);
+
+ $this->assertSame('François O\'Brien-Müller & Co., "Trading" Ltd.', $bankAccount->accountName);
+
+ $array = $bankAccount->toArray();
+ $this->assertSame('François O\'Brien-Müller & Co., "Trading" Ltd.', $array['accountName']);
+ }
+
+ public function test_accepts_various_iban_formats(): void
+ {
+ $testCases = [
+ ['iban' => 'NL91ABNA0417164300', 'country' => 'NL'],
+ ['iban' => 'DE89370400440532013000', 'country' => 'DE'],
+ ['iban' => 'GB29NWBK60161331926819', 'country' => 'GB'],
+ ['iban' => 'IT60X0542811101000000123456', 'country' => 'IT'],
+ ['iban' => 'ES9121000418450200051332', 'country' => 'ES'],
+ ];
+
+ foreach ($testCases as $testCase) {
+ $bankAccount = new BankAccount(['iban' => $testCase['iban']]);
+ $this->assertSame($testCase['iban'], $bankAccount->iban);
+ $this->assertSame($testCase['iban'], $bankAccount->toArray()['iban']);
+ }
+ }
+}
diff --git a/tests/Unit/Models/ClientIPTest.php b/tests/Unit/Models/ClientIPTest.php
new file mode 100644
index 00000000..12bc6e68
--- /dev/null
+++ b/tests/Unit/Models/ClientIPTest.php
@@ -0,0 +1,179 @@
+assertSame('192.168.1.100', $clientIP->Address);
+ $this->assertSame(IPProtocolVersion::IPV4, $clientIP->Type);
+ }
+
+ public function test_constructor_with_ipv4_auto_detects_type(): void
+ {
+ $clientIP = new ClientIP('10.0.0.1');
+
+ $this->assertSame('10.0.0.1', $clientIP->Address);
+ $this->assertSame(IPProtocolVersion::IPV4, $clientIP->Type);
+ }
+
+ public function test_constructor_with_ipv6_auto_detects_type(): void
+ {
+ $clientIP = new ClientIP('2001:0db8:85a3:0000:0000:8a2e:0370:7334');
+
+ $this->assertSame('2001:0db8:85a3:0000:0000:8a2e:0370:7334', $clientIP->Address);
+ $this->assertSame(IPProtocolVersion::IPV6, $clientIP->Type);
+ }
+
+ public function test_constructor_without_parameters_defaults_to_localhost(): void
+ {
+ $originalServer = $_SERVER;
+
+ try {
+ $_SERVER = [];
+ $clientIP = new ClientIP();
+
+ $this->assertSame('127.0.0.1', $clientIP->Address);
+ $this->assertSame(IPProtocolVersion::IPV4, $clientIP->Type);
+ } finally {
+ $_SERVER = $originalServer;
+ }
+ }
+
+ public function test_auto_detection_uses_remote_addr_from_server(): void
+ {
+ $originalServer = $_SERVER;
+ $_SERVER['REMOTE_ADDR'] = '203.0.113.45';
+
+ $clientIP = new ClientIP();
+
+ $this->assertSame('203.0.113.45', $clientIP->Address);
+ $this->assertSame(IPProtocolVersion::IPV4, $clientIP->Type);
+
+ $_SERVER = $originalServer;
+ }
+
+ public function test_auto_detection_uses_http_x_forwarded_for_from_server(): void
+ {
+ $originalServer = $_SERVER;
+ $_SERVER['HTTP_X_FORWARDED_FOR'] = '198.51.100.23';
+ $_SERVER['REMOTE_ADDR'] = '10.0.0.1';
+
+ $clientIP = new ClientIP();
+
+ $this->assertSame('198.51.100.23', $clientIP->Address);
+ $this->assertSame(IPProtocolVersion::IPV4, $clientIP->Type);
+
+ $_SERVER = $originalServer;
+ }
+
+ public function test_auto_detection_skips_invalid_ip_in_http_x_forwarded_for(): void
+ {
+ $originalServer = $_SERVER;
+ $_SERVER['HTTP_X_FORWARDED_FOR'] = 'not-an-ip';
+ $_SERVER['REMOTE_ADDR'] = '192.168.1.50';
+
+ $clientIP = new ClientIP();
+
+ $this->assertSame('192.168.1.50', $clientIP->Address);
+ $this->assertSame(IPProtocolVersion::IPV4, $clientIP->Type);
+
+ $_SERVER = $originalServer;
+ }
+
+ public function test_auto_detection_skips_invalid_remote_addr_and_defaults(): void
+ {
+ $originalServer = $_SERVER;
+ $_SERVER['REMOTE_ADDR'] = 'invalid-ip-address';
+
+ $clientIP = new ClientIP();
+
+ $this->assertSame('127.0.0.1', $clientIP->Address);
+ $this->assertSame(IPProtocolVersion::IPV4, $clientIP->Type);
+
+ $_SERVER = $originalServer;
+ }
+
+ public function test_ipv6_address_gets_correct_type(): void
+ {
+ $clientIP = new ClientIP('::1');
+
+ $this->assertSame('::1', $clientIP->Address);
+ $this->assertSame(IPProtocolVersion::IPV6, $clientIP->Type);
+ }
+
+ public function test_ipv6_from_remote_addr_auto_detects_type(): void
+ {
+ $originalServer = $_SERVER;
+ $_SERVER['REMOTE_ADDR'] = '2001:db8::1';
+
+ $clientIP = new ClientIP();
+
+ $this->assertSame('2001:db8::1', $clientIP->Address);
+ $this->assertSame(IPProtocolVersion::IPV6, $clientIP->Type);
+
+ $_SERVER = $originalServer;
+ }
+
+ public function test_to_array_returns_type_and_address(): void
+ {
+ $clientIP = new ClientIP('172.16.0.1', IPProtocolVersion::IPV4);
+
+ $array = $clientIP->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertArrayHasKey('Type', $array);
+ $this->assertArrayHasKey('Address', $array);
+ $this->assertSame(IPProtocolVersion::IPV4, $array['Type']);
+ $this->assertSame('172.16.0.1', $array['Address']);
+ }
+
+ public function test_to_array_with_ipv6(): void
+ {
+ $clientIP = new ClientIP('fe80::1', IPProtocolVersion::IPV6);
+
+ $array = $clientIP->toArray();
+
+ $this->assertSame(IPProtocolVersion::IPV6, $array['Type']);
+ $this->assertSame('fe80::1', $array['Address']);
+ }
+
+ public function test_null_ip_with_explicit_type_uses_auto_detection_for_ip(): void
+ {
+ $originalServer = $_SERVER;
+ $_SERVER['REMOTE_ADDR'] = '10.20.30.40';
+
+ $clientIP = new ClientIP(null, IPProtocolVersion::IPV6);
+
+ $this->assertSame('10.20.30.40', $clientIP->Address);
+ $this->assertSame(IPProtocolVersion::IPV6, $clientIP->Type);
+
+ $_SERVER = $originalServer;
+ }
+
+ public function test_explicit_type_overrides_auto_detection(): void
+ {
+ $clientIP = new ClientIP('192.168.1.1', IPProtocolVersion::IPV6);
+
+ $this->assertSame('192.168.1.1', $clientIP->Address);
+ $this->assertSame(IPProtocolVersion::IPV6, $clientIP->Type);
+ }
+
+ public function test_property_access_via_magic_get(): void
+ {
+ $clientIP = new ClientIP('8.8.8.8');
+
+ $this->assertSame('8.8.8.8', $clientIP->Address);
+ $this->assertSame(IPProtocolVersion::IPV4, $clientIP->Type);
+ $this->assertNull($clientIP->nonExistentProperty);
+ }
+}
diff --git a/tests/Unit/Models/CompanyTest.php b/tests/Unit/Models/CompanyTest.php
new file mode 100644
index 00000000..bd700269
--- /dev/null
+++ b/tests/Unit/Models/CompanyTest.php
@@ -0,0 +1,302 @@
+ 'Acme Corporation',
+ 'vatApplicable' => true,
+ 'vatNumber' => 'NL123456789B01',
+ 'chamberOfCommerce' => '12345678',
+ ]);
+
+ $this->assertSame('Acme Corporation', $company->companyName);
+ $this->assertTrue($company->vatApplicable);
+ $this->assertSame('NL123456789B01', $company->vatNumber);
+ $this->assertSame('12345678', $company->chamberOfCommerce);
+ }
+
+ public function test_inherits_person_properties(): void
+ {
+ $company = new Company([
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ 'companyName' => 'Acme',
+ ]);
+
+ $this->assertSame('John', $company->firstName);
+ $this->assertSame('Doe', $company->lastName);
+ $this->assertSame('Acme', $company->companyName);
+ }
+
+ public function test_company_is_instance_of_person(): void
+ {
+ $company = new Company();
+
+ $this->assertInstanceOf(Person::class, $company);
+ }
+ public function test_to_array_includes_all_properties(): void
+ {
+ $company = new Company([
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ 'companyName' => 'Doe Enterprises',
+ 'vatApplicable' => true,
+ 'vatNumber' => 'NL111222333B01',
+ 'chamberOfCommerce' => '87654321',
+ ]);
+
+ $array = $company->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertSame('John', $array['firstName']);
+ $this->assertSame('Doe', $array['lastName']);
+ $this->assertSame('Doe Enterprises', $array['companyName']);
+ $this->assertTrue($array['vatApplicable']);
+ $this->assertSame('NL111222333B01', $array['vatNumber']);
+ $this->assertSame('87654321', $array['chamberOfCommerce']);
+ }
+
+ public function test_vat_applicable_preserves_boolean_type(): void
+ {
+ $companyTrue = new Company([
+ 'vatApplicable' => true,
+ ]);
+
+ $companyFalse = new Company([
+ 'vatApplicable' => false,
+ ]);
+
+ $this->assertSame(true, $companyTrue->vatApplicable);
+ $this->assertSame(false, $companyFalse->vatApplicable);
+
+ $arrayTrue = $companyTrue->toArray();
+ $arrayFalse = $companyFalse->toArray();
+
+ $this->assertSame(true, $arrayTrue['vatApplicable']);
+ $this->assertSame(false, $arrayFalse['vatApplicable']);
+ }
+
+ public function test_chamber_of_commerce_nullable(): void
+ {
+ $companyWithChamber = new Company([
+ 'companyName' => 'Registered Inc',
+ 'chamberOfCommerce' => '12345678',
+ ]);
+
+ $companyWithoutChamber = new Company([
+ 'companyName' => 'Unregistered LLC',
+ 'chamberOfCommerce' => null,
+ ]);
+
+ $this->assertSame('12345678', $companyWithChamber->chamberOfCommerce);
+ }
+
+ public function test_person_nullable_properties_still_work(): void
+ {
+ $company = new Company([
+ 'firstName' => 'Alice',
+ 'lastName' => 'Johnson',
+ 'initials' => 'A.J.',
+ 'birthDate' => '1985-05-15',
+ 'companyName' => 'Johnson Corp',
+ ]);
+
+ $this->assertSame('A.J.', $company->initials);
+ $this->assertSame('1985-05-15', $company->birthDate);
+
+ $company->initials = null;
+ $company->birthDate = null;
+
+ $this->assertNull($company->initials);
+ $this->assertNull($company->birthDate);
+ }
+
+ public function test_full_initialization(): void
+ {
+ $company = new Company([
+ 'category' => 'B2B',
+ 'gender' => 'M',
+ 'culture' => 'nl-NL',
+ 'careOf' => 'Finance Department',
+ 'title' => 'CEO',
+ 'initials' => 'J.D.',
+ 'name' => 'John Doe',
+ 'firstName' => 'John',
+ 'lastNamePrefix' => 'van',
+ 'lastName' => 'Doe',
+ 'birthDate' => '1980-01-01',
+ 'placeOfBirth' => 'Amsterdam',
+ 'companyName' => 'Doe Enterprises BV',
+ 'vatApplicable' => true,
+ 'vatNumber' => 'NL123456789B01',
+ 'chamberOfCommerce' => '12345678',
+ ]);
+
+ $this->assertSame('B2B', $company->category);
+ $this->assertSame('M', $company->gender);
+ $this->assertSame('nl-NL', $company->culture);
+ $this->assertSame('Finance Department', $company->careOf);
+ $this->assertSame('CEO', $company->title);
+ $this->assertSame('J.D.', $company->initials);
+ $this->assertSame('John Doe', $company->name);
+ $this->assertSame('John', $company->firstName);
+ $this->assertSame('van', $company->lastNamePrefix);
+ $this->assertSame('Doe', $company->lastName);
+ $this->assertSame('1980-01-01', $company->birthDate);
+ $this->assertSame('Amsterdam', $company->placeOfBirth);
+ $this->assertSame('Doe Enterprises BV', $company->companyName);
+ $this->assertTrue($company->vatApplicable);
+ $this->assertSame('NL123456789B01', $company->vatNumber);
+ $this->assertSame('12345678', $company->chamberOfCommerce);
+ }
+
+ public function test_magic_get_access_to_inherited_properties(): void
+ {
+ $company = new Company([
+ 'firstName' => 'Carol',
+ 'lastName' => 'White',
+ 'title' => 'Director',
+ 'companyName' => 'White & Associates',
+ ]);
+
+ $this->assertSame('Carol', $company->firstName);
+ $this->assertSame('White', $company->lastName);
+ $this->assertSame('Director', $company->title);
+ $this->assertSame('White & Associates', $company->companyName);
+ }
+
+ public function test_magic_set_on_inherited_properties(): void
+ {
+ $company = new Company();
+
+ $company->firstName = 'David';
+ $company->lastName = 'Green';
+ $company->companyName = 'Green Solutions';
+ $company->vatApplicable = true;
+
+ $this->assertSame('David', $company->firstName);
+ $this->assertSame('Green', $company->lastName);
+ $this->assertSame('Green Solutions', $company->companyName);
+ $this->assertTrue($company->vatApplicable);
+ }
+
+ public function test_get_object_vars_includes_both_hierarchies(): void
+ {
+ $company = new Company([
+ 'firstName' => 'Eve',
+ 'lastName' => 'Black',
+ 'companyName' => 'Black Industries',
+ 'vatApplicable' => true,
+ 'vatNumber' => 'NL999888777B01',
+ ]);
+
+ $vars = $company->getObjectVars();
+
+ $this->assertIsArray($vars);
+ $this->assertArrayHasKey('firstName', $vars);
+ $this->assertArrayHasKey('lastName', $vars);
+ $this->assertArrayHasKey('companyName', $vars);
+ $this->assertArrayHasKey('vatApplicable', $vars);
+ $this->assertArrayHasKey('vatNumber', $vars);
+
+ $this->assertSame('Eve', $vars['firstName']);
+ $this->assertSame('Black', $vars['lastName']);
+ $this->assertSame('Black Industries', $vars['companyName']);
+ $this->assertTrue($vars['vatApplicable']);
+ $this->assertSame('NL999888777B01', $vars['vatNumber']);
+ }
+
+ public function test_set_properties_after_construction(): void
+ {
+ $company = new Company([
+ 'companyName' => 'Initial Corp',
+ ]);
+
+ $company->setProperties([
+ 'firstName' => 'Frank',
+ 'lastName' => 'Brown',
+ 'vatApplicable' => true,
+ 'vatNumber' => 'NL555666777B01',
+ 'chamberOfCommerce' => '99887766',
+ ]);
+
+ $this->assertSame('Initial Corp', $company->companyName);
+ $this->assertSame('Frank', $company->firstName);
+ $this->assertSame('Brown', $company->lastName);
+ $this->assertTrue($company->vatApplicable);
+ $this->assertSame('NL555666777B01', $company->vatNumber);
+ $this->assertSame('99887766', $company->chamberOfCommerce);
+ }
+
+ public function test_to_array_with_minimal_data(): void
+ {
+ $company = new Company([
+ 'companyName' => 'Minimal LLC',
+ ]);
+
+ $array = $company->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertSame('Minimal LLC', $array['companyName']);
+ $this->assertArrayHasKey('companyName', $array);
+ }
+
+ public function test_all_person_properties_accessible(): void
+ {
+ $company = new Company([
+ 'category' => 'Business',
+ 'gender' => 'F',
+ 'culture' => 'en-US',
+ 'careOf' => 'Accounting',
+ 'title' => 'CFO',
+ 'initials' => 'S.M.',
+ 'name' => 'Sarah Miller',
+ 'firstName' => 'Sarah',
+ 'lastNamePrefix' => 'de',
+ 'lastName' => 'Miller',
+ 'birthDate' => '1975-12-20',
+ 'placeOfBirth' => 'Rotterdam',
+ ]);
+
+ $this->assertSame('Business', $company->category);
+ $this->assertSame('F', $company->gender);
+ $this->assertSame('en-US', $company->culture);
+ $this->assertSame('Accounting', $company->careOf);
+ $this->assertSame('CFO', $company->title);
+ $this->assertSame('S.M.', $company->initials);
+ $this->assertSame('Sarah Miller', $company->name);
+ $this->assertSame('Sarah', $company->firstName);
+ $this->assertSame('de', $company->lastNamePrefix);
+ $this->assertSame('Miller', $company->lastName);
+ $this->assertSame('1975-12-20', $company->birthDate);
+ $this->assertSame('Rotterdam', $company->placeOfBirth);
+ }
+
+ public function test_to_array_preserves_null_values(): void
+ {
+ $company = new Company([
+ 'companyName' => 'Null Test Corp',
+ 'vatApplicable' => true,
+ 'vatNumber' => 'NL000000000B00',
+ 'chamberOfCommerce' => null,
+ 'initials' => null,
+ 'birthDate' => null,
+ ]);
+
+ $array = $company->toArray();
+
+ $this->assertNull($array['chamberOfCommerce']);
+ $this->assertNull($array['initials']);
+ $this->assertNull($array['birthDate']);
+ }
+}
diff --git a/tests/Unit/Models/CustomParametersTest.php b/tests/Unit/Models/CustomParametersTest.php
new file mode 100644
index 00000000..ea1bbbce
--- /dev/null
+++ b/tests/Unit/Models/CustomParametersTest.php
@@ -0,0 +1,365 @@
+ 'value1',
+ 'key2' => 'value2',
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertArrayHasKey('List', $array);
+ $this->assertCount(2, $array['List']);
+ $this->assertSame('key1', $array['List'][0]['Name']);
+ $this->assertSame('value1', $array['List'][0]['Value']);
+ $this->assertSame('key2', $array['List'][1]['Name']);
+ $this->assertSame('value2', $array['List'][1]['Value']);
+ }
+
+ public function test_handles_empty_parameters(): void
+ {
+ $params = new CustomParameters([]);
+
+ $array = $params->toArray();
+
+ $this->assertTrue(
+ !isset($array['List']) || empty($array['List']),
+ 'Empty parameters should result in empty or missing List'
+ );
+ }
+
+ public function test_handles_null_parameters(): void
+ {
+ $params = new CustomParameters(null);
+
+ $array = $params->toArray();
+
+ $this->assertTrue(
+ !isset($array['List']) || empty($array['List']),
+ 'Null parameters should result in empty or missing List'
+ );
+ }
+
+ public function test_handles_single_parameter(): void
+ {
+ $params = new CustomParameters(['singleKey' => 'singleValue']);
+
+ $array = $params->toArray();
+
+ $this->assertArrayHasKey('List', $array);
+ $this->assertCount(1, $array['List']);
+ $this->assertSame('singleKey', $array['List'][0]['Name']);
+ $this->assertSame('singleValue', $array['List'][0]['Value']);
+ }
+
+ public function test_preserves_numeric_string_values(): void
+ {
+ $params = new CustomParameters([
+ 'amount' => '100.50',
+ 'quantity' => '5',
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertCount(2, $array['List']);
+ $this->assertSame('100.50', $array['List'][0]['Value']);
+ $this->assertSame('5', $array['List'][1]['Value']);
+ }
+
+ public function test_handles_special_characters_in_values(): void
+ {
+ $params = new CustomParameters([
+ 'description' => 'Test & Payment ',
+ 'reference' => 'REF/123/456',
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertSame('Test & Payment ', $array['List'][0]['Value']);
+ $this->assertSame('REF/123/456', $array['List'][1]['Value']);
+ }
+
+ public function test_preserves_original_key_names(): void
+ {
+ $params = new CustomParameters([
+ 'CamelCaseKey' => 'value1',
+ 'snake_case_key' => 'value2',
+ 'mixedCase_Key' => 'value3',
+ ]);
+
+ $array = $params->toArray();
+
+ $keys = array_column($array['List'], 'Name');
+
+ $this->assertContains('CamelCaseKey', $keys);
+ $this->assertContains('snake_case_key', $keys);
+ $this->assertContains('mixedCase_Key', $keys);
+ }
+
+ public function test_multiple_set_properties_calls_accumulate_entries(): void
+ {
+ $params = new CustomParameters(['key1' => 'value1']);
+ $params->setProperties(['key2' => 'value2']);
+ $params->setProperties(['key3' => 'value3']);
+
+ $array = $params->toArray();
+
+ $this->assertCount(3, $array['List']);
+ $this->assertSame('key1', $array['List'][0]['Name']);
+ $this->assertSame('value1', $array['List'][0]['Value']);
+ $this->assertSame('key2', $array['List'][1]['Name']);
+ $this->assertSame('value2', $array['List'][1]['Value']);
+ $this->assertSame('key3', $array['List'][2]['Name']);
+ $this->assertSame('value3', $array['List'][2]['Value']);
+ }
+
+ public function test_preserves_boolean_types(): void
+ {
+ $params = new CustomParameters([
+ 'isActive' => true,
+ 'isDeleted' => false,
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertSame(true, $array['List'][0]['Value']);
+ $this->assertSame(false, $array['List'][1]['Value']);
+ }
+
+ public function test_preserves_integer_types(): void
+ {
+ $params = new CustomParameters([
+ 'count' => 42,
+ 'negative' => -100,
+ 'zero' => 0,
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertSame(42, $array['List'][0]['Value']);
+ $this->assertSame(-100, $array['List'][1]['Value']);
+ $this->assertSame(0, $array['List'][2]['Value']);
+ }
+
+ public function test_preserves_float_types(): void
+ {
+ $params = new CustomParameters([
+ 'price' => 99.99,
+ 'rate' => 0.15,
+ 'zero' => 0.0,
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertSame(99.99, $array['List'][0]['Value']);
+ $this->assertSame(0.15, $array['List'][1]['Value']);
+ $this->assertSame(0.0, $array['List'][2]['Value']);
+ $this->assertIsFloat($array['List'][2]['Value']);
+ }
+
+ public function test_preserves_null_values_in_list(): void
+ {
+ $params = new CustomParameters([
+ 'optional' => null,
+ 'another' => null,
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertCount(2, $array['List']);
+ $this->assertNull($array['List'][0]['Value']);
+ $this->assertNull($array['List'][1]['Value']);
+ $this->assertSame('optional', $array['List'][0]['Name']);
+ $this->assertSame('another', $array['List'][1]['Name']);
+ }
+
+ public function test_preserves_zero_values(): void
+ {
+ $params = new CustomParameters([
+ 'intZero' => 0,
+ 'floatZero' => 0.0,
+ 'stringZero' => '0',
+ 'emptyString' => '',
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertCount(4, $array['List']);
+ $this->assertSame(0, $array['List'][0]['Value']);
+ $this->assertSame(0.0, $array['List'][1]['Value']);
+ $this->assertSame('0', $array['List'][2]['Value']);
+ $this->assertSame('', $array['List'][3]['Value']);
+ }
+
+ public function test_preserves_empty_string_values(): void
+ {
+ $params = new CustomParameters([
+ 'empty' => '',
+ 'whitespace' => ' ',
+ 'tab' => "\t",
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertSame('', $array['List'][0]['Value']);
+ $this->assertSame(' ', $array['List'][1]['Value']);
+ $this->assertSame("\t", $array['List'][2]['Value']);
+ }
+
+ public function test_handles_unicode_values(): void
+ {
+ $params = new CustomParameters([
+ 'emoji' => '🎉💳',
+ 'chinese' => '支付宝',
+ 'arabic' => 'الدفع',
+ 'currency' => '€ £ ¥',
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertSame('🎉💳', $array['List'][0]['Value']);
+ $this->assertSame('支付宝', $array['List'][1]['Value']);
+ $this->assertSame('الدفع', $array['List'][2]['Value']);
+ $this->assertSame('€ £ ¥', $array['List'][3]['Value']);
+ }
+
+ public function test_handles_special_characters_in_keys(): void
+ {
+ $params = new CustomParameters([
+ 'key-with-dash' => 'value1',
+ 'key.with.dot' => 'value2',
+ 'key_with_underscore' => 'value3',
+ 'key:with:colon' => 'value4',
+ ]);
+
+ $array = $params->toArray();
+
+ $keys = array_column($array['List'], 'Name');
+
+ $this->assertContains('key-with-dash', $keys);
+ $this->assertContains('key.with.dot', $keys);
+ $this->assertContains('key_with_underscore', $keys);
+ $this->assertContains('key:with:colon', $keys);
+ }
+
+ public function test_handles_numeric_string_keys(): void
+ {
+ $params = new CustomParameters([
+ '0' => 'zero',
+ '1' => 'one',
+ '10' => 'ten',
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertCount(3, $array['List']);
+ // PHP converts numeric string keys to integers automatically
+ $this->assertSame(0, $array['List'][0]['Name']);
+ $this->assertSame(1, $array['List'][1]['Name']);
+ $this->assertSame(10, $array['List'][2]['Name']);
+ }
+
+ public function test_handles_large_parameter_sets(): void
+ {
+ $largeSet = [];
+ for ($i = 0; $i < 100; $i++) {
+ $largeSet['param' . $i] = 'value' . $i;
+ }
+
+ $params = new CustomParameters($largeSet);
+ $array = $params->toArray();
+
+ $this->assertCount(100, $array['List']);
+ $this->assertSame('param0', $array['List'][0]['Name']);
+ $this->assertSame('value0', $array['List'][0]['Value']);
+ $this->assertSame('param99', $array['List'][99]['Name']);
+ $this->assertSame('value99', $array['List'][99]['Value']);
+ }
+
+ public function test_magic_get_access_to_list(): void
+ {
+ $params = new CustomParameters([
+ 'key1' => 'value1',
+ 'key2' => 'value2',
+ ]);
+
+ $list = $params->List;
+
+ $this->assertIsArray($list);
+ $this->assertCount(2, $list);
+ $this->assertSame('key1', $list[0]['Name']);
+ $this->assertSame('value1', $list[0]['Value']);
+ }
+
+ public function test_get_object_vars_includes_list(): void
+ {
+ $params = new CustomParameters([
+ 'key1' => 'value1',
+ ]);
+
+ $vars = $params->getObjectVars();
+
+ $this->assertArrayHasKey('List', $vars);
+ $this->assertIsArray($vars['List']);
+ $this->assertCount(1, $vars['List']);
+ }
+
+ public function test_preserves_mixed_types_in_single_call(): void
+ {
+ $params = new CustomParameters([
+ 'string' => 'text',
+ 'int' => 42,
+ 'float' => 3.14,
+ 'bool' => true,
+ 'null' => null,
+ 'zero' => 0,
+ 'empty' => '',
+ ]);
+
+ $array = $params->toArray();
+
+ $this->assertCount(7, $array['List']);
+ $this->assertIsString($array['List'][0]['Value']);
+ $this->assertIsInt($array['List'][1]['Value']);
+ $this->assertIsFloat($array['List'][2]['Value']);
+ $this->assertIsBool($array['List'][3]['Value']);
+ $this->assertNull($array['List'][4]['Value']);
+ $this->assertIsInt($array['List'][5]['Value']);
+ $this->assertIsString($array['List'][6]['Value']);
+ }
+
+ public function test_handles_very_long_string_values(): void
+ {
+ $longString = str_repeat('A', 10000);
+ $params = new CustomParameters(['longKey' => $longString]);
+
+ $array = $params->toArray();
+
+ $this->assertSame($longString, $array['List'][0]['Value']);
+ $this->assertSame(10000, strlen($array['List'][0]['Value']));
+ }
+
+ public function test_duplicate_keys_in_multiple_calls_create_duplicate_entries(): void
+ {
+ $params = new CustomParameters(['key' => 'value1']);
+ $params->setProperties(['key' => 'value2']);
+
+ $array = $params->toArray();
+
+ $this->assertCount(2, $array['List']);
+ $this->assertSame('key', $array['List'][0]['Name']);
+ $this->assertSame('value1', $array['List'][0]['Value']);
+ $this->assertSame('key', $array['List'][1]['Name']);
+ $this->assertSame('value2', $array['List'][1]['Value']);
+ }
+}
diff --git a/tests/Unit/Models/DebtorTest.php b/tests/Unit/Models/DebtorTest.php
new file mode 100644
index 00000000..8399a39f
--- /dev/null
+++ b/tests/Unit/Models/DebtorTest.php
@@ -0,0 +1,85 @@
+ 'DEBTOR-12345',
+ ]);
+
+ $this->assertSame('DEBTOR-12345', $debtor->code);
+ }
+
+ public function test_to_array_includes_code(): void
+ {
+ $debtor = new Debtor([
+ 'code' => 'DBT-001',
+ ]);
+
+ $array = $debtor->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertArrayHasKey('code', $array);
+ $this->assertIsString($array['code']);
+ $this->assertSame('DBT-001', $array['code']);
+ }
+
+ public function test_handles_various_code_formats(): void
+ {
+ $alphanumeric = new Debtor(['code' => 'ABC123']);
+ $this->assertSame('ABC123', $alphanumeric->code);
+ $this->assertSame('ABC123', $alphanumeric->toArray()['code']);
+
+ $withDashes = new Debtor(['code' => 'DEBTOR-2024-001']);
+ $this->assertSame('DEBTOR-2024-001', $withDashes->code);
+ $this->assertSame('DEBTOR-2024-001', $withDashes->toArray()['code']);
+
+ $uppercase = new Debtor(['code' => 'UPPERCASE']);
+ $this->assertSame('UPPERCASE', $uppercase->code);
+
+ $lowercase = new Debtor(['code' => 'lowercase']);
+ $this->assertSame('lowercase', $lowercase->code);
+
+ $numeric = new Debtor(['code' => '123456']);
+ $this->assertSame('123456', $numeric->code);
+ }
+
+ public function test_handles_empty_and_special_characters_in_code(): void
+ {
+ $empty = new Debtor(['code' => '']);
+ $this->assertSame('', $empty->code);
+ $this->assertSame('', $empty->toArray()['code']);
+
+ $specialChars = new Debtor(['code' => 'CODE_#123@SPECIAL!']);
+ $this->assertSame('CODE_#123@SPECIAL!', $specialChars->code);
+ $this->assertSame('CODE_#123@SPECIAL!', $specialChars->toArray()['code']);
+
+ $unicode = new Debtor(['code' => 'DÉBTOR-€100']);
+ $this->assertSame('DÉBTOR-€100', $unicode->code);
+ $this->assertSame('DÉBTOR-€100', $unicode->toArray()['code']);
+
+ $whitespace = new Debtor(['code' => ' CODE WITH SPACES ']);
+ $this->assertSame(' CODE WITH SPACES ', $whitespace->code);
+ }
+
+ public function test_code_property_preserves_exact_value(): void
+ {
+ $mixedCase = new Debtor(['code' => 'MiXeD-CaSe-123']);
+ $this->assertSame('MiXeD-CaSe-123', $mixedCase->code);
+
+ $leadingZeros = new Debtor(['code' => '00012345']);
+ $this->assertSame('00012345', $leadingZeros->code);
+
+ $quoted = new Debtor(['code' => 'CODE"WITH\'QUOTES']);
+ $this->assertSame('CODE"WITH\'QUOTES', $quoted->code);
+ $this->assertSame('CODE"WITH\'QUOTES', $quoted->toArray()['code']);
+ }
+}
diff --git a/tests/Unit/Models/EmailTest.php b/tests/Unit/Models/EmailTest.php
new file mode 100644
index 00000000..32e5fc61
--- /dev/null
+++ b/tests/Unit/Models/EmailTest.php
@@ -0,0 +1,172 @@
+assertSame('test@example.com', $email->email);
+ }
+
+ public function test_constructor_with_empty_string(): void
+ {
+ $email = new Email('');
+
+ $this->assertSame('', $email->email);
+ }
+
+ public function test_email_accessible_via_magic_get(): void
+ {
+ $email = new Email('contact@buckaroo.nl');
+
+ $value = $email->email;
+
+ $this->assertSame('contact@buckaroo.nl', $value);
+ }
+
+ public function test_email_settable_via_magic_set(): void
+ {
+ $email = new Email('initial@example.com');
+
+ $email->email = 'updated@example.com';
+
+ $this->assertSame('updated@example.com', $email->email);
+ }
+
+ public function test_set_properties_updates_email(): void
+ {
+ $email = new Email('initial@example.com');
+
+ $email->setProperties(['email' => 'changed@example.com']);
+
+ $this->assertSame('changed@example.com', $email->email);
+ }
+
+ public function test_to_array_includes_email_property(): void
+ {
+ $email = new Email('serialize@example.com');
+
+ $array = $email->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertArrayHasKey('email', $array);
+ $this->assertSame('serialize@example.com', $array['email']);
+ }
+
+ public function test_to_array_with_empty_string(): void
+ {
+ $email = new Email('');
+
+ $array = $email->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertArrayHasKey('email', $array);
+ $this->assertSame('', $array['email']);
+ }
+
+ public function test_stores_invalid_email_format_without_validation(): void
+ {
+ $invalidEmails = [
+ 'not-an-email',
+ 'missing-at-sign.com',
+ '@no-local-part.com',
+ 'no-domain@',
+ 'spaces in@email.com',
+ 'double@@at.com',
+ ];
+
+ foreach ($invalidEmails as $invalidEmail) {
+ $email = new Email($invalidEmail);
+
+ $this->assertSame($invalidEmail, $email->email);
+ }
+ }
+
+ public function test_stores_very_long_email_string(): void
+ {
+ $longLocalPart = str_repeat('a', 500);
+ $longEmail = $longLocalPart . '@example.com';
+
+ $email = new Email($longEmail);
+
+ $this->assertSame($longEmail, $email->email);
+ $this->assertSame(strlen($longEmail), strlen($email->email));
+ }
+
+ public function test_stores_special_characters_in_email(): void
+ {
+ $specialEmails = [
+ 'user+tag@example.com',
+ 'user.name@example.com',
+ 'user_name@example.com',
+ 'user-name@example.com',
+ '123@example.com',
+ 'a!#$%&\'*+/=?^_`{|}~@example.com',
+ ];
+
+ foreach ($specialEmails as $specialEmail) {
+ $email = new Email($specialEmail);
+
+ $this->assertSame($specialEmail, $email->email);
+ }
+ }
+
+ public function test_stores_unicode_characters_in_email(): void
+ {
+ $unicodeEmails = [
+ 'user@münchen.de',
+ 'ñoño@example.com',
+ 'δοκιμή@παράδειγμα.δοκιμή',
+ '测试@例え.jp',
+ '用户@例子.中国',
+ ];
+
+ foreach ($unicodeEmails as $unicodeEmail) {
+ $email = new Email($unicodeEmail);
+
+ $this->assertSame($unicodeEmail, $email->email);
+ }
+ }
+
+ public function test_stores_multiple_at_symbols(): void
+ {
+ $email = new Email('user@@example.com');
+
+ $this->assertSame('user@@example.com', $email->email);
+ }
+
+ public function test_stores_email_with_whitespace(): void
+ {
+ $whitespaceEmails = [
+ ' leading@example.com',
+ 'trailing@example.com ',
+ ' both@example.com ',
+ 'internal space@example.com',
+ "tab\t@example.com",
+ "newline\n@example.com",
+ ];
+
+ foreach ($whitespaceEmails as $whitespaceEmail) {
+ $email = new Email($whitespaceEmail);
+
+ $this->assertSame($whitespaceEmail, $email->email);
+ }
+ }
+
+ public function test_set_properties_with_null_preserves_existing_value(): void
+ {
+ $email = new Email('initial@example.com');
+
+ $email->setProperties(['email' => 'updated@example.com']);
+
+ $this->assertSame('updated@example.com', $email->email);
+ }
+}
diff --git a/tests/Unit/Models/ModelTest.php b/tests/Unit/Models/ModelTest.php
new file mode 100644
index 00000000..d1ad5aa1
--- /dev/null
+++ b/tests/Unit/Models/ModelTest.php
@@ -0,0 +1,415 @@
+ 'Main Street',
+ 'houseNumber' => '123',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ]);
+
+ $this->assertSame('Main Street', $address->street);
+ $this->assertSame('123', $address->houseNumber);
+ $this->assertSame('Amsterdam', $address->city);
+ $this->assertSame('NL', $address->country);
+ }
+
+ public function test_provides_magic_property_access(): void
+ {
+ $address = new Address([
+ 'street' => 'Test Street',
+ 'zipcode' => '1234AB',
+ ]);
+
+ $this->assertSame('Test Street', $address->street);
+ $this->assertSame('1234AB', $address->zipcode);
+ }
+
+ public function test_returns_null_for_missing_properties(): void
+ {
+ $address = new Address([
+ 'street' => 'Test Street',
+ ]);
+
+ $this->assertNull($address->city);
+ $this->assertNull($address->country);
+ $this->assertNull($address->nonExistentProperty);
+ }
+
+ public function test_sets_properties_via_magic_set(): void
+ {
+ $address = new Address();
+
+ $address->street = 'New Street';
+ $address->houseNumber = '456';
+ $address->city = 'Rotterdam';
+
+ $this->assertSame('New Street', $address->street);
+ $this->assertSame('456', $address->houseNumber);
+ $this->assertSame('Rotterdam', $address->city);
+ }
+
+ public function test_ignores_setting_non_existent_properties(): void
+ {
+ $address = new Address();
+
+ $address->nonExistentProperty = 'value';
+
+ $this->assertNull($address->nonExistentProperty);
+ }
+
+ public function test_converts_to_array(): void
+ {
+ $address = new Address([
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ]);
+
+ $array = $address->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertSame('Main Street', $array['street']);
+ $this->assertSame('123', $array['houseNumber']);
+ $this->assertSame('Amsterdam', $array['city']);
+ $this->assertSame('NL', $array['country']);
+ }
+
+ public function test_transforms_property_names_to_ucfirst(): void
+ {
+ $address = new Address();
+
+ $this->assertSame('Street', $address->serviceParameterKeyOf('street'));
+ $this->assertSame('HouseNumber', $address->serviceParameterKeyOf('houseNumber'));
+ $this->assertSame('City', $address->serviceParameterKeyOf('city'));
+ $this->assertSame('Zipcode', $address->serviceParameterKeyOf('zipcode'));
+ }
+
+ public function test_gets_object_vars(): void
+ {
+ $address = new Address([
+ 'street' => 'Main Street',
+ 'city' => 'Amsterdam',
+ ]);
+
+ $vars = $address->getObjectVars();
+
+ $this->assertIsArray($vars);
+ $this->assertArrayHasKey('street', $vars);
+ $this->assertArrayHasKey('city', $vars);
+ $this->assertSame('Main Street', $vars['street']);
+ $this->assertSame('Amsterdam', $vars['city']);
+ }
+
+ public function test_can_set_properties_after_construction(): void
+ {
+ $address = new Address();
+
+ $address->setProperties([
+ 'street' => 'Updated Street',
+ 'houseNumber' => '789',
+ 'city' => 'Utrecht',
+ ]);
+
+ $this->assertSame('Updated Street', $address->street);
+ $this->assertSame('789', $address->houseNumber);
+ $this->assertSame('Utrecht', $address->city);
+ }
+
+ public function test_handles_null_in_constructor(): void
+ {
+ $address = new Address(null);
+
+ $this->assertNull($address->street);
+ $this->assertNull($address->city);
+ }
+
+ public function test_handles_null_in_set_properties(): void
+ {
+ $address = new Address([
+ 'street' => 'Initial Street',
+ ]);
+
+ $address->setProperties(null);
+
+ $this->assertSame('Initial Street', $address->street);
+ }
+
+ public function test_returns_self_from_set_properties(): void
+ {
+ $address = new Address();
+
+ $result = $address->setProperties([
+ 'street' => 'Test',
+ ]);
+
+ $this->assertSame($address, $result);
+ }
+
+ public function test_returns_self_from_magic_set(): void
+ {
+ $address = new Address();
+
+ $result = $address->__set('street', 'Test Street');
+
+ $this->assertSame($address, $result);
+ }
+
+ public function test_handles_empty_array_in_set_properties(): void
+ {
+ $address = new Address([
+ 'street' => 'Original Street',
+ 'city' => 'Original City',
+ ]);
+
+ $address->setProperties([]);
+
+ $this->assertSame('Original Street', $address->street);
+ $this->assertSame('Original City', $address->city);
+ }
+
+ public function test_overwrites_properties_with_set_properties(): void
+ {
+ $address = new Address([
+ 'street' => 'First Street',
+ 'city' => 'First City',
+ ]);
+
+ $address->setProperties([
+ 'street' => 'Second Street',
+ 'city' => 'Second City',
+ ]);
+
+ $this->assertSame('Second Street', $address->street);
+ $this->assertSame('Second City', $address->city);
+ }
+
+ public function test_setting_nullable_property_to_null_explicitly(): void
+ {
+ // Person has nullable properties like initials and birthDate
+ $person = new Person([
+ 'firstName' => 'John',
+ 'initials' => 'J.D.',
+ 'birthDate' => '1990-01-01',
+ ]);
+
+ $person->initials = null;
+ $person->birthDate = null;
+
+ $this->assertNull($person->initials);
+ $this->assertNull($person->birthDate);
+ }
+
+ public function test_chaining_magic_set_calls(): void
+ {
+ $address = new Address();
+
+ $result = $address->__set('street', 'Street 1')
+ ->__set('city', 'City 1')
+ ->__set('country', 'NL');
+
+ $this->assertSame($address, $result);
+ $this->assertSame('Street 1', $address->street);
+ $this->assertSame('City 1', $address->city);
+ $this->assertSame('NL', $address->country);
+ }
+
+ public function test_converts_nested_arrayable_objects_to_array(): void
+ {
+ $services = new Services();
+
+ $address1 = new Address([
+ 'street' => 'Street 1',
+ 'city' => 'City 1',
+ ]);
+
+ $address2 = new Address([
+ 'street' => 'Street 2',
+ 'city' => 'City 2',
+ ]);
+
+ $services->setProperties([
+ 'ServiceList' => [$address1, $address2],
+ ]);
+
+ $array = $services->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertIsArray($array['ServiceList']);
+ $this->assertCount(2, $array['ServiceList']);
+
+ $this->assertIsArray($array['ServiceList'][0]);
+ $this->assertSame('Street 1', $array['ServiceList'][0]['street']);
+ $this->assertSame('City 1', $array['ServiceList'][0]['city']);
+
+ $this->assertIsArray($array['ServiceList'][1]);
+ $this->assertSame('Street 2', $array['ServiceList'][1]['street']);
+ $this->assertSame('City 2', $array['ServiceList'][1]['city']);
+ }
+
+ public function test_converts_nested_plain_arrays_to_array(): void
+ {
+ // Services has ServiceList which can contain nested arrays
+ $services = new Services();
+
+ $services->setProperties([
+ 'ServiceList' => [
+ 'level1' => [
+ 'level2' => 'deep value',
+ 'level2b' => ['level3' => 'deeper'],
+ ],
+ ],
+ ]);
+
+ $array = $services->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertIsArray($array['ServiceList']);
+ $this->assertIsArray($array['ServiceList']['level1']);
+ $this->assertSame('deep value', $array['ServiceList']['level1']['level2']);
+ $this->assertSame('deeper', $array['ServiceList']['level1']['level2b']['level3']);
+ }
+
+ public function test_converts_deeply_nested_structures_to_array(): void
+ {
+ $services = new Services();
+
+ $innerAddress = new Address([
+ 'street' => 'Inner Street',
+ 'city' => 'Inner City',
+ ]);
+
+ $middleData = [
+ 'address' => $innerAddress,
+ 'metadata' => [
+ 'created' => '2024-01-01',
+ 'updated' => '2024-01-02',
+ ],
+ ];
+
+ $services->setProperties([
+ 'ServiceList' => [
+ 'outer' => $middleData,
+ ],
+ ]);
+
+ $array = $services->toArray();
+
+ $this->assertIsArray($array['ServiceList']['outer']['address']);
+ $this->assertSame('Inner Street', $array['ServiceList']['outer']['address']['street']);
+ $this->assertSame('Inner City', $array['ServiceList']['outer']['address']['city']);
+ $this->assertIsArray($array['ServiceList']['outer']['metadata']);
+ $this->assertSame('2024-01-01', $array['ServiceList']['outer']['metadata']['created']);
+ }
+
+ public function test_to_array_with_empty_model(): void
+ {
+ $address = new Address();
+
+ $array = $address->toArray();
+
+ // In PHP 8+, uninitialized typed properties are not included in get_object_vars()
+ $this->assertIsArray($array);
+ $this->assertEmpty($array);
+ }
+
+ public function test_to_array_with_nullable_values(): void
+ {
+ // Person has nullable properties (initials, birthDate)
+ $person = new Person([
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ 'initials' => null,
+ 'birthDate' => null,
+ ]);
+
+ $array = $person->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertSame('John', $array['firstName']);
+ $this->assertSame('Doe', $array['lastName']);
+ $this->assertNull($array['initials']);
+ $this->assertNull($array['birthDate']);
+ }
+
+ public function test_to_array_preserves_null_in_nested_arrays(): void
+ {
+ $services = new Services();
+ $services->setProperties([
+ 'ServiceList' => [
+ 'metadata' => [
+ 'valid' => true,
+ 'verified' => null,
+ 'score' => 100,
+ ],
+ ],
+ ]);
+
+ $array = $services->toArray();
+
+ $this->assertIsArray($array['ServiceList']['metadata']);
+ $this->assertTrue($array['ServiceList']['metadata']['valid']);
+ $this->assertNull($array['ServiceList']['metadata']['verified']);
+ $this->assertSame(100, $array['ServiceList']['metadata']['score']);
+ }
+
+ public function test_service_parameter_key_handles_already_capitalized(): void
+ {
+ $address = new Address();
+
+ $this->assertSame('Street', $address->serviceParameterKeyOf('Street'));
+ $this->assertSame('ALLCAPS', $address->serviceParameterKeyOf('ALLCAPS'));
+ }
+
+ public function test_service_parameter_key_handles_empty_string(): void
+ {
+ $address = new Address();
+
+ $this->assertSame('', $address->serviceParameterKeyOf(''));
+ }
+
+ public function test_service_parameter_key_handles_numeric_strings(): void
+ {
+ $address = new Address();
+
+ $this->assertSame('123', $address->serviceParameterKeyOf('123'));
+ $this->assertSame('456abc', $address->serviceParameterKeyOf('456abc'));
+ }
+
+ public function test_get_object_vars_includes_initialized_properties(): void
+ {
+ $address = new Address([
+ 'street' => 'Test Street',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ]);
+
+ $vars = $address->getObjectVars();
+
+ // In PHP 8+, only initialized typed properties are included
+ $this->assertArrayHasKey('street', $vars);
+ $this->assertArrayHasKey('city', $vars);
+ $this->assertArrayHasKey('country', $vars);
+ $this->assertSame('Test Street', $vars['street']);
+ $this->assertSame('Amsterdam', $vars['city']);
+ $this->assertSame('NL', $vars['country']);
+
+ // Uninitialized properties are not included in PHP 8
+ $this->assertArrayNotHasKey('houseNumber', $vars);
+ $this->assertArrayNotHasKey('zipcode', $vars);
+ }
+}
diff --git a/tests/Unit/Models/Payload/DataRequestPayloadTest.php b/tests/Unit/Models/Payload/DataRequestPayloadTest.php
new file mode 100644
index 00000000..7c065cb7
--- /dev/null
+++ b/tests/Unit/Models/Payload/DataRequestPayloadTest.php
@@ -0,0 +1,247 @@
+ ['param1' => 'value1', 'param2' => 'value2'],
+ ]);
+
+ $this->assertInstanceOf(AdditionalParameters::class, $payload->additionalParameters);
+
+ $array = $payload->additionalParameters->toArray();
+ $this->assertArrayHasKey('List', $array);
+ $this->assertArrayHasKey('AdditionalParameter', $array);
+ $this->assertNotEmpty($array['List']);
+ $this->assertEmpty($array['AdditionalParameter']);
+ }
+
+ public function test_to_array_shows_list_populated_additional_parameter_empty(): void
+ {
+ $payload = new DataRequestPayload([
+ 'additionalParameters' => ['key1' => 'val1'],
+ ]);
+
+ $array = $payload->toArray();
+
+ $this->assertArrayHasKey('additionalParameters', $array);
+ $this->assertIsArray($array['additionalParameters']);
+ $this->assertArrayHasKey('List', $array['additionalParameters']);
+ $this->assertArrayHasKey('AdditionalParameter', $array['additionalParameters']);
+ $this->assertNotEmpty($array['additionalParameters']['List']);
+ $this->assertEmpty($array['additionalParameters']['AdditionalParameter']);
+ }
+
+ public function test_additional_parameters_receives_is_data_request_true_flag(): void
+ {
+ $payload = new DataRequestPayload([
+ 'additionalParameters' => ['test' => 'data'],
+ ]);
+
+ $additionalParams = $payload->additionalParameters;
+ $array = $additionalParams->toArray();
+
+ $this->assertArrayHasKey('List', $array);
+ $this->assertCount(1, $array['List']);
+ $this->assertSame('test', $array['List'][0]['Name']);
+ $this->assertSame('data', $array['List'][0]['Value']);
+ }
+
+ public function test_constructor_with_additional_parameters_creates_correct_structure(): void
+ {
+ $payload = new DataRequestPayload([
+ 'additionalParameters' => ['param1' => 'value1', 'param2' => 'value2'],
+ ]);
+
+ $array = $payload->additionalParameters->toArray();
+
+ $this->assertArrayHasKey('List', $array);
+ $this->assertCount(2, $array['List']);
+
+ $names = array_column($array['List'], 'Name');
+ $this->assertContains('param1', $names);
+ $this->assertContains('param2', $names);
+ }
+
+ public function test_set_properties_with_additional_parameters_creates_correct_structure(): void
+ {
+ $payload = new DataRequestPayload();
+
+ $payload->setProperties([
+ 'additionalParameters' => ['key1' => 'val1', 'key2' => 'val2'],
+ ]);
+
+ $array = $payload->additionalParameters->toArray();
+
+ $this->assertArrayHasKey('List', $array);
+ $this->assertCount(2, $array['List']);
+ }
+
+ public function test_additional_parameters_works_with_custom_parameters(): void
+ {
+ $payload = new DataRequestPayload([
+ 'additionalParameters' => ['additional' => 'data'],
+ 'customParameters' => ['custom' => 'value'],
+ ]);
+
+ $this->assertInstanceOf(AdditionalParameters::class, $payload->additionalParameters);
+ $this->assertInstanceOf(CustomParameters::class, $payload->customParameters);
+
+ $additionalArray = $payload->additionalParameters->toArray();
+ $this->assertArrayHasKey('List', $additionalArray);
+ $this->assertNotEmpty($additionalArray['List']);
+ $this->assertEmpty($additionalArray['AdditionalParameter']);
+
+ $customArray = $payload->customParameters->toArray();
+ $this->assertArrayHasKey('List', $customArray);
+ }
+
+ public function test_additional_parameters_works_with_client_ip(): void
+ {
+ $payload = new DataRequestPayload([
+ 'additionalParameters' => ['param' => 'value'],
+ 'clientIP' => ['address' => '192.168.1.1', 'type' => 0],
+ ]);
+
+ $this->assertInstanceOf(AdditionalParameters::class, $payload->additionalParameters);
+ $this->assertInstanceOf(ClientIP::class, $payload->clientIP);
+
+ $additionalArray = $payload->additionalParameters->toArray();
+ $this->assertArrayHasKey('List', $additionalArray);
+ }
+
+ public function test_all_three_nested_objects_together(): void
+ {
+ $payload = new DataRequestPayload([
+ 'additionalParameters' => ['add1' => 'val1'],
+ 'customParameters' => ['custom1' => 'val2'],
+ 'clientIP' => ['address' => '10.0.0.1', 'type' => 0],
+ ]);
+
+ $this->assertInstanceOf(AdditionalParameters::class, $payload->additionalParameters);
+ $this->assertInstanceOf(CustomParameters::class, $payload->customParameters);
+ $this->assertInstanceOf(ClientIP::class, $payload->clientIP);
+
+ $array = $payload->toArray();
+ $this->assertArrayHasKey('additionalParameters', $array);
+ $this->assertArrayHasKey('customParameters', $array);
+ $this->assertArrayHasKey('clientIP', $array);
+
+ $this->assertArrayHasKey('List', $array['additionalParameters']);
+ $this->assertNotEmpty($array['additionalParameters']['List']);
+ $this->assertEmpty($array['additionalParameters']['AdditionalParameter']);
+ }
+
+ public function test_multiple_additional_parameters_entries_populate_list_array(): void
+ {
+ $payload = new DataRequestPayload([
+ 'additionalParameters' => [
+ 'param1' => 'value1',
+ 'param2' => 'value2',
+ 'param3' => 'value3',
+ ],
+ ]);
+
+ $array = $payload->additionalParameters->toArray();
+
+ $this->assertArrayHasKey('List', $array);
+ $this->assertCount(3, $array['List']);
+
+ foreach ($array['List'] as $item) {
+ $this->assertArrayHasKey('Name', $item);
+ $this->assertArrayHasKey('Value', $item);
+ }
+ }
+
+ public function test_overwrites_additional_parameters_on_subsequent_set_properties(): void
+ {
+ $payload = new DataRequestPayload([
+ 'additionalParameters' => ['first' => 'value1'],
+ ]);
+
+ $firstAdditionalParams = $payload->additionalParameters;
+ $this->assertInstanceOf(AdditionalParameters::class, $firstAdditionalParams);
+
+ $payload->setProperties([
+ 'additionalParameters' => ['second' => 'value2'],
+ ]);
+
+ $secondAdditionalParams = $payload->additionalParameters;
+ $this->assertInstanceOf(AdditionalParameters::class, $secondAdditionalParams);
+ $this->assertNotSame($firstAdditionalParams, $secondAdditionalParams);
+
+ $array = $secondAdditionalParams->toArray();
+ $names = array_column($array['List'], 'Name');
+ $this->assertContains('second', $names);
+ $this->assertNotContains('first', $names);
+ }
+
+ public function test_returns_self_from_set_properties(): void
+ {
+ $payload = new DataRequestPayload();
+
+ $result = $payload->setProperties([
+ 'additionalParameters' => ['key' => 'value'],
+ 'currency' => 'USD',
+ ]);
+
+ $this->assertSame($payload, $result);
+ }
+
+ public function test_preserves_flat_properties_when_setting_additional_parameters(): void
+ {
+ $payload = new DataRequestPayload([
+ 'currency' => 'EUR',
+ 'invoice' => 'INV-001',
+ 'description' => 'Initial',
+ ]);
+
+ $payload->setProperties([
+ 'additionalParameters' => ['param' => 'value'],
+ ]);
+
+ $this->assertSame('EUR', $payload->currency);
+ $this->assertSame('INV-001', $payload->invoice);
+ $this->assertSame('Initial', $payload->description);
+ $this->assertInstanceOf(AdditionalParameters::class, $payload->additionalParameters);
+ }
+
+ public function test_constructor_with_null_initializes_properly(): void
+ {
+ $payload = new DataRequestPayload(null);
+
+ $this->assertNull($payload->currency);
+ $this->assertNull($payload->invoice);
+ }
+
+ public function test_constructor_with_empty_array_initializes_properly(): void
+ {
+ $payload = new DataRequestPayload([]);
+
+ $this->assertNull($payload->currency);
+ $this->assertNull($payload->invoice);
+ }
+
+ public function test_nested_objects_are_independent_instances(): void
+ {
+ $sharedData = ['shared' => 'value'];
+
+ $payload1 = new DataRequestPayload(['additionalParameters' => $sharedData]);
+ $payload2 = new DataRequestPayload(['additionalParameters' => $sharedData]);
+
+ $this->assertNotSame($payload1->additionalParameters, $payload2->additionalParameters);
+ $this->assertInstanceOf(AdditionalParameters::class, $payload1->additionalParameters);
+ $this->assertInstanceOf(AdditionalParameters::class, $payload2->additionalParameters);
+ }
+}
diff --git a/tests/Unit/Models/Payload/PayPayloadTest.php b/tests/Unit/Models/Payload/PayPayloadTest.php
new file mode 100644
index 00000000..cb681e5d
--- /dev/null
+++ b/tests/Unit/Models/Payload/PayPayloadTest.php
@@ -0,0 +1,222 @@
+ 10.50]);
+
+ $this->assertStringStartsWith('ORDER_NO_', $payload->order);
+ }
+
+ public function test_order_uniqueness_across_instances(): void
+ {
+ $payload1 = new PayPayload(['amountDebit' => 10.00]);
+ $payload2 = new PayPayload(['amountDebit' => 20.00]);
+ $payload3 = new PayPayload(['amountDebit' => 30.00]);
+
+ $this->assertNotSame($payload1->order, $payload2->order);
+ $this->assertNotSame($payload2->order, $payload3->order);
+ $this->assertNotSame($payload1->order, $payload3->order);
+ }
+
+ public function test_order_generated_with_null_payload(): void
+ {
+ $payload = new PayPayload(null);
+
+ $this->assertNotNull($payload->order);
+ $this->assertStringStartsWith('ORDER_NO_', $payload->order);
+ }
+
+ public function test_order_generated_with_empty_payload(): void
+ {
+ $payload = new PayPayload([]);
+
+ $this->assertNotNull($payload->order);
+ $this->assertStringStartsWith('ORDER_NO_', $payload->order);
+ }
+
+ public function test_sets_amount_debit_from_payload(): void
+ {
+ $payload = new PayPayload(['amountDebit' => 99.99]);
+
+ $this->assertSame(99.99, $payload->amountDebit);
+ }
+
+ public function test_preserves_float_type_for_amount_debit(): void
+ {
+ $payload = new PayPayload(['amountDebit' => 150.75]);
+
+ $this->assertIsFloat($payload->amountDebit);
+ $this->assertSame(150.75, $payload->amountDebit);
+ }
+
+ public function test_preserves_float_precision(): void
+ {
+ $payload = new PayPayload(['amountDebit' => 123.456]);
+
+ $this->assertSame(123.456, $payload->amountDebit);
+ }
+
+ public function test_handles_zero_amount_debit(): void
+ {
+ $payload = new PayPayload(['amountDebit' => 0.0]);
+
+ $this->assertSame(0.0, $payload->amountDebit);
+ $this->assertIsFloat($payload->amountDebit);
+ }
+
+ public function test_to_array_includes_order_and_amount_debit(): void
+ {
+ $payload = new PayPayload(['amountDebit' => 75.50]);
+
+ $array = $payload->toArray();
+
+ $this->assertArrayHasKey('order', $array);
+ $this->assertArrayHasKey('amountDebit', $array);
+ $this->assertStringStartsWith('ORDER_NO_', $array['order']);
+ $this->assertSame(75.50, $array['amountDebit']);
+ }
+
+ public function test_to_array_includes_inherited_payload_properties(): void
+ {
+ $payload = new PayPayload([
+ 'amountDebit' => 100.00,
+ 'currency' => 'EUR',
+ 'invoice' => 'INV-001',
+ 'description' => 'Test payment',
+ ]);
+
+ $array = $payload->toArray();
+
+ $this->assertArrayHasKey('order', $array);
+ $this->assertArrayHasKey('amountDebit', $array);
+ $this->assertArrayHasKey('currency', $array);
+ $this->assertArrayHasKey('invoice', $array);
+ $this->assertArrayHasKey('description', $array);
+ $this->assertSame('EUR', $array['currency']);
+ $this->assertSame('INV-001', $array['invoice']);
+ $this->assertSame('Test payment', $array['description']);
+ }
+
+ public function test_to_array_with_nested_objects(): void
+ {
+ $payload = new PayPayload([
+ 'amountDebit' => 200.00,
+ 'currency' => 'USD',
+ 'customParameters' => ['ref' => 'REF-999'],
+ 'additionalParameters' => ['meta' => 'data'],
+ 'clientIP' => ['address' => '10.0.0.1', 'type' => 0],
+ ]);
+
+ $array = $payload->toArray();
+
+ $this->assertArrayHasKey('order', $array);
+ $this->assertArrayHasKey('amountDebit', $array);
+ $this->assertArrayHasKey('currency', $array);
+ $this->assertArrayHasKey('customParameters', $array);
+ $this->assertArrayHasKey('additionalParameters', $array);
+ $this->assertArrayHasKey('clientIP', $array);
+
+ $this->assertIsArray($array['customParameters']);
+ $this->assertIsArray($array['additionalParameters']);
+ $this->assertIsArray($array['clientIP']);
+
+ $this->assertSame(200.00, $array['amountDebit']);
+ $this->assertSame('USD', $array['currency']);
+ }
+
+ public function test_magic_getter_accesses_order_and_amount_debit(): void
+ {
+ $payload = new PayPayload(['amountDebit' => 55.00]);
+
+ $order = $payload->order;
+ $amount = $payload->amountDebit;
+
+ $this->assertStringStartsWith('ORDER_NO_', $order);
+ $this->assertSame(55.00, $amount);
+ }
+
+ public function test_combines_pay_payload_and_payload_properties(): void
+ {
+ $payload = new PayPayload([
+ 'amountDebit' => 125.99,
+ 'currency' => 'GBP',
+ 'invoice' => 'INV-COMBINED',
+ 'returnURL' => 'https://example.com/return',
+ 'pushURL' => 'https://example.com/push',
+ 'description' => 'Combined properties test',
+ 'customParameters' => ['source' => 'web', 'userId' => '12345'],
+ ]);
+
+ $this->assertStringStartsWith('ORDER_NO_', $payload->order);
+ $this->assertSame(125.99, $payload->amountDebit);
+ $this->assertSame('GBP', $payload->currency);
+ $this->assertSame('INV-COMBINED', $payload->invoice);
+ $this->assertSame('https://example.com/return', $payload->returnURL);
+ $this->assertSame('https://example.com/push', $payload->pushURL);
+ $this->assertSame('Combined properties test', $payload->description);
+ $this->assertInstanceOf(CustomParameters::class, $payload->customParameters);
+
+ $array = $payload->toArray();
+ $this->assertArrayHasKey('order', $array);
+ $this->assertArrayHasKey('amountDebit', $array);
+ $this->assertArrayHasKey('currency', $array);
+ $this->assertArrayHasKey('invoice', $array);
+ $this->assertArrayHasKey('returnURL', $array);
+ $this->assertArrayHasKey('pushURL', $array);
+ $this->assertArrayHasKey('description', $array);
+ $this->assertArrayHasKey('customParameters', $array);
+ }
+
+ public function test_order_can_be_overridden_via_payload(): void
+ {
+ $payload = new PayPayload([
+ 'order' => 'CUSTOM-ORDER-123',
+ 'amountDebit' => 10.00,
+ ]);
+
+ $this->assertSame('CUSTOM-ORDER-123', $payload->order);
+ }
+
+ public function test_set_properties_can_override_auto_generated_order(): void
+ {
+ $payload = new PayPayload(['amountDebit' => 10.00]);
+
+ $this->assertStringStartsWith('ORDER_NO_', $payload->order);
+
+ $payload->setProperties([
+ 'order' => 'OVERRIDDEN-ORDER',
+ 'currency' => 'EUR',
+ ]);
+
+ $this->assertSame('OVERRIDDEN-ORDER', $payload->order);
+ $this->assertSame('EUR', $payload->currency);
+ }
+
+ public function test_to_array_with_only_required_properties(): void
+ {
+ $payload = new PayPayload(['amountDebit' => 99.99]);
+
+ $array = $payload->toArray();
+
+ $this->assertArrayHasKey('order', $array);
+ $this->assertArrayHasKey('amountDebit', $array);
+ }
+
+ public function test_handles_large_amount_values(): void
+ {
+ $payload = new PayPayload(['amountDebit' => 999999.99]);
+
+ $this->assertSame(999999.99, $payload->amountDebit);
+ $this->assertIsFloat($payload->amountDebit);
+ }
+}
diff --git a/tests/Unit/Models/Payload/PayloadTest.php b/tests/Unit/Models/Payload/PayloadTest.php
new file mode 100644
index 00000000..77dfb406
--- /dev/null
+++ b/tests/Unit/Models/Payload/PayloadTest.php
@@ -0,0 +1,323 @@
+makePayload([
+ 'customParameters' => ['key1' => 'value1'],
+ 'additionalParameters' => ['key2' => 'value2'],
+ 'clientIP' => ['address' => '192.168.1.1', 'type' => 0],
+ ]);
+
+ $this->assertInstanceOf(CustomParameters::class, $payload->customParameters);
+ $this->assertInstanceOf(AdditionalParameters::class, $payload->additionalParameters);
+ $this->assertInstanceOf(ClientIP::class, $payload->clientIP);
+ }
+
+ public function test_creates_custom_parameters_object_only(): void
+ {
+ $payload = $this->makePayload([
+ 'customParameters' => ['orderRef' => 'ORD-123', 'source' => 'web'],
+ ]);
+
+ $this->assertInstanceOf(CustomParameters::class, $payload->customParameters);
+
+ $array = $payload->customParameters->toArray();
+ $this->assertArrayHasKey('List', $array);
+ $this->assertCount(2, $array['List']);
+ }
+
+ public function test_creates_additional_parameters_object_only(): void
+ {
+ $payload = $this->makePayload([
+ 'additionalParameters' => ['param1' => 'value1', 'param2' => 'value2'],
+ ]);
+
+ $this->assertInstanceOf(AdditionalParameters::class, $payload->additionalParameters);
+
+ $array = $payload->additionalParameters->toArray();
+ $this->assertTrue(
+ isset($array['AdditionalParameter']) || isset($array['List']),
+ 'AdditionalParameters should contain AdditionalParameter or List'
+ );
+ }
+
+ public function test_creates_client_ip_with_both_address_and_type(): void
+ {
+ $payload = $this->makePayload([
+ 'clientIP' => ['address' => '10.0.0.1', 'type' => 0],
+ ]);
+
+ $this->assertInstanceOf(ClientIP::class, $payload->clientIP);
+
+ $array = $payload->clientIP->toArray();
+ $this->assertSame('10.0.0.1', $array['Address']);
+ $this->assertSame(0, $array['Type']);
+ }
+
+ public function test_combines_nested_and_flat_properties(): void
+ {
+ $payload = $this->makePayload([
+ 'currency' => 'EUR',
+ 'invoice' => 'INV-001',
+ 'customParameters' => ['orderId' => '12345'],
+ 'description' => 'Test payment',
+ 'additionalParameters' => ['meta' => 'data'],
+ ]);
+
+ $this->assertSame('EUR', $payload->currency);
+ $this->assertSame('INV-001', $payload->invoice);
+ $this->assertSame('Test payment', $payload->description);
+ $this->assertInstanceOf(CustomParameters::class, $payload->customParameters);
+ $this->assertInstanceOf(AdditionalParameters::class, $payload->additionalParameters);
+ }
+
+ public function test_unsets_nested_keys_before_parent_processing(): void
+ {
+ $data = [
+ 'customParameters' => ['key' => 'value'],
+ 'additionalParameters' => ['param' => 'val'],
+ 'clientIP' => ['address' => '127.0.0.1'],
+ 'currency' => 'USD',
+ ];
+
+ $payload = $this->makePayload($data);
+
+ $this->assertSame('USD', $payload->currency);
+ $this->assertInstanceOf(CustomParameters::class, $payload->customParameters);
+ $this->assertInstanceOf(AdditionalParameters::class, $payload->additionalParameters);
+ $this->assertInstanceOf(ClientIP::class, $payload->clientIP);
+ }
+
+ public function test_set_properties_creates_nested_objects(): void
+ {
+ $payload = $this->makePayload();
+
+ $payload->setProperties([
+ 'customParameters' => ['key1' => 'value1'],
+ 'additionalParameters' => ['key2' => 'value2'],
+ 'clientIP' => ['address' => '192.168.0.1', 'type' => 0],
+ ]);
+
+ $this->assertInstanceOf(CustomParameters::class, $payload->customParameters);
+ $this->assertInstanceOf(AdditionalParameters::class, $payload->additionalParameters);
+ $this->assertInstanceOf(ClientIP::class, $payload->clientIP);
+ }
+
+ public function test_set_properties_with_partial_nested_data(): void
+ {
+ $payload = $this->makePayload([
+ 'currency' => 'EUR',
+ ]);
+
+ $payload->setProperties([
+ 'customParameters' => ['ref' => 'REF-001'],
+ ]);
+
+ $this->assertSame('EUR', $payload->currency);
+ $this->assertInstanceOf(CustomParameters::class, $payload->customParameters);
+ }
+
+ public function test_to_array_converts_nested_arrayable_objects(): void
+ {
+ $payload = $this->makePayload([
+ 'customParameters' => ['orderRef' => 'ORD-999'],
+ 'additionalParameters' => ['metadata' => 'extra'],
+ 'currency' => 'GBP',
+ ]);
+
+ $array = $payload->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertSame('GBP', $array['currency']);
+ $this->assertIsArray($array['customParameters']);
+ $this->assertArrayHasKey('List', $array['customParameters']);
+ $this->assertIsArray($array['additionalParameters']);
+ }
+
+ public function test_to_array_includes_client_ip(): void
+ {
+ $payload = $this->makePayload([
+ 'clientIP' => ['address' => '203.0.113.1', 'type' => 0],
+ 'invoice' => 'INV-123',
+ ]);
+
+ $array = $payload->toArray();
+
+ $this->assertArrayHasKey('clientIP', $array);
+ $this->assertIsArray($array['clientIP']);
+ $this->assertSame('203.0.113.1', $array['clientIP']['Address']);
+ $this->assertSame(0, $array['clientIP']['Type']);
+ $this->assertSame('INV-123', $array['invoice']);
+ }
+
+ public function test_to_array_with_all_properties(): void
+ {
+ $payload = $this->makePayload([
+ 'currency' => 'EUR',
+ 'returnURL' => 'https://example.com/return',
+ 'returnURLError' => 'https://example.com/error',
+ 'returnURLCancel' => 'https://example.com/cancel',
+ 'returnURLReject' => 'https://example.com/reject',
+ 'pushURL' => 'https://example.com/push',
+ 'pushURLFailure' => 'https://example.com/push-failure',
+ 'invoice' => 'INV-FULL-001',
+ 'description' => 'Complete payment',
+ 'originalTransactionKey' => 'ORIG-TX-KEY',
+ 'originalTransactionReference' => 'ORIG-REF',
+ 'websiteKey' => 'WEBSITE-KEY',
+ 'culture' => 'nl-NL',
+ 'startRecurrent' => true,
+ 'continueOnIncomplete' => 'RedirectToHTML',
+ 'servicesSelectableByClient' => 'ideal,paypal',
+ 'servicesExcludedForClient' => 'bancontact',
+ 'customParameters' => ['custom1' => 'val1'],
+ 'additionalParameters' => ['add1' => 'val2'],
+ 'clientIP' => ['address' => '192.0.2.1', 'type' => 0],
+ ]);
+
+ $array = $payload->toArray();
+
+ $this->assertSame('EUR', $array['currency']);
+ $this->assertSame('https://example.com/return', $array['returnURL']);
+ $this->assertSame('https://example.com/error', $array['returnURLError']);
+ $this->assertSame('https://example.com/cancel', $array['returnURLCancel']);
+ $this->assertSame('https://example.com/reject', $array['returnURLReject']);
+ $this->assertSame('https://example.com/push', $array['pushURL']);
+ $this->assertSame('https://example.com/push-failure', $array['pushURLFailure']);
+ $this->assertSame('INV-FULL-001', $array['invoice']);
+ $this->assertSame('Complete payment', $array['description']);
+ $this->assertSame('ORIG-TX-KEY', $array['originalTransactionKey']);
+ $this->assertSame('ORIG-REF', $array['originalTransactionReference']);
+ $this->assertSame('WEBSITE-KEY', $array['websiteKey']);
+ $this->assertSame('nl-NL', $array['culture']);
+ $this->assertTrue($array['startRecurrent']);
+ $this->assertSame('RedirectToHTML', $array['continueOnIncomplete']);
+ $this->assertSame('ideal,paypal', $array['servicesSelectableByClient']);
+ $this->assertSame('bancontact', $array['servicesExcludedForClient']);
+ $this->assertIsArray($array['customParameters']);
+ $this->assertIsArray($array['additionalParameters']);
+ $this->assertIsArray($array['clientIP']);
+ }
+
+ public function test_to_array_with_empty_payload(): void
+ {
+ $payload = $this->makePayload();
+
+ $array = $payload->toArray();
+
+ $this->assertIsArray($array);
+ }
+
+ public function test_multiple_set_properties_calls_with_nested_objects(): void
+ {
+ $payload = $this->makePayload([
+ 'currency' => 'EUR',
+ ]);
+
+ $payload->setProperties([
+ 'customParameters' => ['key1' => 'value1'],
+ ]);
+
+ $payload->setProperties([
+ 'additionalParameters' => ['key2' => 'value2'],
+ 'invoice' => 'INV-002',
+ ]);
+
+ $this->assertSame('EUR', $payload->currency);
+ $this->assertSame('INV-002', $payload->invoice);
+ $this->assertInstanceOf(CustomParameters::class, $payload->customParameters);
+ $this->assertInstanceOf(AdditionalParameters::class, $payload->additionalParameters);
+ }
+
+ public function test_overwrites_nested_objects_on_subsequent_set_properties(): void
+ {
+ $payload = $this->makePayload([
+ 'customParameters' => ['first' => 'value1'],
+ ]);
+
+ $firstCustomParams = $payload->customParameters;
+ $this->assertInstanceOf(CustomParameters::class, $firstCustomParams);
+
+ $payload->setProperties([
+ 'customParameters' => ['second' => 'value2'],
+ ]);
+
+ $secondCustomParams = $payload->customParameters;
+ $this->assertInstanceOf(CustomParameters::class, $secondCustomParams);
+ $this->assertNotSame($firstCustomParams, $secondCustomParams);
+
+ $array = $secondCustomParams->toArray();
+ $names = array_column($array['List'], 'Name');
+ $this->assertContains('second', $names);
+ }
+
+ public function test_handles_null_in_nested_client_ip_properties(): void
+ {
+ $payload = $this->makePayload([
+ 'clientIP' => ['address' => null, 'type' => null],
+ ]);
+
+ $this->assertInstanceOf(ClientIP::class, $payload->clientIP);
+ }
+
+ public function test_preserves_flat_properties_when_setting_nested_objects(): void
+ {
+ $payload = $this->makePayload([
+ 'currency' => 'EUR',
+ 'invoice' => 'INV-001',
+ 'description' => 'Initial',
+ ]);
+
+ $payload->setProperties([
+ 'customParameters' => ['ref' => 'REF-001'],
+ 'additionalParameters' => ['meta' => 'data'],
+ ]);
+
+ $this->assertSame('EUR', $payload->currency);
+ $this->assertSame('INV-001', $payload->invoice);
+ $this->assertSame('Initial', $payload->description);
+ }
+
+ public function test_to_array_handles_mixed_initialized_and_uninitialized_properties(): void
+ {
+ $payload = $this->makePayload([
+ 'currency' => 'EUR',
+ 'customParameters' => ['key' => 'value'],
+ ]);
+
+ $array = $payload->toArray();
+
+ $this->assertArrayHasKey('currency', $array);
+ $this->assertArrayHasKey('customParameters', $array);
+ $this->assertSame('EUR', $array['currency']);
+ }
+
+ public function test_nested_objects_are_independent_instances(): void
+ {
+ $sharedData = ['shared' => 'value'];
+
+ $payload1 = $this->makePayload(['customParameters' => $sharedData]);
+ $payload2 = $this->makePayload(['customParameters' => $sharedData]);
+
+ $this->assertNotSame($payload1->customParameters, $payload2->customParameters);
+ $this->assertInstanceOf(CustomParameters::class, $payload1->customParameters);
+ $this->assertInstanceOf(CustomParameters::class, $payload2->customParameters);
+ }
+}
diff --git a/tests/Unit/Models/Payload/RefundPayloadTest.php b/tests/Unit/Models/Payload/RefundPayloadTest.php
new file mode 100644
index 00000000..ce42f08e
--- /dev/null
+++ b/tests/Unit/Models/Payload/RefundPayloadTest.php
@@ -0,0 +1,91 @@
+ 99.99]);
+
+ $this->assertSame(99.99, $payload->amountCredit);
+ $this->assertIsFloat($payload->amountCredit);
+ }
+
+ public function test_preserves_float_precision_for_amount_credit(): void
+ {
+ $payload = new RefundPayload(['amountCredit' => 123.456789]);
+
+ $this->assertSame(123.456789, $payload->amountCredit);
+ $this->assertIsFloat($payload->amountCredit);
+ }
+
+ public function test_handles_zero_and_negative_amounts(): void
+ {
+ $zeroPayload = new RefundPayload(['amountCredit' => 0.0]);
+ $this->assertSame(0.0, $zeroPayload->amountCredit);
+ $this->assertIsFloat($zeroPayload->amountCredit);
+
+ $negativePayload = new RefundPayload(['amountCredit' => -50.00]);
+ $this->assertSame(-50.00, $negativePayload->amountCredit);
+ $this->assertIsFloat($negativePayload->amountCredit);
+ }
+
+ public function test_to_array_includes_amount_credit_and_inherited_properties(): void
+ {
+ $payload = new RefundPayload([
+ 'amountCredit' => 100.00,
+ 'currency' => 'EUR',
+ 'invoice' => 'INV-001',
+ 'originalTransactionKey' => 'TX-KEY-123',
+ 'description' => 'Refund for order',
+ ]);
+
+ $array = $payload->toArray();
+
+ $this->assertArrayHasKey('amountCredit', $array);
+ $this->assertSame(100.00, $array['amountCredit']);
+ $this->assertArrayHasKey('currency', $array);
+ $this->assertSame('EUR', $array['currency']);
+ $this->assertArrayHasKey('invoice', $array);
+ $this->assertSame('INV-001', $array['invoice']);
+ $this->assertArrayHasKey('originalTransactionKey', $array);
+ $this->assertSame('TX-KEY-123', $array['originalTransactionKey']);
+ $this->assertArrayHasKey('description', $array);
+ $this->assertSame('Refund for order', $array['description']);
+ }
+
+ public function test_combines_refund_and_payload_properties(): void
+ {
+ $payload = new RefundPayload([
+ 'amountCredit' => 75.50,
+ 'currency' => 'USD',
+ 'invoice' => 'INV-REFUND-001',
+ 'originalTransactionKey' => 'ORIGINAL-TX-456',
+ 'returnURL' => 'https://example.com/refund-return',
+ 'pushURL' => 'https://example.com/refund-push',
+ 'description' => 'Customer refund request',
+ 'customParameters' => ['reason' => 'damaged', 'userId' => '67890'],
+ ]);
+
+ $this->assertSame(75.50, $payload->amountCredit);
+ $this->assertSame('USD', $payload->currency);
+ $this->assertSame('INV-REFUND-001', $payload->invoice);
+ $this->assertSame('ORIGINAL-TX-456', $payload->originalTransactionKey);
+ $this->assertSame('https://example.com/refund-return', $payload->returnURL);
+ $this->assertSame('https://example.com/refund-push', $payload->pushURL);
+ $this->assertSame('Customer refund request', $payload->description);
+ $this->assertInstanceOf(CustomParameters::class, $payload->customParameters);
+
+ $array = $payload->toArray();
+ $this->assertCount(8, $array);
+ $this->assertArrayHasKey('amountCredit', $array);
+ $this->assertArrayHasKey('customParameters', $array);
+ }
+}
diff --git a/tests/Unit/Models/PersonTest.php b/tests/Unit/Models/PersonTest.php
new file mode 100644
index 00000000..e6006a88
--- /dev/null
+++ b/tests/Unit/Models/PersonTest.php
@@ -0,0 +1,235 @@
+ 'B2C',
+ 'gender' => 'Male',
+ 'culture' => 'nl-NL',
+ 'careOf' => 'John Doe',
+ 'title' => 'Mr.',
+ 'initials' => 'J.D.',
+ 'name' => 'John Doe',
+ 'firstName' => 'John',
+ 'lastNamePrefix' => 'van',
+ 'lastName' => 'Doe',
+ 'birthDate' => '1990-05-15',
+ 'placeOfBirth' => 'Amsterdam',
+ ]);
+
+ $this->assertSame('B2C', $person->category);
+ $this->assertSame('Male', $person->gender);
+ $this->assertSame('nl-NL', $person->culture);
+ $this->assertSame('John Doe', $person->careOf);
+ $this->assertSame('Mr.', $person->title);
+ $this->assertSame('J.D.', $person->initials);
+ $this->assertSame('John Doe', $person->name);
+ $this->assertSame('John', $person->firstName);
+ $this->assertSame('van', $person->lastNamePrefix);
+ $this->assertSame('Doe', $person->lastName);
+ $this->assertSame('1990-05-15', $person->birthDate);
+ $this->assertSame('Amsterdam', $person->placeOfBirth);
+ }
+
+ public function test_handles_nullable_properties(): void
+ {
+ $personWithNulls = new Person([
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ 'initials' => null,
+ 'birthDate' => null,
+ ]);
+
+ $personWithValues = new Person([
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ 'initials' => 'J.D.',
+ 'birthDate' => '1985-03-20',
+ ]);
+
+ $this->assertSame('J.D.', $personWithValues->initials);
+ $this->assertSame('1985-03-20', $personWithValues->birthDate);
+
+ $arrayWithNulls = $personWithNulls->toArray();
+ $this->assertNull($arrayWithNulls['initials']);
+ $this->assertNull($arrayWithNulls['birthDate']);
+
+ $arrayWithValues = $personWithValues->toArray();
+ $this->assertSame('J.D.', $arrayWithValues['initials']);
+ $this->assertSame('1985-03-20', $arrayWithValues['birthDate']);
+ }
+
+ public function test_implements_recipient_interface(): void
+ {
+ $person = new Person([
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ]);
+
+ $this->assertInstanceOf(Recipient::class, $person);
+ }
+
+ public function test_handles_name_prefixes(): void
+ {
+ $dutchPrefixes = [
+ 'van',
+ 'de',
+ 'van der',
+ 'van den',
+ 'van de',
+ 'ter',
+ 'te',
+ 'den',
+ 'der',
+ ];
+
+ foreach ($dutchPrefixes as $prefix) {
+ $person = new Person([
+ 'firstName' => 'Jan',
+ 'lastNamePrefix' => $prefix,
+ 'lastName' => 'Berg',
+ ]);
+
+ $this->assertSame($prefix, $person->lastNamePrefix);
+ $this->assertSame('Berg', $person->lastName);
+
+ $array = $person->toArray();
+ $this->assertSame($prefix, $array['lastNamePrefix']);
+ }
+ }
+
+ public function test_handles_special_characters_in_names(): void
+ {
+ $person = new Person([
+ 'firstName' => "Anne-Marie",
+ 'lastName' => "O'Connor",
+ 'name' => "Anne-Marie O'Connor",
+ 'culture' => 'en-IE',
+ ]);
+
+ $this->assertSame("Anne-Marie", $person->firstName);
+ $this->assertSame("O'Connor", $person->lastName);
+
+ $accentedPerson = new Person([
+ 'firstName' => 'François',
+ 'lastName' => 'Müller',
+ 'culture' => 'fr-FR',
+ ]);
+
+ $this->assertSame('François', $accentedPerson->firstName);
+ $this->assertSame('Müller', $accentedPerson->lastName);
+
+ $array = $accentedPerson->toArray();
+ $this->assertSame('François', $array['firstName']);
+ $this->assertSame('Müller', $array['lastName']);
+ }
+
+ public function test_handles_unicode_names(): void
+ {
+ $arabicPerson = new Person([
+ 'firstName' => 'محمد',
+ 'lastName' => 'علي',
+ 'culture' => 'ar-SA',
+ ]);
+
+ $this->assertSame('محمد', $arabicPerson->firstName);
+ $this->assertSame('علي', $arabicPerson->lastName);
+
+ $chinesePerson = new Person([
+ 'firstName' => '伟',
+ 'lastName' => '王',
+ 'culture' => 'zh-CN',
+ ]);
+
+ $this->assertSame('伟', $chinesePerson->firstName);
+ $this->assertSame('王', $chinesePerson->lastName);
+
+ $cyrillicPerson = new Person([
+ 'firstName' => 'Иван',
+ 'lastName' => 'Петров',
+ 'culture' => 'ru-RU',
+ ]);
+
+ $this->assertSame('Иван', $cyrillicPerson->firstName);
+ $this->assertSame('Петров', $cyrillicPerson->lastName);
+
+ $arabicArray = $arabicPerson->toArray();
+ $this->assertSame('محمد', $arabicArray['firstName']);
+ $this->assertSame('علي', $arabicArray['lastName']);
+ }
+
+ public function test_handles_partial_initialization(): void
+ {
+ $minimalPerson = new Person([
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ]);
+
+ $this->assertSame('John', $minimalPerson->firstName);
+ $this->assertSame('Doe', $minimalPerson->lastName);
+
+ $fullPerson = new Person([
+ 'category' => 'B2B',
+ 'gender' => 'Female',
+ 'culture' => 'en-US',
+ 'careOf' => 'Jane Smith',
+ 'title' => 'Dr.',
+ 'initials' => 'J.S.',
+ 'name' => 'Dr. Jane Smith',
+ 'firstName' => 'Jane',
+ 'lastNamePrefix' => 'von',
+ 'lastName' => 'Smith',
+ 'birthDate' => '1988-12-01',
+ 'placeOfBirth' => 'New York',
+ ]);
+
+ $this->assertSame('B2B', $fullPerson->category);
+ $this->assertSame('Female', $fullPerson->gender);
+ $this->assertSame('Dr.', $fullPerson->title);
+ $this->assertSame('1988-12-01', $fullPerson->birthDate);
+ }
+
+ public function test_to_array_preserves_all_values_including_edge_cases(): void
+ {
+ $person = new Person([
+ 'category' => 'B2C',
+ 'gender' => 'Male',
+ 'culture' => 'nl-NL',
+ 'careOf' => 'T.A. Parent',
+ 'title' => 'Drs.',
+ 'initials' => 'P.J.M.',
+ 'name' => 'Pieter Jan Maria van den Berg',
+ 'firstName' => 'Pieter Jan Maria',
+ 'lastNamePrefix' => 'van den',
+ 'lastName' => 'Berg',
+ 'birthDate' => '1975-11-23',
+ 'placeOfBirth' => "'s-Gravenhage",
+ ]);
+
+ $array = $person->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertSame('B2C', $array['category']);
+ $this->assertSame('Male', $array['gender']);
+ $this->assertSame('nl-NL', $array['culture']);
+ $this->assertSame('T.A. Parent', $array['careOf']);
+ $this->assertSame('Drs.', $array['title']);
+ $this->assertSame('P.J.M.', $array['initials']);
+ $this->assertSame('Pieter Jan Maria van den Berg', $array['name']);
+ $this->assertSame('Pieter Jan Maria', $array['firstName']);
+ $this->assertSame('van den', $array['lastNamePrefix']);
+ $this->assertSame('Berg', $array['lastName']);
+ $this->assertSame('1975-11-23', $array['birthDate']);
+ $this->assertSame("'s-Gravenhage", $array['placeOfBirth']);
+ }
+}
diff --git a/tests/Unit/Models/PhoneTest.php b/tests/Unit/Models/PhoneTest.php
new file mode 100644
index 00000000..e76b83d9
--- /dev/null
+++ b/tests/Unit/Models/PhoneTest.php
@@ -0,0 +1,147 @@
+ '+31 20 1234567',
+ 'mobile' => '+31 6 12345678',
+ 'phone' => '0201234567',
+ 'fax' => '+31 20 7654321',
+ ]);
+
+ $this->assertSame('+31 20 1234567', $phone->landLine);
+ $this->assertSame('+31 6 12345678', $phone->mobile);
+ $this->assertSame('0201234567', $phone->phone);
+ $this->assertSame('+31 20 7654321', $phone->fax);
+ }
+
+ public function test_handles_partial_initialization(): void
+ {
+ $phone = new Phone([
+ 'mobile' => '+31612345678',
+ 'phone' => '020-1234567',
+ ]);
+
+ $this->assertSame('+31612345678', $phone->mobile);
+ $this->assertSame('020-1234567', $phone->phone);
+ }
+
+ public function test_to_array_preserves_type_integrity(): void
+ {
+ $phone = new Phone([
+ 'landLine' => '+31201234567',
+ 'mobile' => '+31612345678',
+ 'phone' => '0201234567',
+ 'fax' => '+31207654321',
+ ]);
+
+ $array = $phone->toArray();
+
+ $this->assertIsString($array['landLine']);
+ $this->assertIsString($array['mobile']);
+ $this->assertIsString($array['phone']);
+ $this->assertIsString($array['fax']);
+
+ $this->assertSame('+31201234567', $array['landLine']);
+ $this->assertSame('+31612345678', $array['mobile']);
+ $this->assertSame('0201234567', $array['phone']);
+ $this->assertSame('+31207654321', $array['fax']);
+ }
+
+ public function test_handles_international_formats_with_special_characters(): void
+ {
+ $formats = [
+ 'landLine' => '+1 (555) 123-4567',
+ 'mobile' => '+44 7700 900123',
+ 'phone' => '+49 (0)30 123456',
+ 'fax' => '+33 1 42 86 82 00',
+ ];
+
+ $phone = new Phone($formats);
+
+ $this->assertSame('+1 (555) 123-4567', $phone->landLine);
+ $this->assertSame('+44 7700 900123', $phone->mobile);
+ $this->assertSame('+49 (0)30 123456', $phone->phone);
+ $this->assertSame('+33 1 42 86 82 00', $phone->fax);
+
+ $array = $phone->toArray();
+ $this->assertSame('+1 (555) 123-4567', $array['landLine']);
+ $this->assertSame('+44 7700 900123', $array['mobile']);
+ $this->assertSame('+49 (0)30 123456', $array['phone']);
+ $this->assertSame('+33 1 42 86 82 00', $array['fax']);
+ }
+
+ public function test_handles_multiple_phone_types_simultaneously(): void
+ {
+ $phone = new Phone([
+ 'landLine' => '020-1234567',
+ 'mobile' => '06-12345678',
+ 'phone' => '+31 20 1234567',
+ 'fax' => '020-7654321',
+ ]);
+
+ $this->assertSame('020-1234567', $phone->landLine);
+ $this->assertSame('06-12345678', $phone->mobile);
+ $this->assertSame('+31 20 1234567', $phone->phone);
+ $this->assertSame('020-7654321', $phone->fax);
+
+ $array = $phone->toArray();
+ $this->assertCount(4, $array);
+ $this->assertArrayHasKey('landLine', $array);
+ $this->assertArrayHasKey('mobile', $array);
+ $this->assertArrayHasKey('phone', $array);
+ $this->assertArrayHasKey('fax', $array);
+ }
+
+ public function test_handles_empty_strings_and_numeric_only_values(): void
+ {
+ $phone = new Phone([
+ 'landLine' => '',
+ 'mobile' => '0612345678',
+ 'phone' => '1234567890',
+ 'fax' => '',
+ ]);
+
+ $this->assertSame('', $phone->landLine);
+ $this->assertSame('0612345678', $phone->mobile);
+ $this->assertSame('1234567890', $phone->phone);
+ $this->assertSame('', $phone->fax);
+
+ $array = $phone->toArray();
+ $this->assertSame('', $array['landLine']);
+ $this->assertSame('0612345678', $array['mobile']);
+ $this->assertSame('1234567890', $array['phone']);
+ $this->assertSame('', $array['fax']);
+ }
+
+ public function test_handles_extremely_long_phone_numbers(): void
+ {
+ $longInternational = '+1-555-123-4567 ext. 12345';
+ $longMobile = '+86 138 0013 8000';
+ $longFax = '+81 (0)3-1234-5678';
+
+ $phone = new Phone([
+ 'landLine' => $longInternational,
+ 'mobile' => $longMobile,
+ 'fax' => $longFax,
+ ]);
+
+ $this->assertSame($longInternational, $phone->landLine);
+ $this->assertSame($longMobile, $phone->mobile);
+ $this->assertSame($longFax, $phone->fax);
+
+ $array = $phone->toArray();
+ $this->assertSame($longInternational, $array['landLine']);
+ $this->assertSame($longMobile, $array['mobile']);
+ $this->assertSame($longFax, $array['fax']);
+ }
+}
diff --git a/tests/Unit/Models/ServiceListTest.php b/tests/Unit/Models/ServiceListTest.php
new file mode 100644
index 00000000..a541c1a4
--- /dev/null
+++ b/tests/Unit/Models/ServiceListTest.php
@@ -0,0 +1,372 @@
+assertSame('ideal', $serviceList->name);
+ $this->assertSame(2, $serviceList->version);
+ $this->assertSame('Pay', $serviceList->action);
+ $this->assertIsArray($serviceList->parameters());
+ $this->assertEmpty($serviceList->parameters());
+ }
+
+ public function test_constructor_without_model_does_not_trigger_decoration(): void
+ {
+ $serviceList = new ServiceList('creditcard', 1, 'Authorize', null);
+
+ $this->assertSame('creditcard', $serviceList->name);
+ $this->assertSame(1, $serviceList->version);
+ $this->assertSame('Authorize', $serviceList->action);
+ $this->assertEmpty($serviceList->parameters());
+ }
+
+ public function test_constructor_with_model_triggers_decoration(): void
+ {
+ $address = new Address([
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ]);
+
+ $serviceList = new ServiceList('ideal', 2, 'Pay', $address);
+
+ $this->assertSame('ideal', $serviceList->name);
+ $this->assertNotEmpty($serviceList->parameters());
+ }
+
+ public function test_appendParameter_without_key_appends_to_array(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $serviceList->appendParameter(['Name' => 'param1', 'Value' => 'value1']);
+ $serviceList->appendParameter(['Name' => 'param2', 'Value' => 'value2']);
+
+ $params = $serviceList->parameters();
+
+ $this->assertCount(2, $params);
+ $this->assertSame(['Name' => 'param1', 'Value' => 'value1'], $params[0]);
+ $this->assertSame(['Name' => 'param2', 'Value' => 'value2'], $params[1]);
+ }
+
+ public function test_appendParameter_with_key_sets_specific_index(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $serviceList->appendParameter(['Name' => 'amount', 'Value' => '10.50'], 'payment_amount');
+ $serviceList->appendParameter(['Name' => 'currency', 'Value' => 'EUR'], 'payment_currency');
+
+ $params = $serviceList->parameters();
+
+ $this->assertArrayHasKey('payment_amount', $params);
+ $this->assertArrayHasKey('payment_currency', $params);
+ $this->assertSame(['Name' => 'amount', 'Value' => '10.50'], $params['payment_amount']);
+ $this->assertSame(['Name' => 'currency', 'Value' => 'EUR'], $params['payment_currency']);
+ }
+
+ public function test_appendParameter_with_nested_arrays_iterates_recursively(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $nestedParams = [
+ ['Name' => 'item1', 'Value' => 'value1'],
+ ['Name' => 'item2', 'Value' => 'value2'],
+ ['Name' => 'item3', 'Value' => 'value3'],
+ ];
+
+ $serviceList->appendParameter($nestedParams);
+
+ $params = $serviceList->parameters();
+
+ $this->assertCount(3, $params);
+ $this->assertSame(['Name' => 'item1', 'Value' => 'value1'], $params[0]);
+ $this->assertSame(['Name' => 'item2', 'Value' => 'value2'], $params[1]);
+ $this->assertSame(['Name' => 'item3', 'Value' => 'value3'], $params[2]);
+ }
+
+ public function test_appendParameter_with_nested_arrays_and_key_overwrites(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $nestedParams = [
+ ['Name' => 'param1', 'Value' => 'val1'],
+ ['Name' => 'param2', 'Value' => 'val2'],
+ ];
+
+ $serviceList->appendParameter($nestedParams, 'batch');
+
+ $params = $serviceList->parameters();
+
+ $this->assertCount(1, $params);
+ $this->assertArrayHasKey('batch', $params);
+ $this->assertSame(['Name' => 'param2', 'Value' => 'val2'], $params['batch']);
+ }
+
+ public function test_appendParameter_multiple_calls_accumulate(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $serviceList->appendParameter(['Name' => 'first', 'Value' => '1']);
+ $serviceList->appendParameter(['Name' => 'second', 'Value' => '2']);
+ $serviceList->appendParameter(['Name' => 'third', 'Value' => '3']);
+
+ $params = $serviceList->parameters();
+
+ $this->assertCount(3, $params);
+ }
+
+ public function test_appendParameter_returns_fluent_interface(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $result = $serviceList->appendParameter(['Name' => 'test', 'Value' => 'value']);
+
+ $this->assertSame($serviceList, $result);
+ }
+
+ public function test_appendParameter_fluent_chaining(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $serviceList
+ ->appendParameter(['Name' => 'param1', 'Value' => 'val1'])
+ ->appendParameter(['Name' => 'param2', 'Value' => 'val2'])
+ ->appendParameter(['Name' => 'param3', 'Value' => 'val3']);
+
+ $params = $serviceList->parameters();
+
+ $this->assertCount(3, $params);
+ $this->assertSame(['Name' => 'param1', 'Value' => 'val1'], $params[0]);
+ $this->assertSame(['Name' => 'param2', 'Value' => 'val2'], $params[1]);
+ $this->assertSame(['Name' => 'param3', 'Value' => 'val3'], $params[2]);
+ }
+
+ public function test_parameters_getter_returns_parameters_array(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $serviceList->appendParameter(['Name' => 'key', 'Value' => 'value']);
+
+ $params = $serviceList->parameters();
+
+ $this->assertIsArray($params);
+ $this->assertCount(1, $params);
+ }
+
+ public function test_decorateParameters_with_address_model(): void
+ {
+ $address = new Address([
+ 'street' => 'Keizersgracht',
+ 'houseNumber' => '500',
+ 'zipcode' => '1017EK',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ]);
+
+ $serviceList = new ServiceList('ideal', 2, 'Pay', $address);
+
+ $params = $serviceList->parameters();
+
+ $this->assertNotEmpty($params);
+
+ $paramValues = array_column($params, 'Value');
+ $this->assertContains('Keizersgracht', $paramValues);
+ $this->assertContains('500', $paramValues);
+ $this->assertContains('1017EK', $paramValues);
+ $this->assertContains('Amsterdam', $paramValues);
+ $this->assertContains('NL', $paramValues);
+ }
+
+ public function test_decorateParameters_with_person_model(): void
+ {
+ $person = new Person([
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ 'gender' => 'Male',
+ 'culture' => 'nl-NL',
+ ]);
+
+ $serviceList = new ServiceList('creditcard', 1, 'Pay', $person);
+
+ $params = $serviceList->parameters();
+
+ $this->assertNotEmpty($params);
+
+ $paramValues = array_column($params, 'Value');
+ $this->assertContains('John', $paramValues);
+ $this->assertContains('Doe', $paramValues);
+ $this->assertContains('Male', $paramValues);
+ $this->assertContains('nl-NL', $paramValues);
+ }
+
+ public function test_decorateParameters_transforms_property_names(): void
+ {
+ $address = new Address([
+ 'street' => 'Main Street',
+ 'houseNumber' => '42',
+ 'city' => 'Rotterdam',
+ ]);
+
+ $serviceList = new ServiceList('test', 1, 'Pay', $address);
+
+ $params = $serviceList->parameters();
+
+ $this->assertNotEmpty($params);
+
+ $paramNames = array_column($params, 'Name');
+ $this->assertContains('Street', $paramNames);
+ $this->assertContains('HouseNumber', $paramNames);
+ $this->assertContains('City', $paramNames);
+ }
+
+ public function test_empty_parameters_on_initialization(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $params = $serviceList->parameters();
+
+ $this->assertIsArray($params);
+ $this->assertEmpty($params);
+ }
+
+ public function test_appendParameter_with_single_value_array(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $serviceList->appendParameter(['Name' => 'single', 'Value' => 'value']);
+
+ $params = $serviceList->parameters();
+
+ $this->assertCount(1, $params);
+ $this->assertSame(['Name' => 'single', 'Value' => 'value'], $params[0]);
+ }
+
+ public function test_large_parameter_array(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $largeArray = [];
+ for ($i = 0; $i < 100; $i++) {
+ $largeArray[] = ['Name' => 'param' . $i, 'Value' => 'value' . $i];
+ }
+
+ $serviceList->appendParameter($largeArray);
+
+ $params = $serviceList->parameters();
+
+ $this->assertCount(100, $params);
+ $this->assertSame(['Name' => 'param0', 'Value' => 'value0'], $params[0]);
+ $this->assertSame(['Name' => 'param99', 'Value' => 'value99'], $params[99]);
+ }
+
+ public function test_appendParameter_preserves_existing_parameters(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $serviceList->appendParameter(['Name' => 'existing', 'Value' => 'old']);
+ $serviceList->appendParameter(['Name' => 'new', 'Value' => 'fresh']);
+
+ $params = $serviceList->parameters();
+
+ $this->assertCount(2, $params);
+ $this->assertSame(['Name' => 'existing', 'Value' => 'old'], $params[0]);
+ $this->assertSame(['Name' => 'new', 'Value' => 'fresh'], $params[1]);
+ }
+
+ public function test_appendParameter_with_mixed_keys_and_no_keys(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $serviceList->appendParameter(['Name' => 'indexed1', 'Value' => 'val1']);
+ $serviceList->appendParameter(['Name' => 'keyed', 'Value' => 'val2'], 'my_key');
+ $serviceList->appendParameter(['Name' => 'indexed2', 'Value' => 'val3']);
+
+ $params = $serviceList->parameters();
+
+ $this->assertCount(3, $params);
+ $this->assertSame(['Name' => 'indexed1', 'Value' => 'val1'], $params[0]);
+ $this->assertArrayHasKey('my_key', $params);
+ $this->assertSame(['Name' => 'keyed', 'Value' => 'val2'], $params['my_key']);
+ $this->assertSame(['Name' => 'indexed2', 'Value' => 'val3'], $params[1]);
+ }
+
+ public function test_constructor_with_different_action_values(): void
+ {
+ $actions = ['Pay', 'Authorize', 'Refund', 'Cancel', 'Capture'];
+
+ foreach ($actions as $action) {
+ $serviceList = new ServiceList('test', 1, $action);
+ $this->assertSame($action, $serviceList->action);
+ }
+ }
+
+ public function test_constructor_with_different_version_numbers(): void
+ {
+ $versions = [1, 2, 3, 10, 99];
+
+ foreach ($versions as $version) {
+ $serviceList = new ServiceList('test', $version, 'Pay');
+ $this->assertSame($version, $serviceList->version);
+ }
+ }
+
+ public function test_deeply_nested_arrays_are_flattened(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $deeplyNested = [
+ [
+ ['Name' => 'nested1', 'Value' => 'val1'],
+ ['Name' => 'nested2', 'Value' => 'val2'],
+ ],
+ [
+ ['Name' => 'nested3', 'Value' => 'val3'],
+ ],
+ ];
+
+ $serviceList->appendParameter($deeplyNested);
+
+ $params = $serviceList->parameters();
+
+ $this->assertCount(3, $params);
+ $this->assertSame(['Name' => 'nested1', 'Value' => 'val1'], $params[0]);
+ $this->assertSame(['Name' => 'nested2', 'Value' => 'val2'], $params[1]);
+ $this->assertSame(['Name' => 'nested3', 'Value' => 'val3'], $params[2]);
+ }
+
+ public function test_appendParameter_with_empty_array_appends_empty_entry(): void
+ {
+ $serviceList = new ServiceList('test', 1, 'action');
+
+ $emptyArray = [];
+ $serviceList->appendParameter($emptyArray);
+
+ $params = $serviceList->parameters();
+
+ $this->assertCount(1, $params);
+ $this->assertIsArray($params[0]);
+ $this->assertEmpty($params[0]);
+ }
+
+ public function test_magic_property_access_to_name_version_action(): void
+ {
+ $serviceList = new ServiceList('paypal', 5, 'Capture');
+
+ $this->assertSame('paypal', $serviceList->name);
+ $this->assertSame(5, $serviceList->version);
+ $this->assertSame('Capture', $serviceList->action);
+ }
+}
diff --git a/tests/Unit/Models/ServiceParameterTest.php b/tests/Unit/Models/ServiceParameterTest.php
new file mode 100644
index 00000000..de2d3bb1
--- /dev/null
+++ b/tests/Unit/Models/ServiceParameterTest.php
@@ -0,0 +1,235 @@
+ [
+ 'groupType' => 'Article',
+ ],
+ 'billing' => [
+ 'groupType' => 'Billing',
+ 'groupKey' => 1,
+ ],
+ 'shipping' => [
+ 'groupType' => 'Shipping',
+ 'groupKey' => 2,
+ ],
+ ];
+
+ public function testMethod($value)
+ {
+ $this->methodWasCalled = $value;
+ $this->methodWasInvoked = true;
+ }
+
+ public function methodWithReturn($value)
+ {
+ $this->returnedValue = 'RETURNED: ' . $value;
+
+ return $this->returnedValue;
+ }
+ };
+ }
+
+ public function test_invokes_method_when_property_name_matches_method(): void
+ {
+ $parameter = $this->makeTestable();
+
+ $parameter->setProperties([
+ 'testMethod' => 'method-value',
+ ]);
+
+ $this->assertSame('method-value', $parameter->methodWasCalled);
+ $this->assertNull($parameter->testMethod);
+ }
+
+ public function test_sets_property_directly_when_no_method_exists(): void
+ {
+ $parameter = $this->makeTestable();
+
+ $parameter->setProperties([
+ 'regularProperty' => 'property-value',
+ ]);
+
+ $this->assertSame('property-value', $parameter->regularProperty);
+ $this->assertNull($parameter->methodWasCalled);
+ }
+
+ public function test_handles_mixed_method_invocation_and_property_assignment(): void
+ {
+ $parameter = $this->makeTestable();
+
+ $parameter->setProperties([
+ 'testMethod' => 'method-value',
+ 'regularProperty' => 'property-value',
+ 'anotherProperty' => 'another-value',
+ ]);
+
+ $this->assertSame('method-value', $parameter->methodWasCalled);
+ $this->assertSame('property-value', $parameter->regularProperty);
+ $this->assertSame('another-value', $parameter->anotherProperty);
+ }
+
+ public function test_invokes_method_with_null_value(): void
+ {
+ $parameter = $this->makeTestable();
+
+ $parameter->setProperties([
+ 'testMethod' => null,
+ ]);
+
+ $this->assertNull($parameter->methodWasCalled);
+ $this->assertTrue($parameter->methodWasInvoked);
+ }
+
+ public function test_returns_self_from_set_properties(): void
+ {
+ $parameter = $this->makeTestable();
+
+ $result = $parameter->setProperties([
+ 'regularProperty' => 'value',
+ ]);
+
+ $this->assertSame($parameter, $result);
+ }
+
+ public function test_handles_empty_array_in_set_properties(): void
+ {
+ $parameter = $this->makeTestable();
+ $parameter->setProperties(['regularProperty' => 'initial']);
+
+ $parameter->setProperties([]);
+
+ $this->assertSame('initial', $parameter->regularProperty);
+ }
+
+ public function test_handles_null_in_set_properties(): void
+ {
+ $parameter = $this->makeTestable();
+ $parameter->setProperties(['regularProperty' => 'initial']);
+
+ $parameter->setProperties(null);
+
+ $this->assertSame('initial', $parameter->regularProperty);
+ }
+
+ public function test_uses_set_properties_in_constructor(): void
+ {
+ $parameter = $this->makeTestable([
+ 'testMethod' => 'constructor-method-value',
+ 'regularProperty' => 'constructor-property-value',
+ ]);
+
+ $this->assertSame('constructor-method-value', $parameter->methodWasCalled);
+ $this->assertSame('constructor-property-value', $parameter->regularProperty);
+ }
+
+ public function test_get_group_type_returns_correct_value(): void
+ {
+ $parameter = $this->makeTestable();
+
+ $groupType = $parameter->getGroupType('articles');
+
+ $this->assertSame('Article', $groupType);
+ }
+
+ public function test_get_group_type_returns_null_for_missing_key(): void
+ {
+ $parameter = $this->makeTestable();
+
+ $groupType = $parameter->getGroupType('nonExistentKey');
+
+ $this->assertNull($groupType);
+ }
+
+ public function test_get_group_key_returns_correct_value(): void
+ {
+ $parameter = $this->makeTestable();
+
+ $groupKey = $parameter->getGroupKey('billing');
+
+ $this->assertSame(1, $groupKey);
+ }
+
+ public function test_get_group_key_returns_null_for_missing_key(): void
+ {
+ $parameter = $this->makeTestable();
+
+ $groupKey = $parameter->getGroupKey('nonExistentKey');
+
+ $this->assertNull($groupKey);
+ }
+
+ public function test_group_data_included_in_to_array(): void
+ {
+ $parameter = $this->makeTestable([
+ 'regularProperty' => 'value',
+ ]);
+
+ $array = $parameter->toArray();
+
+ $this->assertArrayHasKey('groupData', $array);
+ $this->assertIsArray($array['groupData']);
+ $this->assertArrayHasKey('articles', $array['groupData']);
+ $this->assertSame('Article', $array['groupData']['articles']['groupType']);
+ $this->assertArrayHasKey('billing', $array['groupData']);
+ $this->assertSame('Billing', $array['groupData']['billing']['groupType']);
+ $this->assertSame(1, $array['groupData']['billing']['groupKey']);
+ }
+
+ public function test_handles_multiple_group_data_entries(): void
+ {
+ $parameter = $this->makeTestable();
+
+ $this->assertSame('Article', $parameter->getGroupType('articles'));
+ $this->assertNull($parameter->getGroupKey('articles'));
+ $this->assertSame('Billing', $parameter->getGroupType('billing'));
+ $this->assertSame(1, $parameter->getGroupKey('billing'));
+ $this->assertSame('Shipping', $parameter->getGroupType('shipping'));
+ $this->assertSame(2, $parameter->getGroupKey('shipping'));
+ }
+
+ public function test_method_invocation_can_return_values(): void
+ {
+ $parameter = $this->makeTestable();
+
+ $parameter->setProperties([
+ 'methodWithReturn' => 'input',
+ ]);
+
+ $this->assertSame('RETURNED: input', $parameter->returnedValue);
+ }
+
+ public function test_get_group_type_with_empty_string_key(): void
+ {
+ $parameter = $this->makeTestable();
+
+ $groupType = $parameter->getGroupType('');
+
+ $this->assertNull($groupType);
+ }
+
+ public function test_get_group_key_with_empty_string_key(): void
+ {
+ $parameter = $this->makeTestable();
+
+ $groupKey = $parameter->getGroupKey('');
+
+ $this->assertNull($groupKey);
+ }
+}
diff --git a/tests/Unit/Models/ServicesTest.php b/tests/Unit/Models/ServicesTest.php
new file mode 100644
index 00000000..3be03268
--- /dev/null
+++ b/tests/Unit/Models/ServicesTest.php
@@ -0,0 +1,202 @@
+assertIsArray($services->serviceList());
+ $this->assertEmpty($services->serviceList());
+ }
+
+ public function test_sets_service_list_via_constructor(): void
+ {
+ $serviceList1 = new ServiceList('ideal', 2, 'Pay');
+ $serviceList2 = new ServiceList('creditcard', 1, 'Authorize');
+
+ $services = new Services([
+ 'ServiceList' => [$serviceList1, $serviceList2],
+ ]);
+
+ $list = $services->serviceList();
+
+ $this->assertCount(2, $list);
+ $this->assertSame($serviceList1, $list[0]);
+ $this->assertSame($serviceList2, $list[1]);
+ }
+
+ public function test_pushServiceList_appends_to_array(): void
+ {
+ $services = new Services();
+ $serviceList = new ServiceList('paypal', 1, 'Pay');
+
+ $services->pushServiceList($serviceList);
+
+ $list = $services->serviceList();
+
+ $this->assertCount(1, $list);
+ $this->assertSame($serviceList, $list[0]);
+ }
+
+ public function test_pushServiceList_returns_self_for_fluent_interface(): void
+ {
+ $services = new Services();
+ $serviceList = new ServiceList('ideal', 2, 'Pay');
+
+ $result = $services->pushServiceList($serviceList);
+
+ $this->assertSame($services, $result);
+ }
+
+ public function test_multiple_pushServiceList_calls_accumulate(): void
+ {
+ $services = new Services();
+
+ $serviceList1 = new ServiceList('ideal', 2, 'Pay');
+ $serviceList2 = new ServiceList('creditcard', 1, 'Authorize');
+ $serviceList3 = new ServiceList('paypal', 1, 'Refund');
+
+ $services->pushServiceList($serviceList1);
+ $services->pushServiceList($serviceList2);
+ $services->pushServiceList($serviceList3);
+
+ $list = $services->serviceList();
+
+ $this->assertCount(3, $list);
+ $this->assertSame($serviceList1, $list[0]);
+ $this->assertSame($serviceList2, $list[1]);
+ $this->assertSame($serviceList3, $list[2]);
+ }
+
+ public function test_can_chain_multiple_pushServiceList_calls(): void
+ {
+ $services = new Services();
+
+ $serviceList1 = new ServiceList('ideal', 2, 'Pay');
+ $serviceList2 = new ServiceList('creditcard', 1, 'Authorize');
+ $serviceList3 = new ServiceList('sepa', 1, 'Refund');
+
+ $services
+ ->pushServiceList($serviceList1)
+ ->pushServiceList($serviceList2)
+ ->pushServiceList($serviceList3);
+
+ $list = $services->serviceList();
+
+ $this->assertCount(3, $list);
+ $this->assertSame($serviceList1, $list[0]);
+ $this->assertSame($serviceList2, $list[1]);
+ $this->assertSame($serviceList3, $list[2]);
+ }
+
+ public function test_serviceList_getter_returns_correct_array(): void
+ {
+ $services = new Services();
+
+ $serviceList1 = new ServiceList('ideal', 2, 'Pay');
+ $serviceList2 = new ServiceList('creditcard', 1, 'Authorize');
+
+ $services->pushServiceList($serviceList1);
+ $services->pushServiceList($serviceList2);
+
+ $list = $services->serviceList();
+
+ $this->assertIsArray($list);
+ $this->assertCount(2, $list);
+ $this->assertInstanceOf(ServiceList::class, $list[0]);
+ $this->assertInstanceOf(ServiceList::class, $list[1]);
+ }
+
+ public function test_toArray_includes_service_list_property(): void
+ {
+ $services = new Services();
+
+ $serviceList = new ServiceList('ideal', 2, 'Pay');
+ $services->pushServiceList($serviceList);
+
+ $array = $services->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertArrayHasKey('ServiceList', $array);
+ $this->assertIsArray($array['ServiceList']);
+ $this->assertCount(1, $array['ServiceList']);
+ }
+
+ public function test_toArray_converts_nested_service_list_objects(): void
+ {
+ $services = new Services();
+
+ $address = new Address([
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'city' => 'Amsterdam',
+ ]);
+
+ $serviceList1 = new ServiceList('ideal', 2, 'Pay', $address);
+ $serviceList2 = new ServiceList('creditcard', 1, 'Authorize');
+
+ $services
+ ->pushServiceList($serviceList1)
+ ->pushServiceList($serviceList2);
+
+ $array = $services->toArray();
+
+ $this->assertIsArray($array['ServiceList']);
+ $this->assertCount(2, $array['ServiceList']);
+
+ $this->assertIsArray($array['ServiceList'][0]);
+ $this->assertArrayHasKey('name', $array['ServiceList'][0]);
+ $this->assertArrayHasKey('version', $array['ServiceList'][0]);
+ $this->assertArrayHasKey('action', $array['ServiceList'][0]);
+ $this->assertSame('ideal', $array['ServiceList'][0]['name']);
+ $this->assertSame(2, $array['ServiceList'][0]['version']);
+ $this->assertSame('Pay', $array['ServiceList'][0]['action']);
+
+ $this->assertIsArray($array['ServiceList'][1]);
+ $this->assertSame('creditcard', $array['ServiceList'][1]['name']);
+ $this->assertSame(1, $array['ServiceList'][1]['version']);
+ $this->assertSame('Authorize', $array['ServiceList'][1]['action']);
+ }
+
+ public function test_toArray_with_empty_services(): void
+ {
+ $services = new Services();
+
+ $array = $services->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertArrayHasKey('ServiceList', $array);
+ $this->assertIsArray($array['ServiceList']);
+ $this->assertEmpty($array['ServiceList']);
+ }
+
+ public function test_preserves_service_list_order(): void
+ {
+ $services = new Services();
+
+ $serviceList1 = new ServiceList('first', 1, 'Pay');
+ $serviceList2 = new ServiceList('second', 2, 'Authorize');
+ $serviceList3 = new ServiceList('third', 3, 'Refund');
+
+ $services
+ ->pushServiceList($serviceList1)
+ ->pushServiceList($serviceList2)
+ ->pushServiceList($serviceList3);
+
+ $list = $services->serviceList();
+
+ $this->assertSame('first', $list[0]->name);
+ $this->assertSame('second', $list[1]->name);
+ $this->assertSame('third', $list[2]->name);
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Afterpay/Models/RecipientTest.php b/tests/Unit/PaymentMethods/Afterpay/Models/RecipientTest.php
new file mode 100644
index 00000000..3ca15244
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Afterpay/Models/RecipientTest.php
@@ -0,0 +1,106 @@
+ [
+ 'category' => RecipientCategory::PERSON,
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $this->assertInstanceOf(RecipientAdapter::class, $recipient->recipient());
+ }
+
+ /** @test */
+ public function it_creates_company_recipient(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'recipient' => [
+ 'category' => RecipientCategory::COMPANY,
+ 'companyName' => 'ACME Inc.',
+ 'chamberOfCommerce' => '12345678',
+ ],
+ ]);
+
+ $this->assertInstanceOf(RecipientAdapter::class, $recipient->recipient());
+ }
+
+ /** @test */
+ public function it_throws_exception_for_invalid_recipient_category(): void
+ {
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage('No recipient category found.');
+
+ new Recipient('Billing', [
+ 'recipient' => [
+ 'category' => 'invalid',
+ 'firstName' => 'John',
+ ],
+ ]);
+ }
+
+ /** @test */
+ public function it_sets_and_returns_address_adapter(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'address' => [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ ]);
+
+ $this->assertInstanceOf(AddressAdapter::class, $recipient->address());
+ }
+
+ /** @test */
+ public function it_sets_and_returns_phone_adapter(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ ]);
+
+ $this->assertInstanceOf(PhoneAdapter::class, $recipient->phone());
+ }
+
+ /** @test */
+ public function it_sets_and_returns_email(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'email' => 'john@example.com',
+ ]);
+
+ $this->assertInstanceOf(Email::class, $recipient->email());
+ }
+
+ /** @test */
+ public function it_returns_group_type_with_customer_suffix(): void
+ {
+ $billingRecipient = new Recipient('Billing', []);
+ $shippingRecipient = new Recipient('Shipping', []);
+
+ $this->assertSame('BillingCustomer', $billingRecipient->getGroupType('any'));
+ $this->assertSame('ShippingCustomer', $shippingRecipient->getGroupType('any'));
+ }
+}
diff --git a/tests/Unit/PaymentMethods/AfterpayDigiAccept/RecipientAdapterTest.php b/tests/Unit/PaymentMethods/AfterpayDigiAccept/RecipientAdapterTest.php
new file mode 100644
index 00000000..4cd492a7
--- /dev/null
+++ b/tests/Unit/PaymentMethods/AfterpayDigiAccept/RecipientAdapterTest.php
@@ -0,0 +1,95 @@
+ 'John', 'lastName' => 'Doe']);
+ $adapter = new RecipientAdapter('Billing', $person);
+
+ $key = $adapter->serviceParameterKeyOf('firstName');
+
+ $this->assertSame('BillingFirstName', $key);
+ }
+
+ /** @test */
+ public function it_returns_unprefixed_key_for_company_name(): void
+ {
+ $company = new Company(['companyName' => 'ACME Inc.']);
+ $adapter = new RecipientAdapter('Billing', $company);
+
+ $key = $adapter->serviceParameterKeyOf('companyName');
+
+ $this->assertSame('CompanyName', $key);
+ }
+
+ /** @test */
+ public function it_returns_mapped_key_for_chamber_of_commerce(): void
+ {
+ $company = new Company(['chamberOfCommerce' => '12345678']);
+ $adapter = new RecipientAdapter('Billing', $company);
+
+ $key = $adapter->serviceParameterKeyOf('chamberOfCommerce');
+
+ $this->assertSame('CompanyCOCRegistration', $key);
+ }
+
+ /** @test */
+ public function it_returns_unprefixed_key_for_vat_number(): void
+ {
+ $company = new Company(['vatNumber' => 'NL123456789B01']);
+ $adapter = new RecipientAdapter('Billing', $company);
+
+ $key = $adapter->serviceParameterKeyOf('vatNumber');
+
+ $this->assertSame('VatNumber', $key);
+ }
+
+ /** @test */
+ public function it_returns_mapped_and_prefixed_key_for_culture(): void
+ {
+ $person = new Person(['culture' => 'nl-NL']);
+ $adapter = new RecipientAdapter('Shipping', $person);
+
+ $key = $adapter->serviceParameterKeyOf('culture');
+
+ $this->assertSame('ShippingLanguage', $key);
+ }
+
+ /** @test */
+ public function it_works_with_recipient_model_for_person(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'recipient' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $this->assertInstanceOf(RecipientAdapter::class, $recipient->recipient());
+ }
+
+ /** @test */
+ public function it_works_with_recipient_model_for_company(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'recipient' => [
+ 'companyName' => 'ACME Inc.',
+ 'chamberOfCommerce' => '12345678',
+ ],
+ ]);
+
+ $this->assertInstanceOf(RecipientAdapter::class, $recipient->recipient());
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Bancontact/BancontactTest.php b/tests/Unit/PaymentMethods/Bancontact/BancontactTest.php
new file mode 100644
index 00000000..cd336fc1
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Bancontact/BancontactTest.php
@@ -0,0 +1,139 @@
+useMock();
+ }
+
+ private function mockSuccessResponse(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/Transaction/', TestHelpers::successResponse()),
+ ]);
+ }
+
+ /** @test */
+ public function it_can_pay(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('bancontact')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-PAY',
+ 'saveToken' => true,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_pay_encrypted(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('bancontact')->payEncrypted([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-PAY-ENCRYPTED',
+ 'encryptedCardData' => 'encrypted-data-here',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_pay_recurring(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('bancontact')->payRecurring([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-PAY-RECURRING',
+ 'originalTransactionKey' => 'original-tx-key',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_pay_one_click(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('bancontact')->payOneClick([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-PAY-ONE-CLICK',
+ 'originalTransactionKey' => 'original-tx-key',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_authenticate_deprecated(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('bancontact')->authenticate([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-AUTHENTICATE',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_authorize(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('bancontact')->authorize([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-AUTHORIZE',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_capture(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('bancontact')->capture([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-CAPTURE',
+ 'originalTransactionKey' => 'original-tx-key',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_cancel_authorize(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('bancontact')->cancelAuthorize([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-CANCEL-AUTH',
+ 'originalTransactionKey' => 'original-tx-key',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+}
diff --git a/tests/Unit/PaymentMethods/BatchTransactionsTest.php b/tests/Unit/PaymentMethods/BatchTransactionsTest.php
new file mode 100644
index 00000000..a3151836
--- /dev/null
+++ b/tests/Unit/PaymentMethods/BatchTransactionsTest.php
@@ -0,0 +1,106 @@
+useMock();
+ }
+
+ public function test_creates_instance_with_empty_transactions(): void
+ {
+ $batch = new BatchTransactions($this->buckaroo->client(), []);
+
+ $this->assertInstanceOf(BatchTransactions::class, $batch);
+ }
+
+ public function test_creates_instance_with_transactions(): void
+ {
+ $client = $this->buckaroo->client();
+ $payment = $this->createIdealPayment($client, 'TEST-001', 10.00);
+
+ $batch = new BatchTransactions($client, [$payment]);
+
+ $this->assertInstanceOf(BatchTransactions::class, $batch);
+ }
+
+ public function test_stores_client_reference(): void
+ {
+ $client = $this->buckaroo->client();
+ $batch = new BatchTransactions($client, []);
+
+ $storedClient = $this->getProperty($batch, 'client');
+
+ $this->assertSame($client, $storedClient);
+ }
+
+ public function test_creates_batch_request_internally(): void
+ {
+ $batch = new BatchTransactions($this->buckaroo->client(), []);
+
+ $batchRequest = $this->getProperty($batch, 'batch_transactions');
+
+ $this->assertInstanceOf(BatchRequest::class, $batchRequest);
+ }
+
+ public function test_created_via_buckaroo_client(): void
+ {
+ $batch = $this->buckaroo->batch([]);
+
+ $this->assertInstanceOf(BatchTransactions::class, $batch);
+ }
+
+ public function test_batch_request_contains_transactions(): void
+ {
+ $client = $this->buckaroo->client();
+ $payment1 = $this->createIdealPayment($client, 'INV-001', 10.00);
+ $payment2 = $this->createIdealPayment($client, 'INV-002', 20.00);
+
+ $batch = new BatchTransactions($client, [$payment1, $payment2]);
+
+ $batchRequest = $this->getProperty($batch, 'batch_transactions');
+ $transactions = $this->getProperty($batchRequest, 'transactions');
+
+ $this->assertCount(2, $transactions);
+ $this->assertSame($payment1, $transactions[0]);
+ $this->assertSame($payment2, $transactions[1]);
+ }
+
+ private function createIdealPayment($client, string $invoice, float $amount): iDeal
+ {
+ $payment = new iDeal($client, 'ideal');
+ $payment->manually(true);
+ $payment->setPayload([
+ 'amountDebit' => $amount,
+ 'invoice' => $invoice,
+ 'issuer' => 'ABNANL2A',
+ ]);
+ $payment->pay();
+
+ return $payment;
+ }
+
+ private function getProperty(object $object, string $property)
+ {
+ $reflection = new ReflectionClass($object);
+ $prop = $reflection->getProperty($property);
+ $prop->setAccessible(true);
+
+ return $prop->getValue($object);
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Billink/Models/RecipientTest.php b/tests/Unit/PaymentMethods/Billink/Models/RecipientTest.php
new file mode 100644
index 00000000..27b8657d
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Billink/Models/RecipientTest.php
@@ -0,0 +1,118 @@
+ [
+ 'category' => 'B2C',
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $this->assertInstanceOf(RecipientAdapter::class, $recipient->recipient());
+ }
+
+ /** @test */
+ public function it_creates_b2b_company_recipient(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'recipient' => [
+ 'category' => 'B2B',
+ 'companyName' => 'ACME Inc.',
+ 'chamberOfCommerce' => '12345678',
+ ],
+ ]);
+
+ $this->assertInstanceOf(RecipientAdapter::class, $recipient->recipient());
+ }
+
+ /** @test */
+ public function it_throws_exception_for_invalid_recipient_category(): void
+ {
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage('No recipient category found.');
+
+ new Recipient('Billing', [
+ 'recipient' => [
+ 'category' => 'invalid',
+ 'firstName' => 'John',
+ ],
+ ]);
+ }
+
+ /** @test */
+ public function it_throws_exception_when_category_is_missing(): void
+ {
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage('No recipient category found.');
+
+ new Recipient('Billing', [
+ 'recipient' => [
+ 'firstName' => 'John',
+ ],
+ ]);
+ }
+
+ /** @test */
+ public function it_sets_and_returns_address_adapter(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'address' => [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ ]);
+
+ $this->assertInstanceOf(AddressAdapter::class, $recipient->address());
+ }
+
+ /** @test */
+ public function it_sets_and_returns_phone_adapter(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ ]);
+
+ $this->assertInstanceOf(PhoneAdapter::class, $recipient->phone());
+ }
+
+ /** @test */
+ public function it_sets_and_returns_email(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'email' => 'john@example.com',
+ ]);
+
+ $this->assertInstanceOf(Email::class, $recipient->email());
+ }
+
+ /** @test */
+ public function it_returns_group_type_with_customer_suffix(): void
+ {
+ $billingRecipient = new Recipient('Billing', []);
+ $shippingRecipient = new Recipient('Shipping', []);
+
+ $this->assertSame('BillingCustomer', $billingRecipient->getGroupType('any'));
+ $this->assertSame('ShippingCustomer', $shippingRecipient->getGroupType('any'));
+ }
+}
diff --git a/tests/Unit/PaymentMethods/BuckarooVoucher/BuckarooVoucherTest.php b/tests/Unit/PaymentMethods/BuckarooVoucher/BuckarooVoucherTest.php
new file mode 100644
index 00000000..129e611d
--- /dev/null
+++ b/tests/Unit/PaymentMethods/BuckarooVoucher/BuckarooVoucherTest.php
@@ -0,0 +1,112 @@
+useMock();
+ }
+
+ /** @test */
+ public function it_can_pay_with_voucher(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('buckaroovoucher')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-VOUCHER-PAY',
+ 'voucherCode' => 'VOUCHER123',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_pay_remainder_with_voucher(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('buckaroovoucher')->payRemainder([
+ 'amountDebit' => 5.00,
+ 'invoice' => 'TEST-VOUCHER-REMAINDER',
+ 'voucherCode' => 'VOUCHER123',
+ 'originalTransactionKey' => 'ORIGINAL-TX-KEY',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_get_balance(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/DataRequest/', TestHelpers::successResponse([
+ 'ServiceParameters' => [
+ ['Name' => 'Balance', 'Value' => '50.00'],
+ ],
+ ])),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroovoucher')->getBalance([
+ 'voucherCode' => 'VOUCHER123',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_create_voucher(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/DataRequest/', TestHelpers::successResponse([
+ 'ServiceParameters' => [
+ ['Name' => 'VoucherCode', 'Value' => 'NEW-VOUCHER-123'],
+ ],
+ ])),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroovoucher')->create([
+ 'groupGuid' => 'group-123',
+ 'usageType' => '1',
+ 'validFrom' => '2024-01-01',
+ 'validUntil' => '2025-12-31',
+ 'creationBalance' => 100.00,
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_deactivate_voucher(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/DataRequest/', TestHelpers::successResponse()),
+ ]);
+
+ $response = $this->buckaroo->method('buckaroovoucher')->deactivate([
+ 'voucherCode' => 'VOUCHER123',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ private function mockSuccessResponse(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/Transaction/', TestHelpers::successResponse()),
+ ]);
+ }
+}
diff --git a/tests/Unit/PaymentMethods/BuckarooWallet/Models/WalletTest.php b/tests/Unit/PaymentMethods/BuckarooWallet/Models/WalletTest.php
new file mode 100644
index 00000000..7caadeac
--- /dev/null
+++ b/tests/Unit/PaymentMethods/BuckarooWallet/Models/WalletTest.php
@@ -0,0 +1,108 @@
+ [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $this->assertInstanceOf(CustomerAdapter::class, $wallet->customer());
+ }
+
+ /** @test */
+ public function it_returns_existing_customer_when_called_without_data(): void
+ {
+ $wallet = new Wallet([
+ 'customer' => [
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ ],
+ ]);
+
+ $customer = $wallet->customer();
+ $sameCustomer = $wallet->customer();
+
+ $this->assertSame($customer, $sameCustomer);
+ }
+
+ /** @test */
+ public function it_sets_and_returns_email_adapter(): void
+ {
+ $wallet = new Wallet([
+ 'email' => 'test@example.com',
+ ]);
+
+ $this->assertInstanceOf(EmailAdapter::class, $wallet->email());
+ }
+
+ /** @test */
+ public function it_returns_existing_email_when_called_without_data(): void
+ {
+ $wallet = new Wallet([
+ 'email' => 'user@example.com',
+ ]);
+
+ $email = $wallet->email();
+ $sameEmail = $wallet->email();
+
+ $this->assertSame($email, $sameEmail);
+ }
+
+ /** @test */
+ public function it_sets_and_returns_bank_account_adapter(): void
+ {
+ $wallet = new Wallet([
+ 'bankAccount' => [
+ 'iban' => 'NL91ABNA0417164300',
+ 'bic' => 'ABNANL2A',
+ ],
+ ]);
+
+ $this->assertInstanceOf(BankAccountAdapter::class, $wallet->bankAccount());
+ }
+
+ /** @test */
+ public function it_returns_existing_bank_account_when_called_without_data(): void
+ {
+ $wallet = new Wallet([
+ 'bankAccount' => [
+ 'iban' => 'NL91ABNA0417164300',
+ ],
+ ]);
+
+ $bankAccount = $wallet->bankAccount();
+ $sameBankAccount = $wallet->bankAccount();
+
+ $this->assertSame($bankAccount, $sameBankAccount);
+ }
+
+ /** @test */
+ public function it_sets_wallet_properties_from_constructor(): void
+ {
+ $wallet = new Wallet([
+ 'walletId' => 'WALLET-123',
+ 'status' => 'active',
+ 'walletMutationGuid' => 'MUTATION-GUID-123',
+ ]);
+
+ $this->assertSame('WALLET-123', $wallet->walletId);
+ $this->assertSame('active', $wallet->status);
+ $this->assertSame('MUTATION-GUID-123', $wallet->walletMutationGuid);
+ }
+}
diff --git a/tests/Unit/PaymentMethods/CreditCard/CreditCardTest.php b/tests/Unit/PaymentMethods/CreditCard/CreditCardTest.php
new file mode 100644
index 00000000..3470c83c
--- /dev/null
+++ b/tests/Unit/PaymentMethods/CreditCard/CreditCardTest.php
@@ -0,0 +1,239 @@
+useMock();
+ }
+
+ private function mockSuccessResponse(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json('POST', '*/Transaction/', TestHelpers::successResponse()),
+ ]);
+ }
+
+ /** @test */
+ public function it_can_pay_encrypted(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('creditcard')->payEncrypted([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-PAY-ENCRYPTED',
+ 'name' => 'visa',
+ 'encryptedCardData' => 'encrypted-data-here',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_authorize_with_token(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('creditcard')->authorizeWithToken([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-AUTH-TOKEN',
+ 'name' => 'visa',
+ 'token' => 'session-token-here',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_pay_with_token(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('creditcard')->payWithToken([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-PAY-TOKEN',
+ 'name' => 'visa',
+ 'token' => 'session-token-here',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_pay_remainder_with_token(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('creditcard')->payRemainderWithToken([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-PAY-REMAINDER-TOKEN',
+ 'name' => 'visa',
+ 'token' => 'session-token-here',
+ 'originalTransactionKey' => 'original-tx-key',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_authorize_encrypted(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('creditcard')->authorizeEncrypted([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-AUTH-ENCRYPTED',
+ 'name' => 'visa',
+ 'encryptedCardData' => 'encrypted-data-here',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_pay_with_security_code(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('creditcard')->payWithSecurityCode([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-PAY-SECURITY-CODE',
+ 'name' => 'visa',
+ 'originalTransactionKey' => 'original-tx-key',
+ 'securityCode' => '123',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_authorize_with_security_code(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('creditcard')->authorizeWithSecurityCode([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-AUTH-SECURITY-CODE',
+ 'name' => 'visa',
+ 'originalTransactionKey' => 'original-tx-key',
+ 'securityCode' => '123',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_authorize(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('creditcard')->authorize([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-AUTHORIZE',
+ 'name' => 'visa',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_capture(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('creditcard')->capture([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-CAPTURE',
+ 'name' => 'visa',
+ 'originalTransactionKey' => 'original-tx-key',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_pay_recurrent(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('creditcard')->payRecurrent([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-PAY-RECURRENT',
+ 'name' => 'visa',
+ 'originalTransactionKey' => 'original-tx-key',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_pay_remainder_encrypted(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('creditcard')->payRemainderEncrypted([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-PAY-REMAINDER-ENCRYPTED',
+ 'name' => 'visa',
+ 'encryptedCardData' => 'encrypted-data-here',
+ 'originalTransactionKey' => 'original-tx-key',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_can_cancel_authorize(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('creditcard')->cancelAuthorize([
+ 'amountCredit' => 10.00,
+ 'invoice' => 'TEST-CANCEL-AUTH',
+ 'name' => 'visa',
+ 'originalTransactionKey' => 'original-tx-key',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_returns_payment_name_from_payload(): void
+ {
+ $this->mockSuccessResponse();
+
+ $response = $this->buckaroo->method('creditcard')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-PAYMENT-NAME',
+ 'name' => 'mastercard',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ /** @test */
+ public function it_throws_exception_when_payment_name_missing(): void
+ {
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage('Missing creditcard name');
+
+ // No mock needed - exception thrown before request is made
+ $this->buckaroo->method('creditcard')->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-NO-NAME',
+ ]);
+ }
+}
diff --git a/tests/Unit/PaymentMethods/In3/Models/RecipientTest.php b/tests/Unit/PaymentMethods/In3/Models/RecipientTest.php
new file mode 100644
index 00000000..68885462
--- /dev/null
+++ b/tests/Unit/PaymentMethods/In3/Models/RecipientTest.php
@@ -0,0 +1,113 @@
+ [
+ 'category' => 'B2C',
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $this->assertInstanceOf(RecipientAdapter::class, $recipient->recipient());
+ }
+
+ /** @test */
+ public function it_creates_b2b_company_recipient(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'recipient' => [
+ 'category' => 'B2B',
+ 'companyName' => 'ACME Inc.',
+ ],
+ ]);
+
+ $this->assertInstanceOf(RecipientAdapter::class, $recipient->recipient());
+ }
+
+ /** @test */
+ public function it_throws_exception_for_invalid_recipient_category(): void
+ {
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage('No recipient category found.');
+
+ new Recipient('Billing', [
+ 'recipient' => [
+ 'category' => 'invalid',
+ ],
+ ]);
+ }
+
+ /** @test */
+ public function it_throws_exception_when_category_is_missing(): void
+ {
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage('No recipient category found.');
+
+ new Recipient('Billing', [
+ 'recipient' => [
+ 'firstName' => 'John',
+ ],
+ ]);
+ }
+
+ /** @test */
+ public function it_sets_and_returns_address_adapter(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'address' => [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ ],
+ ]);
+
+ $this->assertInstanceOf(AddressAdapter::class, $recipient->address());
+ }
+
+ /** @test */
+ public function it_sets_and_returns_phone_adapter(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ ]);
+
+ $this->assertInstanceOf(PhoneAdapter::class, $recipient->phone());
+ }
+
+ /** @test */
+ public function it_sets_and_returns_email(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'email' => 'john@example.com',
+ ]);
+
+ $this->assertInstanceOf(Email::class, $recipient->email());
+ }
+
+ /** @test */
+ public function it_returns_group_type_with_customer_suffix(): void
+ {
+ $billingRecipient = new Recipient('Billing', []);
+ $shippingRecipient = new Recipient('Shipping', []);
+
+ $this->assertSame('BillingCustomer', $billingRecipient->getGroupType('any'));
+ $this->assertSame('ShippingCustomer', $shippingRecipient->getGroupType('any'));
+ }
+}
diff --git a/tests/Unit/PaymentMethods/KlarnaKP/Models/PayloadTest.php b/tests/Unit/PaymentMethods/KlarnaKP/Models/PayloadTest.php
new file mode 100644
index 00000000..20bb05db
--- /dev/null
+++ b/tests/Unit/PaymentMethods/KlarnaKP/Models/PayloadTest.php
@@ -0,0 +1,112 @@
+ [
+ 'recipient' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ],
+ ]);
+
+ $this->assertInstanceOf(Recipient::class, $payload->billing());
+ }
+
+ /** @test */
+ public function it_sets_shipping_same_as_billing_by_default(): void
+ {
+ $payload = new Payload([
+ 'billing' => [
+ 'recipient' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ],
+ ]);
+
+ // When only billing is set, shipping should be same as billing
+ $this->assertInstanceOf(Recipient::class, $payload->billing());
+ }
+
+ /** @test */
+ public function it_sets_and_returns_separate_shipping_recipient(): void
+ {
+ $payload = new Payload([
+ 'billing' => [
+ 'recipient' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ],
+ 'shipping' => [
+ 'recipient' => [
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ ],
+ ],
+ ]);
+
+ $this->assertInstanceOf(Recipient::class, $payload->shipping());
+ }
+
+ /** @test */
+ public function it_sets_and_returns_articles(): void
+ {
+ $payload = new Payload([
+ 'articles' => [
+ [
+ 'identifier' => 'ART001',
+ 'description' => 'Product 1',
+ 'quantity' => 1,
+ 'unitPrice' => 10.00,
+ 'vatPercentage' => 21,
+ ],
+ [
+ 'identifier' => 'ART002',
+ 'description' => 'Product 2',
+ 'quantity' => 2,
+ 'unitPrice' => 20.00,
+ 'vatPercentage' => 21,
+ ],
+ ],
+ ]);
+
+ $articles = $payload->articles();
+
+ $this->assertIsArray($articles);
+ $this->assertCount(2, $articles);
+ foreach ($articles as $article) {
+ $this->assertInstanceOf(ArticleAdapter::class, $article);
+ }
+ }
+
+ /** @test */
+ public function it_can_be_created_with_all_properties(): void
+ {
+ $payload = new Payload([
+ 'gender' => 1,
+ 'operatingCountry' => 'NL',
+ 'pno' => '12345678',
+ 'reservationNumber' => 'RES-001',
+ ]);
+
+ $this->assertSame(1, $payload->gender);
+ $this->assertSame('NL', $payload->operatingCountry);
+ $this->assertSame('12345678', $payload->pno);
+ $this->assertSame('RES-001', $payload->reservationNumber);
+ }
+}
diff --git a/tests/Unit/PaymentMethods/KlarnaPay/Models/RecipientTest.php b/tests/Unit/PaymentMethods/KlarnaPay/Models/RecipientTest.php
new file mode 100644
index 00000000..d5c73065
--- /dev/null
+++ b/tests/Unit/PaymentMethods/KlarnaPay/Models/RecipientTest.php
@@ -0,0 +1,73 @@
+ [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $this->assertInstanceOf(Person::class, $recipient->recipient());
+ }
+
+ /** @test */
+ public function it_sets_and_returns_address_adapter(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'address' => [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ ],
+ ]);
+
+ $this->assertInstanceOf(AddressAdapter::class, $recipient->address());
+ }
+
+ /** @test */
+ public function it_sets_and_returns_phone_adapter(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ ]);
+
+ $this->assertInstanceOf(PhoneAdapter::class, $recipient->phone());
+ }
+
+ /** @test */
+ public function it_sets_and_returns_email(): void
+ {
+ $recipient = new Recipient('Billing', [
+ 'email' => 'john@example.com',
+ ]);
+
+ $this->assertInstanceOf(Email::class, $recipient->email());
+ }
+
+ /** @test */
+ public function it_returns_group_type_with_customer_suffix(): void
+ {
+ $billingRecipient = new Recipient('Billing', []);
+ $shippingRecipient = new Recipient('Shipping', []);
+
+ $this->assertSame('BillingCustomer', $billingRecipient->getGroupType('any'));
+ $this->assertSame('ShippingCustomer', $shippingRecipient->getGroupType('any'));
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/AfterpayDigiAcceptRefundTest.php b/tests/Unit/PaymentMethods/Models/AfterpayDigiAcceptRefundTest.php
new file mode 100644
index 00000000..e6f51595
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/AfterpayDigiAcceptRefundTest.php
@@ -0,0 +1,64 @@
+ [
+ [
+ 'identifier' => 'ART001',
+ 'description' => 'Test Article',
+ 'quantity' => 1,
+ 'price' => 100.00,
+ ],
+ [
+ 'identifier' => 'ART002',
+ 'description' => 'Another Article',
+ 'quantity' => 2,
+ 'price' => 50.00,
+ ],
+ ],
+ ]);
+
+ $articles = $refund->articles();
+
+ $this->assertIsArray($articles);
+ $this->assertCount(2, $articles);
+ $this->assertInstanceOf(ArticleAdapter::class, $articles[0]);
+ $this->assertInstanceOf(ArticleAdapter::class, $articles[1]);
+ }
+
+ /** @test */
+ public function it_returns_empty_articles_array_without_parameter(): void
+ {
+ $refund = new Refund([]);
+
+ $articles = $refund->articles();
+
+ $this->assertIsArray($articles);
+ $this->assertEmpty($articles);
+ }
+
+ /** @test */
+ public function it_sets_all_properties(): void
+ {
+ $refund = new Refund([
+ 'shippingCosts' => 5.95,
+ 'articles' => [
+ ['identifier' => 'ART001', 'description' => 'Article 1', 'quantity' => 1, 'price' => 100.00],
+ ],
+ ]);
+
+ $this->assertCount(1, $refund->articles());
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/AfterpayRefundTest.php b/tests/Unit/PaymentMethods/Models/AfterpayRefundTest.php
new file mode 100644
index 00000000..2ca42da3
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/AfterpayRefundTest.php
@@ -0,0 +1,65 @@
+ [
+ [
+ 'identifier' => 'ART001',
+ 'description' => 'Test Article',
+ 'quantity' => 1,
+ 'price' => 100.00,
+ ],
+ [
+ 'identifier' => 'ART002',
+ 'description' => 'Another Article',
+ 'quantity' => 2,
+ 'price' => 50.00,
+ ],
+ ],
+ ]);
+
+ $articles = $refund->articles();
+
+ $this->assertIsArray($articles);
+ $this->assertCount(2, $articles);
+ $this->assertInstanceOf(ArticleAdapter::class, $articles[0]);
+ $this->assertInstanceOf(ArticleAdapter::class, $articles[1]);
+ }
+
+ /** @test */
+ public function it_returns_empty_articles_array_without_parameter(): void
+ {
+ $refund = new Refund([]);
+
+ $articles = $refund->articles();
+
+ $this->assertIsArray($articles);
+ $this->assertEmpty($articles);
+ }
+
+ /** @test */
+ public function it_sets_all_properties(): void
+ {
+ $refund = new Refund([
+ 'merchantImageUrl' => 'https://example.com/image.png',
+ 'summaryImageUrl' => 'https://example.com/summary.png',
+ 'articles' => [
+ ['identifier' => 'ART001', 'description' => 'Article 1', 'quantity' => 1, 'price' => 100.00],
+ ],
+ ]);
+
+ $this->assertCount(1, $refund->articles());
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/BankTransferPayTest.php b/tests/Unit/PaymentMethods/Models/BankTransferPayTest.php
new file mode 100644
index 00000000..6b9474b7
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/BankTransferPayTest.php
@@ -0,0 +1,91 @@
+ [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $customer = $pay->customer();
+
+ $this->assertInstanceOf(CustomerAdapter::class, $customer);
+ }
+
+ /** @test */
+ public function it_returns_customer_without_parameter(): void
+ {
+ $pay = new Pay([
+ 'customer' => [
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ ],
+ ]);
+
+ $customer = $pay->customer();
+ $this->assertInstanceOf(CustomerAdapter::class, $customer);
+
+ // Call again without parameter should return same instance
+ $sameCustomer = $pay->customer(null);
+ $this->assertSame($customer, $sameCustomer);
+ }
+
+ /** @test */
+ public function it_sets_email_from_string(): void
+ {
+ $pay = new Pay([
+ 'email' => 'test@example.com',
+ ]);
+
+ $email = $pay->email();
+
+ $this->assertInstanceOf(EmailAdapter::class, $email);
+ }
+
+ /** @test */
+ public function it_returns_email_without_parameter(): void
+ {
+ $pay = new Pay([
+ 'email' => 'john@doe.com',
+ ]);
+
+ $email = $pay->email();
+ $this->assertInstanceOf(EmailAdapter::class, $email);
+
+ // Call again without parameter
+ $sameEmail = $pay->email(null);
+ $this->assertSame($email, $sameEmail);
+ }
+
+ /** @test */
+ public function it_sets_all_properties_together(): void
+ {
+ $pay = new Pay([
+ 'sendMail' => true,
+ 'dateDue' => '2024-02-15',
+ 'country' => 'NL',
+ 'customer' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ 'email' => 'test@example.com',
+ ]);
+
+ $this->assertInstanceOf(CustomerAdapter::class, $pay->customer());
+ $this->assertInstanceOf(EmailAdapter::class, $pay->email());
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/CreditManagementDebtorInfoTest.php b/tests/Unit/PaymentMethods/Models/CreditManagementDebtorInfoTest.php
new file mode 100644
index 00000000..4ff87cba
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/CreditManagementDebtorInfoTest.php
@@ -0,0 +1,42 @@
+ [
+ 'code' => 'DEBTOR001',
+ ],
+ ]);
+
+ $debtor = $debtorInfo->debtor();
+
+ $this->assertInstanceOf(DebtorInfoAdapter::class, $debtor);
+ }
+
+ /** @test */
+ public function it_returns_debtor_without_parameter(): void
+ {
+ $debtorInfo = new DebtorInfo([
+ 'debtor' => [
+ 'code' => 'DEBTOR002',
+ ],
+ ]);
+
+ $debtor = $debtorInfo->debtor();
+ $this->assertInstanceOf(DebtorInfoAdapter::class, $debtor);
+
+ $sameDebtor = $debtorInfo->debtor(null);
+ $this->assertSame($debtor, $sameDebtor);
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/CreditManagementInvoiceTest.php b/tests/Unit/PaymentMethods/Models/CreditManagementInvoiceTest.php
new file mode 100644
index 00000000..1f89eaa1
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/CreditManagementInvoiceTest.php
@@ -0,0 +1,280 @@
+ [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ ]);
+
+ $address = $invoice->address();
+
+ $this->assertInstanceOf(Address::class, $address);
+ }
+
+ /** @test */
+ public function it_returns_address_without_parameter(): void
+ {
+ $invoice = new Invoice([
+ 'address' => [
+ 'street' => 'Test Street',
+ ],
+ ]);
+
+ $address = $invoice->address();
+ $this->assertInstanceOf(Address::class, $address);
+
+ $sameAddress = $invoice->address(null);
+ $this->assertSame($address, $sameAddress);
+ }
+
+ /** @test */
+ public function it_sets_company_from_array(): void
+ {
+ $invoice = new Invoice([
+ 'company' => [
+ 'name' => 'Test Company B.V.',
+ 'chamberOfCommerce' => '12345678',
+ ],
+ ]);
+
+ $company = $invoice->company();
+
+ $this->assertInstanceOf(Company::class, $company);
+ }
+
+ /** @test */
+ public function it_returns_company_without_parameter(): void
+ {
+ $invoice = new Invoice([
+ 'company' => [
+ 'name' => 'Acme Corp',
+ ],
+ ]);
+
+ $company = $invoice->company();
+ $this->assertInstanceOf(Company::class, $company);
+
+ $sameCompany = $invoice->company(null);
+ $this->assertSame($company, $sameCompany);
+ }
+
+ /** @test */
+ public function it_sets_person_from_array(): void
+ {
+ $invoice = new Invoice([
+ 'person' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $person = $invoice->person();
+
+ $this->assertInstanceOf(Person::class, $person);
+ }
+
+ /** @test */
+ public function it_returns_person_without_parameter(): void
+ {
+ $invoice = new Invoice([
+ 'person' => [
+ 'firstName' => 'Jane',
+ ],
+ ]);
+
+ $person = $invoice->person();
+ $this->assertInstanceOf(Person::class, $person);
+
+ $samePerson = $invoice->person(null);
+ $this->assertSame($person, $samePerson);
+ }
+
+ /** @test */
+ public function it_sets_debtor_from_array(): void
+ {
+ $invoice = new Invoice([
+ 'debtor' => [
+ 'code' => 'DEBTOR001',
+ ],
+ ]);
+
+ $debtor = $invoice->debtor();
+
+ $this->assertInstanceOf(Debtor::class, $debtor);
+ }
+
+ /** @test */
+ public function it_returns_debtor_without_parameter(): void
+ {
+ $invoice = new Invoice([
+ 'debtor' => [
+ 'code' => 'DEBTOR002',
+ ],
+ ]);
+
+ $debtor = $invoice->debtor();
+ $this->assertInstanceOf(Debtor::class, $debtor);
+
+ $sameDebtor = $invoice->debtor(null);
+ $this->assertSame($debtor, $sameDebtor);
+ }
+
+ /** @test */
+ public function it_sets_email_from_string(): void
+ {
+ $invoice = new Invoice([
+ 'email' => 'test@example.com',
+ ]);
+
+ $email = $invoice->email();
+
+ $this->assertInstanceOf(Email::class, $email);
+ }
+
+ /** @test */
+ public function it_returns_email_without_parameter(): void
+ {
+ $invoice = new Invoice([
+ 'email' => 'john@doe.com',
+ ]);
+
+ $email = $invoice->email();
+ $this->assertInstanceOf(Email::class, $email);
+
+ $sameEmail = $invoice->email(null);
+ $this->assertSame($email, $sameEmail);
+ }
+
+ /** @test */
+ public function it_sets_phone_from_array(): void
+ {
+ $invoice = new Invoice([
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ ]);
+
+ $phone = $invoice->phone();
+
+ $this->assertInstanceOf(Phone::class, $phone);
+ }
+
+ /** @test */
+ public function it_returns_phone_without_parameter(): void
+ {
+ $invoice = new Invoice([
+ 'phone' => [
+ 'mobile' => '0698765432',
+ ],
+ ]);
+
+ $phone = $invoice->phone();
+ $this->assertInstanceOf(Phone::class, $phone);
+
+ $samePhone = $invoice->phone(null);
+ $this->assertSame($phone, $samePhone);
+ }
+
+ /** @test */
+ public function it_sets_articles_from_array(): void
+ {
+ $invoice = new Invoice([
+ 'articles' => [
+ [
+ 'identifier' => 'ART001',
+ 'description' => 'Test Article',
+ 'quantity' => 1,
+ 'price' => 100.00,
+ ],
+ [
+ 'identifier' => 'ART002',
+ 'description' => 'Another Article',
+ 'quantity' => 2,
+ 'price' => 50.00,
+ ],
+ ],
+ ]);
+
+ $articles = $invoice->articles();
+
+ $this->assertIsArray($articles);
+ $this->assertCount(2, $articles);
+ $this->assertInstanceOf(ArticleAdapter::class, $articles[0]);
+ $this->assertInstanceOf(ArticleAdapter::class, $articles[1]);
+ }
+
+ /** @test */
+ public function it_returns_empty_articles_array_without_parameter(): void
+ {
+ $invoice = new Invoice([]);
+
+ $articles = $invoice->articles();
+
+ $this->assertIsArray($articles);
+ $this->assertEmpty($articles);
+ }
+
+ /** @test */
+ public function it_sets_all_properties_together(): void
+ {
+ $invoice = new Invoice([
+ 'invoiceNumber' => 'INV-001',
+ 'invoiceAmount' => 1000.00,
+ 'invoiceAmountVAT' => 210.00,
+ 'invoiceDate' => '2024-01-15',
+ 'dueDate' => '2024-02-15',
+ 'address' => [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ ],
+ 'company' => [
+ 'name' => 'Test Company',
+ ],
+ 'person' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ 'debtor' => [
+ 'code' => 'DEBTOR001',
+ ],
+ 'email' => 'test@example.com',
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ 'articles' => [
+ ['identifier' => 'ART001', 'description' => 'Article 1', 'quantity' => 1, 'price' => 100.00],
+ ],
+ ]);
+
+ $this->assertInstanceOf(Address::class, $invoice->address());
+ $this->assertInstanceOf(Company::class, $invoice->company());
+ $this->assertInstanceOf(Person::class, $invoice->person());
+ $this->assertInstanceOf(Debtor::class, $invoice->debtor());
+ $this->assertInstanceOf(Email::class, $invoice->email());
+ $this->assertInstanceOf(Phone::class, $invoice->phone());
+ $this->assertCount(1, $invoice->articles());
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/CreditManagementMultipleInvoiceInfoTest.php b/tests/Unit/PaymentMethods/Models/CreditManagementMultipleInvoiceInfoTest.php
new file mode 100644
index 00000000..d83f3887
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/CreditManagementMultipleInvoiceInfoTest.php
@@ -0,0 +1,47 @@
+ [
+ [
+ 'invoiceNumber' => 'INV-001',
+ 'invoiceAmount' => 100.00,
+ ],
+ [
+ 'invoiceNumber' => 'INV-002',
+ 'invoiceAmount' => 200.00,
+ ],
+ ],
+ ]);
+
+ $invoices = $multipleInvoiceInfo->invoices();
+
+ $this->assertIsArray($invoices);
+ $this->assertCount(2, $invoices);
+ $this->assertInstanceOf(Invoice::class, $invoices[0]);
+ $this->assertInstanceOf(Invoice::class, $invoices[1]);
+ }
+
+ /** @test */
+ public function it_returns_empty_invoices_array_without_parameter(): void
+ {
+ $multipleInvoiceInfo = new MultipleInvoiceInfo([]);
+
+ $invoices = $multipleInvoiceInfo->invoices();
+
+ $this->assertIsArray($invoices);
+ $this->assertEmpty($invoices);
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/In3OldPayTest.php b/tests/Unit/PaymentMethods/Models/In3OldPayTest.php
new file mode 100644
index 00000000..eb3f4fe5
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/In3OldPayTest.php
@@ -0,0 +1,292 @@
+ [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $customer = $pay->customer();
+
+ $this->assertInstanceOf(Person::class, $customer);
+ }
+
+ /** @test */
+ public function it_returns_customer_without_parameter(): void
+ {
+ $pay = new Pay([
+ 'customer' => [
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ ],
+ ]);
+
+ $customer = $pay->customer();
+ $this->assertInstanceOf(Person::class, $customer);
+
+ // Call again without parameter should return same instance
+ $sameCustomer = $pay->customer(null);
+ $this->assertSame($customer, $sameCustomer);
+ }
+
+ /** @test */
+ public function it_sets_company_from_array(): void
+ {
+ $pay = new Pay([
+ 'company' => [
+ 'name' => 'Test Company B.V.',
+ 'chamberOfCommerce' => '12345678',
+ ],
+ ]);
+
+ $company = $pay->company();
+
+ $this->assertInstanceOf(CompanyAdapter::class, $company);
+ }
+
+ /** @test */
+ public function it_returns_company_without_parameter(): void
+ {
+ $pay = new Pay([
+ 'company' => [
+ 'name' => 'Acme Corp',
+ ],
+ ]);
+
+ $company = $pay->company();
+ $this->assertInstanceOf(CompanyAdapter::class, $company);
+
+ // Call again without parameter
+ $sameCompany = $pay->company(null);
+ $this->assertSame($company, $sameCompany);
+ }
+
+ /** @test */
+ public function it_sets_address_from_array(): void
+ {
+ $pay = new Pay([
+ 'address' => [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ ]);
+
+ $address = $pay->address();
+
+ $this->assertInstanceOf(AddressAdapter::class, $address);
+ }
+
+ /** @test */
+ public function it_returns_address_without_parameter(): void
+ {
+ $pay = new Pay([
+ 'address' => [
+ 'street' => 'Test Street',
+ 'houseNumber' => '1',
+ ],
+ ]);
+
+ $address = $pay->address();
+ $this->assertInstanceOf(AddressAdapter::class, $address);
+
+ // Call again without parameter
+ $sameAddress = $pay->address(null);
+ $this->assertSame($address, $sameAddress);
+ }
+
+ /** @test */
+ public function it_sets_email_from_string(): void
+ {
+ $pay = new Pay([
+ 'email' => 'test@example.com',
+ ]);
+
+ $email = $pay->email();
+
+ $this->assertInstanceOf(Email::class, $email);
+ }
+
+ /** @test */
+ public function it_returns_email_without_parameter(): void
+ {
+ $pay = new Pay([
+ 'email' => 'john@doe.com',
+ ]);
+
+ $email = $pay->email();
+ $this->assertInstanceOf(Email::class, $email);
+
+ // Call again without parameter
+ $sameEmail = $pay->email(null);
+ $this->assertSame($email, $sameEmail);
+ }
+
+ /** @test */
+ public function it_sets_phone_from_array(): void
+ {
+ $pay = new Pay([
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ ]);
+
+ $phone = $pay->phone();
+
+ $this->assertInstanceOf(PhoneAdapter::class, $phone);
+ }
+
+ /** @test */
+ public function it_returns_phone_without_parameter(): void
+ {
+ $pay = new Pay([
+ 'phone' => [
+ 'mobile' => '0698765432',
+ ],
+ ]);
+
+ $phone = $pay->phone();
+ $this->assertInstanceOf(PhoneAdapter::class, $phone);
+
+ // Call again without parameter
+ $samePhone = $pay->phone(null);
+ $this->assertSame($phone, $samePhone);
+ }
+
+ /** @test */
+ public function it_sets_articles_from_array(): void
+ {
+ $pay = new Pay([
+ 'articles' => [
+ [
+ 'identifier' => 'ART001',
+ 'description' => 'Test Article',
+ 'quantity' => 1,
+ 'price' => 100.00,
+ ],
+ [
+ 'identifier' => 'ART002',
+ 'description' => 'Another Article',
+ 'quantity' => 2,
+ 'price' => 50.00,
+ ],
+ ],
+ ]);
+
+ $articles = $pay->articles();
+
+ $this->assertIsArray($articles);
+ $this->assertCount(2, $articles);
+ $this->assertInstanceOf(ArticleAdapter::class, $articles[0]);
+ $this->assertInstanceOf(ArticleAdapter::class, $articles[1]);
+ }
+
+ /** @test */
+ public function it_returns_empty_articles_array_without_parameter(): void
+ {
+ $pay = new Pay([]);
+
+ $articles = $pay->articles();
+
+ $this->assertIsArray($articles);
+ $this->assertEmpty($articles);
+ }
+
+ /** @test */
+ public function it_sets_subtotals_from_array(): void
+ {
+ $pay = new Pay([
+ 'subtotals' => [
+ [
+ 'name' => 'Subtotal 1',
+ 'value' => 100.00,
+ ],
+ [
+ 'name' => 'Tax',
+ 'value' => 21.00,
+ ],
+ ],
+ ]);
+
+ $subtotals = $pay->subtotals();
+
+ $this->assertIsArray($subtotals);
+ $this->assertCount(2, $subtotals);
+ $this->assertInstanceOf(Subtotal::class, $subtotals[0]);
+ $this->assertInstanceOf(Subtotal::class, $subtotals[1]);
+ }
+
+ /** @test */
+ public function it_returns_empty_subtotals_array_without_parameter(): void
+ {
+ $pay = new Pay([]);
+
+ $subtotals = $pay->subtotals();
+
+ $this->assertIsArray($subtotals);
+ $this->assertEmpty($subtotals);
+ }
+
+ /** @test */
+ public function it_sets_all_properties_together(): void
+ {
+ $pay = new Pay([
+ 'customerType' => 'B2C',
+ 'invoiceDate' => '2024-01-15',
+ 'customer' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ 'company' => [
+ 'name' => 'Test Company',
+ ],
+ 'address' => [
+ 'street' => 'Test Street',
+ 'houseNumber' => '1',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'email' => 'test@example.com',
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ 'articles' => [
+ ['identifier' => 'ART001', 'description' => 'Article 1', 'quantity' => 1, 'price' => 100.00],
+ ],
+ 'subtotals' => [
+ ['name' => 'Total', 'value' => 100.00],
+ ],
+ ]);
+
+ $this->assertInstanceOf(Person::class, $pay->customer());
+ $this->assertInstanceOf(CompanyAdapter::class, $pay->company());
+ $this->assertInstanceOf(AddressAdapter::class, $pay->address());
+ $this->assertInstanceOf(Email::class, $pay->email());
+ $this->assertInstanceOf(PhoneAdapter::class, $pay->phone());
+ $this->assertCount(1, $pay->articles());
+ $this->assertCount(1, $pay->subtotals());
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/In3RefundTest.php b/tests/Unit/PaymentMethods/Models/In3RefundTest.php
new file mode 100644
index 00000000..b7ad2007
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/In3RefundTest.php
@@ -0,0 +1,65 @@
+ [
+ [
+ 'identifier' => 'ART001',
+ 'description' => 'Test Article',
+ 'quantity' => 1,
+ 'price' => 100.00,
+ ],
+ [
+ 'identifier' => 'ART002',
+ 'description' => 'Another Article',
+ 'quantity' => 2,
+ 'price' => 50.00,
+ ],
+ ],
+ ]);
+
+ $articles = $refund->articles();
+
+ $this->assertIsArray($articles);
+ $this->assertCount(2, $articles);
+ $this->assertInstanceOf(ArticleAdapter::class, $articles[0]);
+ $this->assertInstanceOf(ArticleAdapter::class, $articles[1]);
+ }
+
+ /** @test */
+ public function it_returns_empty_articles_array_without_parameter(): void
+ {
+ $refund = new Refund([]);
+
+ $articles = $refund->articles();
+
+ $this->assertIsArray($articles);
+ $this->assertEmpty($articles);
+ }
+
+ /** @test */
+ public function it_sets_all_properties(): void
+ {
+ $refund = new Refund([
+ 'merchantImageUrl' => 'https://example.com/image.png',
+ 'summaryImageUrl' => 'https://example.com/summary.png',
+ 'articles' => [
+ ['identifier' => 'ART001', 'description' => 'Article 1', 'quantity' => 1, 'price' => 100.00],
+ ],
+ ]);
+
+ $this->assertCount(1, $refund->articles());
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/PayPerEmailPaymentInvitationTest.php b/tests/Unit/PaymentMethods/Models/PayPerEmailPaymentInvitationTest.php
new file mode 100644
index 00000000..7e8a0280
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/PayPerEmailPaymentInvitationTest.php
@@ -0,0 +1,129 @@
+ [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $customer = $invitation->customer();
+
+ $this->assertInstanceOf(CustomerAdapter::class, $customer);
+ }
+
+ /** @test */
+ public function it_returns_customer_without_parameter(): void
+ {
+ $invitation = new PaymentInvitation([
+ 'customer' => [
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ ],
+ ]);
+
+ $customer = $invitation->customer();
+ $this->assertInstanceOf(CustomerAdapter::class, $customer);
+
+ $sameCustomer = $invitation->customer(null);
+ $this->assertSame($customer, $sameCustomer);
+ }
+
+ /** @test */
+ public function it_sets_email_from_string(): void
+ {
+ $invitation = new PaymentInvitation([
+ 'email' => 'test@example.com',
+ ]);
+
+ $email = $invitation->email();
+
+ $this->assertInstanceOf(EmailAdapter::class, $email);
+ }
+
+ /** @test */
+ public function it_returns_email_without_parameter(): void
+ {
+ $invitation = new PaymentInvitation([
+ 'email' => 'john@doe.com',
+ ]);
+
+ $email = $invitation->email();
+ $this->assertInstanceOf(EmailAdapter::class, $email);
+
+ $sameEmail = $invitation->email(null);
+ $this->assertSame($email, $sameEmail);
+ }
+
+ /** @test */
+ public function it_sets_attachments_from_array(): void
+ {
+ $invitation = new PaymentInvitation([
+ 'attachments' => [
+ [
+ 'name' => 'invoice.pdf',
+ 'content' => base64_encode('PDF content'),
+ ],
+ [
+ 'name' => 'terms.pdf',
+ 'content' => base64_encode('Terms content'),
+ ],
+ ],
+ ]);
+
+ $attachments = $invitation->attachments();
+
+ $this->assertIsArray($attachments);
+ $this->assertCount(2, $attachments);
+ $this->assertInstanceOf(AttachmentAdapter::class, $attachments[0]);
+ $this->assertInstanceOf(AttachmentAdapter::class, $attachments[1]);
+ }
+
+ /** @test */
+ public function it_returns_empty_attachments_array_without_parameter(): void
+ {
+ $invitation = new PaymentInvitation([]);
+
+ $attachments = $invitation->attachments();
+
+ $this->assertIsArray($attachments);
+ $this->assertEmpty($attachments);
+ }
+
+ /** @test */
+ public function it_sets_all_properties_together(): void
+ {
+ $invitation = new PaymentInvitation([
+ 'merchantSendsEmail' => true,
+ 'expirationDate' => '2024-02-15',
+ 'paymentMethodsAllowed' => 'ideal,creditcard',
+ 'customer' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ 'email' => 'test@example.com',
+ 'attachments' => [
+ ['name' => 'invoice.pdf', 'content' => base64_encode('content')],
+ ],
+ ]);
+
+ $this->assertInstanceOf(CustomerAdapter::class, $invitation->customer());
+ $this->assertInstanceOf(EmailAdapter::class, $invitation->email());
+ $this->assertCount(1, $invitation->attachments());
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/PaypalExtraInfoTest.php b/tests/Unit/PaymentMethods/Models/PaypalExtraInfoTest.php
new file mode 100644
index 00000000..a7bd31a7
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/PaypalExtraInfoTest.php
@@ -0,0 +1,137 @@
+ [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ ]);
+
+ $address = $extraInfo->address();
+
+ $this->assertInstanceOf(AddressAdapter::class, $address);
+ }
+
+ /** @test */
+ public function it_returns_address_without_parameter(): void
+ {
+ $extraInfo = new ExtraInfo([
+ 'address' => [
+ 'street' => 'Test Street',
+ ],
+ ]);
+
+ $address = $extraInfo->address();
+ $this->assertInstanceOf(AddressAdapter::class, $address);
+
+ $sameAddress = $extraInfo->address(null);
+ $this->assertSame($address, $sameAddress);
+ }
+
+ /** @test */
+ public function it_sets_customer_from_array(): void
+ {
+ $extraInfo = new ExtraInfo([
+ 'customer' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $customer = $extraInfo->customer();
+
+ $this->assertInstanceOf(Person::class, $customer);
+ }
+
+ /** @test */
+ public function it_returns_customer_without_parameter(): void
+ {
+ $extraInfo = new ExtraInfo([
+ 'customer' => [
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ ],
+ ]);
+
+ $customer = $extraInfo->customer();
+ $this->assertInstanceOf(Person::class, $customer);
+
+ $sameCustomer = $extraInfo->customer(null);
+ $this->assertSame($customer, $sameCustomer);
+ }
+
+ /** @test */
+ public function it_sets_phone_from_array(): void
+ {
+ $extraInfo = new ExtraInfo([
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ ]);
+
+ $phone = $extraInfo->phone();
+
+ $this->assertInstanceOf(PhoneAdapter::class, $phone);
+ }
+
+ /** @test */
+ public function it_returns_phone_without_parameter(): void
+ {
+ $extraInfo = new ExtraInfo([
+ 'phone' => [
+ 'mobile' => '0698765432',
+ ],
+ ]);
+
+ $phone = $extraInfo->phone();
+ $this->assertInstanceOf(PhoneAdapter::class, $phone);
+
+ $samePhone = $extraInfo->phone(null);
+ $this->assertSame($phone, $samePhone);
+ }
+
+ /** @test */
+ public function it_sets_all_properties_together(): void
+ {
+ $extraInfo = new ExtraInfo([
+ 'noShipping' => '1',
+ 'addressOverride' => true,
+ 'address' => [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ 'customer' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ ]);
+
+ $this->assertInstanceOf(AddressAdapter::class, $extraInfo->address());
+ $this->assertInstanceOf(Person::class, $extraInfo->customer());
+ $this->assertInstanceOf(PhoneAdapter::class, $extraInfo->phone());
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/SubscriptionTest.php b/tests/Unit/PaymentMethods/Models/SubscriptionTest.php
new file mode 100644
index 00000000..7432b250
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/SubscriptionTest.php
@@ -0,0 +1,351 @@
+ [
+ 'code' => 'DEBTOR001',
+ ],
+ ]);
+
+ $debtor = $subscription->debtor();
+
+ $this->assertInstanceOf(Debtor::class, $debtor);
+ }
+
+ /** @test */
+ public function it_returns_debtor_without_parameter(): void
+ {
+ $subscription = new Subscription([
+ 'debtor' => [
+ 'code' => 'DEBTOR002',
+ ],
+ ]);
+
+ $debtor = $subscription->debtor();
+ $this->assertInstanceOf(Debtor::class, $debtor);
+
+ $sameDebtor = $subscription->debtor(null);
+ $this->assertSame($debtor, $sameDebtor);
+ }
+
+ /** @test */
+ public function it_sets_bank_account_from_array(): void
+ {
+ $subscription = new Subscription([
+ 'bankAccount' => [
+ 'iban' => 'NL91ABNA0417164300',
+ 'bic' => 'ABNANL2A',
+ 'accountName' => 'John Doe',
+ ],
+ ]);
+
+ $bankAccount = $subscription->bankAccount();
+
+ $this->assertInstanceOf(BankAccountAdapter::class, $bankAccount);
+ }
+
+ /** @test */
+ public function it_returns_bank_account_without_parameter(): void
+ {
+ $subscription = new Subscription([
+ 'bankAccount' => [
+ 'iban' => 'NL91ABNA0417164300',
+ ],
+ ]);
+
+ $bankAccount = $subscription->bankAccount();
+ $this->assertInstanceOf(BankAccountAdapter::class, $bankAccount);
+
+ $sameBankAccount = $subscription->bankAccount(null);
+ $this->assertSame($bankAccount, $sameBankAccount);
+ }
+
+ /** @test */
+ public function it_sets_email_from_string(): void
+ {
+ $subscription = new Subscription([
+ 'email' => 'test@example.com',
+ ]);
+
+ $email = $subscription->email();
+
+ $this->assertInstanceOf(Email::class, $email);
+ }
+
+ /** @test */
+ public function it_returns_email_without_parameter(): void
+ {
+ $subscription = new Subscription([
+ 'email' => 'john@doe.com',
+ ]);
+
+ $email = $subscription->email();
+ $this->assertInstanceOf(Email::class, $email);
+
+ $sameEmail = $subscription->email(null);
+ $this->assertSame($email, $sameEmail);
+ }
+
+ /** @test */
+ public function it_sets_phone_from_array(): void
+ {
+ $subscription = new Subscription([
+ 'phone' => [
+ 'mobile' => '0612345678',
+ ],
+ ]);
+
+ $phone = $subscription->phone();
+
+ $this->assertInstanceOf(Phone::class, $phone);
+ }
+
+ /** @test */
+ public function it_returns_phone_without_parameter(): void
+ {
+ $subscription = new Subscription([
+ 'phone' => [
+ 'mobile' => '0698765432',
+ ],
+ ]);
+
+ $phone = $subscription->phone();
+ $this->assertInstanceOf(Phone::class, $phone);
+
+ $samePhone = $subscription->phone(null);
+ $this->assertSame($phone, $samePhone);
+ }
+
+ /** @test */
+ public function it_sets_address_from_array(): void
+ {
+ $subscription = new Subscription([
+ 'address' => [
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'zipcode' => '1234AB',
+ 'city' => 'Amsterdam',
+ 'country' => 'NL',
+ ],
+ ]);
+
+ $address = $subscription->address();
+
+ $this->assertInstanceOf(AddressAdapter::class, $address);
+ }
+
+ /** @test */
+ public function it_returns_address_without_parameter(): void
+ {
+ $subscription = new Subscription([
+ 'address' => [
+ 'street' => 'Test Street',
+ ],
+ ]);
+
+ $address = $subscription->address();
+ $this->assertInstanceOf(AddressAdapter::class, $address);
+
+ $sameAddress = $subscription->address(null);
+ $this->assertSame($address, $sameAddress);
+ }
+
+ /** @test */
+ public function it_sets_person_from_array(): void
+ {
+ $subscription = new Subscription([
+ 'person' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $person = $subscription->person();
+
+ $this->assertInstanceOf(Person::class, $person);
+ }
+
+ /** @test */
+ public function it_returns_person_without_parameter(): void
+ {
+ $subscription = new Subscription([
+ 'person' => [
+ 'firstName' => 'Jane',
+ ],
+ ]);
+
+ $person = $subscription->person();
+ $this->assertInstanceOf(Person::class, $person);
+
+ $samePerson = $subscription->person(null);
+ $this->assertSame($person, $samePerson);
+ }
+
+ /** @test */
+ public function it_sets_company_from_array(): void
+ {
+ $subscription = new Subscription([
+ 'company' => [
+ 'name' => 'Test Company B.V.',
+ 'chamberOfCommerce' => '12345678',
+ ],
+ ]);
+
+ $company = $subscription->company();
+
+ $this->assertInstanceOf(CompanyAdapter::class, $company);
+ }
+
+ /** @test */
+ public function it_returns_company_without_parameter(): void
+ {
+ $subscription = new Subscription([
+ 'company' => [
+ 'name' => 'Acme Corp',
+ ],
+ ]);
+
+ $company = $subscription->company();
+ $this->assertInstanceOf(CompanyAdapter::class, $company);
+
+ $sameCompany = $subscription->company(null);
+ $this->assertSame($company, $sameCompany);
+ }
+
+ /** @test */
+ public function it_sets_configuration_from_array(): void
+ {
+ $subscription = new Subscription([
+ 'configuration' => [
+ 'name' => 'Test Configuration',
+ ],
+ ]);
+
+ $configuration = $subscription->configuration();
+
+ $this->assertInstanceOf(Configuration::class, $configuration);
+ }
+
+ /** @test */
+ public function it_returns_configuration_without_parameter(): void
+ {
+ $subscription = new Subscription([
+ 'configuration' => [
+ 'name' => 'Config',
+ ],
+ ]);
+
+ $configuration = $subscription->configuration();
+ $this->assertInstanceOf(Configuration::class, $configuration);
+
+ $sameConfiguration = $subscription->configuration(null);
+ $this->assertSame($configuration, $sameConfiguration);
+ }
+
+ /** @test */
+ public function it_sets_rate_plans_from_array(): void
+ {
+ $subscription = new Subscription([
+ 'ratePlans' => [
+ 'add' => [
+ 'ratePlanCode' => 'PLAN001',
+ ],
+ 'update' => [
+ 'ratePlanCode' => 'PLAN002',
+ ],
+ 'disable' => [
+ 'ratePlanCode' => 'PLAN003',
+ ],
+ ],
+ ]);
+
+ $result = $subscription->ratePlans();
+
+ $this->assertSame($subscription, $result);
+ }
+
+ /** @test */
+ public function it_returns_self_for_rate_plans_without_parameter(): void
+ {
+ $subscription = new Subscription([]);
+
+ $result = $subscription->ratePlans();
+
+ $this->assertSame($subscription, $result);
+ }
+
+ /** @test */
+ public function it_sets_rate_plan_charges_from_array(): void
+ {
+ $subscription = new Subscription([
+ 'ratePlanCharges' => [
+ 'add' => [
+ 'ratePlanChargeCode' => 'CHARGE001',
+ ],
+ 'update' => [
+ 'ratePlanChargeCode' => 'CHARGE002',
+ ],
+ ],
+ ]);
+
+ $result = $subscription->ratePlanCharges();
+
+ $this->assertSame($subscription, $result);
+ }
+
+ /** @test */
+ public function it_returns_self_for_rate_plan_charges_without_parameter(): void
+ {
+ $subscription = new Subscription([]);
+
+ $result = $subscription->ratePlanCharges();
+
+ $this->assertSame($subscription, $result);
+ }
+
+ /** @test */
+ public function it_sets_all_properties_together(): void
+ {
+ $subscription = new Subscription([
+ 'includeTransaction' => true,
+ 'transactionVatPercentage' => 21.0,
+ 'configurationCode' => 'CONFIG001',
+ 'debtor' => ['code' => 'DEBTOR001'],
+ 'bankAccount' => ['iban' => 'NL91ABNA0417164300'],
+ 'email' => 'test@example.com',
+ 'phone' => ['mobile' => '0612345678'],
+ 'address' => ['street' => 'Main Street', 'houseNumber' => '123'],
+ 'person' => ['firstName' => 'John', 'lastName' => 'Doe'],
+ 'company' => ['name' => 'Test Company'],
+ 'configuration' => ['name' => 'Test Config'],
+ ]);
+
+ $this->assertInstanceOf(Debtor::class, $subscription->debtor());
+ $this->assertInstanceOf(BankAccountAdapter::class, $subscription->bankAccount());
+ $this->assertInstanceOf(Email::class, $subscription->email());
+ $this->assertInstanceOf(Phone::class, $subscription->phone());
+ $this->assertInstanceOf(AddressAdapter::class, $subscription->address());
+ $this->assertInstanceOf(Person::class, $subscription->person());
+ $this->assertInstanceOf(CompanyAdapter::class, $subscription->company());
+ $this->assertInstanceOf(Configuration::class, $subscription->configuration());
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/SurepayVerifyTest.php b/tests/Unit/PaymentMethods/Models/SurepayVerifyTest.php
new file mode 100644
index 00000000..a9f3e497
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/SurepayVerifyTest.php
@@ -0,0 +1,44 @@
+ [
+ 'iban' => 'NL91ABNA0417164300',
+ 'bic' => 'ABNANL2A',
+ 'accountName' => 'John Doe',
+ ],
+ ]);
+
+ $bankAccount = $verify->bankAccount();
+
+ $this->assertInstanceOf(BankAccountAdapter::class, $bankAccount);
+ }
+
+ /** @test */
+ public function it_returns_bank_account_without_parameter(): void
+ {
+ $verify = new Verify([
+ 'bankAccount' => [
+ 'iban' => 'NL91ABNA0417164300',
+ ],
+ ]);
+
+ $bankAccount = $verify->bankAccount();
+ $this->assertInstanceOf(BankAccountAdapter::class, $bankAccount);
+
+ $sameBankAccount = $verify->bankAccount(null);
+ $this->assertSame($bankAccount, $sameBankAccount);
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/ThunesPayTest.php b/tests/Unit/PaymentMethods/Models/ThunesPayTest.php
new file mode 100644
index 00000000..317a0929
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/ThunesPayTest.php
@@ -0,0 +1,67 @@
+ [
+ [
+ 'identifier' => 'ART001',
+ 'description' => 'Test Article',
+ 'quantity' => 1,
+ 'price' => 100.00,
+ ],
+ [
+ 'identifier' => 'ART002',
+ 'description' => 'Another Article',
+ 'quantity' => 2,
+ 'price' => 50.00,
+ ],
+ ],
+ ]);
+
+ $articles = $pay->articles();
+
+ $this->assertIsArray($articles);
+ $this->assertCount(2, $articles);
+ $this->assertInstanceOf(ArticleAdapter::class, $articles[0]);
+ $this->assertInstanceOf(ArticleAdapter::class, $articles[1]);
+ }
+
+ /** @test */
+ public function it_returns_empty_articles_array_without_parameter(): void
+ {
+ $pay = new Pay([]);
+
+ $articles = $pay->articles();
+
+ $this->assertIsArray($articles);
+ $this->assertEmpty($articles);
+ }
+
+ /** @test */
+ public function it_returns_articles_without_parameter_after_setting(): void
+ {
+ $pay = new Pay([
+ 'articles' => [
+ ['identifier' => 'ART001', 'description' => 'Test', 'quantity' => 1, 'price' => 10.00],
+ ],
+ ]);
+
+ $articles = $pay->articles();
+ $this->assertCount(1, $articles);
+
+ $sameArticles = $pay->articles(null);
+ $this->assertCount(1, $sameArticles);
+ }
+}
diff --git a/tests/Unit/PaymentMethods/Models/TrustlyPayTest.php b/tests/Unit/PaymentMethods/Models/TrustlyPayTest.php
new file mode 100644
index 00000000..b9d1631c
--- /dev/null
+++ b/tests/Unit/PaymentMethods/Models/TrustlyPayTest.php
@@ -0,0 +1,87 @@
+ [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ ]);
+
+ $customer = $pay->customer();
+
+ $this->assertInstanceOf(CustomerAdapter::class, $customer);
+ }
+
+ /** @test */
+ public function it_returns_customer_without_parameter(): void
+ {
+ $pay = new Pay([
+ 'customer' => [
+ 'firstName' => 'Jane',
+ 'lastName' => 'Smith',
+ ],
+ ]);
+
+ $customer = $pay->customer();
+ $this->assertInstanceOf(CustomerAdapter::class, $customer);
+
+ $sameCustomer = $pay->customer(null);
+ $this->assertSame($customer, $sameCustomer);
+ }
+
+ /** @test */
+ public function it_sets_email_from_string(): void
+ {
+ $pay = new Pay([
+ 'email' => 'test@example.com',
+ ]);
+
+ $email = $pay->email();
+
+ $this->assertInstanceOf(EmailAdapter::class, $email);
+ }
+
+ /** @test */
+ public function it_returns_email_without_parameter(): void
+ {
+ $pay = new Pay([
+ 'email' => 'john@doe.com',
+ ]);
+
+ $email = $pay->email();
+ $this->assertInstanceOf(EmailAdapter::class, $email);
+
+ $sameEmail = $pay->email(null);
+ $this->assertSame($email, $sameEmail);
+ }
+
+ /** @test */
+ public function it_sets_all_properties_together(): void
+ {
+ $pay = new Pay([
+ 'country' => 'NL',
+ 'customer' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ 'email' => 'test@example.com',
+ ]);
+
+ $this->assertInstanceOf(CustomerAdapter::class, $pay->customer());
+ $this->assertInstanceOf(EmailAdapter::class, $pay->email());
+ }
+}
diff --git a/tests/Unit/PaymentMethods/PaymentFacadeTest.php b/tests/Unit/PaymentMethods/PaymentFacadeTest.php
new file mode 100644
index 00000000..67f564f3
--- /dev/null
+++ b/tests/Unit/PaymentMethods/PaymentFacadeTest.php
@@ -0,0 +1,272 @@
+useMock();
+
+ $this->client = $this->buckaroo->client();
+ }
+
+ public function test_it_creates_facade_with_payment_method(): void
+ {
+ $facade = new PaymentFacade($this->client, 'ideal');
+
+ $this->assertInstanceOf(PaymentFacade::class, $facade);
+ $this->assertInstanceOf(PaymentMethod::class, $facade->paymentMethod());
+ $this->assertInstanceOf(iDeal::class, $facade->paymentMethod());
+ }
+
+ public function test_it_creates_facade_with_null_payment_method(): void
+ {
+ $facade = new PaymentFacade($this->client, null);
+
+ $this->assertInstanceOf(NoServiceSpecifiedPayment::class, $facade->paymentMethod());
+ }
+
+ public function test_it_returns_payment_method(): void
+ {
+ $facade = new PaymentFacade($this->client, 'paypal');
+
+ $paymentMethod = $facade->paymentMethod();
+
+ $this->assertInstanceOf(Paypal::class, $paymentMethod);
+ $this->assertSame('paypal', $paymentMethod->paymentName());
+ }
+
+ public function test_manually_returns_self(): void
+ {
+ $facade = new PaymentFacade($this->client, 'ideal');
+
+ $result = $facade->manually();
+
+ $this->assertSame($facade, $result);
+ }
+
+ public function test_manually_sets_flag_on_payment_method(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([]);
+
+ $facade = new PaymentFacade($this->client, 'ideal');
+
+ // After calling manually(), the payment method should return itself
+ // instead of making an HTTP request
+ $result = $facade->manually()->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-001',
+ 'issuer' => 'ABNANL2A',
+ ]);
+
+ // When manually is set, the payment method returns itself
+ $this->assertInstanceOf(PaymentMethod::class, $result);
+ }
+
+ public function test_combine_returns_self(): void
+ {
+ $facade = new PaymentFacade($this->client, 'ideal');
+
+ $result = $facade->combine([]);
+
+ $this->assertSame($facade, $result);
+ }
+
+ public function test_combine_accepts_combinable_payment(): void
+ {
+ $facade = new PaymentFacade($this->client, 'ideal');
+ $creditCardFacade = new PaymentFacade($this->client, 'creditcard');
+
+ // Get the credit card payment method which implements Combinable
+ $creditCardPayment = $creditCardFacade->manually()->pay([
+ 'amountDebit' => 5.00,
+ 'invoice' => 'TEST-001',
+ 'name' => 'visa',
+ ]);
+
+ // CreditCard implements Combinable, so it can be combined
+ $this->assertInstanceOf(Combinable::class, $creditCardPayment);
+
+ $result = $facade->combine($creditCardPayment);
+
+ $this->assertSame($facade, $result);
+ }
+
+ public function test_combine_accepts_array_of_combinable_payments(): void
+ {
+ $facade = new PaymentFacade($this->client, 'ideal');
+
+ // Create multiple combinable payments
+ $creditCardFacade1 = new PaymentFacade($this->client, 'creditcard');
+ $payment1 = $creditCardFacade1->manually()->pay([
+ 'amountDebit' => 5.00,
+ 'invoice' => 'TEST-001',
+ 'name' => 'visa',
+ ]);
+
+ $creditCardFacade2 = new PaymentFacade($this->client, 'creditcard');
+ $payment2 = $creditCardFacade2->manually()->pay([
+ 'amountDebit' => 5.00,
+ 'invoice' => 'TEST-002',
+ 'name' => 'mastercard',
+ ]);
+
+ $result = $facade->combine([$payment1, $payment2]);
+
+ $this->assertSame($facade, $result);
+ }
+
+ public function test_combine_ignores_non_combinable_payment(): void
+ {
+ $facade = new PaymentFacade($this->client, 'ideal');
+
+ // Passing a non-Combinable value should not throw an error
+ $result = $facade->combine('not-a-combinable');
+
+ $this->assertSame($facade, $result);
+ }
+
+ public function test_magic_call_delegates_to_payment_method(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/Transaction/',
+ TestHelpers::successResponse([
+ 'Key' => TestHelpers::generateTransactionKey(),
+ ])
+ ),
+ ]);
+
+ $facade = new PaymentFacade($this->client, 'ideal');
+
+ $response = $facade->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-001',
+ 'issuer' => 'ABNANL2A',
+ ]);
+
+ $this->assertTrue($response->isSuccess());
+ }
+
+ public function test_magic_call_throws_exception_for_undefined_method(): void
+ {
+ $facade = new PaymentFacade($this->client, 'ideal');
+
+ $this->expectException(BuckarooException::class);
+ $this->expectExceptionMessage('Payment method nonExistentMethod on payment ideal you requested does not exist.');
+
+ $facade->nonExistentMethod([]);
+ }
+
+ public function test_set_service_version_via_magic_call(): void
+ {
+ $facade = new PaymentFacade($this->client, 'ideal');
+
+ $result = $facade->setServiceVersion(2);
+
+ $this->assertSame($facade, $result);
+ $this->assertSame(2, $facade->paymentMethod()->serviceVersion());
+ }
+
+ public function test_magic_call_with_empty_arguments(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Transaction/Specification/ideal*',
+ [
+ 'Services' => [
+ [
+ 'Name' => 'ideal',
+ 'Version' => 2,
+ 'ActionDescriptions' => [
+ [
+ 'Name' => 'Pay',
+ 'RequestParameters' => [
+ [
+ 'Name' => 'issuer',
+ 'DataType' => 'list',
+ 'ListItemDescriptions' => [
+ ['Value' => 'ABNANL2A', 'Description' => 'ABN AMRO'],
+ ['Value' => 'INGBNL2A', 'Description' => 'ING'],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ]
+ ),
+ ]);
+
+ $facade = new PaymentFacade($this->client, 'ideal');
+
+ // issuers() doesn't require arguments
+ $response = $facade->issuers();
+
+ $this->assertIsArray($response);
+ }
+
+ public function test_method_chaining(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([]);
+
+ $facade = new PaymentFacade($this->client, 'ideal');
+
+ // Test that methods can be chained
+ $result = $facade
+ ->manually()
+ ->setServiceVersion(2)
+ ->pay([
+ 'amountDebit' => 10.00,
+ 'invoice' => 'TEST-001',
+ 'issuer' => 'ABNANL2A',
+ ]);
+
+ $this->assertInstanceOf(PaymentMethod::class, $result);
+ }
+
+ public function test_facade_created_via_buckaroo_client_method(): void
+ {
+ $facade = $this->buckaroo->method('ideal');
+
+ $this->assertInstanceOf(PaymentFacade::class, $facade);
+ $this->assertInstanceOf(iDeal::class, $facade->paymentMethod());
+ }
+
+ public function test_different_payment_methods_via_facade(): void
+ {
+ $idealFacade = new PaymentFacade($this->client, 'ideal');
+ $paypalFacade = new PaymentFacade($this->client, 'paypal');
+ $creditCardFacade = new PaymentFacade($this->client, 'creditcard');
+
+ $this->assertInstanceOf(iDeal::class, $idealFacade->paymentMethod());
+ $this->assertInstanceOf(Paypal::class, $paypalFacade->paymentMethod());
+ $this->assertInstanceOf(CreditCard::class, $creditCardFacade->paymentMethod());
+ }
+}
diff --git a/tests/Unit/PaymentMethods/PaymentMethodFactoryTest.php b/tests/Unit/PaymentMethods/PaymentMethodFactoryTest.php
new file mode 100644
index 00000000..c88844a5
--- /dev/null
+++ b/tests/Unit/PaymentMethods/PaymentMethodFactoryTest.php
@@ -0,0 +1,287 @@
+useMock();
+
+ $this->client = $this->buckaroo->client();
+ }
+
+ public function test_it_creates_payment_method_by_name(): void
+ {
+ $factory = new PaymentMethodFactory($this->client, 'ideal');
+
+ $paymentMethod = $factory->getPaymentMethod();
+
+ $this->assertInstanceOf(PaymentMethod::class, $paymentMethod);
+ $this->assertInstanceOf(iDeal::class, $paymentMethod);
+ }
+
+ public function test_it_creates_payment_method_via_static_get(): void
+ {
+ $paymentMethod = PaymentMethodFactory::get($this->client, 'ideal');
+
+ $this->assertInstanceOf(PaymentMethod::class, $paymentMethod);
+ $this->assertInstanceOf(iDeal::class, $paymentMethod);
+ }
+
+ /**
+ * @dataProvider paymentMethodProvider
+ */
+ public function test_it_creates_supported_payment_methods(string $alias, string $expectedClass): void
+ {
+ $paymentMethod = PaymentMethodFactory::get($this->client, $alias);
+
+ $this->assertInstanceOf(PaymentMethod::class, $paymentMethod);
+ $this->assertInstanceOf($expectedClass, $paymentMethod);
+ }
+
+ public static function paymentMethodProvider(): array
+ {
+ return [
+ 'ideal' => ['ideal', iDeal::class],
+ 'creditcard' => ['creditcard', CreditCard::class],
+ 'paypal' => ['paypal', Paypal::class],
+ 'applepay' => ['applepay', ApplePay::class],
+ 'googlepay' => ['googlepay', GooglePay::class],
+ 'bancontact' => ['bancontact', Bancontact::class],
+ 'afterpay' => ['afterpay', Afterpay::class],
+ 'klarnakp' => ['klarnakp', KlarnaKP::class],
+ 'in3' => ['in3', In3::class],
+ 'sepadirectdebit' => ['sepadirectdebit', SEPA::class],
+ 'giftcard' => ['giftcard', GiftCard::class],
+ 'thunes' => ['thunes', Thunes::class],
+ 'trustly' => ['trustly', Trustly::class],
+ 'przelewy24' => ['przelewy24', Przelewy24::class],
+ ];
+ }
+
+ /**
+ * @dataProvider creditCardAliasProvider
+ */
+ public function test_it_resolves_credit_card_aliases_to_credit_card_class(string $alias): void
+ {
+ $paymentMethod = PaymentMethodFactory::get($this->client, $alias);
+
+ $this->assertInstanceOf(CreditCard::class, $paymentMethod);
+ }
+
+ public static function creditCardAliasProvider(): array
+ {
+ return [
+ 'creditcard' => ['creditcard'],
+ 'mastercard' => ['mastercard'],
+ 'visa' => ['visa'],
+ 'amex' => ['amex'],
+ 'vpay' => ['vpay'],
+ 'maestro' => ['maestro'],
+ 'visaelectron' => ['visaelectron'],
+ 'cartebleuevisa' => ['cartebleuevisa'],
+ 'cartebancaire' => ['cartebancaire'],
+ 'dankort' => ['dankort'],
+ 'nexi' => ['nexi'],
+ 'postepay' => ['postepay'],
+ ];
+ }
+
+ /**
+ * @dataProvider giftCardAliasProvider
+ */
+ public function test_it_resolves_gift_card_aliases_to_gift_card_class(string $alias): void
+ {
+ $paymentMethod = PaymentMethodFactory::get($this->client, $alias);
+
+ $this->assertInstanceOf(GiftCard::class, $paymentMethod);
+ }
+
+ public static function giftCardAliasProvider(): array
+ {
+ return [
+ 'giftcard' => ['giftcard'],
+ 'westlandbon' => ['westlandbon'],
+ 'babygiftcard' => ['babygiftcard'],
+ 'vvvgiftcard' => ['vvvgiftcard'],
+ 'fashioncheque' => ['fashioncheque'],
+ 'boekenbon' => ['boekenbon'],
+ ];
+ }
+
+ /**
+ * @dataProvider thunesAliasProvider
+ */
+ public function test_it_resolves_thunes_aliases_to_thunes_class(string $alias): void
+ {
+ $paymentMethod = PaymentMethodFactory::get($this->client, $alias);
+
+ $this->assertInstanceOf(Thunes::class, $paymentMethod);
+ }
+
+ public static function thunesAliasProvider(): array
+ {
+ return [
+ 'thunes' => ['thunes'],
+ 'monizzemealvoucher' => ['monizzemealvoucher'],
+ 'monizzeecovoucher' => ['monizzeecovoucher'],
+ 'sodexomealvoucher' => ['sodexomealvoucher'],
+ ];
+ }
+
+ /**
+ * @dataProvider sepaAliasProvider
+ */
+ public function test_it_resolves_sepa_aliases_to_sepa_class(string $alias): void
+ {
+ $paymentMethod = PaymentMethodFactory::get($this->client, $alias);
+
+ $this->assertInstanceOf(SEPA::class, $paymentMethod);
+ }
+
+ public static function sepaAliasProvider(): array
+ {
+ return [
+ 'sepadirectdebit' => ['sepadirectdebit'],
+ 'sepa' => ['sepa'],
+ ];
+ }
+
+ /**
+ * @dataProvider bancontactAliasProvider
+ */
+ public function test_it_resolves_bancontact_aliases_to_bancontact_class(string $alias): void
+ {
+ $paymentMethod = PaymentMethodFactory::get($this->client, $alias);
+
+ $this->assertInstanceOf(Bancontact::class, $paymentMethod);
+ }
+
+ public static function bancontactAliasProvider(): array
+ {
+ return [
+ 'bancontact' => ['bancontact'],
+ 'bancontactmrcash' => ['bancontactmrcash'],
+ ];
+ }
+
+ public function test_it_handles_case_insensitive_names(): void
+ {
+ $uppercase = PaymentMethodFactory::get($this->client, 'IDEAL');
+ $lowercase = PaymentMethodFactory::get($this->client, 'ideal');
+ $mixedCase = PaymentMethodFactory::get($this->client, 'IdEaL');
+
+ $this->assertInstanceOf(iDeal::class, $uppercase);
+ $this->assertInstanceOf(iDeal::class, $lowercase);
+ $this->assertInstanceOf(iDeal::class, $mixedCase);
+ }
+
+ public function test_it_handles_case_insensitive_credit_card_aliases(): void
+ {
+ $uppercase = PaymentMethodFactory::get($this->client, 'VISA');
+ $lowercase = PaymentMethodFactory::get($this->client, 'visa');
+ $mixedCase = PaymentMethodFactory::get($this->client, 'ViSa');
+
+ $this->assertInstanceOf(CreditCard::class, $uppercase);
+ $this->assertInstanceOf(CreditCard::class, $lowercase);
+ $this->assertInstanceOf(CreditCard::class, $mixedCase);
+ }
+
+ public function test_it_throws_exception_for_invalid_payment_method(): void
+ {
+ $this->expectException(BuckarooException::class);
+ $this->expectExceptionMessage('Wrong payment method code has been given');
+
+ PaymentMethodFactory::get($this->client, 'nonexistent_payment_method');
+ }
+
+ public function test_it_returns_no_service_for_null_payment_method(): void
+ {
+ $paymentMethod = PaymentMethodFactory::get($this->client, null);
+
+ $this->assertInstanceOf(NoServiceSpecifiedPayment::class, $paymentMethod);
+ }
+
+ public function test_it_returns_no_service_for_empty_string_payment_method(): void
+ {
+ // Empty string is treated as falsy, so NoServiceSpecifiedPayment is returned
+ $paymentMethod = PaymentMethodFactory::get($this->client, '');
+
+ $this->assertInstanceOf(NoServiceSpecifiedPayment::class, $paymentMethod);
+ }
+
+ public function test_it_stores_service_code_from_factory(): void
+ {
+ $paymentMethod = PaymentMethodFactory::get($this->client, 'ideal');
+
+ // For iDeal, paymentName returns the hardcoded 'ideal'
+ $this->assertSame('ideal', $paymentMethod->paymentName());
+ }
+
+ public function test_payment_method_has_correct_payment_name(): void
+ {
+ $ideal = PaymentMethodFactory::get($this->client, 'ideal');
+ $paypal = PaymentMethodFactory::get($this->client, 'paypal');
+ $bancontact = PaymentMethodFactory::get($this->client, 'bancontact');
+
+ $this->assertSame('ideal', $ideal->paymentName());
+ $this->assertSame('paypal', $paypal->paymentName());
+ // Bancontact has hardcoded paymentName 'bancontactmrcash'
+ $this->assertSame('bancontactmrcash', $bancontact->paymentName());
+ }
+
+ public function test_sepa_has_correct_payment_name(): void
+ {
+ $sepa = PaymentMethodFactory::get($this->client, 'sepa');
+
+ // SEPA has hardcoded paymentName 'SepaDirectDebit'
+ $this->assertSame('SepaDirectDebit', $sepa->paymentName());
+ }
+
+ public function test_thunes_has_correct_payment_name(): void
+ {
+ $thunes = PaymentMethodFactory::get($this->client, 'monizzemealvoucher');
+
+ // Thunes has hardcoded paymentName 'thunes'
+ $this->assertSame('thunes', $thunes->paymentName());
+ }
+
+ public function test_giftcard_has_correct_payment_name(): void
+ {
+ $giftcard = PaymentMethodFactory::get($this->client, 'westlandbon');
+
+ // GiftCard has hardcoded paymentName 'giftcard'
+ $this->assertSame('giftcard', $giftcard->paymentName());
+ }
+}
diff --git a/tests/Unit/Resources/Constants/CreditManagementInstallmentIntervalTest.php b/tests/Unit/Resources/Constants/CreditManagementInstallmentIntervalTest.php
new file mode 100644
index 00000000..d43e289f
--- /dev/null
+++ b/tests/Unit/Resources/Constants/CreditManagementInstallmentIntervalTest.php
@@ -0,0 +1,93 @@
+assertSame('Day', CreditManagementInstallmentInterval::DAY);
+ }
+
+ public function test_two_days_constant(): void
+ {
+ $this->assertSame('TwoDays', CreditManagementInstallmentInterval::TWODAYS);
+ }
+
+ public function test_week_constant(): void
+ {
+ $this->assertSame('Week', CreditManagementInstallmentInterval::WEEK);
+ }
+
+ public function test_two_weeks_constant(): void
+ {
+ $this->assertSame('TwoWeeks', CreditManagementInstallmentInterval::TWOWEEKS);
+ }
+
+ public function test_half_month_constant(): void
+ {
+ $this->assertSame('HalfMonth', CreditManagementInstallmentInterval::HALFMONTH);
+ }
+
+ public function test_month_constant(): void
+ {
+ $this->assertSame('Month', CreditManagementInstallmentInterval::MONTH);
+ }
+
+ public function test_two_months_constant(): void
+ {
+ $this->assertSame('TwoMonths', CreditManagementInstallmentInterval::TWOMONTHS);
+ }
+
+ public function test_quarter_year_constant(): void
+ {
+ $this->assertSame('QuarterYear', CreditManagementInstallmentInterval::QUARTERYEAR);
+ }
+
+ public function test_half_year_constant(): void
+ {
+ $this->assertSame('HalfYear', CreditManagementInstallmentInterval::HALFYEAR);
+ }
+
+ public function test_year_constant(): void
+ {
+ $this->assertSame('Year', CreditManagementInstallmentInterval::YEAR);
+ }
+
+ public function test_all_constants_are_strings(): void
+ {
+ $this->assertIsString(CreditManagementInstallmentInterval::DAY);
+ $this->assertIsString(CreditManagementInstallmentInterval::TWODAYS);
+ $this->assertIsString(CreditManagementInstallmentInterval::WEEK);
+ $this->assertIsString(CreditManagementInstallmentInterval::TWOWEEKS);
+ $this->assertIsString(CreditManagementInstallmentInterval::HALFMONTH);
+ $this->assertIsString(CreditManagementInstallmentInterval::MONTH);
+ $this->assertIsString(CreditManagementInstallmentInterval::TWOMONTHS);
+ $this->assertIsString(CreditManagementInstallmentInterval::QUARTERYEAR);
+ $this->assertIsString(CreditManagementInstallmentInterval::HALFYEAR);
+ $this->assertIsString(CreditManagementInstallmentInterval::YEAR);
+ }
+
+ public function test_all_values_are_unique(): void
+ {
+ $values = [
+ CreditManagementInstallmentInterval::DAY,
+ CreditManagementInstallmentInterval::TWODAYS,
+ CreditManagementInstallmentInterval::WEEK,
+ CreditManagementInstallmentInterval::TWOWEEKS,
+ CreditManagementInstallmentInterval::HALFMONTH,
+ CreditManagementInstallmentInterval::MONTH,
+ CreditManagementInstallmentInterval::TWOMONTHS,
+ CreditManagementInstallmentInterval::QUARTERYEAR,
+ CreditManagementInstallmentInterval::HALFYEAR,
+ CreditManagementInstallmentInterval::YEAR,
+ ];
+
+ $this->assertCount(10, array_unique($values));
+ }
+}
diff --git a/tests/Unit/Resources/Constants/EndpointsTest.php b/tests/Unit/Resources/Constants/EndpointsTest.php
new file mode 100644
index 00000000..05f83b8e
--- /dev/null
+++ b/tests/Unit/Resources/Constants/EndpointsTest.php
@@ -0,0 +1,50 @@
+assertSame('https://checkout.buckaroo.nl/', Endpoints::LIVE);
+ }
+
+ public function test_test_endpoint(): void
+ {
+ $this->assertSame('https://testcheckout.buckaroo.nl/', Endpoints::TEST);
+ }
+
+ public function test_endpoints_are_https(): void
+ {
+ $this->assertStringStartsWith('https://', Endpoints::LIVE);
+ $this->assertStringStartsWith('https://', Endpoints::TEST);
+ }
+
+ public function test_endpoints_end_with_slash(): void
+ {
+ $this->assertStringEndsWith('/', Endpoints::LIVE);
+ $this->assertStringEndsWith('/', Endpoints::TEST);
+ }
+
+ public function test_endpoints_contain_buckaroo_domain(): void
+ {
+ $this->assertStringContainsString('buckaroo.nl', Endpoints::LIVE);
+ $this->assertStringContainsString('buckaroo.nl', Endpoints::TEST);
+ }
+
+ public function test_test_endpoint_contains_test_prefix(): void
+ {
+ $this->assertStringContainsString('test', Endpoints::TEST);
+ }
+
+ public function test_endpoints_are_valid_urls(): void
+ {
+ $this->assertNotFalse(filter_var(Endpoints::LIVE, FILTER_VALIDATE_URL));
+ $this->assertNotFalse(filter_var(Endpoints::TEST, FILTER_VALIDATE_URL));
+ }
+}
diff --git a/tests/Unit/Resources/Constants/GenderTest.php b/tests/Unit/Resources/Constants/GenderTest.php
new file mode 100644
index 00000000..fba8ecf2
--- /dev/null
+++ b/tests/Unit/Resources/Constants/GenderTest.php
@@ -0,0 +1,51 @@
+assertSame(0, Gender::UNKNOWN);
+ }
+
+ public function test_male_constant(): void
+ {
+ $this->assertSame(1, Gender::MALE);
+ }
+
+ public function test_female_constant(): void
+ {
+ $this->assertSame(2, Gender::FEMALE);
+ }
+
+ public function test_not_applicable_constant(): void
+ {
+ $this->assertSame(9, Gender::NOT_APPLICABLE);
+ }
+
+ public function test_gender_constants_are_integer_type(): void
+ {
+ $this->assertIsInt(Gender::UNKNOWN);
+ $this->assertIsInt(Gender::MALE);
+ $this->assertIsInt(Gender::FEMALE);
+ $this->assertIsInt(Gender::NOT_APPLICABLE);
+ }
+
+ public function test_gender_values_are_unique(): void
+ {
+ $values = [
+ Gender::UNKNOWN,
+ Gender::MALE,
+ Gender::FEMALE,
+ Gender::NOT_APPLICABLE,
+ ];
+
+ $this->assertCount(4, array_unique($values));
+ }
+}
diff --git a/tests/Unit/Resources/Constants/IPProtocolVersionTest.php b/tests/Unit/Resources/Constants/IPProtocolVersionTest.php
new file mode 100644
index 00000000..39b348cb
--- /dev/null
+++ b/tests/Unit/Resources/Constants/IPProtocolVersionTest.php
@@ -0,0 +1,128 @@
+assertSame(0, IPProtocolVersion::IPV4);
+ }
+
+ public function test_ipv6_constant(): void
+ {
+ $this->assertSame(1, IPProtocolVersion::IPV6);
+ }
+
+ public function test_constants_are_integer_type(): void
+ {
+ $this->assertIsInt(IPProtocolVersion::IPV4);
+ $this->assertIsInt(IPProtocolVersion::IPV6);
+ }
+
+ public function test_get_version_returns_ipv4_for_ipv4_address(): void
+ {
+ $this->assertSame(
+ IPProtocolVersion::IPV4,
+ IPProtocolVersion::getVersion('192.168.1.1')
+ );
+ }
+
+ public function test_get_version_returns_ipv4_for_localhost(): void
+ {
+ $this->assertSame(
+ IPProtocolVersion::IPV4,
+ IPProtocolVersion::getVersion('127.0.0.1')
+ );
+ }
+
+ public function test_get_version_returns_ipv6_for_ipv6_address(): void
+ {
+ $this->assertSame(
+ IPProtocolVersion::IPV6,
+ IPProtocolVersion::getVersion('2001:0db8:85a3:0000:0000:8a2e:0370:7334')
+ );
+ }
+
+ public function test_get_version_returns_ipv6_for_short_ipv6(): void
+ {
+ $this->assertSame(
+ IPProtocolVersion::IPV6,
+ IPProtocolVersion::getVersion('::1')
+ );
+ }
+
+ public function test_get_version_returns_ipv4_for_default(): void
+ {
+ $this->assertSame(
+ IPProtocolVersion::IPV4,
+ IPProtocolVersion::getVersion()
+ );
+ }
+
+ public function test_get_version_returns_ipv4_for_invalid_ip(): void
+ {
+ // Invalid IP addresses are treated as IPv4 (default behavior)
+ $this->assertSame(
+ IPProtocolVersion::IPV4,
+ IPProtocolVersion::getVersion('not-an-ip')
+ );
+ }
+
+ public function test_get_version_returns_ipv4_for_empty_string(): void
+ {
+ $this->assertSame(
+ IPProtocolVersion::IPV4,
+ IPProtocolVersion::getVersion('')
+ );
+ }
+
+ /**
+ * @dataProvider ipv4AddressesProvider
+ */
+ public function test_get_version_identifies_ipv4_addresses(string $ip): void
+ {
+ $this->assertSame(
+ IPProtocolVersion::IPV4,
+ IPProtocolVersion::getVersion($ip)
+ );
+ }
+
+ public static function ipv4AddressesProvider(): array
+ {
+ return [
+ 'standard' => ['192.168.1.1'],
+ 'localhost' => ['127.0.0.1'],
+ 'broadcast' => ['255.255.255.255'],
+ 'zero' => ['0.0.0.0'],
+ 'public' => ['8.8.8.8'],
+ ];
+ }
+
+ /**
+ * @dataProvider ipv6AddressesProvider
+ */
+ public function test_get_version_identifies_ipv6_addresses(string $ip): void
+ {
+ $this->assertSame(
+ IPProtocolVersion::IPV6,
+ IPProtocolVersion::getVersion($ip)
+ );
+ }
+
+ public static function ipv6AddressesProvider(): array
+ {
+ return [
+ 'full' => ['2001:0db8:85a3:0000:0000:8a2e:0370:7334'],
+ 'compressed' => ['2001:db8:85a3::8a2e:370:7334'],
+ 'localhost' => ['::1'],
+ 'all_zeros' => ['::'],
+ 'link_local' => ['fe80::1'],
+ ];
+ }
+}
diff --git a/tests/Unit/Resources/Constants/RecipientCategoryTest.php b/tests/Unit/Resources/Constants/RecipientCategoryTest.php
new file mode 100644
index 00000000..dd3bfc83
--- /dev/null
+++ b/tests/Unit/Resources/Constants/RecipientCategoryTest.php
@@ -0,0 +1,33 @@
+assertSame('Person', RecipientCategory::PERSON);
+ }
+
+ public function test_company_constant(): void
+ {
+ $this->assertSame('Company', RecipientCategory::COMPANY);
+ }
+
+ public function test_constants_are_string_type(): void
+ {
+ $this->assertIsString(RecipientCategory::PERSON);
+ $this->assertIsString(RecipientCategory::COMPANY);
+ }
+
+ public function test_constants_are_capitalized(): void
+ {
+ $this->assertMatchesRegularExpression('/^[A-Z]/', RecipientCategory::PERSON);
+ $this->assertMatchesRegularExpression('/^[A-Z]/', RecipientCategory::COMPANY);
+ }
+}
diff --git a/tests/Unit/Resources/Constants/ResponseStatusTest.php b/tests/Unit/Resources/Constants/ResponseStatusTest.php
new file mode 100644
index 00000000..531c10d5
--- /dev/null
+++ b/tests/Unit/Resources/Constants/ResponseStatusTest.php
@@ -0,0 +1,93 @@
+assertSame('190', ResponseStatus::BUCKAROO_STATUSCODE_SUCCESS);
+ }
+
+ public function test_failed_status_code(): void
+ {
+ $this->assertSame('490', ResponseStatus::BUCKAROO_STATUSCODE_FAILED);
+ }
+
+ public function test_validation_failure_status_code(): void
+ {
+ $this->assertSame('491', ResponseStatus::BUCKAROO_STATUSCODE_VALIDATION_FAILURE);
+ }
+
+ public function test_technical_error_status_code(): void
+ {
+ $this->assertSame('492', ResponseStatus::BUCKAROO_STATUSCODE_TECHNICAL_ERROR);
+ }
+
+ public function test_rejected_status_code(): void
+ {
+ $this->assertSame('690', ResponseStatus::BUCKAROO_STATUSCODE_REJECTED);
+ }
+
+ public function test_waiting_on_user_input_status_code(): void
+ {
+ $this->assertSame('790', ResponseStatus::BUCKAROO_STATUSCODE_WAITING_ON_USER_INPUT);
+ }
+
+ public function test_pending_processing_status_code(): void
+ {
+ $this->assertSame('791', ResponseStatus::BUCKAROO_STATUSCODE_PENDING_PROCESSING);
+ }
+
+ public function test_waiting_on_consumer_status_code(): void
+ {
+ $this->assertSame('792', ResponseStatus::BUCKAROO_STATUSCODE_WAITING_ON_CONSUMER);
+ }
+
+ public function test_payment_on_hold_status_code(): void
+ {
+ $this->assertSame('793', ResponseStatus::BUCKAROO_STATUSCODE_PAYMENT_ON_HOLD);
+ }
+
+ public function test_pending_approval_status_code(): void
+ {
+ $this->assertSame('794', ResponseStatus::BUCKAROO_STATUSCODE_PENDING_APPROVAL);
+ }
+
+ public function test_cancelled_by_user_status_code(): void
+ {
+ $this->assertSame('890', ResponseStatus::BUCKAROO_STATUSCODE_CANCELLED_BY_USER);
+ }
+
+ public function test_cancelled_by_merchant_status_code(): void
+ {
+ $this->assertSame('891', ResponseStatus::BUCKAROO_STATUSCODE_CANCELLED_BY_MERCHANT);
+ }
+
+ public function test_authorize_type_cancel(): void
+ {
+ $this->assertSame('I014', ResponseStatus::BUCKAROO_AUTHORIZE_TYPE_CANCEL);
+ }
+
+ public function test_authorize_type_accept(): void
+ {
+ $this->assertSame('I013', ResponseStatus::BUCKAROO_AUTHORIZE_TYPE_ACCEPT);
+ }
+
+ public function test_authorize_type_group_transaction(): void
+ {
+ $this->assertSame('I150', ResponseStatus::BUCKAROO_AUTHORIZE_TYPE_GROUP_TRANSACTION);
+ }
+
+ public function test_status_codes_are_string_type(): void
+ {
+ $this->assertIsString(ResponseStatus::BUCKAROO_STATUSCODE_SUCCESS);
+ $this->assertIsString(ResponseStatus::BUCKAROO_STATUSCODE_FAILED);
+ $this->assertIsString(ResponseStatus::BUCKAROO_STATUSCODE_PENDING_PROCESSING);
+ }
+}
diff --git a/tests/Unit/Services/ActiveSubscriptionsTest.php b/tests/Unit/Services/ActiveSubscriptionsTest.php
new file mode 100644
index 00000000..697b0b8b
--- /dev/null
+++ b/tests/Unit/Services/ActiveSubscriptionsTest.php
@@ -0,0 +1,268 @@
+useMock();
+ }
+
+ public function test_it_returns_active_subscriptions(): void
+ {
+ $xmlResponse = '
+
+
+ PLAN-001
+ SUB-001
+
+ EUR
+ USD
+
+
+';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/DataRequest*',
+ [
+ 'Key' => 'DATARESPONSE-001',
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ 'SubCode' => ['Code' => 'S001'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'GetActiveSubscriptions',
+ 'Action' => 'GetActiveSubscriptions',
+ 'Parameters' => [
+ [
+ 'Name' => 'activesubscriptions',
+ 'Value' => $xmlResponse,
+ ],
+ ],
+ ],
+ ],
+ ]
+ ),
+ ]);
+
+ $service = new ActiveSubscriptions($this->buckaroo->client());
+
+ $subscriptions = $service->get();
+
+ $this->assertIsArray($subscriptions);
+ $this->assertCount(1, $subscriptions);
+ $this->assertArrayHasKey('ratePlanCode', $subscriptions[0]);
+ $this->assertSame('PLAN-001', $subscriptions[0]['ratePlanCode']);
+ $this->assertSame('SUB-001', $subscriptions[0]['subscriptionGuid']);
+ $this->assertIsArray($subscriptions[0]['currencies']);
+ $this->assertContains('EUR', $subscriptions[0]['currencies']);
+ $this->assertContains('USD', $subscriptions[0]['currencies']);
+ }
+
+ public function test_it_returns_multiple_subscriptions(): void
+ {
+ $xmlResponse = '
+
+
+ PLAN-001
+ SUB-001
+
+ EUR
+
+
+
+ PLAN-002
+ SUB-002
+
+ USD
+
+
+';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/DataRequest*',
+ [
+ 'Key' => 'DATARESPONSE-002',
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'GetActiveSubscriptions',
+ 'Action' => 'GetActiveSubscriptions',
+ 'Parameters' => [
+ [
+ 'Name' => 'activesubscriptions',
+ 'Value' => $xmlResponse,
+ ],
+ ],
+ ],
+ ],
+ ]
+ ),
+ ]);
+
+ $service = new ActiveSubscriptions($this->buckaroo->client());
+
+ $subscriptions = $service->get();
+
+ $this->assertCount(2, $subscriptions);
+ $this->assertSame('PLAN-001', $subscriptions[0]['ratePlanCode']);
+ $this->assertSame('PLAN-002', $subscriptions[1]['ratePlanCode']);
+ }
+
+ public function test_it_returns_empty_array_when_no_subscriptions(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/DataRequest*',
+ [
+ 'Key' => 'DATARESPONSE-003',
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ ],
+ 'Services' => [],
+ ]
+ ),
+ ]);
+
+ $service = new ActiveSubscriptions($this->buckaroo->client());
+
+ $subscriptions = $service->get();
+
+ $this->assertIsArray($subscriptions);
+ $this->assertEmpty($subscriptions);
+ }
+
+ public function test_it_handles_single_currency_as_array(): void
+ {
+ $xmlResponse = '
+
+
+ PLAN-SINGLE
+ SUB-SINGLE
+
+ EUR
+
+
+';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/DataRequest*',
+ [
+ 'Key' => 'DATARESPONSE-004',
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'GetActiveSubscriptions',
+ 'Action' => 'GetActiveSubscriptions',
+ 'Parameters' => [
+ [
+ 'Name' => 'activesubscriptions',
+ 'Value' => $xmlResponse,
+ ],
+ ],
+ ],
+ ],
+ ]
+ ),
+ ]);
+
+ $service = new ActiveSubscriptions($this->buckaroo->client());
+
+ $subscriptions = $service->get();
+
+ // Single currency should still be returned as an array
+ $this->assertIsArray($subscriptions[0]['currencies']);
+ $this->assertCount(1, $subscriptions[0]['currencies']);
+ $this->assertSame('EUR', $subscriptions[0]['currencies'][0]);
+ }
+
+ public function test_it_converts_keys_to_camel_case(): void
+ {
+ $xmlResponse = '
+
+
+ PLAN-CC
+ SUB-CC
+ value
+
+';
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/DataRequest*',
+ [
+ 'Key' => 'DATARESPONSE-005',
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ ],
+ 'Services' => [
+ [
+ 'Name' => 'GetActiveSubscriptions',
+ 'Action' => 'GetActiveSubscriptions',
+ 'Parameters' => [
+ [
+ 'Name' => 'activesubscriptions',
+ 'Value' => $xmlResponse,
+ ],
+ ],
+ ],
+ ],
+ ]
+ ),
+ ]);
+
+ $service = new ActiveSubscriptions($this->buckaroo->client());
+
+ $subscriptions = $service->get();
+
+ // Keys should be camelCase (first letter lowercase)
+ $this->assertArrayHasKey('ratePlanCode', $subscriptions[0]);
+ $this->assertArrayHasKey('subscriptionGuid', $subscriptions[0]);
+ $this->assertArrayHasKey('someOtherField', $subscriptions[0]);
+ }
+
+ public function test_it_returns_empty_array_on_exception(): void
+ {
+ // Empty response without proper structure should return empty array
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/DataRequest*',
+ []
+ ),
+ ]);
+
+ $service = new ActiveSubscriptions($this->buckaroo->client());
+
+ $subscriptions = $service->get();
+
+ $this->assertIsArray($subscriptions);
+ $this->assertEmpty($subscriptions);
+ }
+}
diff --git a/tests/Unit/Services/PayloadServiceTest.php b/tests/Unit/Services/PayloadServiceTest.php
new file mode 100644
index 00000000..ca8a7352
--- /dev/null
+++ b/tests/Unit/Services/PayloadServiceTest.php
@@ -0,0 +1,183 @@
+ 'value']);
+
+ $this->assertInstanceOf(Arrayable::class, $service);
+ }
+
+ public function test_it_accepts_array_payload(): void
+ {
+ $payload = ['amount' => 10.50, 'invoice' => 'TEST-001'];
+
+ $service = new PayloadService($payload);
+
+ $this->assertSame($payload, $service->toArray());
+ }
+
+ public function test_it_accepts_json_string_payload(): void
+ {
+ $jsonPayload = '{"amount": 10.50, "invoice": "TEST-001"}';
+
+ $service = new PayloadService($jsonPayload);
+
+ $result = $service->toArray();
+
+ $this->assertIsArray($result);
+ $this->assertSame(10.50, $result['amount']);
+ $this->assertSame('TEST-001', $result['invoice']);
+ }
+
+ public function test_it_handles_nested_json_structure(): void
+ {
+ $jsonPayload = '{"customer": {"firstName": "John", "lastName": "Doe"}, "amount": 25.00}';
+
+ $service = new PayloadService($jsonPayload);
+
+ $result = $service->toArray();
+
+ $this->assertArrayHasKey('customer', $result);
+ $this->assertSame('John', $result['customer']['firstName']);
+ $this->assertSame('Doe', $result['customer']['lastName']);
+ $this->assertSame(25.00, $result['amount']);
+ }
+
+ public function test_it_handles_nested_array_structure(): void
+ {
+ $payload = [
+ 'customer' => [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ],
+ 'amount' => 25.00,
+ ];
+
+ $service = new PayloadService($payload);
+
+ $result = $service->toArray();
+
+ $this->assertSame($payload, $result);
+ }
+
+ public function test_it_handles_json_array(): void
+ {
+ $jsonPayload = '[{"id": 1}, {"id": 2}]';
+
+ $service = new PayloadService($jsonPayload);
+
+ $result = $service->toArray();
+
+ $this->assertIsArray($result);
+ $this->assertCount(2, $result);
+ $this->assertSame(1, $result[0]['id']);
+ $this->assertSame(2, $result[1]['id']);
+ }
+
+ public function test_it_throws_exception_for_empty_json_object(): void
+ {
+ // Empty JSON object {} decodes to empty array which is treated as null-ish
+ $this->expectException(Exception::class);
+ $this->expectExceptionMessage('Invalid or empty payload. Array or json format required.');
+
+ new PayloadService('{}');
+ }
+
+ public function test_it_handles_empty_array(): void
+ {
+ $service = new PayloadService([]);
+
+ $this->assertSame([], $service->toArray());
+ }
+
+ public function test_it_throws_error_for_invalid_json(): void
+ {
+ // Invalid JSON causes TypeError when trying to assign null to typed array property
+ $this->expectException(\TypeError::class);
+
+ new PayloadService('invalid json string');
+ }
+
+ public function test_it_throws_error_for_null_payload(): void
+ {
+ // Null payload causes Error when checking uninitialized typed property
+ $this->expectException(\Error::class);
+
+ new PayloadService(null);
+ }
+
+ public function test_it_throws_error_for_empty_string(): void
+ {
+ // Empty string causes TypeError when trying to assign null to typed array property
+ $this->expectException(\TypeError::class);
+
+ new PayloadService('');
+ }
+
+ public function test_it_throws_error_for_json_null(): void
+ {
+ // JSON null causes TypeError when trying to assign null to typed array property
+ $this->expectException(\TypeError::class);
+
+ new PayloadService('null');
+ }
+
+ public function test_it_handles_json_with_special_characters(): void
+ {
+ $jsonPayload = '{"description": "Test with special chars: \\"quoted\\" and unicode: é"}';
+
+ $service = new PayloadService($jsonPayload);
+
+ $result = $service->toArray();
+
+ $this->assertStringContainsString('"quoted"', $result['description']);
+ $this->assertStringContainsString('é', $result['description']);
+ }
+
+ public function test_it_handles_json_with_numbers(): void
+ {
+ $jsonPayload = '{"integer": 42, "float": 3.14, "scientific": 1.5e10}';
+
+ $service = new PayloadService($jsonPayload);
+
+ $result = $service->toArray();
+
+ $this->assertSame(42, $result['integer']);
+ $this->assertSame(3.14, $result['float']);
+ $this->assertSame(1.5e10, $result['scientific']);
+ }
+
+ public function test_it_handles_json_with_boolean_values(): void
+ {
+ $jsonPayload = '{"active": true, "deleted": false}';
+
+ $service = new PayloadService($jsonPayload);
+
+ $result = $service->toArray();
+
+ $this->assertTrue($result['active']);
+ $this->assertFalse($result['deleted']);
+ }
+
+ public function test_to_array_returns_same_array_multiple_times(): void
+ {
+ $payload = ['key' => 'value'];
+ $service = new PayloadService($payload);
+
+ $first = $service->toArray();
+ $second = $service->toArray();
+
+ $this->assertSame($first, $second);
+ }
+}
diff --git a/tests/Unit/Services/ServiceListParameters/DefaultParametersTest.php b/tests/Unit/Services/ServiceListParameters/DefaultParametersTest.php
new file mode 100644
index 00000000..ab4df0b0
--- /dev/null
+++ b/tests/Unit/Services/ServiceListParameters/DefaultParametersTest.php
@@ -0,0 +1,86 @@
+assertInstanceOf(ServiceListParameter::class, $defaultParams);
+ }
+
+ public function test_returns_service_list_from_data_method(): void
+ {
+ $serviceList = new ServiceList('TestService', 1, 'Pay');
+ $defaultParams = new DefaultParameters($serviceList);
+
+ $result = $defaultParams->data();
+
+ $this->assertSame($serviceList, $result);
+ }
+
+ public function test_service_list_has_empty_parameters_initially(): void
+ {
+ $serviceList = new ServiceList('TestService', 1, 'Pay');
+ $defaultParams = new DefaultParameters($serviceList);
+
+ $result = $defaultParams->data();
+
+ $this->assertEmpty($result->parameters());
+ }
+
+ public function test_stores_service_list_correctly(): void
+ {
+ $serviceList = new ServiceList('CreditCard', 2, 'Authorize');
+ $defaultParams = new DefaultParameters($serviceList);
+
+ $result = $defaultParams->data();
+
+ $this->assertInstanceOf(ServiceList::class, $result);
+ $this->assertSame('CreditCard', $result->name);
+ $this->assertSame(2, $result->version);
+ $this->assertSame('Authorize', $result->action);
+ }
+
+ public function test_can_be_used_as_base_for_decorator_pattern(): void
+ {
+ $serviceList = new ServiceList('iDEAL', 1, 'Pay');
+ $defaultParams = new DefaultParameters($serviceList);
+
+ // DefaultParameters acts as the base in the decorator pattern
+ $data = $defaultParams->data();
+
+ $this->assertInstanceOf(ServiceList::class, $data);
+ }
+
+ public function test_handles_different_service_names(): void
+ {
+ $services = [
+ ['name' => 'CreditCard', 'version' => 1, 'action' => 'Pay'],
+ ['name' => 'iDEAL', 'version' => 2, 'action' => 'Refund'],
+ ['name' => 'PayPal', 'version' => 1, 'action' => 'Authorize'],
+ ['name' => 'Bancontact', 'version' => 1, 'action' => 'Capture'],
+ ];
+
+ foreach ($services as $service) {
+ $serviceList = new ServiceList($service['name'], $service['version'], $service['action']);
+ $defaultParams = new DefaultParameters($serviceList);
+
+ $result = $defaultParams->data();
+
+ $this->assertSame($service['name'], $result->name);
+ $this->assertSame($service['version'], $result->version);
+ $this->assertSame($service['action'], $result->action);
+ }
+ }
+}
diff --git a/tests/Unit/Services/ServiceListParameters/ModelParametersTest.php b/tests/Unit/Services/ServiceListParameters/ModelParametersTest.php
new file mode 100644
index 00000000..b5af4321
--- /dev/null
+++ b/tests/Unit/Services/ServiceListParameters/ModelParametersTest.php
@@ -0,0 +1,220 @@
+ 'Test Street']);
+
+ $modelParams = new ModelParameters($defaultParams, $address);
+
+ $this->assertInstanceOf(ServiceListParameter::class, $modelParams);
+ }
+
+ public function test_adds_model_properties_as_parameters(): void
+ {
+ $serviceList = new ServiceList('TestService', 1, 'Pay');
+ $defaultParams = new DefaultParameters($serviceList);
+ $address = new Address([
+ 'street' => 'Main Street',
+ 'houseNumber' => '123',
+ 'city' => 'Amsterdam',
+ ]);
+
+ $modelParams = new ModelParameters($defaultParams, $address);
+ $result = $modelParams->data();
+
+ $parameters = $result->parameters();
+
+ // Check that parameters were added
+ $this->assertNotEmpty($parameters);
+
+ // Find the street parameter
+ $streetParam = array_filter($parameters, fn ($p) => $p['Name'] === 'Street');
+ $this->assertNotEmpty($streetParam);
+ $this->assertEquals('Main Street', array_values($streetParam)[0]['Value']);
+ }
+
+ public function test_uses_service_parameter_key_for_property_names(): void
+ {
+ $serviceList = new ServiceList('TestService', 1, 'Pay');
+ $defaultParams = new DefaultParameters($serviceList);
+ $address = new Address([
+ 'houseNumber' => '456',
+ ]);
+
+ $modelParams = new ModelParameters($defaultParams, $address);
+ $result = $modelParams->data();
+
+ $parameters = $result->parameters();
+
+ // The property name should be transformed to the service parameter key
+ $houseNumberParam = array_filter($parameters, fn ($p) => $p['Name'] === 'HouseNumber');
+ $this->assertNotEmpty($houseNumberParam);
+ }
+
+ public function test_sets_group_type_when_provided(): void
+ {
+ $serviceList = new ServiceList('TestService', 1, 'Pay');
+ $defaultParams = new DefaultParameters($serviceList);
+ $address = new Address([
+ 'street' => 'Test Street',
+ ]);
+
+ $modelParams = new ModelParameters($defaultParams, $address, 'BillingCustomer', null);
+ $result = $modelParams->data();
+
+ $parameters = $result->parameters();
+
+ $streetParam = array_filter($parameters, fn ($p) => $p['Name'] === 'Street');
+ $this->assertNotEmpty($streetParam);
+ $firstParam = array_values($streetParam)[0];
+ $this->assertEquals('BillingCustomer', $firstParam['GroupType']);
+ }
+
+ public function test_sets_group_key_when_provided(): void
+ {
+ $serviceList = new ServiceList('TestService', 1, 'Pay');
+ $defaultParams = new DefaultParameters($serviceList);
+ $address = new Address([
+ 'city' => 'Rotterdam',
+ ]);
+
+ $modelParams = new ModelParameters($defaultParams, $address, 'Address', 1);
+ $result = $modelParams->data();
+
+ $parameters = $result->parameters();
+
+ $cityParam = array_filter($parameters, fn ($p) => $p['Name'] === 'City');
+ $this->assertNotEmpty($cityParam);
+ $firstParam = array_values($cityParam)[0];
+ $this->assertEquals(1, $firstParam['GroupID']);
+ }
+
+ public function test_uses_empty_string_for_null_group_type(): void
+ {
+ $serviceList = new ServiceList('TestService', 1, 'Pay');
+ $defaultParams = new DefaultParameters($serviceList);
+ $address = new Address([
+ 'zipcode' => '1234AB',
+ ]);
+
+ $modelParams = new ModelParameters($defaultParams, $address);
+ $result = $modelParams->data();
+
+ $parameters = $result->parameters();
+
+ $zipcodeParam = array_filter($parameters, fn ($p) => $p['Name'] === 'Zipcode');
+ $this->assertNotEmpty($zipcodeParam);
+ $firstParam = array_values($zipcodeParam)[0];
+ $this->assertEquals('', $firstParam['GroupType']);
+ }
+
+ public function test_uses_empty_string_for_null_group_key(): void
+ {
+ $serviceList = new ServiceList('TestService', 1, 'Pay');
+ $defaultParams = new DefaultParameters($serviceList);
+ $address = new Address([
+ 'country' => 'NL',
+ ]);
+
+ $modelParams = new ModelParameters($defaultParams, $address);
+ $result = $modelParams->data();
+
+ $parameters = $result->parameters();
+
+ $countryParam = array_filter($parameters, fn ($p) => $p['Name'] === 'Country');
+ $this->assertNotEmpty($countryParam);
+ $firstParam = array_values($countryParam)[0];
+ $this->assertEquals('', $firstParam['GroupID']);
+ }
+
+ public function test_skips_unset_values(): void
+ {
+ $serviceList = new ServiceList('TestService', 1, 'Pay');
+ $defaultParams = new DefaultParameters($serviceList);
+ // Only set street - city remains uninitialized
+ $address = new Address([
+ 'street' => 'Test Street',
+ ]);
+
+ $modelParams = new ModelParameters($defaultParams, $address);
+ $result = $modelParams->data();
+
+ $parameters = $result->parameters();
+
+ // City should not be in parameters since it was never set
+ $cityParam = array_filter($parameters, fn ($p) => $p['Name'] === 'City');
+ $this->assertEmpty($cityParam);
+ }
+
+ public function test_skips_array_values(): void
+ {
+ $serviceList = new ServiceList('TestService', 1, 'Pay');
+ $defaultParams = new DefaultParameters($serviceList);
+ $person = new Person([
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ]);
+
+ $modelParams = new ModelParameters($defaultParams, $person);
+ $result = $modelParams->data();
+
+ $parameters = $result->parameters();
+
+ // Find firstName parameter
+ $firstNameParam = array_filter($parameters, fn ($p) => $p['Name'] === 'FirstName');
+ $this->assertNotEmpty($firstNameParam);
+ }
+
+ public function test_decorates_base_parameter_service(): void
+ {
+ $serviceList = new ServiceList('TestService', 1, 'Pay');
+ $defaultParams = new DefaultParameters($serviceList);
+
+ // Add first model
+ $address1 = new Address(['street' => 'Street 1']);
+ $modelParams1 = new ModelParameters($defaultParams, $address1);
+
+ // Chain another model (decorator pattern)
+ $address2 = new Address(['city' => 'City 2']);
+ $modelParams2 = new ModelParameters($modelParams1, $address2);
+
+ $result = $modelParams2->data();
+ $parameters = $result->parameters();
+
+ // Both parameters should be present
+ $streetParam = array_filter($parameters, fn ($p) => $p['Name'] === 'Street');
+ $cityParam = array_filter($parameters, fn ($p) => $p['Name'] === 'City');
+
+ $this->assertNotEmpty($streetParam);
+ $this->assertNotEmpty($cityParam);
+ }
+
+ public function test_handles_empty_model(): void
+ {
+ $serviceList = new ServiceList('TestService', 1, 'Pay');
+ $defaultParams = new DefaultParameters($serviceList);
+ $address = new Address();
+
+ $modelParams = new ModelParameters($defaultParams, $address);
+ $result = $modelParams->data();
+
+ // Should not throw exception, just return empty parameters
+ $this->assertInstanceOf(ServiceList::class, $result);
+ }
+}
diff --git a/tests/Unit/Services/TraitHelpers/HasIssuersTest.php b/tests/Unit/Services/TraitHelpers/HasIssuersTest.php
new file mode 100644
index 00000000..c1f6721a
--- /dev/null
+++ b/tests/Unit/Services/TraitHelpers/HasIssuersTest.php
@@ -0,0 +1,127 @@
+useMock();
+ }
+
+ /** @test */
+ public function it_fetches_issuers_list_via_ideal(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Specification/ideal*',
+ [
+ 'Actions' => [
+ [
+ 'Name' => 'Pay',
+ 'RequestParameters' => [
+ [
+ 'Name' => 'issuer',
+ 'ListItemDescriptions' => [
+ ['Value' => 'ABNANL2A', 'Description' => 'ABN AMRO'],
+ ['Value' => 'INGBNL2A', 'Description' => 'ING'],
+ ['Value' => 'RABONL2U', 'Description' => 'Rabobank'],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ]
+ ),
+ ]);
+
+ $issuers = $this->buckaroo->method('ideal')->issuers();
+
+ $this->assertIsArray($issuers);
+ $this->assertCount(3, $issuers);
+ $this->assertSame(['id' => 'ABNANL2A', 'name' => 'ABN AMRO'], $issuers[0]);
+ $this->assertSame(['id' => 'INGBNL2A', 'name' => 'ING'], $issuers[1]);
+ $this->assertSame(['id' => 'RABONL2U', 'name' => 'Rabobank'], $issuers[2]);
+ }
+
+ /** @test */
+ public function it_returns_empty_array_when_no_issuers_found(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Specification/ideal*',
+ [
+ 'Actions' => [
+ [
+ 'Name' => 'Pay',
+ 'RequestParameters' => [],
+ ],
+ ],
+ ]
+ ),
+ ]);
+
+ $issuers = $this->buckaroo->method('ideal')->issuers();
+
+ $this->assertIsArray($issuers);
+ $this->assertEmpty($issuers);
+ }
+
+ /** @test */
+ public function it_returns_empty_array_on_api_error(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Specification/ideal*',
+ []
+ ),
+ ]);
+
+ $issuers = $this->buckaroo->method('ideal')->issuers();
+
+ $this->assertIsArray($issuers);
+ $this->assertEmpty($issuers);
+ }
+
+ /** @test */
+ public function it_handles_empty_list_item_descriptions(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Specification/ideal*',
+ [
+ 'Actions' => [
+ [
+ 'Name' => 'Pay',
+ 'RequestParameters' => [
+ [
+ 'Name' => 'issuer',
+ 'ListItemDescriptions' => [],
+ ],
+ ],
+ ],
+ ],
+ ]
+ ),
+ ]);
+
+ $issuers = $this->buckaroo->method('ideal')->issuers();
+
+ $this->assertIsArray($issuers);
+ $this->assertEmpty($issuers);
+ }
+}
diff --git a/tests/Unit/Services/TransactionHeaders/ChannelHeaderTest.php b/tests/Unit/Services/TransactionHeaders/ChannelHeaderTest.php
new file mode 100644
index 00000000..d51057c7
--- /dev/null
+++ b/tests/Unit/Services/TransactionHeaders/ChannelHeaderTest.php
@@ -0,0 +1,118 @@
+getHeaders();
+
+ $this->assertCount(2, $headers);
+ $this->assertSame('Content-Type: application/json', $headers[0]);
+ $this->assertStringStartsWith('Channel: ', $headers[1]);
+ }
+
+ public function test_uses_default_channel_web(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader = new DefaultHeader();
+
+ $channelHeader = new ChannelHeader($baseHeader, $config);
+ $headers = $channelHeader->getHeaders();
+
+ $this->assertCount(1, $headers);
+ $this->assertSame('Channel: Web', $headers[0]);
+ }
+
+ public function test_uses_custom_channel_from_config(): void
+ {
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'test',
+ 'EUR',
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 'Mobile'
+ );
+ $baseHeader = new DefaultHeader();
+
+ $channelHeader = new ChannelHeader($baseHeader, $config);
+ $headers = $channelHeader->getHeaders();
+
+ $this->assertSame('Channel: Mobile', $headers[0]);
+ }
+
+ public function test_uses_merged_channel_value(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $config->merge(['channel' => 'API']);
+ $baseHeader = new DefaultHeader();
+
+ $channelHeader = new ChannelHeader($baseHeader, $config);
+ $headers = $channelHeader->getHeaders();
+
+ $this->assertSame('Channel: API', $headers[0]);
+ }
+
+ public function test_preserves_base_headers_from_chain(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader = new DefaultHeader([
+ 'Authorization: Bearer token',
+ 'X-Custom-Header: value',
+ ]);
+
+ $channelHeader = new ChannelHeader($baseHeader, $config);
+ $headers = $channelHeader->getHeaders();
+
+ $this->assertCount(3, $headers);
+ $this->assertSame('Authorization: Bearer token', $headers[0]);
+ $this->assertSame('X-Custom-Header: value', $headers[1]);
+ $this->assertSame('Channel: Web', $headers[2]);
+ }
+
+ public function test_channel_header_with_empty_base_headers(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader = new DefaultHeader([]);
+
+ $channelHeader = new ChannelHeader($baseHeader, $config);
+ $headers = $channelHeader->getHeaders();
+
+ $this->assertCount(1, $headers);
+ $this->assertSame('Channel: Web', $headers[0]);
+ }
+}
diff --git a/tests/Unit/Services/TransactionHeaders/CultureHeaderTest.php b/tests/Unit/Services/TransactionHeaders/CultureHeaderTest.php
new file mode 100644
index 00000000..9e7770fb
--- /dev/null
+++ b/tests/Unit/Services/TransactionHeaders/CultureHeaderTest.php
@@ -0,0 +1,117 @@
+getHeaders();
+
+ $this->assertCount(2, $headers);
+ $this->assertSame('Content-Type: application/json', $headers[0]);
+ $this->assertStringStartsWith('Culture: ', $headers[1]);
+ }
+
+ public function test_uses_default_culture_en_gb(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader = new DefaultHeader();
+
+ $cultureHeader = new CultureHeader($baseHeader, $config);
+ $headers = $cultureHeader->getHeaders();
+
+ $this->assertCount(1, $headers);
+ $this->assertSame('Culture: en-GB', $headers[0]);
+ }
+
+ public function test_uses_custom_culture_from_config(): void
+ {
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'test',
+ 'EUR',
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 'nl-NL'
+ );
+ $baseHeader = new DefaultHeader();
+
+ $cultureHeader = new CultureHeader($baseHeader, $config);
+ $headers = $cultureHeader->getHeaders();
+
+ $this->assertSame('Culture: nl-NL', $headers[0]);
+ }
+
+ public function test_uses_merged_culture_value(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $config->merge(['culture' => 'de-DE']);
+ $baseHeader = new DefaultHeader();
+
+ $cultureHeader = new CultureHeader($baseHeader, $config);
+ $headers = $cultureHeader->getHeaders();
+
+ $this->assertSame('Culture: de-DE', $headers[0]);
+ }
+
+ public function test_preserves_base_headers_from_chain(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader = new DefaultHeader([
+ 'Authorization: Bearer token',
+ 'X-Custom-Header: value',
+ ]);
+
+ $cultureHeader = new CultureHeader($baseHeader, $config);
+ $headers = $cultureHeader->getHeaders();
+
+ $this->assertCount(3, $headers);
+ $this->assertSame('Authorization: Bearer token', $headers[0]);
+ $this->assertSame('X-Custom-Header: value', $headers[1]);
+ $this->assertSame('Culture: en-GB', $headers[2]);
+ }
+
+ public function test_culture_header_with_empty_base_headers(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader = new DefaultHeader([]);
+
+ $cultureHeader = new CultureHeader($baseHeader, $config);
+ $headers = $cultureHeader->getHeaders();
+
+ $this->assertCount(1, $headers);
+ $this->assertSame('Culture: en-GB', $headers[0]);
+ }
+}
diff --git a/tests/Unit/Services/TransactionHeaders/DefaultHeaderTest.php b/tests/Unit/Services/TransactionHeaders/DefaultHeaderTest.php
new file mode 100644
index 00000000..2e038a07
--- /dev/null
+++ b/tests/Unit/Services/TransactionHeaders/DefaultHeaderTest.php
@@ -0,0 +1,83 @@
+assertSame([], $header->getHeaders());
+ }
+
+ public function test_returns_empty_array_when_constructed_with_null(): void
+ {
+ $header = new DefaultHeader(null);
+
+ $this->assertSame([], $header->getHeaders());
+ }
+
+ public function test_returns_provided_headers_array(): void
+ {
+ $headers = [
+ 'Content-Type: application/json',
+ 'Accept: application/json',
+ ];
+
+ $header = new DefaultHeader($headers);
+
+ $this->assertSame($headers, $header->getHeaders());
+ }
+
+ public function test_returns_single_header(): void
+ {
+ $headers = ['Authorization: Bearer token123'];
+
+ $header = new DefaultHeader($headers);
+
+ $this->assertSame($headers, $header->getHeaders());
+ }
+
+ public function test_preserves_header_order(): void
+ {
+ $headers = [
+ 'Header-A: value1',
+ 'Header-B: value2',
+ 'Header-C: value3',
+ ];
+
+ $header = new DefaultHeader($headers);
+
+ $this->assertSame($headers, $header->getHeaders());
+ $this->assertSame('Header-A: value1', $header->getHeaders()[0]);
+ $this->assertSame('Header-B: value2', $header->getHeaders()[1]);
+ $this->assertSame('Header-C: value3', $header->getHeaders()[2]);
+ }
+
+ public function test_handles_empty_string_headers(): void
+ {
+ $headers = [''];
+
+ $header = new DefaultHeader($headers);
+
+ $this->assertSame([''], $header->getHeaders());
+ }
+
+ public function test_handles_headers_with_special_characters(): void
+ {
+ $headers = [
+ 'X-Custom-Header: value=with;special&chars',
+ 'X-Unicode-Header: value-with-\u00e9',
+ ];
+
+ $header = new DefaultHeader($headers);
+
+ $this->assertSame($headers, $header->getHeaders());
+ }
+}
diff --git a/tests/Unit/Services/TransactionHeaders/HmacHeaderTest.php b/tests/Unit/Services/TransactionHeaders/HmacHeaderTest.php
new file mode 100644
index 00000000..f83d049d
--- /dev/null
+++ b/tests/Unit/Services/TransactionHeaders/HmacHeaderTest.php
@@ -0,0 +1,204 @@
+getHeaders();
+
+ $this->assertCount(2, $headers);
+ $this->assertSame('Content-Type: application/json', $headers[0]);
+ $this->assertStringStartsWith('Authorization: hmac ', $headers[1]);
+ }
+
+ public function test_authorization_header_contains_website_key(): void
+ {
+ $config = new DefaultConfig('myWebsiteKey', 'mySecretKey');
+ $baseHeader = new DefaultHeader();
+
+ $hmacHeader = new HmacHeader(
+ $baseHeader,
+ $config,
+ 'https://checkout.buckaroo.nl/json/Transaction',
+ '{"test":"data"}',
+ 'POST'
+ );
+ $headers = $hmacHeader->getHeaders();
+
+ $authHeader = str_replace('Authorization: hmac ', '', $headers[0]);
+ $parts = explode(':', $authHeader);
+
+ $this->assertSame('myWebsiteKey', $parts[0]);
+ }
+
+ public function test_authorization_header_has_four_parts(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader = new DefaultHeader();
+
+ $hmacHeader = new HmacHeader(
+ $baseHeader,
+ $config,
+ 'https://checkout.buckaroo.nl/json/Transaction',
+ '{"test":"data"}',
+ 'POST'
+ );
+ $headers = $hmacHeader->getHeaders();
+
+ $authHeader = str_replace('Authorization: hmac ', '', $headers[0]);
+ $parts = explode(':', $authHeader);
+
+ // Format: websiteKey:hmac:nonce:timestamp
+ $this->assertCount(4, $parts);
+ $this->assertSame('websiteKey', $parts[0]);
+ $this->assertNotEmpty($parts[1]); // hmac signature
+ $this->assertNotEmpty($parts[2]); // nonce
+ $this->assertIsNumeric($parts[3]); // timestamp
+ }
+
+ public function test_preserves_base_headers_from_chain(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader = new DefaultHeader([
+ 'Accept: application/json',
+ 'X-Custom-Header: value',
+ ]);
+
+ $hmacHeader = new HmacHeader(
+ $baseHeader,
+ $config,
+ 'https://checkout.buckaroo.nl/json/Transaction',
+ '{"test":"data"}',
+ 'POST'
+ );
+ $headers = $hmacHeader->getHeaders();
+
+ $this->assertCount(3, $headers);
+ $this->assertSame('Accept: application/json', $headers[0]);
+ $this->assertSame('X-Custom-Header: value', $headers[1]);
+ $this->assertStringStartsWith('Authorization: hmac ', $headers[2]);
+ }
+
+ public function test_generates_different_hmac_for_different_content(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader1 = new DefaultHeader();
+ $baseHeader2 = new DefaultHeader();
+
+ $hmacHeader1 = new HmacHeader(
+ $baseHeader1,
+ $config,
+ 'https://checkout.buckaroo.nl/json/Transaction',
+ '{"content":"first"}',
+ 'POST'
+ );
+
+ $hmacHeader2 = new HmacHeader(
+ $baseHeader2,
+ $config,
+ 'https://checkout.buckaroo.nl/json/Transaction',
+ '{"content":"second"}',
+ 'POST'
+ );
+
+ $auth1 = str_replace('Authorization: hmac ', '', $hmacHeader1->getHeaders()[0]);
+ $auth2 = str_replace('Authorization: hmac ', '', $hmacHeader2->getHeaders()[0]);
+
+ $hmac1 = explode(':', $auth1)[1];
+ $hmac2 = explode(':', $auth2)[1];
+
+ $this->assertNotSame($hmac1, $hmac2);
+ }
+
+ public function test_generates_different_nonce_for_each_request(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader1 = new DefaultHeader();
+ $baseHeader2 = new DefaultHeader();
+
+ $hmacHeader1 = new HmacHeader(
+ $baseHeader1,
+ $config,
+ 'https://checkout.buckaroo.nl/json/Transaction',
+ '{"test":"data"}',
+ 'POST'
+ );
+
+ $hmacHeader2 = new HmacHeader(
+ $baseHeader2,
+ $config,
+ 'https://checkout.buckaroo.nl/json/Transaction',
+ '{"test":"data"}',
+ 'POST'
+ );
+
+ $auth1 = str_replace('Authorization: hmac ', '', $hmacHeader1->getHeaders()[0]);
+ $auth2 = str_replace('Authorization: hmac ', '', $hmacHeader2->getHeaders()[0]);
+
+ $nonce1 = explode(':', $auth1)[2];
+ $nonce2 = explode(':', $auth2)[2];
+
+ $this->assertNotSame($nonce1, $nonce2);
+ }
+
+ public function test_hmac_header_with_empty_content(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader = new DefaultHeader();
+
+ $hmacHeader = new HmacHeader(
+ $baseHeader,
+ $config,
+ 'https://checkout.buckaroo.nl/json/Transaction',
+ '',
+ 'GET'
+ );
+ $headers = $hmacHeader->getHeaders();
+
+ $this->assertCount(1, $headers);
+ $this->assertStringStartsWith('Authorization: hmac ', $headers[0]);
+ }
+
+ public function test_hmac_header_with_different_http_methods(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $methods = ['POST', 'GET', 'PUT', 'DELETE'];
+
+ foreach ($methods as $method) {
+ $baseHeader = new DefaultHeader();
+ $hmacHeader = new HmacHeader(
+ $baseHeader,
+ $config,
+ 'https://checkout.buckaroo.nl/json/Transaction',
+ '{"test":"data"}',
+ $method
+ );
+ $headers = $hmacHeader->getHeaders();
+
+ $this->assertStringStartsWith(
+ 'Authorization: hmac ',
+ $headers[0],
+ "Failed for HTTP method: {$method}"
+ );
+ }
+ }
+}
diff --git a/tests/Unit/Services/TransactionHeaders/SoftwareHeaderTest.php b/tests/Unit/Services/TransactionHeaders/SoftwareHeaderTest.php
new file mode 100644
index 00000000..9266a822
--- /dev/null
+++ b/tests/Unit/Services/TransactionHeaders/SoftwareHeaderTest.php
@@ -0,0 +1,138 @@
+getHeaders();
+
+ $this->assertCount(2, $headers);
+ $this->assertSame('Content-Type: application/json', $headers[0]);
+ $this->assertStringStartsWith('Software: ', $headers[1]);
+ }
+
+ public function test_software_header_contains_platform_info(): void
+ {
+ $config = new DefaultConfig(
+ 'websiteKey',
+ 'secretKey',
+ 'test',
+ 'EUR',
+ null,
+ null,
+ null,
+ 'MyPlatform',
+ '2.0.0',
+ 'MySupplier',
+ 'MyModule',
+ '1.5.0'
+ );
+ $baseHeader = new DefaultHeader();
+
+ $softwareHeader = new SoftwareHeader($baseHeader, $config);
+ $headers = $softwareHeader->getHeaders();
+
+ $this->assertCount(1, $headers);
+
+ $softwareJson = str_replace('Software: ', '', $headers[0]);
+ $softwareData = json_decode($softwareJson, true);
+
+ $this->assertSame('MyPlatform', $softwareData['PlatformName']);
+ $this->assertSame('2.0.0', $softwareData['PlatformVersion']);
+ $this->assertSame('MySupplier', $softwareData['ModuleSupplier']);
+ $this->assertSame('MyModule', $softwareData['ModuleName']);
+ $this->assertSame('1.5.0', $softwareData['ModuleVersion']);
+ }
+
+ public function test_software_header_uses_default_platform_values(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader = new DefaultHeader();
+
+ $softwareHeader = new SoftwareHeader($baseHeader, $config);
+ $headers = $softwareHeader->getHeaders();
+
+ $softwareJson = str_replace('Software: ', '', $headers[0]);
+ $softwareData = json_decode($softwareJson, true);
+
+ $this->assertSame('Empty Platform Name', $softwareData['PlatformName']);
+ $this->assertSame('1.0.0', $softwareData['PlatformVersion']);
+ $this->assertSame('Empty Module Supplier', $softwareData['ModuleSupplier']);
+ $this->assertSame('Empty Module name', $softwareData['ModuleName']);
+ $this->assertSame('1.0.0', $softwareData['ModuleVersion']);
+ }
+
+ public function test_software_header_produces_valid_json(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader = new DefaultHeader();
+
+ $softwareHeader = new SoftwareHeader($baseHeader, $config);
+ $headers = $softwareHeader->getHeaders();
+
+ $softwareJson = str_replace('Software: ', '', $headers[0]);
+ $decoded = json_decode($softwareJson, true);
+
+ $this->assertIsArray($decoded);
+ $this->assertArrayHasKey('PlatformName', $decoded);
+ $this->assertArrayHasKey('PlatformVersion', $decoded);
+ $this->assertArrayHasKey('ModuleSupplier', $decoded);
+ $this->assertArrayHasKey('ModuleName', $decoded);
+ $this->assertArrayHasKey('ModuleVersion', $decoded);
+ }
+
+ public function test_preserves_base_headers_from_chain(): void
+ {
+ $config = new DefaultConfig('websiteKey', 'secretKey');
+ $baseHeader = new DefaultHeader([
+ 'Authorization: Bearer token',
+ 'X-Custom-Header: value',
+ ]);
+
+ $softwareHeader = new SoftwareHeader($baseHeader, $config);
+ $headers = $softwareHeader->getHeaders();
+
+ $this->assertCount(3, $headers);
+ $this->assertSame('Authorization: Bearer token', $headers[0]);
+ $this->assertSame('X-Custom-Header: value', $headers[1]);
+ $this->assertStringStartsWith('Software: ', $headers[2]);
+ }
+}
diff --git a/tests/Unit/Services/TransactionServiceTest.php b/tests/Unit/Services/TransactionServiceTest.php
new file mode 100644
index 00000000..9fcce208
--- /dev/null
+++ b/tests/Unit/Services/TransactionServiceTest.php
@@ -0,0 +1,267 @@
+useMock();
+ }
+
+ public function test_status_fetches_transaction_status_successfully(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ "*/Transaction/Status/{$transactionKey}",
+ TestHelpers::successResponse([
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ 'SubCode' => ['Code' => 'S001'],
+ ],
+ ])
+ ),
+ ]);
+
+ $service = $this->buckaroo->transaction($transactionKey);
+ $response = $service->status();
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ $this->assertTrue($response->isSuccess());
+ $this->assertSame($transactionKey, $response->getTransactionKey());
+ }
+
+ public function test_status_parses_transaction_response_data(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $paymentKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ "*/Transaction/Status/{$transactionKey}",
+ TestHelpers::successResponse([
+ 'Key' => $transactionKey,
+ 'PaymentKey' => $paymentKey,
+ 'Invoice' => 'INV-99999',
+ 'AmountDebit' => 25.50,
+ 'Currency' => 'USD',
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ 'SubCode' => ['Code' => 'S001'],
+ ],
+ ])
+ ),
+ ]);
+
+ $service = $this->buckaroo->transaction($transactionKey);
+ $response = $service->status();
+
+ $this->assertSame($transactionKey, $response->getTransactionKey());
+ $this->assertSame($paymentKey, $response->getPaymentKey());
+ $this->assertSame('INV-99999', $response->getInvoice());
+ $this->assertSame('25.5', $response->getAmount());
+ $this->assertSame('USD', $response->getCurrency());
+ $this->assertSame(190, $response->getStatusCode());
+ }
+
+ public function test_refund_info_fetches_refund_information_successfully(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ "*/Transaction/RefundInfo/{$transactionKey}",
+ TestHelpers::successResponse([
+ 'Key' => $transactionKey,
+ 'AmountCredit' => 10.00,
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ ],
+ ])
+ ),
+ ]);
+
+ $service = $this->buckaroo->transaction($transactionKey);
+ $response = $service->refundInfo();
+
+ $this->assertInstanceOf(Response::class, $response);
+ $data = $response->toArray();
+ $this->assertSame($transactionKey, $data['Key']);
+ }
+
+ public function test_refund_info_returns_response_object(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ "*/Transaction/RefundInfo/{$transactionKey}",
+ TestHelpers::successResponse([
+ 'Key' => $transactionKey,
+ 'MaximumAmountToRefund' => 50.00,
+ 'RemainingAmountToRefund' => 25.00,
+ ])
+ ),
+ ]);
+
+ $service = $this->buckaroo->transaction($transactionKey);
+ $response = $service->refundInfo();
+
+ $this->assertInstanceOf(Response::class, $response);
+ $this->assertNotInstanceOf(TransactionResponse::class, $response);
+
+ $data = $response->toArray();
+ $this->assertArrayHasKey('Key', $data);
+ $this->assertSame($transactionKey, $data['Key']);
+ }
+
+ public function test_cancel_info_fetches_cancel_information_successfully(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ "*/Transaction/Cancel/{$transactionKey}",
+ TestHelpers::successResponse([
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ ],
+ ])
+ ),
+ ]);
+
+ $service = $this->buckaroo->transaction($transactionKey);
+ $response = $service->cancelInfo();
+
+ $this->assertInstanceOf(Response::class, $response);
+ $data = $response->toArray();
+ $this->assertSame($transactionKey, $data['Key']);
+ }
+
+ public function test_cancel_info_returns_response_object(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ "*/Transaction/Cancel/{$transactionKey}",
+ TestHelpers::successResponse([
+ 'Key' => $transactionKey,
+ 'IsCancellable' => true,
+ ])
+ ),
+ ]);
+
+ $service = $this->buckaroo->transaction($transactionKey);
+ $response = $service->cancelInfo();
+
+ $this->assertInstanceOf(Response::class, $response);
+ $this->assertNotInstanceOf(TransactionResponse::class, $response);
+
+ $data = $response->toArray();
+ $this->assertArrayHasKey('Key', $data);
+ $this->assertSame($transactionKey, $data['Key']);
+ }
+
+ public function test_handles_http_404_not_found_error(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ "*/Transaction/Status/{$transactionKey}",
+ [
+ 'Message' => 'Transaction not found',
+ 'Status' => [
+ 'Code' => ['Code' => 404],
+ ],
+ ],
+ 404
+ ),
+ ]);
+
+ $service = $this->buckaroo->transaction($transactionKey);
+ $response = $service->status();
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ $this->assertFalse($response->isSuccess());
+ }
+
+ public function test_handles_http_401_unauthorized_error(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ "*/Transaction/RefundInfo/{$transactionKey}",
+ [
+ 'Message' => 'Unauthorized',
+ 'Status' => [
+ 'Code' => ['Code' => 401],
+ ],
+ ],
+ 401
+ ),
+ ]);
+
+ $service = $this->buckaroo->transaction($transactionKey);
+ $response = $service->refundInfo();
+
+ $this->assertInstanceOf(Response::class, $response);
+ $data = $response->toArray();
+ $this->assertArrayHasKey('Message', $data);
+ $this->assertStringContainsString('Unauthorized', $data['Message']);
+ }
+
+ public function test_handles_http_500_server_error(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ "*/Transaction/Cancel/{$transactionKey}",
+ [
+ 'Message' => 'Internal server error',
+ 'Status' => [
+ 'Code' => ['Code' => 500],
+ ],
+ ],
+ 500
+ ),
+ ]);
+
+ $service = $this->buckaroo->transaction($transactionKey);
+ $response = $service->cancelInfo();
+
+ $this->assertInstanceOf(Response::class, $response);
+ $data = $response->toArray();
+ $this->assertArrayHasKey('Message', $data);
+ $this->assertStringContainsString('Internal server error', $data['Message']);
+ }
+}
diff --git a/tests/Unit/Transaction/ClientTest.php b/tests/Unit/Transaction/ClientTest.php
new file mode 100644
index 00000000..784891b9
--- /dev/null
+++ b/tests/Unit/Transaction/ClientTest.php
@@ -0,0 +1,341 @@
+useMock();
+ }
+
+ public function test_creates_client_with_config(): void
+ {
+ $config = new DefaultConfig('test-key', 'test-secret', 'test');
+ $client = new Client($config);
+
+ $this->assertInstanceOf(Client::class, $client);
+ $this->assertSame($config, $client->config());
+ }
+
+ public function test_builds_endpoint_urls_in_test_mode(): void
+ {
+ $config = new DefaultConfig('test-key', 'test-secret', 'test');
+ $client = new Client($config);
+
+ $baseUrl = 'https://testcheckout.buckaroo.nl';
+
+ $endpoints = [
+ 'json/Transaction/' => "{$baseUrl}/json/Transaction/",
+ 'json/Transaction/Status/TX-12345' => "{$baseUrl}/json/Transaction/Status/TX-12345",
+ 'json/Transaction/RefundInfo/TX-REFUND-001' => "{$baseUrl}/json/Transaction/RefundInfo/TX-REFUND-001",
+ 'json/Transaction/Cancel/TX-CANCEL-123' => "{$baseUrl}/json/Transaction/Cancel/TX-CANCEL-123",
+ 'json/DataRequest/' => "{$baseUrl}/json/DataRequest/",
+ 'json/batch/Transactions' => "{$baseUrl}/json/batch/Transactions",
+ 'json/batch/DataRequests' => "{$baseUrl}/json/batch/DataRequests",
+ 'json/Transaction/Specification/ideal?serviceVersion=2' => "{$baseUrl}/json/Transaction/Specification/ideal?serviceVersion=2",
+ ];
+
+ foreach ($endpoints as $path => $expected) {
+ $this->assertSame($expected, $client->getEndpoint($path));
+ }
+ }
+
+ public function test_builds_endpoint_urls_in_live_mode(): void
+ {
+ $config = new DefaultConfig('live-key', 'live-secret', 'live');
+ $client = new Client($config);
+
+ $endpoint = $client->getEndpoint('json/Transaction/');
+
+ $this->assertSame('https://checkout.buckaroo.nl/json/Transaction/', $endpoint);
+ }
+
+ public function test_switches_between_test_and_live_mode(): void
+ {
+ $testConfig = new DefaultConfig('test-key', 'test-secret', 'test');
+ $testClient = new Client($testConfig);
+
+ $liveConfig = new DefaultConfig('live-key', 'live-secret', 'live');
+ $liveClient = new Client($liveConfig);
+
+ $testEndpoint = $testClient->getEndpoint('json/Transaction/');
+ $liveEndpoint = $liveClient->getEndpoint('json/Transaction/');
+
+ $this->assertSame('https://testcheckout.buckaroo.nl/json/Transaction/', $testEndpoint);
+ $this->assertSame('https://checkout.buckaroo.nl/json/Transaction/', $liveEndpoint);
+ $this->assertNotSame($testEndpoint, $liveEndpoint);
+ }
+
+ public function test_returns_transaction_url(): void
+ {
+ $config = new DefaultConfig('test-key', 'test-secret', 'test');
+ $client = new Client($config);
+
+ $endpoint = $client->getTransactionUrl();
+
+ $this->assertSame('https://testcheckout.buckaroo.nl/json/Transaction/', $endpoint);
+ }
+
+ public function test_sends_get_request_to_endpoint(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ "*/Transaction/Status/{$transactionKey}",
+ TestHelpers::successResponse(['Key' => $transactionKey])
+ ),
+ ]);
+
+ $client = $this->buckaroo->client();
+ $response = $client->get(Response::class, $client->getEndpoint("json/Transaction/Status/{$transactionKey}"));
+
+ $this->assertInstanceOf(Response::class, $response);
+ $data = $response->toArray();
+ $this->assertSame($transactionKey, $data['Key']);
+ }
+
+ public function test_uses_default_transaction_endpoint_for_get(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Transaction/',
+ TestHelpers::successResponse()
+ ),
+ ]);
+
+ $client = $this->buckaroo->client();
+ $response = $client->get();
+
+ $this->assertInstanceOf(Response::class, $response);
+ }
+
+ public function test_get_request_returns_custom_response_class(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Transaction/',
+ TestHelpers::successResponse()
+ ),
+ ]);
+
+ $client = $this->buckaroo->client();
+ $response = $client->get(TransactionResponse::class);
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ }
+
+ public function test_sends_post_request_with_payload(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/Transaction/',
+ TestHelpers::successResponse([
+ 'Key' => $transactionKey,
+ 'Invoice' => 'INV-POST-001',
+ ])
+ ),
+ ]);
+
+ $request = new TransactionRequest();
+ $request->setData('AmountDebit', 25.00);
+ $request->setData('Invoice', 'INV-POST-001');
+
+ $client = $this->buckaroo->client();
+ $response = $client->post($request);
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ $this->assertTrue($response->isSuccess());
+ $this->assertSame($transactionKey, $response->getTransactionKey());
+ $this->assertSame('INV-POST-001', $response->getInvoice());
+ }
+
+ public function test_post_request_handles_empty_payload(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/Transaction/',
+ TestHelpers::successResponse()
+ ),
+ ]);
+
+ $client = $this->buckaroo->client();
+ $response = $client->post(null);
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ $this->assertTrue($response->isSuccess());
+ }
+
+ public function test_sends_batch_transaction_request(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/batch/Transactions',
+ TestHelpers::successResponse(['Key' => $transactionKey])
+ ),
+ ]);
+
+ $request = new TransactionRequest();
+ $request->setData('Transactions', [
+ ['AmountDebit' => 10.00, 'Invoice' => 'BATCH-1'],
+ ['AmountDebit' => 20.00, 'Invoice' => 'BATCH-2'],
+ ]);
+
+ $client = $this->buckaroo->client();
+ $response = $client->transactionBatchRequest($request);
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ $this->assertSame($transactionKey, $response->getTransactionKey());
+ }
+
+ public function test_sends_batch_data_request(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/batch/DataRequests',
+ TestHelpers::successResponse()
+ ),
+ ]);
+
+ $request = new TransactionRequest();
+ $request->setData('DataRequests', [
+ ['Type' => 'Status', 'TransactionKey' => TestHelpers::generateTransactionKey()],
+ ['Type' => 'RefundInfo', 'TransactionKey' => TestHelpers::generateTransactionKey()],
+ ]);
+
+ $client = $this->buckaroo->client();
+ $response = $client->dataBatchRequest($request);
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ }
+
+ public function test_fetches_payment_method_specification(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Specification/ideal*',
+ [
+ 'Services' => [
+ [
+ 'Name' => 'ideal',
+ 'Version' => 2,
+ 'Actions' => ['Pay', 'Refund'],
+ ],
+ ],
+ ]
+ ),
+ ]);
+
+ $client = $this->buckaroo->client();
+ $response = $client->specification('ideal', 2);
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ }
+
+ public function test_specification_defaults_service_version_to_zero(): void
+ {
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'GET',
+ '*/Specification/paypal*',
+ ['Services' => []]
+ ),
+ ]);
+
+ $client = $this->buckaroo->client();
+ $response = $client->specification('paypal');
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ }
+
+ public function test_sends_data_request_to_data_endpoint(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+
+ $this->mockBuckaroo->mockTransportRequests([
+ BuckarooMockRequest::json(
+ 'POST',
+ '*/DataRequest/',
+ TestHelpers::successResponse([
+ 'Key' => $transactionKey,
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ 'SubCode' => ['Code' => 'S001'],
+ 'DateTime' => date('Y-m-d\TH:i:s'),
+ ],
+ ])
+ ),
+ ]);
+
+ $request = new TransactionRequest();
+ $request->setData('Services', [
+ 'Name' => 'GetActiveSubscriptions',
+ 'Action' => 'GetActiveSubscriptions',
+ ]);
+
+ $client = $this->buckaroo->client();
+ $response = $client->dataRequest($request);
+
+ $this->assertInstanceOf(TransactionResponse::class, $response);
+ $this->assertTrue($response->isSuccess());
+ $this->assertSame($transactionKey, $response->getTransactionKey());
+ }
+
+ public function test_returns_current_config(): void
+ {
+ $config = new DefaultConfig('get-key', 'get-secret', 'test');
+ $client = new Client($config);
+
+ $retrievedConfig = $client->config();
+
+ $this->assertSame($config, $retrievedConfig);
+ $this->assertSame('get-key', $retrievedConfig->websiteKey());
+ $this->assertSame('get-secret', $retrievedConfig->secretKey());
+ }
+
+ public function test_updates_config_dynamically(): void
+ {
+ $originalConfig = new DefaultConfig('original-key', 'original-secret', 'test');
+ $client = new Client($originalConfig);
+
+ $newConfig = new DefaultConfig('updated-key', 'updated-secret', 'live');
+ $client->config($newConfig);
+
+ $retrievedConfig = $client->config();
+
+ $this->assertSame($newConfig, $retrievedConfig);
+ $this->assertNotSame($originalConfig, $retrievedConfig);
+ $this->assertSame('updated-key', $retrievedConfig->websiteKey());
+ $this->assertSame('updated-secret', $retrievedConfig->secretKey());
+ $this->assertSame('live', $retrievedConfig->mode());
+ }
+}
diff --git a/tests/Unit/Transaction/Request/BatchRequestTest.php b/tests/Unit/Transaction/Request/BatchRequestTest.php
new file mode 100644
index 00000000..257dbc7c
--- /dev/null
+++ b/tests/Unit/Transaction/Request/BatchRequestTest.php
@@ -0,0 +1,180 @@
+useMock();
+ }
+
+ public function test_creates_instance(): void
+ {
+ $batchRequest = new BatchRequest([]);
+
+ $this->assertInstanceOf(BatchRequest::class, $batchRequest);
+ }
+
+ public function test_extends_request_class(): void
+ {
+ $batchRequest = new BatchRequest([]);
+
+ $this->assertInstanceOf(Request::class, $batchRequest);
+ }
+
+ public function test_stores_empty_transactions(): void
+ {
+ $batchRequest = new BatchRequest([]);
+
+ $this->assertSame([], $this->getTransactions($batchRequest));
+ }
+
+ public function test_stores_transactions(): void
+ {
+ $client = $this->buckaroo->client();
+ $payment1 = $this->createIdealPayment($client, 'INV-001', 10.00);
+ $payment2 = $this->createIdealPayment($client, 'INV-002', 20.00);
+
+ $batchRequest = new BatchRequest([$payment1, $payment2]);
+ $transactions = $this->getTransactions($batchRequest);
+
+ $this->assertCount(2, $transactions);
+ $this->assertSame($payment1, $transactions[0]);
+ $this->assertSame($payment2, $transactions[1]);
+ }
+
+ public function test_preserves_transaction_order(): void
+ {
+ $client = $this->buckaroo->client();
+ $payment1 = $this->createIdealPayment($client, 'FIRST', 10.00);
+ $payment2 = $this->createIdealPayment($client, 'SECOND', 20.00);
+ $payment3 = $this->createIdealPayment($client, 'THIRD', 30.00);
+
+ $batchRequest = new BatchRequest([$payment1, $payment2, $payment3]);
+ $transactions = $this->getTransactions($batchRequest);
+
+ $this->assertSame($payment1, $transactions[0]);
+ $this->assertSame($payment2, $transactions[1]);
+ $this->assertSame($payment3, $transactions[2]);
+ }
+
+ public function test_to_json_returns_string(): void
+ {
+ $batchRequest = new BatchRequest([]);
+
+ $this->assertIsString($batchRequest->toJson());
+ }
+
+ public function test_to_json_returns_valid_json(): void
+ {
+ $client = $this->buckaroo->client();
+ $payment = $this->createIdealPayment($client, 'INV-001', 10.00);
+
+ $batchRequest = new BatchRequest([$payment]);
+
+ $decoded = json_decode($batchRequest->toJson(), true);
+
+ $this->assertSame(JSON_ERROR_NONE, json_last_error());
+ $this->assertIsArray($decoded);
+ }
+
+ public function test_to_json_empty_transactions(): void
+ {
+ $batchRequest = new BatchRequest([]);
+
+ $this->assertSame('[]', $batchRequest->toJson());
+ }
+
+ public function test_to_json_single_transaction(): void
+ {
+ $client = $this->buckaroo->client();
+ $payment = $this->createIdealPayment($client, 'SINGLE-001', 25.50);
+
+ $batchRequest = new BatchRequest([$payment]);
+
+ $decoded = json_decode($batchRequest->toJson(), true);
+
+ $this->assertCount(1, $decoded);
+ $this->assertArrayHasKey('Invoice', $decoded[0]);
+ $this->assertSame('SINGLE-001', $decoded[0]['Invoice']);
+ }
+
+ public function test_to_json_multiple_transactions(): void
+ {
+ $client = $this->buckaroo->client();
+
+ $batchRequest = new BatchRequest([
+ $this->createIdealPayment($client, 'INV-001', 10.00),
+ $this->createIdealPayment($client, 'INV-002', 20.00),
+ $this->createIdealPayment($client, 'INV-003', 30.00),
+ ]);
+
+ $decoded = json_decode($batchRequest->toJson(), true);
+
+ $this->assertCount(3, $decoded);
+ $this->assertSame('INV-001', $decoded[0]['Invoice']);
+ $this->assertSame('INV-002', $decoded[1]['Invoice']);
+ $this->assertSame('INV-003', $decoded[2]['Invoice']);
+ }
+
+ public function test_to_json_includes_amount(): void
+ {
+ $client = $this->buckaroo->client();
+ $payment = $this->createIdealPayment($client, 'INV-001', 99.99);
+
+ $batchRequest = new BatchRequest([$payment]);
+
+ $decoded = json_decode($batchRequest->toJson(), true);
+
+ $this->assertSame(99.99, $decoded[0]['AmountDebit']);
+ }
+
+ public function test_to_json_includes_service_parameters(): void
+ {
+ $client = $this->buckaroo->client();
+ $payment = $this->createIdealPayment($client, 'INV-001', 10.00, 'RABONL2U');
+
+ $batchRequest = new BatchRequest([$payment]);
+
+ $decoded = json_decode($batchRequest->toJson(), true);
+
+ $this->assertArrayHasKey('Services', $decoded[0]);
+ }
+
+ private function createIdealPayment($client, string $invoice, float $amount, string $issuer = 'ABNANL2A'): iDeal
+ {
+ $payment = new iDeal($client, 'ideal');
+ $payment->manually(true);
+ $payment->setPayload([
+ 'amountDebit' => $amount,
+ 'invoice' => $invoice,
+ 'issuer' => $issuer,
+ ]);
+ $payment->pay();
+
+ return $payment;
+ }
+
+ private function getTransactions(BatchRequest $batchRequest): array
+ {
+ $reflection = new ReflectionClass($batchRequest);
+ $property = $reflection->getProperty('transactions');
+ $property->setAccessible(true);
+
+ return $property->getValue($batchRequest);
+ }
+}
diff --git a/tests/Unit/Transaction/Request/HttpClient/GuzzleHttpClientV7Test.php b/tests/Unit/Transaction/Request/HttpClient/GuzzleHttpClientV7Test.php
new file mode 100644
index 00000000..7af5c597
--- /dev/null
+++ b/tests/Unit/Transaction/Request/HttpClient/GuzzleHttpClientV7Test.php
@@ -0,0 +1,197 @@
+assertInstanceOf(HttpClientInterface::class, $client);
+ }
+
+ public function test_constructor_sets_logger_from_config(): void
+ {
+ $config = new DefaultConfig('test-key', 'test-secret', 'test');
+ $client = new GuzzleHttpClientV7($config);
+
+ $reflection = new ReflectionClass($client);
+ $loggerProperty = $reflection->getProperty('logger');
+ $loggerProperty->setAccessible(true);
+
+ $this->assertNotNull($loggerProperty->getValue($client));
+ }
+
+ public function test_constructor_creates_http_client(): void
+ {
+ $config = new DefaultConfig('test-key', 'test-secret', 'test');
+ $client = new GuzzleHttpClientV7($config);
+
+ $reflection = new ReflectionClass($client);
+ $httpClientProperty = $reflection->getProperty('httpClient');
+ $httpClientProperty->setAccessible(true);
+
+ $this->assertInstanceOf(Client::class, $httpClientProperty->getValue($client));
+ }
+
+ public function test_call_method_sends_request_and_returns_response(): void
+ {
+ $responseBody = ['Key' => 'TX-123', 'Status' => ['Code' => ['Code' => 190]]];
+ $mock = new MockHandler([
+ new Response(200, ['Content-Type' => 'application/json'], json_encode($responseBody)),
+ ]);
+ $handlerStack = HandlerStack::create($mock);
+ $guzzleClient = new Client(['handler' => $handlerStack]);
+
+ $config = new DefaultConfig('test-key', 'test-secret', 'test');
+ $client = new GuzzleHttpClientV7($config);
+
+ // Inject our mocked Guzzle client
+ $reflection = new ReflectionClass($client);
+ $httpClientProperty = $reflection->getProperty('httpClient');
+ $httpClientProperty->setAccessible(true);
+ $httpClientProperty->setValue($client, $guzzleClient);
+
+ $result = $client->call(
+ 'https://testcheckout.buckaroo.nl/json/Transaction/',
+ ['Content-Type: application/json'],
+ 'POST',
+ '{"AmountDebit": 10.00}'
+ );
+
+ $this->assertIsArray($result);
+ $this->assertCount(2, $result);
+ $this->assertInstanceOf(Response::class, $result[0]);
+ $this->assertSame($responseBody, $result[1]);
+ }
+
+ public function test_call_method_handles_get_request(): void
+ {
+ $responseBody = ['Key' => 'TX-456'];
+ $mock = new MockHandler([
+ new Response(200, ['Content-Type' => 'application/json'], json_encode($responseBody)),
+ ]);
+ $handlerStack = HandlerStack::create($mock);
+ $guzzleClient = new Client(['handler' => $handlerStack]);
+
+ $config = new DefaultConfig('test-key', 'test-secret', 'test');
+ $client = new GuzzleHttpClientV7($config);
+
+ $reflection = new ReflectionClass($client);
+ $httpClientProperty = $reflection->getProperty('httpClient');
+ $httpClientProperty->setAccessible(true);
+ $httpClientProperty->setValue($client, $guzzleClient);
+
+ $result = $client->call(
+ 'https://testcheckout.buckaroo.nl/json/Transaction/Status/TX-456',
+ ['Accept: application/json'],
+ 'GET'
+ );
+
+ $this->assertIsArray($result);
+ $this->assertSame($responseBody, $result[1]);
+ }
+
+ public function test_call_method_throws_transfer_exception_on_guzzle_error(): void
+ {
+ $mock = new MockHandler([
+ new RequestException('Connection error', new Request('POST', 'test')),
+ ]);
+ $handlerStack = HandlerStack::create($mock);
+ $guzzleClient = new Client(['handler' => $handlerStack]);
+
+ $config = new DefaultConfig('test-key', 'test-secret', 'test');
+ $client = new GuzzleHttpClientV7($config);
+
+ $reflection = new ReflectionClass($client);
+ $httpClientProperty = $reflection->getProperty('httpClient');
+ $httpClientProperty->setAccessible(true);
+ $httpClientProperty->setValue($client, $guzzleClient);
+
+ $this->expectException(TransferException::class);
+ $this->expectExceptionMessage('Transfer failed');
+
+ $client->call(
+ 'https://testcheckout.buckaroo.nl/json/Transaction/',
+ ['Content-Type: application/json'],
+ 'POST',
+ '{"test": "data"}'
+ );
+ }
+
+ public function test_call_method_uses_custom_timeout_from_config(): void
+ {
+ // Config constructor accepts timeout and connectTimeout as parameters
+ $config = new DefaultConfig(
+ 'test-key',
+ 'test-secret',
+ 'test',
+ 'EUR',
+ '',
+ '',
+ '',
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 60, // timeout
+ 10 // connectTimeout
+ );
+
+ $client = new GuzzleHttpClientV7($config);
+
+ // Verify the client was created with custom timeouts
+ $this->assertInstanceOf(GuzzleHttpClientV7::class, $client);
+ $this->assertSame(60, $config->getTimeout());
+ $this->assertSame(10, $config->getConnectTimeout());
+ }
+
+ public function test_call_method_handles_null_data(): void
+ {
+ $responseBody = ['Status' => 'OK'];
+ $mock = new MockHandler([
+ new Response(200, ['Content-Type' => 'application/json'], json_encode($responseBody)),
+ ]);
+ $handlerStack = HandlerStack::create($mock);
+ $guzzleClient = new Client(['handler' => $handlerStack]);
+
+ $config = new DefaultConfig('test-key', 'test-secret', 'test');
+ $client = new GuzzleHttpClientV7($config);
+
+ $reflection = new ReflectionClass($client);
+ $httpClientProperty = $reflection->getProperty('httpClient');
+ $httpClientProperty->setAccessible(true);
+ $httpClientProperty->setValue($client, $guzzleClient);
+
+ $result = $client->call(
+ 'https://testcheckout.buckaroo.nl/json/Transaction/',
+ ['Accept: application/json'],
+ 'GET',
+ null
+ );
+
+ $this->assertIsArray($result);
+ $this->assertSame($responseBody, $result[1]);
+ }
+}
diff --git a/tests/Unit/Transaction/Request/HttpClient/HttpClientAbstractTest.php b/tests/Unit/Transaction/Request/HttpClient/HttpClientAbstractTest.php
new file mode 100644
index 00000000..2ee741cd
--- /dev/null
+++ b/tests/Unit/Transaction/Request/HttpClient/HttpClientAbstractTest.php
@@ -0,0 +1,190 @@
+logger = new DefaultLogger();
+
+ // Create a concrete implementation of the abstract class for testing
+ $this->httpClient = new class($this->logger) extends HttpClientAbstract {
+ public function call(string $url, array $headers, string $method, ?string $data = null)
+ {
+ return null;
+ }
+
+ // Expose protected methods for testing
+ public function testConvertHeadersFormat(array $headers): array
+ {
+ return $this->convertHeadersFormat($headers);
+ }
+
+ public function testGetDecodedResult($response, $result): array
+ {
+ return $this->getDecodedResult($response, $result);
+ }
+ };
+ }
+
+ public function test_implements_http_client_interface(): void
+ {
+ $this->assertInstanceOf(HttpClientInterface::class, $this->httpClient);
+ }
+
+ public function test_convert_headers_format_converts_string_headers_to_associative_array(): void
+ {
+ $headers = [
+ 'Content-Type: application/json',
+ 'Authorization: Bearer token123',
+ 'Accept: application/json',
+ ];
+
+ $result = $this->httpClient->testConvertHeadersFormat($headers);
+
+ $this->assertArrayHasKey('Content-Type', $result);
+ $this->assertArrayHasKey('Authorization', $result);
+ $this->assertArrayHasKey('Accept', $result);
+ $this->assertSame('application/json', $result['Content-Type']);
+ $this->assertSame('Bearer token123', $result['Authorization']);
+ $this->assertSame('application/json', $result['Accept']);
+ }
+
+ public function test_convert_headers_format_handles_empty_array(): void
+ {
+ $result = $this->httpClient->testConvertHeadersFormat([]);
+
+ $this->assertIsArray($result);
+ $this->assertEmpty($result);
+ }
+
+ public function test_convert_headers_format_handles_headers_with_colons_in_value(): void
+ {
+ $headers = [
+ 'X-Custom: value:with:colons',
+ ];
+
+ $result = $this->httpClient->testConvertHeadersFormat($headers);
+
+ $this->assertSame('value:with:colons', $result['X-Custom']);
+ }
+
+ public function test_get_decoded_result_returns_decoded_json(): void
+ {
+ $response = new Response(200);
+ $result = '{"key": "value", "number": 123}';
+
+ $decoded = $this->httpClient->testGetDecodedResult($response, $result);
+
+ $this->assertIsArray($decoded);
+ $this->assertSame('value', $decoded['key']);
+ $this->assertSame(123, $decoded['number']);
+ }
+
+ public function test_get_decoded_result_handles_nested_json(): void
+ {
+ $response = new Response(200);
+ $result = '{"outer": {"inner": {"deep": "value"}}}';
+
+ $decoded = $this->httpClient->testGetDecodedResult($response, $result);
+
+ $this->assertIsArray($decoded);
+ $this->assertSame('value', $decoded['outer']['inner']['deep']);
+ }
+
+ public function test_get_decoded_result_handles_empty_json_object(): void
+ {
+ $response = new Response(200);
+ $result = '{}';
+
+ $decoded = $this->httpClient->testGetDecodedResult($response, $result);
+
+ $this->assertIsArray($decoded);
+ $this->assertEmpty($decoded);
+ }
+
+ public function test_get_decoded_result_handles_json_array(): void
+ {
+ $response = new Response(200);
+ $result = '[{"id": 1}, {"id": 2}]';
+
+ $decoded = $this->httpClient->testGetDecodedResult($response, $result);
+
+ $this->assertIsArray($decoded);
+ $this->assertCount(2, $decoded);
+ $this->assertSame(1, $decoded[0]['id']);
+ $this->assertSame(2, $decoded[1]['id']);
+ }
+
+ public function test_get_decoded_result_throws_exception_for_invalid_json(): void
+ {
+ $response = new Response(500);
+ $result = 'Invalid JSON response';
+
+ $this->expectException(BuckarooException::class);
+ $this->expectExceptionMessage('Status code: 500 Message: Invalid JSON response');
+
+ $this->httpClient->testGetDecodedResult($response, $result);
+ }
+
+ public function test_get_decoded_result_throws_exception_for_html_response(): void
+ {
+ $response = new Response(502);
+ $result = 'Bad Gateway';
+
+ $this->expectException(BuckarooException::class);
+ $this->expectExceptionMessage('Status code: 502');
+
+ $this->httpClient->testGetDecodedResult($response, $result);
+ }
+
+ public function test_get_decoded_result_throws_exception_for_empty_response(): void
+ {
+ $response = new Response(204);
+ $result = '';
+
+ $this->expectException(BuckarooException::class);
+ $this->expectExceptionMessage('Status code: 204');
+
+ $this->httpClient->testGetDecodedResult($response, $result);
+ }
+
+ public function test_get_decoded_result_throws_exception_for_null_json(): void
+ {
+ $response = new Response(200);
+ $result = 'null';
+
+ $this->expectException(BuckarooException::class);
+
+ $this->httpClient->testGetDecodedResult($response, $result);
+ }
+
+ public function test_timeout_constants_are_defined(): void
+ {
+ // Use reflection to access protected constants
+ $reflection = new \ReflectionClass(HttpClientAbstract::class);
+
+ $timeout = $reflection->getConstant('TIMEOUT');
+ $connectTimeout = $reflection->getConstant('CONNECT_TIMEOUT');
+
+ $this->assertSame(30, $timeout);
+ $this->assertSame(5, $connectTimeout);
+ }
+}
diff --git a/tests/Unit/Transaction/Request/HttpClient/HttpClientFactoryTest.php b/tests/Unit/Transaction/Request/HttpClient/HttpClientFactoryTest.php
new file mode 100644
index 00000000..bcedcc8e
--- /dev/null
+++ b/tests/Unit/Transaction/Request/HttpClient/HttpClientFactoryTest.php
@@ -0,0 +1,54 @@
+assertInstanceOf(HttpClientInterface::class, $client);
+ }
+
+ public function test_creates_guzzle_v7_client_for_modern_guzzle(): void
+ {
+ $config = new DefaultConfig('test-key', 'test-secret', 'test');
+
+ $client = HttpClientFactory::createClient($config);
+
+ // Modern Guzzle (v6+) should create GuzzleHttpClientV7
+ $this->assertInstanceOf(GuzzleHttpClientV7::class, $client);
+ }
+
+ public function test_factory_method_is_static(): void
+ {
+ $reflection = new \ReflectionMethod(HttpClientFactory::class, 'createClient');
+
+ $this->assertTrue($reflection->isStatic());
+ $this->assertTrue($reflection->isPublic());
+ }
+
+ public function test_factory_accepts_config_parameter(): void
+ {
+ $reflection = new \ReflectionMethod(HttpClientFactory::class, 'createClient');
+ $parameters = $reflection->getParameters();
+
+ $this->assertCount(1, $parameters);
+ $this->assertSame('config', $parameters[0]->getName());
+ }
+}
diff --git a/tests/Unit/Transaction/Request/RequestTest.php b/tests/Unit/Transaction/Request/RequestTest.php
new file mode 100644
index 00000000..e2e8483a
--- /dev/null
+++ b/tests/Unit/Transaction/Request/RequestTest.php
@@ -0,0 +1,196 @@
+request = new Request();
+ }
+
+ public function test_it_implements_json_serializable(): void
+ {
+ $this->assertInstanceOf(JsonSerializable::class, $this->request);
+ }
+
+ public function test_it_implements_array_access(): void
+ {
+ $this->assertInstanceOf(ArrayAccess::class, $this->request);
+ }
+
+ public function test_it_implements_arrayable(): void
+ {
+ $this->assertInstanceOf(Arrayable::class, $this->request);
+ }
+
+ public function test_offset_set_with_key(): void
+ {
+ $this->request['key'] = 'value';
+
+ $this->assertSame('value', $this->request['key']);
+ }
+
+ public function test_offset_set_without_key(): void
+ {
+ $this->request[] = 'value1';
+ $this->request[] = 'value2';
+
+ $this->assertSame('value1', $this->request[0]);
+ $this->assertSame('value2', $this->request[1]);
+ }
+
+ public function test_offset_exists(): void
+ {
+ $this->request['exists'] = 'value';
+
+ $this->assertTrue(isset($this->request['exists']));
+ $this->assertFalse(isset($this->request['not_exists']));
+ }
+
+ public function test_offset_unset(): void
+ {
+ $this->request['key'] = 'value';
+
+ unset($this->request['key']);
+
+ $this->assertFalse(isset($this->request['key']));
+ }
+
+ public function test_offset_get_returns_null_for_non_existent(): void
+ {
+ $this->assertNull($this->request['non_existent']);
+ }
+
+ public function test_json_serialize(): void
+ {
+ $this->request['amount'] = 10.50;
+ $this->request['invoice'] = 'INV-001';
+
+ $json = json_encode($this->request);
+
+ $this->assertSame('{"amount":10.5,"invoice":"INV-001"}', $json);
+ }
+
+ public function test_to_array(): void
+ {
+ $this->request['key1'] = 'value1';
+ $this->request['key2'] = 'value2';
+
+ $array = $this->request->toArray();
+
+ $this->assertSame(['key1' => 'value1', 'key2' => 'value2'], $array);
+ }
+
+ public function test_to_json(): void
+ {
+ $this->request['amount'] = 25.00;
+
+ $json = $this->request->toJson();
+
+ $this->assertSame('{"amount":25}', $json);
+ }
+
+ public function test_set_header(): void
+ {
+ $this->request->setHeader('Content-Type', 'application/json');
+
+ $this->assertSame('application/json', $this->request->getHeader('Content-Type'));
+ }
+
+ public function test_set_header_is_case_insensitive(): void
+ {
+ $this->request->setHeader('Content-Type', 'application/json');
+
+ $this->assertSame('application/json', $this->request->getHeader('content-type'));
+ $this->assertSame('application/json', $this->request->getHeader('CONTENT-TYPE'));
+ }
+
+ public function test_get_header_returns_null_for_non_existent(): void
+ {
+ $this->assertNull($this->request->getHeader('Non-Existent'));
+ }
+
+ public function test_get_headers_method_exists(): void
+ {
+ // Note: The getHeaders() method has a bug in array_map usage
+ // (callback expects 2 args but only 1 array is passed)
+ // This test just verifies the method exists and returns array for empty headers
+ $this->assertTrue(method_exists($this->request, 'getHeaders'));
+ }
+
+ public function test_get_headers_returns_empty_array_when_no_headers(): void
+ {
+ $headers = $this->request->getHeaders();
+
+ $this->assertIsArray($headers);
+ $this->assertEmpty($headers);
+ }
+
+ public function test_get_data(): void
+ {
+ $this->request['key'] = 'value';
+
+ $data = $this->request->getData();
+
+ $this->assertSame(['key' => 'value'], $data);
+ }
+
+ public function test_get_data_returns_empty_array_by_default(): void
+ {
+ $data = $this->request->getData();
+
+ $this->assertIsArray($data);
+ $this->assertEmpty($data);
+ }
+
+ public function test_data_can_contain_nested_arrays(): void
+ {
+ $this->request['customer'] = [
+ 'firstName' => 'John',
+ 'lastName' => 'Doe',
+ ];
+
+ $data = $this->request->toArray();
+
+ $this->assertSame('John', $data['customer']['firstName']);
+ $this->assertSame('Doe', $data['customer']['lastName']);
+ }
+
+ public function test_header_overwrites_existing_value(): void
+ {
+ $this->request->setHeader('Authorization', 'Bearer old-token');
+ $this->request->setHeader('Authorization', 'Bearer new-token');
+
+ $this->assertSame('Bearer new-token', $this->request->getHeader('Authorization'));
+ }
+
+ /**
+ * Test documents a known bug in Request::getHeaders().
+ *
+ * The array_map callback expects ($value, $key) but only one array
+ * is passed, causing ArgumentCountError when headers exist.
+ * This line (123) cannot be covered until the source bug is fixed.
+ */
+ public function test_get_headers_has_known_bug_when_headers_exist(): void
+ {
+ $this->request->setHeader('Content-Type', 'application/json');
+
+ // The method throws ArgumentCountError due to incorrect array_map usage
+ $this->expectException(\ArgumentCountError::class);
+
+ $this->request->getHeaders();
+ }
+}
diff --git a/tests/Unit/Transaction/Request/TransactionRequestTest.php b/tests/Unit/Transaction/Request/TransactionRequestTest.php
new file mode 100644
index 00000000..95125e19
--- /dev/null
+++ b/tests/Unit/Transaction/Request/TransactionRequestTest.php
@@ -0,0 +1,407 @@
+data();
+
+ $this->assertArrayHasKey('ClientUserAgent', $data);
+ $this->assertSame('Test User Agent/1.0', $data['ClientUserAgent']);
+ }
+
+ public function test_initializes_with_empty_user_agent_if_not_set(): void
+ {
+ unset($_SERVER['HTTP_USER_AGENT']);
+
+ $request = new TransactionRequest();
+ $data = $request->data();
+
+ $this->assertArrayHasKey('ClientUserAgent', $data);
+ $this->assertSame('', $data['ClientUserAgent']);
+ }
+
+ public function test_sets_data_with_set_data_method(): void
+ {
+ $request = new TransactionRequest();
+ $request->setData('AmountDebit', 10.50);
+ $request->setData('Currency', 'EUR');
+ $request->setData('Invoice', 'INV-12345');
+
+ $data = $request->data();
+
+ $this->assertSame(10.50, $data['AmountDebit']);
+ $this->assertSame('EUR', $data['Currency']);
+ $this->assertSame('INV-12345', $data['Invoice']);
+ }
+
+ public function test_returns_this_from_set_data_for_method_chaining(): void
+ {
+ $request = new TransactionRequest();
+ $result = $request->setData('AmountDebit', 10.50);
+
+ $this->assertSame($request, $result);
+ }
+
+ public function test_chains_multiple_set_data_calls(): void
+ {
+ $request = new TransactionRequest();
+
+ $request->setData('AmountDebit', 25.00)
+ ->setData('Currency', 'USD')
+ ->setData('Invoice', 'TEST-001')
+ ->setData('Description', 'Test payment');
+
+ $data = $request->data();
+
+ $this->assertSame(25.00, $data['AmountDebit']);
+ $this->assertSame('USD', $data['Currency']);
+ $this->assertSame('TEST-001', $data['Invoice']);
+ $this->assertSame('Test payment', $data['Description']);
+ }
+
+ public function test_overwrites_existing_data_values(): void
+ {
+ $request = new TransactionRequest();
+ $request->setData('AmountDebit', 10.00);
+ $request->setData('AmountDebit', 20.00);
+
+ $data = $request->data();
+
+ $this->assertSame(20.00, $data['AmountDebit']);
+ }
+
+ public function test_returns_data_array_with_data_method(): void
+ {
+ $request = new TransactionRequest();
+ $request->setData('Key1', 'Value1');
+ $request->setData('Key2', 'Value2');
+
+ $data = $request->data();
+
+ $this->assertIsArray($data);
+ $this->assertArrayHasKey('Key1', $data);
+ $this->assertArrayHasKey('Key2', $data);
+ }
+
+ public function test_returns_data_array_with_get_data_method(): void
+ {
+ $request = new TransactionRequest();
+ $request->setData('TestKey', 'TestValue');
+
+ $data = $request->getData();
+
+ $this->assertIsArray($data);
+ $this->assertArrayHasKey('TestKey', $data);
+ $this->assertSame('TestValue', $data['TestKey']);
+ }
+
+ public function test_sets_payload_from_model(): void
+ {
+ $model = new class extends Model {
+ protected string $amountDebit = '15.50';
+ protected string $currency = 'EUR';
+ protected string $invoice = 'MODEL-123';
+ };
+
+ $request = new TransactionRequest();
+ $request->setPayload($model);
+
+ $data = $request->data();
+
+ $this->assertSame('15.50', $data['AmountDebit']);
+ $this->assertSame('EUR', $data['Currency']);
+ $this->assertSame('MODEL-123', $data['Invoice']);
+ }
+
+ public function test_returns_this_from_set_payload_for_method_chaining(): void
+ {
+ $model = new class extends Model {
+ protected string $amount = '10.00';
+ };
+
+ $request = new TransactionRequest();
+ $result = $request->setPayload($model);
+
+ $this->assertSame($request, $result);
+ }
+
+ public function test_chains_set_payload_with_set_data(): void
+ {
+ $model = new class extends Model {
+ protected string $amountDebit = '10.00';
+ };
+
+ $request = new TransactionRequest();
+ $request->setPayload($model)
+ ->setData('Currency', 'USD')
+ ->setData('Invoice', 'CHAIN-001');
+
+ $data = $request->data();
+
+ $this->assertSame('10.00', $data['AmountDebit']);
+ $this->assertSame('USD', $data['Currency']);
+ $this->assertSame('CHAIN-001', $data['Invoice']);
+ }
+
+ public function test_returns_services_instance(): void
+ {
+ $request = new TransactionRequest();
+ $services = $request->getServices();
+
+ $this->assertInstanceOf(Services::class, $services);
+ }
+
+ public function test_lazy_initializes_services(): void
+ {
+ $request = new TransactionRequest();
+ $data = $request->data();
+
+ $this->assertArrayNotHasKey('Services', $data);
+
+ $services = $request->getServices();
+
+ $this->assertInstanceOf(Services::class, $services);
+ $this->assertArrayHasKey('Services', $request->data());
+ }
+
+ public function test_returns_same_services_instance_on_multiple_calls(): void
+ {
+ $request = new TransactionRequest();
+
+ $services1 = $request->getServices();
+ $services2 = $request->getServices();
+
+ $this->assertSame($services1, $services2);
+ }
+
+ public function test_converts_to_array(): void
+ {
+ $request = new TransactionRequest();
+ $request->setData('AmountDebit', 10.00);
+ $request->setData('Currency', 'EUR');
+
+ $array = $request->toArray();
+
+ $this->assertIsArray($array);
+ $this->assertArrayHasKey('AmountDebit', $array);
+ $this->assertSame(10.00, $array['AmountDebit']);
+ $this->assertSame('EUR', $array['Currency']);
+ }
+
+ public function test_converts_arrayable_objects_to_arrays_in_to_array(): void
+ {
+ $arrayable = new class implements Arrayable {
+ public function toArray(): array
+ {
+ return ['key' => 'value', 'number' => 42];
+ }
+ };
+
+ $request = new TransactionRequest();
+ $request->setData('CustomObject', $arrayable);
+
+ $array = $request->toArray();
+
+ $this->assertIsArray($array['CustomObject']);
+ $this->assertSame('value', $array['CustomObject']['key']);
+ $this->assertSame(42, $array['CustomObject']['number']);
+ }
+
+ public function test_converts_services_to_array_in_to_array(): void
+ {
+ $request = new TransactionRequest();
+ $services = $request->getServices();
+
+ $array = $request->toArray();
+
+ $this->assertIsArray($array['Services']);
+ }
+
+ public function test_converts_to_json(): void
+ {
+ $request = new TransactionRequest();
+ $request->setData('AmountDebit', 10.50);
+ $request->setData('Currency', 'EUR');
+
+ $json = $request->toJson();
+
+ $this->assertJson($json);
+
+ $decoded = json_decode($json, true);
+ $this->assertSame(10.50, $decoded['AmountDebit']);
+ $this->assertSame('EUR', $decoded['Currency']);
+ }
+
+ public function test_implements_array_access_interface(): void
+ {
+ $request = new TransactionRequest();
+
+ // Test offsetSet
+ $request['AmountDebit'] = 15.00;
+ $request['Currency'] = 'USD';
+
+ // Test offsetGet
+ $this->assertSame(15.00, $request['AmountDebit']);
+ $this->assertSame('USD', $request['Currency']);
+
+ // Test offsetExists
+ $this->assertTrue(isset($request['Currency']));
+ $this->assertFalse(isset($request['NonExistent']));
+
+ // Test offsetGet for non-existent key
+ $this->assertNull($request['NonExistent']);
+
+ // Test offsetUnset
+ unset($request['AmountDebit']);
+ $this->assertFalse(isset($request['AmountDebit']));
+ $this->assertTrue(isset($request['Currency']));
+ }
+
+ public function test_implements_json_serializable(): void
+ {
+ $request = new TransactionRequest();
+ $request->setData('AmountDebit', 20.50);
+ $request->setData('Currency', 'GBP');
+
+ $json = json_encode($request);
+
+ $this->assertJson($json);
+
+ $decoded = json_decode($json, true);
+ $this->assertSame(20.50, $decoded['AmountDebit']);
+ $this->assertSame('GBP', $decoded['Currency']);
+ }
+
+ public function test_sets_request_header(): void
+ {
+ $request = new TransactionRequest();
+ $request->setHeader('Content-Type', 'application/json');
+ $request->setHeader('Authorization', 'Bearer token123');
+
+ $this->assertSame('application/json', $request->getHeader('Content-Type'));
+ $this->assertSame('Bearer token123', $request->getHeader('Authorization'));
+ }
+
+ public function test_gets_request_header_case_insensitively(): void
+ {
+ $request = new TransactionRequest();
+ $request->setHeader('Content-Type', 'application/json');
+
+ $this->assertSame('application/json', $request->getHeader('content-type'));
+ $this->assertSame('application/json', $request->getHeader('CONTENT-TYPE'));
+ $this->assertSame('application/json', $request->getHeader('Content-Type'));
+ }
+
+ public function test_returns_null_for_non_existent_header(): void
+ {
+ $request = new TransactionRequest();
+
+ $this->assertNull($request->getHeader('Non-Existent-Header'));
+ }
+
+ public function test_handles_complex_nested_data_structures(): void
+ {
+ $request = new TransactionRequest();
+ $request->setData('Services', [
+ 'Name' => 'ideal',
+ 'Action' => 'Pay',
+ 'Parameters' => [
+ ['Name' => 'issuer', 'Value' => 'ABNANL2A'],
+ ],
+ ]);
+
+ $data = $request->data();
+
+ $this->assertIsArray($data['Services']);
+ $this->assertSame('ideal', $data['Services']['Name']);
+ $this->assertIsArray($data['Services']['Parameters']);
+ }
+
+ public function test_supports_all_data_types(): void
+ {
+ $request = new TransactionRequest();
+
+ // Numeric values
+ $request->setData('AmountDebit', 10.50);
+ $request->setData('Quantity', 5);
+
+ // Boolean values
+ $request->setData('IsTest', true);
+ $request->setData('SendInvoice', false);
+
+ // Null values
+ $request->setData('OptionalField', null);
+
+ // Array values
+ $request->setData('Items', ['item1', 'item2', 'item3']);
+
+ $data = $request->data();
+
+ // Assert numeric values
+ $this->assertSame(10.50, $data['AmountDebit']);
+ $this->assertSame(5, $data['Quantity']);
+
+ // Assert boolean values
+ $this->assertTrue($data['IsTest']);
+ $this->assertFalse($data['SendInvoice']);
+
+ // Assert null values
+ $this->assertArrayHasKey('OptionalField', $data);
+ $this->assertNull($data['OptionalField']);
+
+ // Assert array values
+ $this->assertIsArray($data['Items']);
+ $this->assertCount(3, $data['Items']);
+ $this->assertSame(['item1', 'item2', 'item3'], $data['Items']);
+ }
+
+ public function test_handles_empty_data(): void
+ {
+ $_SERVER['HTTP_USER_AGENT'] = '';
+ $request = new TransactionRequest();
+
+ $array = $request->toArray();
+ $json = $request->toJson();
+
+ $this->assertIsArray($array);
+ $this->assertJson($json);
+ }
+
+ public function test_can_build_complete_payment_request(): void
+ {
+ $request = new TransactionRequest();
+ $request->setData('AmountDebit', 50.00)
+ ->setData('Currency', 'EUR')
+ ->setData('Invoice', 'COMPLETE-001')
+ ->setData('Description', 'Complete payment test')
+ ->setData('ReturnURL', 'https://example.com/return')
+ ->setData('Services', [
+ 'Name' => 'creditcard',
+ 'Action' => 'Pay',
+ ]);
+
+ $array = $request->toArray();
+
+ $this->assertSame(50.00, $array['AmountDebit']);
+ $this->assertSame('EUR', $array['Currency']);
+ $this->assertSame('COMPLETE-001', $array['Invoice']);
+ $this->assertSame('Complete payment test', $array['Description']);
+ $this->assertSame('https://example.com/return', $array['ReturnURL']);
+ $this->assertIsArray($array['Services']);
+ $this->assertSame('creditcard', $array['Services']['Name']);
+ }
+}
diff --git a/tests/Unit/Transaction/Response/ResponseTest.php b/tests/Unit/Transaction/Response/ResponseTest.php
new file mode 100644
index 00000000..739d8416
--- /dev/null
+++ b/tests/Unit/Transaction/Response/ResponseTest.php
@@ -0,0 +1,196 @@
+ 200];
+ $data = ['Key' => 'TX-123', 'Status' => ['Code' => ['Code' => 190]]];
+
+ $response = new Response($httpResponse, $data);
+
+ $this->assertInstanceOf(Response::class, $response);
+ $this->assertSame($httpResponse, $response->getHttpResponse());
+ $this->assertSame($data, $response->toArray());
+ }
+
+ public function test_returns_http_response_object(): void
+ {
+ $httpResponse = (object)['status' => 200, 'body' => 'test'];
+ $data = ['test' => 'value'];
+
+ $response = new Response($httpResponse, $data);
+
+ $this->assertSame($httpResponse, $response->getHttpResponse());
+ $this->assertSame(200, $response->getHttpResponse()->status);
+ $this->assertSame('test', $response->getHttpResponse()->body);
+ }
+
+ public function test_converts_to_array(): void
+ {
+ $data = [
+ 'Key' => 'TX-123',
+ 'Status' => ['Code' => ['Code' => 190]],
+ 'Invoice' => 'INV-001',
+ 'Currency' => 'EUR',
+ ];
+
+ $response = new Response(null, $data);
+
+ $this->assertSame($data, $response->toArray());
+ }
+
+ public function test_implements_array_access_interface(): void
+ {
+ $data = [
+ 'Key' => 'TX-456',
+ 'Invoice' => 'INV-002',
+ 'Currency' => 'USD',
+ ];
+
+ $response = new Response(null, $data);
+
+ // Test offsetGet
+ $this->assertSame('TX-456', $response['Key']);
+ $this->assertSame('INV-002', $response['Invoice']);
+ $this->assertSame('USD', $response['Currency']);
+
+ // Test offsetExists
+ $this->assertTrue(isset($response['Key']));
+ $this->assertTrue(isset($response['Invoice']));
+ $this->assertFalse(isset($response['NonExistent']));
+
+ // Test offsetGet returns null for non-existent
+ $this->assertNull($response['NonExistent']);
+ $this->assertNull($response['Missing']);
+
+ // Test offsetSet throws exception
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage("Can't set a value of a Response");
+ $response['Key'] = 'NEW-VALUE';
+ }
+
+ public function test_allows_unsetting_values_via_array_access(): void
+ {
+ $data = ['Key' => 'TX-222', 'Invoice' => 'INV-004'];
+
+ $response = new Response(null, $data);
+
+ unset($response['Invoice']);
+
+ $this->assertFalse(isset($response['Invoice']));
+ $this->assertTrue(isset($response['Key']));
+ }
+
+ public function test_handles_magic_get_methods(): void
+ {
+ $data = [
+ 'TransactionKey' => 'TX-MAGIC-001',
+ 'PaymentKey' => 'PAY-MAGIC-001',
+ 'Invoice' => 'INV-MAGIC-001',
+ ];
+
+ $response = new Response(null, $data);
+
+ // Test magic get methods work
+ $this->assertSame('TX-MAGIC-001', $response->getTransactionKey());
+ $this->assertSame('PAY-MAGIC-001', $response->getPaymentKey());
+ $this->assertSame('INV-MAGIC-001', $response->getInvoice());
+
+ // Test returns null for non-existent
+ $this->assertNull($response->getNonExistent());
+ $this->assertNull($response->getMissingField());
+
+ // Test non-get prefixed methods throw exception
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage('Call to undefined method');
+ $response->setKey('NEW-KEY');
+ }
+
+ public function test_handles_nested_data_structures(): void
+ {
+ $data = [
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ 'SubCode' => ['Code' => 'S001'],
+ ],
+ 'Services' => [
+ ['Name' => 'ideal', 'Action' => 'Pay'],
+ ],
+ ];
+
+ $response = new Response(null, $data);
+
+ $this->assertIsArray($response['Status']);
+ $this->assertSame(190, $response['Status']['Code']['Code']);
+ $this->assertIsArray($response['Services']);
+ $this->assertSame('ideal', $response['Services'][0]['Name']);
+ }
+
+ public function test_handles_empty_data(): void
+ {
+ $response = new Response(null, []);
+
+ $this->assertSame([], $response->toArray());
+ $this->assertNull($response['AnyKey']);
+ $this->assertFalse(isset($response['AnyKey']));
+ }
+
+ public function test_preserves_data_types_in_array(): void
+ {
+ $data = [
+ 'StringValue' => 'test',
+ 'IntValue' => 123,
+ 'FloatValue' => 45.67,
+ 'BoolTrue' => true,
+ 'BoolFalse' => false,
+ 'NullValue' => null,
+ 'ArrayValue' => ['item1', 'item2'],
+ ];
+
+ $response = new Response(null, $data);
+ $array = $response->toArray();
+
+ $this->assertSame('test', $array['StringValue']);
+ $this->assertSame(123, $array['IntValue']);
+ $this->assertSame(45.67, $array['FloatValue']);
+ $this->assertTrue($array['BoolTrue']);
+ $this->assertFalse($array['BoolFalse']);
+ $this->assertNull($array['NullValue']);
+ $this->assertSame(['item1', 'item2'], $array['ArrayValue']);
+ }
+
+ public function test_handles_null_http_response(): void
+ {
+ $data = ['Key' => 'TX-555'];
+
+ $response = new Response(null, $data);
+
+ $this->assertNull($response->getHttpResponse());
+ $this->assertSame($data, $response->toArray());
+ }
+
+ public function test_handles_complex_http_response_object(): void
+ {
+ $httpResponse = (object)[
+ 'status' => 200,
+ 'headers' => ['Content-Type' => 'application/json'],
+ 'body' => '{"test": "data"}',
+ 'timestamp' => time(),
+ ];
+
+ $response = new Response($httpResponse, ['Key' => 'TX-666']);
+
+ $retrievedHttp = $response->getHttpResponse();
+ $this->assertSame(200, $retrievedHttp->status);
+ $this->assertIsArray($retrievedHttp->headers);
+ $this->assertSame('application/json', $retrievedHttp->headers['Content-Type']);
+ }
+}
diff --git a/tests/Unit/Transaction/Response/TransactionResponseTest.php b/tests/Unit/Transaction/Response/TransactionResponseTest.php
new file mode 100644
index 00000000..04eafc22
--- /dev/null
+++ b/tests/Unit/Transaction/Response/TransactionResponseTest.php
@@ -0,0 +1,805 @@
+assertInstanceOf(\Buckaroo\Transaction\Response\Response::class, $response);
+ }
+
+ public function test_returns_true_for_is_success_with_status_190(): void
+ {
+ $data = [
+ 'Status' => ['Code' => ['Code' => 190]],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->isSuccess());
+ $this->assertFalse($response->isFailed());
+ }
+
+ public function test_returns_true_for_is_failed_with_status_490(): void
+ {
+ $data = [
+ 'Status' => ['Code' => ['Code' => 490]],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->isFailed());
+ $this->assertFalse($response->isSuccess());
+ }
+
+ public function test_returns_true_for_is_canceled_by_user(): void
+ {
+ $data = [
+ 'Status' => ['Code' => ['Code' => 890]],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->isCanceled());
+ $this->assertFalse($response->isSuccess());
+ }
+
+ public function test_returns_true_for_is_canceled_by_merchant(): void
+ {
+ $data = [
+ 'Status' => ['Code' => ['Code' => 891]],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->isCanceled());
+ $this->assertFalse($response->isSuccess());
+ }
+
+ public function test_returns_true_for_is_awaiting_consumer(): void
+ {
+ $data = [
+ 'Status' => ['Code' => ['Code' => 792]],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->isAwaitingConsumer());
+ $this->assertFalse($response->isSuccess());
+ }
+
+ public function test_returns_true_for_is_pending_processing(): void
+ {
+ $data = [
+ 'Status' => ['Code' => ['Code' => 791]],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->isPendingProcessing());
+ $this->assertFalse($response->isSuccess());
+ }
+
+ public function test_returns_true_for_is_waiting_on_user_input(): void
+ {
+ $data = [
+ 'Status' => ['Code' => ['Code' => 790]],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->isWaitingOnUserInput());
+ $this->assertFalse($response->isSuccess());
+ }
+
+ public function test_returns_true_for_is_rejected(): void
+ {
+ $data = [
+ 'Status' => ['Code' => ['Code' => 690]],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->isRejected());
+ $this->assertFalse($response->isSuccess());
+ }
+
+ public function test_returns_true_for_is_pending_approval(): void
+ {
+ $data = [
+ 'Status' => ['Code' => ['Code' => 794]],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->isPendingApproval());
+ $this->assertFalse($response->isSuccess());
+ }
+
+ public function test_returns_true_for_is_validation_failure(): void
+ {
+ $data = [
+ 'Status' => ['Code' => ['Code' => 491]],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->isValidationFailure());
+ $this->assertFalse($response->isSuccess());
+ }
+
+ public function test_returns_status_code(): void
+ {
+ $data = [
+ 'Status' => ['Code' => ['Code' => 190]],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame(190, $response->getStatusCode());
+ }
+
+ public function test_returns_null_for_missing_status_code(): void
+ {
+ $response = new TransactionResponse(null, []);
+
+ $this->assertNull($response->getStatusCode());
+ }
+
+ public function test_returns_sub_status_code(): void
+ {
+ $data = [
+ 'Status' => [
+ 'Code' => ['Code' => 190],
+ 'SubCode' => ['Code' => 'S001'],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('S001', $response->getSubStatusCode());
+ }
+
+ public function test_returns_null_for_missing_sub_status_code(): void
+ {
+ $data = [
+ 'Status' => ['Code' => ['Code' => 190]],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertNull($response->getSubStatusCode());
+ }
+
+ public function test_returns_transaction_key(): void
+ {
+ $transactionKey = TestHelpers::generateTransactionKey();
+ $data = ['Key' => $transactionKey];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame($transactionKey, $response->getTransactionKey());
+ }
+
+ public function test_returns_payment_key(): void
+ {
+ $paymentKey = TestHelpers::generateTransactionKey();
+ $data = ['PaymentKey' => $paymentKey];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame($paymentKey, $response->getPaymentKey());
+ }
+
+ public function test_returns_invoice(): void
+ {
+ $data = ['Invoice' => 'INV-12345'];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('INV-12345', $response->getInvoice());
+ }
+
+ public function test_returns_amount_as_string(): void
+ {
+ $data = ['AmountDebit' => 25.50];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('25.5', $response->getAmount());
+ }
+
+ public function test_returns_currency(): void
+ {
+ $data = ['Currency' => 'EUR'];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('EUR', $response->getCurrency());
+ }
+
+ public function test_returns_customer_name(): void
+ {
+ $data = ['CustomerName' => 'John Doe'];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('John Doe', $response->getCustomerName());
+ }
+
+ public function test_checks_for_redirect(): void
+ {
+ $data = [
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => 'https://payment.example.com/3ds',
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->hasRedirect());
+ }
+
+ public function test_returns_false_for_has_redirect_when_no_redirect(): void
+ {
+ $data = ['RequiredAction' => null];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertFalse($response->hasRedirect());
+ }
+
+ public function test_returns_false_for_has_redirect_when_action_is_not_redirect(): void
+ {
+ $data = [
+ 'RequiredAction' => [
+ 'Name' => 'Other',
+ 'RedirectURL' => 'https://example.com',
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertFalse($response->hasRedirect());
+ }
+
+ public function test_returns_redirect_url(): void
+ {
+ $data = [
+ 'RequiredAction' => [
+ 'Name' => 'Redirect',
+ 'RedirectURL' => 'https://payment.example.com/3ds',
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('https://payment.example.com/3ds', $response->getRedirectUrl());
+ }
+
+ public function test_returns_empty_string_when_no_redirect_url(): void
+ {
+ $response = new TransactionResponse(null, []);
+
+ $this->assertSame('', $response->getRedirectUrl());
+ }
+
+ public function test_returns_payment_method(): void
+ {
+ $data = [
+ 'Services' => [
+ ['Name' => 'ideal', 'Action' => 'Pay'],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('ideal', $response->getMethod());
+ }
+
+ public function test_returns_service_action(): void
+ {
+ $data = [
+ 'Services' => [
+ ['Name' => 'creditcard', 'Action' => 'Authorize'],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('Authorize', $response->getServiceAction());
+ }
+
+ public function test_returns_service_parameters(): void
+ {
+ $data = [
+ 'Services' => [
+ [
+ 'Name' => 'ideal',
+ 'Parameters' => [
+ ['Name' => 'Issuer', 'Value' => 'ABNANL2A'],
+ ['Name' => 'BIC', 'Value' => 'RABONL2U'],
+ ],
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+ $params = $response->getServiceParameters();
+
+ $this->assertIsArray($params);
+ $this->assertSame('ABNANL2A', $params['issuer']);
+ $this->assertSame('RABONL2U', $params['bic']);
+ }
+
+ public function test_converts_service_parameter_keys_to_lowercase(): void
+ {
+ $data = [
+ 'Services' => [
+ [
+ 'Parameters' => [
+ ['Name' => 'TransactionId', 'Value' => 'TX-123'],
+ ['Name' => 'UPPERCASE', 'Value' => 'test'],
+ ],
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+ $params = $response->getServiceParameters();
+
+ $this->assertArrayHasKey('transactionid', $params);
+ $this->assertArrayHasKey('uppercase', $params);
+ $this->assertSame('TX-123', $params['transactionid']);
+ }
+
+ public function test_returns_empty_array_when_no_service_parameters(): void
+ {
+ $data = [
+ 'Services' => [
+ ['Name' => 'ideal'],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame([], $response->getServiceParameters());
+ }
+
+ public function test_returns_custom_parameters(): void
+ {
+ $data = [
+ 'CustomParameters' => [
+ 'List' => [
+ ['Name' => 'OrderId', 'Value' => 'ORD-123'],
+ ['Name' => 'CustomField', 'Value' => 'CustomValue'],
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+ $params = $response->getCustomParameters();
+
+ $this->assertIsArray($params);
+ $this->assertSame('ORD-123', $params['OrderId']);
+ $this->assertSame('CustomValue', $params['CustomField']);
+ }
+
+ public function test_returns_empty_array_when_no_custom_parameters(): void
+ {
+ $response = new TransactionResponse(null, []);
+
+ $this->assertSame([], $response->getCustomParameters());
+ }
+
+ public function test_returns_additional_parameters(): void
+ {
+ $data = [
+ 'AdditionalParameters' => [
+ 'AdditionalParameter' => [
+ ['Name' => 'token', 'Value' => 'abc123'],
+ ['Name' => 'signature', 'Value' => 'xyz789'],
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+ $params = $response->getAdditionalParameters();
+
+ $this->assertIsArray($params);
+ $this->assertSame('abc123', $params['token']);
+ $this->assertSame('xyz789', $params['signature']);
+ }
+
+ public function test_returns_empty_array_when_no_additional_parameters(): void
+ {
+ $response = new TransactionResponse(null, []);
+
+ $this->assertSame([], $response->getAdditionalParameters());
+ }
+
+ public function test_returns_token_from_additional_parameters(): void
+ {
+ $data = [
+ 'AdditionalParameters' => [
+ 'AdditionalParameter' => [
+ ['Name' => 'token', 'Value' => ' my-token-123 '],
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('my-token-123', $response->getToken());
+ }
+
+ public function test_returns_signature_from_additional_parameters(): void
+ {
+ $data = [
+ 'AdditionalParameters' => [
+ 'AdditionalParameter' => [
+ ['Name' => 'signature', 'Value' => ' sig-abc-xyz '],
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('sig-abc-xyz', $response->getSignature());
+ }
+
+ public function test_returns_data_with_specific_key(): void
+ {
+ $data = [
+ 'Key' => 'TX-123',
+ 'Invoice' => 'INV-456',
+ 'Currency' => 'EUR',
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('TX-123', $response->data('Key'));
+ $this->assertSame('INV-456', $response->data('Invoice'));
+ $this->assertSame('EUR', $response->data('Currency'));
+ }
+
+ public function test_returns_all_data_when_no_key_specified(): void
+ {
+ $data = [
+ 'Key' => 'TX-789',
+ 'Invoice' => 'INV-999',
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame($data, $response->data());
+ }
+
+ public function test_returns_null_for_non_existent_data_key(): void
+ {
+ $data = ['Key' => 'TX-000'];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertNull($response->data('NonExistent'));
+ }
+
+ public function test_returns_value_with_get_method(): void
+ {
+ $data = [
+ 'CustomField' => 'CustomValue',
+ 'AnotherField' => 'AnotherValue',
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('CustomValue', $response->get('CustomField'));
+ $this->assertSame('AnotherValue', $response->get('AnotherField'));
+ }
+
+ public function test_returns_null_for_non_existent_get_key(): void
+ {
+ $response = new TransactionResponse(null, ['Key' => 'TX-111']);
+
+ $this->assertNull($response->get('NonExistent'));
+ }
+
+ public function test_detects_request_errors(): void
+ {
+ $data = [
+ 'RequestErrors' => [
+ 'ServiceErrors' => [
+ ['ErrorMessage' => 'Invalid service configuration'],
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->hasError());
+ }
+
+ public function test_detects_channel_errors(): void
+ {
+ $data = [
+ 'RequestErrors' => [
+ 'ChannelErrors' => [
+ ['ErrorMessage' => 'Channel not available'],
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->hasError());
+ }
+
+ public function test_detects_parameter_errors(): void
+ {
+ $data = [
+ 'RequestErrors' => [
+ 'ParameterErrors' => [
+ ['ErrorMessage' => 'Invalid parameter value'],
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->hasError());
+ }
+
+ public function test_returns_false_for_has_error_when_no_errors(): void
+ {
+ $response = new TransactionResponse(null, []);
+
+ $this->assertFalse($response->hasError());
+ }
+
+ public function test_returns_first_error(): void
+ {
+ $data = [
+ 'RequestErrors' => [
+ 'ServiceErrors' => [
+ ['ErrorMessage' => 'First service error'],
+ ['ErrorMessage' => 'Second service error'],
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+ $error = $response->getFirstError();
+
+ $this->assertIsArray($error);
+ $this->assertSame('First service error', $error['ErrorMessage']);
+ }
+
+ public function test_returns_empty_array_when_no_errors(): void
+ {
+ $response = new TransactionResponse(null, []);
+
+ $this->assertSame([], $response->getFirstError());
+ }
+
+ public function test_prioritizes_error_types_correctly(): void
+ {
+ $data = [
+ 'RequestErrors' => [
+ 'ActionErrors' => [
+ ['ErrorMessage' => 'Action error'],
+ ],
+ 'ChannelErrors' => [
+ ['ErrorMessage' => 'Channel error'],
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+ $error = $response->getFirstError();
+
+ $this->assertSame('Channel error', $error['ErrorMessage']);
+ }
+
+ public function test_detects_has_message(): void
+ {
+ $data = ['Message' => 'Transaction completed'];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->hasMessage());
+ }
+
+ public function test_returns_message(): void
+ {
+ $data = ['Message' => 'Payment successful'];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('Payment successful', $response->getMessage());
+ }
+
+ public function test_returns_empty_string_when_no_message(): void
+ {
+ $response = new TransactionResponse(null, []);
+
+ $this->assertSame('', $response->getMessage());
+ }
+
+ public function test_detects_has_consumer_message(): void
+ {
+ $data = [
+ 'ConsumerMessage' => [
+ 'HtmlText' => 'Payment pending
',
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->hasConsumerMessage());
+ }
+
+ public function test_returns_consumer_message(): void
+ {
+ $data = [
+ 'ConsumerMessage' => [
+ 'HtmlText' => 'Payment approved
',
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('Payment approved
', $response->getConsumerMessage());
+ }
+
+ public function test_returns_empty_string_when_no_consumer_message(): void
+ {
+ $response = new TransactionResponse(null, []);
+
+ $this->assertSame('', $response->getConsumerMessage());
+ }
+
+ public function test_detects_has_sub_code_message(): void
+ {
+ $data = [
+ 'Status' => [
+ 'SubCode' => [
+ 'Code' => 'S001',
+ 'Description' => 'Transaction successful',
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->hasSubCodeMessage());
+ }
+
+ public function test_returns_sub_code_message(): void
+ {
+ $data = [
+ 'Status' => [
+ 'SubCode' => [
+ 'Description' => 'Awaiting confirmation',
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('Awaiting confirmation', $response->getSubCodeMessage());
+ }
+
+ public function test_returns_empty_string_when_no_sub_code_message(): void
+ {
+ $response = new TransactionResponse(null, []);
+
+ $this->assertSame('', $response->getSubCodeMessage());
+ }
+
+ public function test_detects_has_some_error(): void
+ {
+ $data = [
+ 'RequestErrors' => [
+ 'ServiceErrors' => [
+ ['ErrorMessage' => 'Service unavailable'],
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertTrue($response->hasSomeError());
+ }
+
+ public function test_returns_some_error_from_request_errors(): void
+ {
+ $data = [
+ 'RequestErrors' => [
+ 'ServiceErrors' => [
+ ['ErrorMessage' => 'Service temporarily unavailable'],
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('Service temporarily unavailable', $response->getSomeError());
+ }
+
+ public function test_returns_some_error_from_consumer_message(): void
+ {
+ $data = [
+ 'ConsumerMessage' => [
+ 'HtmlText' => 'Payment failed',
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('Payment failed', $response->getSomeError());
+ }
+
+ public function test_returns_some_error_from_message(): void
+ {
+ $data = ['Message' => 'Transaction declined'];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('Transaction declined', $response->getSomeError());
+ }
+
+ public function test_returns_some_error_from_sub_code(): void
+ {
+ $data = [
+ 'Status' => [
+ 'SubCode' => [
+ 'Description' => 'Insufficient funds',
+ ],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('Insufficient funds', $response->getSomeError());
+ }
+
+ public function test_prioritizes_errors_correctly_in_get_some_error(): void
+ {
+ $data = [
+ 'RequestErrors' => [
+ 'ServiceErrors' => [
+ ['ErrorMessage' => 'Service error'],
+ ],
+ ],
+ 'ConsumerMessage' => [
+ 'HtmlText' => 'Consumer message',
+ ],
+ 'Message' => 'General message',
+ 'Status' => [
+ 'SubCode' => ['Description' => 'SubCode description'],
+ ],
+ ];
+
+ $response = new TransactionResponse(null, $data);
+
+ $this->assertSame('Service error', $response->getSomeError());
+ }
+
+ public function test_returns_empty_string_when_no_errors_at_all(): void
+ {
+ $response = new TransactionResponse(null, []);
+
+ $this->assertSame('', $response->getSomeError());
+ $this->assertFalse($response->hasSomeError());
+ }
+}