diff --git a/.editorconfig b/.editorconfig
index c1e2c64..e795376 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -10,3 +10,6 @@ end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
+
+[*.yaml]
+indent_size = 2
diff --git a/.github/workflows/static-analysis.yaml b/.github/workflows/static-analysis.yaml
new file mode 100644
index 0000000..f763077
--- /dev/null
+++ b/.github/workflows/static-analysis.yaml
@@ -0,0 +1,14 @@
+name: static analysis
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+
+permissions:
+ contents: read
+
+jobs:
+ tests:
+ uses: laravel/.github/.github/workflows/static-analysis.yml@main
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
new file mode 100644
index 0000000..33e6e49
--- /dev/null
+++ b/.github/workflows/tests.yaml
@@ -0,0 +1,24 @@
+name: Tests
+
+on: ["push", "pull_request"]
+
+jobs:
+ ci:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.2
+ tools: composer:v2
+ coverage: xdebug
+
+ - name: Install Dependencies
+ run: composer install --no-interaction --prefer-dist --optimize-autoloader
+
+ - name: Tests
+ run: ./vendor/bin/pest
diff --git a/.gitignore b/.gitignore
index b840dac..2328e65 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
vendor
+tools
test.php
*.cache
+*.txt
+*.lock
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5721ee1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,29 @@
+## IPay Unoffical Client
+
+```php
+guest()->login([
+ 'userName' => 'yourUsername',
+ 'accessCode' => 'yourPassword'
+ ]);
+
+ foreach ($session->historyTransactions([
+ 'accountNumber' => $session->customer->accountNumber,
+ 'startDate' => new \DateTimeImmutable('-5 days'),
+ ]) as $transaction) {
+ echo $transaction->remark.PHP_EOL;
+ }
+} catch (Throwable $e) {
+ echo $e->getMessage();
+}
+```
diff --git a/composer.json b/composer.json
index 62382b4..9405a6e 100644
--- a/composer.json
+++ b/composer.json
@@ -4,7 +4,8 @@
"type": "library",
"license": "MIT",
"autoload": {
- "classmap": ["src"]
+ "classmap": ["src"],
+ "files": ["src/functions.php"]
},
"authors": [
{
@@ -12,22 +13,28 @@
"email": "thanh1101dev@gmail.com"
}
],
- "minimum-stability": "dev",
+ "minimum-stability": "stable",
"prefer-stable": true,
"require": {
"php-http/httplug": "^2.4",
"php-http/client-common": "^2.7",
"nette/utils": "^4.0",
- "league/object-mapper": "dev-main"
+ "eventsauce/object-hydrator": "^1.4",
+ "symfony/options-resolver": "^7.1",
+ "symfony/dom-crawler": "^7.1",
+ "phpseclib/phpseclib": "^2.0",
+ "symfony/var-exporter": "^7.1"
},
"require-dev": {
"php-http/curl-client": "^2.3",
"nyholm/psr7": "^1.8",
- "phpstan/phpstan": "^1.11"
+ "phpstan/phpstan": "^1.11",
+ "pestphp/pest": "^2.34"
},
"config": {
"allow-plugins": {
- "php-http/discovery": true
+ "php-http/discovery": true,
+ "pestphp/pest-plugin": true
}
}
}
diff --git a/composer.lock b/composer.lock
deleted file mode 100644
index 569b372..0000000
--- a/composer.lock
+++ /dev/null
@@ -1,1140 +0,0 @@
-{
- "_readme": [
- "This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
- "This file is @generated automatically"
- ],
- "content-hash": "aea4d0667f4344934e73e1531cdcdf2e",
- "packages": [
- {
- "name": "clue/stream-filter",
- "version": "v1.7.0",
- "source": {
- "type": "git",
- "url": "https://github.com/clue/stream-filter.git",
- "reference": "049509fef80032cb3f051595029ab75b49a3c2f7"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/clue/stream-filter/zipball/049509fef80032cb3f051595029ab75b49a3c2f7",
- "reference": "049509fef80032cb3f051595029ab75b49a3c2f7",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3"
- },
- "require-dev": {
- "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"
- },
- "type": "library",
- "autoload": {
- "files": [
- "src/functions_include.php"
- ],
- "psr-4": {
- "Clue\\StreamFilter\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Christian Lück",
- "email": "christian@clue.engineering"
- }
- ],
- "description": "A simple and modern approach to stream filtering in PHP",
- "homepage": "https://github.com/clue/stream-filter",
- "keywords": [
- "bucket brigade",
- "callback",
- "filter",
- "php_user_filter",
- "stream",
- "stream_filter_append",
- "stream_filter_register"
- ],
- "support": {
- "issues": "https://github.com/clue/stream-filter/issues",
- "source": "https://github.com/clue/stream-filter/tree/v1.7.0"
- },
- "funding": [
- {
- "url": "https://clue.engineering/support",
- "type": "custom"
- },
- {
- "url": "https://github.com/clue",
- "type": "github"
- }
- ],
- "time": "2023-12-20T15:40:13+00:00"
- },
- {
- "name": "league/object-mapper",
- "version": "dev-main",
- "source": {
- "type": "git",
- "url": "https://github.com/thephpleague/object-mapper.git",
- "reference": "9cbcd21f3ce81aa2d614f65347f6abc901941ea7"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/thephpleague/object-mapper/zipball/9cbcd21f3ce81aa2d614f65347f6abc901941ea7",
- "reference": "9cbcd21f3ce81aa2d614f65347f6abc901941ea7",
- "shasum": ""
- },
- "require": {
- "ext-fileinfo": "*",
- "php": "^8.1"
- },
- "require-dev": {
- "friendsofphp/php-cs-fixer": "^3.4",
- "league/construct-finder": "^1.1",
- "phpbench/phpbench": "^1.2",
- "phpstan/phpstan": "^1.7",
- "phpunit/phpunit": "^10.0.0",
- "ramsey/uuid": "^4.2"
- },
- "suggest": {
- "league/construct-finder": "Find all classes in a directory for the best dumped hydrators."
- },
- "default-branch": true,
- "type": "library",
- "autoload": {
- "psr-4": {
- "League\\ObjectMapper\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Frank de Jonge",
- "email": "info@frankdejonge.nl"
- }
- ],
- "description": "Converts structured data into strict objects.",
- "keywords": [
- "construction",
- "constructor",
- "hydration",
- "mapper"
- ],
- "support": {
- "issues": "https://github.com/thephpleague/object-mapper/issues",
- "source": "https://github.com/thephpleague/object-mapper/tree/main"
- },
- "funding": [
- {
- "url": "https://github.com/frankdejonge",
- "type": "github"
- }
- ],
- "time": "2023-12-16T09:26:10+00:00"
- },
- {
- "name": "nette/utils",
- "version": "v4.0.4",
- "source": {
- "type": "git",
- "url": "https://github.com/nette/utils.git",
- "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/nette/utils/zipball/d3ad0aa3b9f934602cb3e3902ebccf10be34d218",
- "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218",
- "shasum": ""
- },
- "require": {
- "php": ">=8.0 <8.4"
- },
- "conflict": {
- "nette/finder": "<3",
- "nette/schema": "<1.2.2"
- },
- "require-dev": {
- "jetbrains/phpstorm-attributes": "dev-master",
- "nette/tester": "^2.5",
- "phpstan/phpstan": "^1.0",
- "tracy/tracy": "^2.9"
- },
- "suggest": {
- "ext-gd": "to use Image",
- "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
- "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
- "ext-json": "to use Nette\\Utils\\Json",
- "ext-mbstring": "to use Strings::lower() etc...",
- "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.0-dev"
- }
- },
- "autoload": {
- "classmap": [
- "src/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause",
- "GPL-2.0-only",
- "GPL-3.0-only"
- ],
- "authors": [
- {
- "name": "David Grudl",
- "homepage": "https://davidgrudl.com"
- },
- {
- "name": "Nette Community",
- "homepage": "https://nette.org/contributors"
- }
- ],
- "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
- "homepage": "https://nette.org",
- "keywords": [
- "array",
- "core",
- "datetime",
- "images",
- "json",
- "nette",
- "paginator",
- "password",
- "slugify",
- "string",
- "unicode",
- "utf-8",
- "utility",
- "validation"
- ],
- "support": {
- "issues": "https://github.com/nette/utils/issues",
- "source": "https://github.com/nette/utils/tree/v4.0.4"
- },
- "time": "2024-01-17T16:50:36+00:00"
- },
- {
- "name": "php-http/client-common",
- "version": "2.7.1",
- "source": {
- "type": "git",
- "url": "https://github.com/php-http/client-common.git",
- "reference": "1e19c059b0e4d5f717bf5d524d616165aeab0612"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-http/client-common/zipball/1e19c059b0e4d5f717bf5d524d616165aeab0612",
- "reference": "1e19c059b0e4d5f717bf5d524d616165aeab0612",
- "shasum": ""
- },
- "require": {
- "php": "^7.1 || ^8.0",
- "php-http/httplug": "^2.0",
- "php-http/message": "^1.6",
- "psr/http-client": "^1.0",
- "psr/http-factory": "^1.0",
- "psr/http-message": "^1.0 || ^2.0",
- "symfony/options-resolver": "~4.0.15 || ~4.1.9 || ^4.2.1 || ^5.0 || ^6.0 || ^7.0",
- "symfony/polyfill-php80": "^1.17"
- },
- "require-dev": {
- "doctrine/instantiator": "^1.1",
- "guzzlehttp/psr7": "^1.4",
- "nyholm/psr7": "^1.2",
- "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1",
- "phpspec/prophecy": "^1.10.2",
- "phpunit/phpunit": "^7.5.20 || ^8.5.33 || ^9.6.7"
- },
- "suggest": {
- "ext-json": "To detect JSON responses with the ContentTypePlugin",
- "ext-libxml": "To detect XML responses with the ContentTypePlugin",
- "php-http/cache-plugin": "PSR-6 Cache plugin",
- "php-http/logger-plugin": "PSR-3 Logger plugin",
- "php-http/stopwatch-plugin": "Symfony Stopwatch plugin"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Http\\Client\\Common\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Márk Sági-Kazár",
- "email": "mark.sagikazar@gmail.com"
- }
- ],
- "description": "Common HTTP Client implementations and tools for HTTPlug",
- "homepage": "http://httplug.io",
- "keywords": [
- "client",
- "common",
- "http",
- "httplug"
- ],
- "support": {
- "issues": "https://github.com/php-http/client-common/issues",
- "source": "https://github.com/php-http/client-common/tree/2.7.1"
- },
- "time": "2023-11-30T10:31:25+00:00"
- },
- {
- "name": "php-http/httplug",
- "version": "2.4.0",
- "source": {
- "type": "git",
- "url": "https://github.com/php-http/httplug.git",
- "reference": "625ad742c360c8ac580fcc647a1541d29e257f67"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-http/httplug/zipball/625ad742c360c8ac580fcc647a1541d29e257f67",
- "reference": "625ad742c360c8ac580fcc647a1541d29e257f67",
- "shasum": ""
- },
- "require": {
- "php": "^7.1 || ^8.0",
- "php-http/promise": "^1.1",
- "psr/http-client": "^1.0",
- "psr/http-message": "^1.0 || ^2.0"
- },
- "require-dev": {
- "friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0",
- "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Http\\Client\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Eric GELOEN",
- "email": "geloen.eric@gmail.com"
- },
- {
- "name": "Márk Sági-Kazár",
- "email": "mark.sagikazar@gmail.com",
- "homepage": "https://sagikazarmark.hu"
- }
- ],
- "description": "HTTPlug, the HTTP client abstraction for PHP",
- "homepage": "http://httplug.io",
- "keywords": [
- "client",
- "http"
- ],
- "support": {
- "issues": "https://github.com/php-http/httplug/issues",
- "source": "https://github.com/php-http/httplug/tree/2.4.0"
- },
- "time": "2023-04-14T15:10:03+00:00"
- },
- {
- "name": "php-http/message",
- "version": "1.16.1",
- "source": {
- "type": "git",
- "url": "https://github.com/php-http/message.git",
- "reference": "5997f3289332c699fa2545c427826272498a2088"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-http/message/zipball/5997f3289332c699fa2545c427826272498a2088",
- "reference": "5997f3289332c699fa2545c427826272498a2088",
- "shasum": ""
- },
- "require": {
- "clue/stream-filter": "^1.5",
- "php": "^7.2 || ^8.0",
- "psr/http-message": "^1.1 || ^2.0"
- },
- "provide": {
- "php-http/message-factory-implementation": "1.0"
- },
- "require-dev": {
- "ergebnis/composer-normalize": "^2.6",
- "ext-zlib": "*",
- "guzzlehttp/psr7": "^1.0 || ^2.0",
- "laminas/laminas-diactoros": "^2.0 || ^3.0",
- "php-http/message-factory": "^1.0.2",
- "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1",
- "slim/slim": "^3.0"
- },
- "suggest": {
- "ext-zlib": "Used with compressor/decompressor streams",
- "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories",
- "laminas/laminas-diactoros": "Used with Diactoros Factories",
- "slim/slim": "Used with Slim Framework PSR-7 implementation"
- },
- "type": "library",
- "autoload": {
- "files": [
- "src/filters.php"
- ],
- "psr-4": {
- "Http\\Message\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Márk Sági-Kazár",
- "email": "mark.sagikazar@gmail.com"
- }
- ],
- "description": "HTTP Message related tools",
- "homepage": "http://php-http.org",
- "keywords": [
- "http",
- "message",
- "psr-7"
- ],
- "support": {
- "issues": "https://github.com/php-http/message/issues",
- "source": "https://github.com/php-http/message/tree/1.16.1"
- },
- "time": "2024-03-07T13:22:09+00:00"
- },
- {
- "name": "php-http/promise",
- "version": "1.3.1",
- "source": {
- "type": "git",
- "url": "https://github.com/php-http/promise.git",
- "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-http/promise/zipball/fc85b1fba37c169a69a07ef0d5a8075770cc1f83",
- "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83",
- "shasum": ""
- },
- "require": {
- "php": "^7.1 || ^8.0"
- },
- "require-dev": {
- "friends-of-phpspec/phpspec-code-coverage": "^4.3.2 || ^6.3",
- "phpspec/phpspec": "^5.1.2 || ^6.2 || ^7.4"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Http\\Promise\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Joel Wurtz",
- "email": "joel.wurtz@gmail.com"
- },
- {
- "name": "Márk Sági-Kazár",
- "email": "mark.sagikazar@gmail.com"
- }
- ],
- "description": "Promise used for asynchronous HTTP requests",
- "homepage": "http://httplug.io",
- "keywords": [
- "promise"
- ],
- "support": {
- "issues": "https://github.com/php-http/promise/issues",
- "source": "https://github.com/php-http/promise/tree/1.3.1"
- },
- "time": "2024-03-15T13:55:21+00:00"
- },
- {
- "name": "psr/http-client",
- "version": "1.0.3",
- "source": {
- "type": "git",
- "url": "https://github.com/php-fig/http-client.git",
- "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
- "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
- "shasum": ""
- },
- "require": {
- "php": "^7.0 || ^8.0",
- "psr/http-message": "^1.0 || ^2.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Psr\\Http\\Client\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "PHP-FIG",
- "homepage": "https://www.php-fig.org/"
- }
- ],
- "description": "Common interface for HTTP clients",
- "homepage": "https://github.com/php-fig/http-client",
- "keywords": [
- "http",
- "http-client",
- "psr",
- "psr-18"
- ],
- "support": {
- "source": "https://github.com/php-fig/http-client"
- },
- "time": "2023-09-23T14:17:50+00:00"
- },
- {
- "name": "psr/http-factory",
- "version": "1.1.0",
- "source": {
- "type": "git",
- "url": "https://github.com/php-fig/http-factory.git",
- "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
- "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
- "shasum": ""
- },
- "require": {
- "php": ">=7.1",
- "psr/http-message": "^1.0 || ^2.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Psr\\Http\\Message\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "PHP-FIG",
- "homepage": "https://www.php-fig.org/"
- }
- ],
- "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
- "keywords": [
- "factory",
- "http",
- "message",
- "psr",
- "psr-17",
- "psr-7",
- "request",
- "response"
- ],
- "support": {
- "source": "https://github.com/php-fig/http-factory"
- },
- "time": "2024-04-15T12:06:14+00:00"
- },
- {
- "name": "psr/http-message",
- "version": "2.0",
- "source": {
- "type": "git",
- "url": "https://github.com/php-fig/http-message.git",
- "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
- "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
- "shasum": ""
- },
- "require": {
- "php": "^7.2 || ^8.0"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.0.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Psr\\Http\\Message\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "PHP-FIG",
- "homepage": "https://www.php-fig.org/"
- }
- ],
- "description": "Common interface for HTTP messages",
- "homepage": "https://github.com/php-fig/http-message",
- "keywords": [
- "http",
- "http-message",
- "psr",
- "psr-7",
- "request",
- "response"
- ],
- "support": {
- "source": "https://github.com/php-fig/http-message/tree/2.0"
- },
- "time": "2023-04-04T09:54:51+00:00"
- },
- {
- "name": "symfony/deprecation-contracts",
- "version": "v3.5.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
- "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
- "shasum": ""
- },
- "require": {
- "php": ">=8.1"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "3.5-dev"
- },
- "thanks": {
- "name": "symfony/contracts",
- "url": "https://github.com/symfony/contracts"
- }
- },
- "autoload": {
- "files": [
- "function.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "A generic function and convention to trigger deprecation notices",
- "homepage": "https://symfony.com",
- "support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2024-04-18T09:32:20+00:00"
- },
- {
- "name": "symfony/options-resolver",
- "version": "v7.1.1",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/options-resolver.git",
- "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55",
- "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55",
- "shasum": ""
- },
- "require": {
- "php": ">=8.2",
- "symfony/deprecation-contracts": "^2.5|^3"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\OptionsResolver\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Provides an improved replacement for the array_replace PHP function",
- "homepage": "https://symfony.com",
- "keywords": [
- "config",
- "configuration",
- "options"
- ],
- "support": {
- "source": "https://github.com/symfony/options-resolver/tree/v7.1.1"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2024-05-31T14:57:53+00:00"
- },
- {
- "name": "symfony/polyfill-php80",
- "version": "v1.30.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "77fa7995ac1b21ab60769b7323d600a991a90433"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433",
- "reference": "77fa7995ac1b21ab60769b7323d600a991a90433",
- "shasum": ""
- },
- "require": {
- "php": ">=7.1"
- },
- "type": "library",
- "extra": {
- "thanks": {
- "name": "symfony/polyfill",
- "url": "https://github.com/symfony/polyfill"
- }
- },
- "autoload": {
- "files": [
- "bootstrap.php"
- ],
- "psr-4": {
- "Symfony\\Polyfill\\Php80\\": ""
- },
- "classmap": [
- "Resources/stubs"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Ion Bazan",
- "email": "ion.bazan@gmail.com"
- },
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
- "homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "polyfill",
- "portable",
- "shim"
- ],
- "support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2024-05-31T15:07:36+00:00"
- }
- ],
- "packages-dev": [
- {
- "name": "nyholm/psr7",
- "version": "1.8.1",
- "source": {
- "type": "git",
- "url": "https://github.com/Nyholm/psr7.git",
- "reference": "aa5fc277a4f5508013d571341ade0c3886d4d00e"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/Nyholm/psr7/zipball/aa5fc277a4f5508013d571341ade0c3886d4d00e",
- "reference": "aa5fc277a4f5508013d571341ade0c3886d4d00e",
- "shasum": ""
- },
- "require": {
- "php": ">=7.2",
- "psr/http-factory": "^1.0",
- "psr/http-message": "^1.1 || ^2.0"
- },
- "provide": {
- "php-http/message-factory-implementation": "1.0",
- "psr/http-factory-implementation": "1.0",
- "psr/http-message-implementation": "1.0"
- },
- "require-dev": {
- "http-interop/http-factory-tests": "^0.9",
- "php-http/message-factory": "^1.0",
- "php-http/psr7-integration-tests": "^1.0",
- "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
- "symfony/error-handler": "^4.4"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.8-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Nyholm\\Psr7\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Tobias Nyholm",
- "email": "tobias.nyholm@gmail.com"
- },
- {
- "name": "Martijn van der Ven",
- "email": "martijn@vanderven.se"
- }
- ],
- "description": "A fast PHP7 implementation of PSR-7",
- "homepage": "https://tnyholm.se",
- "keywords": [
- "psr-17",
- "psr-7"
- ],
- "support": {
- "issues": "https://github.com/Nyholm/psr7/issues",
- "source": "https://github.com/Nyholm/psr7/tree/1.8.1"
- },
- "funding": [
- {
- "url": "https://github.com/Zegnat",
- "type": "github"
- },
- {
- "url": "https://github.com/nyholm",
- "type": "github"
- }
- ],
- "time": "2023-11-13T09:31:12+00:00"
- },
- {
- "name": "php-http/curl-client",
- "version": "2.3.2",
- "source": {
- "type": "git",
- "url": "https://github.com/php-http/curl-client.git",
- "reference": "0b869922458b1cde9137374545ed4fff7ac83623"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-http/curl-client/zipball/0b869922458b1cde9137374545ed4fff7ac83623",
- "reference": "0b869922458b1cde9137374545ed4fff7ac83623",
- "shasum": ""
- },
- "require": {
- "ext-curl": "*",
- "php": "^7.4 || ^8.0",
- "php-http/discovery": "^1.6",
- "php-http/httplug": "^2.0",
- "php-http/message": "^1.2",
- "psr/http-client": "^1.0",
- "psr/http-factory-implementation": "^1.0",
- "symfony/options-resolver": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0"
- },
- "provide": {
- "php-http/async-client-implementation": "1.0",
- "php-http/client-implementation": "1.0",
- "psr/http-client-implementation": "1.0"
- },
- "require-dev": {
- "guzzlehttp/psr7": "^2.0",
- "laminas/laminas-diactoros": "^2.0",
- "php-http/client-integration-tests": "^3.0",
- "php-http/message-factory": "^1.1",
- "phpunit/phpunit": "^7.5 || ^9.4"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Http\\Client\\Curl\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Михаил Красильников",
- "email": "m.krasilnikov@yandex.ru"
- }
- ],
- "description": "PSR-18 and HTTPlug Async client with cURL",
- "homepage": "http://php-http.org",
- "keywords": [
- "curl",
- "http",
- "psr-18"
- ],
- "support": {
- "issues": "https://github.com/php-http/curl-client/issues",
- "source": "https://github.com/php-http/curl-client/tree/2.3.2"
- },
- "time": "2024-03-03T08:21:07+00:00"
- },
- {
- "name": "php-http/discovery",
- "version": "1.19.4",
- "source": {
- "type": "git",
- "url": "https://github.com/php-http/discovery.git",
- "reference": "0700efda8d7526335132360167315fdab3aeb599"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-http/discovery/zipball/0700efda8d7526335132360167315fdab3aeb599",
- "reference": "0700efda8d7526335132360167315fdab3aeb599",
- "shasum": ""
- },
- "require": {
- "composer-plugin-api": "^1.0|^2.0",
- "php": "^7.1 || ^8.0"
- },
- "conflict": {
- "nyholm/psr7": "<1.0",
- "zendframework/zend-diactoros": "*"
- },
- "provide": {
- "php-http/async-client-implementation": "*",
- "php-http/client-implementation": "*",
- "psr/http-client-implementation": "*",
- "psr/http-factory-implementation": "*",
- "psr/http-message-implementation": "*"
- },
- "require-dev": {
- "composer/composer": "^1.0.2|^2.0",
- "graham-campbell/phpspec-skip-example-extension": "^5.0",
- "php-http/httplug": "^1.0 || ^2.0",
- "php-http/message-factory": "^1.0",
- "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3",
- "sebastian/comparator": "^3.0.5 || ^4.0.8",
- "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1"
- },
- "type": "composer-plugin",
- "extra": {
- "class": "Http\\Discovery\\Composer\\Plugin",
- "plugin-optional": true
- },
- "autoload": {
- "psr-4": {
- "Http\\Discovery\\": "src/"
- },
- "exclude-from-classmap": [
- "src/Composer/Plugin.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Márk Sági-Kazár",
- "email": "mark.sagikazar@gmail.com"
- }
- ],
- "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations",
- "homepage": "http://php-http.org",
- "keywords": [
- "adapter",
- "client",
- "discovery",
- "factory",
- "http",
- "message",
- "psr17",
- "psr7"
- ],
- "support": {
- "issues": "https://github.com/php-http/discovery/issues",
- "source": "https://github.com/php-http/discovery/tree/1.19.4"
- },
- "time": "2024-03-29T13:00:05+00:00"
- },
- {
- "name": "phpstan/phpstan",
- "version": "1.11.5",
- "source": {
- "type": "git",
- "url": "https://github.com/phpstan/phpstan.git",
- "reference": "490f0ae1c92b082f154681d7849aee776a7c1443"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/490f0ae1c92b082f154681d7849aee776a7c1443",
- "reference": "490f0ae1c92b082f154681d7849aee776a7c1443",
- "shasum": ""
- },
- "require": {
- "php": "^7.2|^8.0"
- },
- "conflict": {
- "phpstan/phpstan-shim": "*"
- },
- "bin": [
- "phpstan",
- "phpstan.phar"
- ],
- "type": "library",
- "autoload": {
- "files": [
- "bootstrap.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "description": "PHPStan - PHP Static Analysis Tool",
- "keywords": [
- "dev",
- "static analysis"
- ],
- "support": {
- "docs": "https://phpstan.org/user-guide/getting-started",
- "forum": "https://github.com/phpstan/phpstan/discussions",
- "issues": "https://github.com/phpstan/phpstan/issues",
- "security": "https://github.com/phpstan/phpstan/security/policy",
- "source": "https://github.com/phpstan/phpstan-src"
- },
- "funding": [
- {
- "url": "https://github.com/ondrejmirtes",
- "type": "github"
- },
- {
- "url": "https://github.com/phpstan",
- "type": "github"
- }
- ],
- "time": "2024-06-17T15:10:54+00:00"
- }
- ],
- "aliases": [],
- "minimum-stability": "dev",
- "stability-flags": {
- "league/object-mapper": 20
- },
- "prefer-stable": true,
- "prefer-lowest": false,
- "platform": [],
- "platform-dev": [],
- "plugin-api-version": "2.6.0"
-}
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
new file mode 100644
index 0000000..aab4991
--- /dev/null
+++ b/phpstan-baseline.neon
@@ -0,0 +1,2 @@
+parameters:
+ ignoreErrors: []
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index 6e451fa..b9ca222 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -1,4 +1,7 @@
parameters:
- level: 6
+ level: 8
paths:
- src
+
+includes:
+ - phpstan-baseline.neon
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..0c12bb9
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ ./tests
+
+
+
+
diff --git a/src/Api/AbstractApi.php b/src/Api/AbstractApi.php
index 04ca40c..1f97ba1 100644
--- a/src/Api/AbstractApi.php
+++ b/src/Api/AbstractApi.php
@@ -2,52 +2,64 @@
namespace IPay\Api;
+use EventSauce\ObjectHydrator\DefinitionProvider;
+use EventSauce\ObjectHydrator\KeyFormatterWithoutConversion;
+use EventSauce\ObjectHydrator\ObjectMapper;
+use EventSauce\ObjectHydrator\ObjectMapperUsingReflection;
use IPay\IPayClient;
-use League\ObjectMapper\KeyFormatterWithoutConversion;
-use League\ObjectMapper\ObjectMapper;
-use League\ObjectMapper\ObjectMapperUsingReflection;
-use League\ObjectMapper\ReflectionDefinitionProvider;
use Nette\Utils\Json;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+/**
+ * @template T of SessionInterface
+ */
abstract class AbstractApi
{
- private ObjectMapper $objectMapper;
+ protected ObjectMapper $objectMapper;
+ /**
+ * @param T $session
+ */
public function __construct(
- protected IPayClient $client,
+ protected IPayClient $iPayClient,
+ private SessionInterface $session,
) {
$this->objectMapper = new ObjectMapperUsingReflection(
- new ReflectionDefinitionProvider(
+ new DefinitionProvider(
keyFormatter: new KeyFormatterWithoutConversion(),
),
);
}
/**
- * @param string[] $data
+ * @param string[] $parameters
*
* @return mixed[]
*/
- protected function post(string $uri, array $data = []): array
+ protected function post(string $uri, array $parameters = []): array
{
- $response = $this->client->getClient()->post(
- $uri,
+ $response = $this->iPayClient->getClient()->post(
+ sprintf('ipay/wa/%s', $uri),
[],
- $this->configureFieldBuilder(
- FieldBuilder::with($data)
- )->encrypt()
+ BodyBuilder::from($parameters)
+ ->enhance($this->getSession()->getRequestParameters())
+ ->build()
+ ->encrypt()
);
return Json::decode((string) $response->getBody(), true);
}
- protected function configureFieldBuilder(FieldBuilder $builder): FieldBuilder
+ protected static function createOptionsResolver(): OptionsResolver
{
- return $builder->withRequiredFields();
+ return new OptionsResolver();
}
- public function getObjectMapper(): ObjectMapper
+ /**
+ * @return T
+ */
+ public function getSession(): SessionInterface
{
- return $this->objectMapper;
+ return $this->session;
}
}
diff --git a/src/Api/Auth.php b/src/Api/Auth.php
deleted file mode 100644
index e6dbcde..0000000
--- a/src/Api/Auth.php
+++ /dev/null
@@ -1,17 +0,0 @@
-post('/signIn', get_defined_vars());
-
- return new Session($result['sessionId'], $this->client);
- }
-}
diff --git a/src/Api/AuthenticatedApi.php b/src/Api/AuthenticatedApi.php
new file mode 100644
index 0000000..6202fad
--- /dev/null
+++ b/src/Api/AuthenticatedApi.php
@@ -0,0 +1,122 @@
+
+ */
+final class AuthenticatedApi extends AbstractApi
+{
+ use LazyGhostTrait;
+
+ public Customer $customer;
+
+ /** @var list */
+ public array $accounts;
+
+ public function __construct(IPayClient $iPayClient, AuthenticatedSession $session)
+ {
+ parent::__construct($iPayClient, $session);
+ self::createLazyGhost(
+ initializer: $this->populateLazyProperties(...),
+ instance: $this,
+ );
+ }
+
+ private function populateLazyProperties(): void
+ {
+ $this->customer = $this->customer();
+ $this->accounts = $this->accounts();
+ }
+
+ /**
+ * @throws \IPay\Exception\SessionException
+ */
+ private function customer(): Customer
+ {
+ return $this->objectMapper->hydrateObject(
+ Customer::class,
+ $this->post('getCustomerDetails')['customerInfo'],
+ );
+ }
+
+ /**
+ * @return list
+ *
+ * @throws \IPay\Exception\SessionException
+ */
+ private function accounts(): array
+ {
+ return $this->objectMapper->hydrateObjects(
+ Account::class,
+ $this->post('getEntitiesAndAccounts')['accounts'],
+ )->toArray();
+ }
+
+ /**
+ * @param array{
+ * accountNumber?: string,
+ * tranType?: 'Credit'|'Debit'|'',
+ * startDate?: \DateTimeInterface,
+ * endDate?: \DateTimeInterface,
+ * } $parameters
+ *
+ * @return \Iterator
+ *
+ * @throws \IPay\Exception\SessionException
+ */
+ public function historyTransactions(array $parameters): \Iterator
+ {
+ $datetimeNormalizer = static function (
+ Options $resolver,
+ \DateTimeInterface $value
+ ): string {
+ return $value->format('Y-m-d');
+ };
+
+ $resolver = self::createOptionsResolver()
+ ->setRequired([
+ 'accountNumber',
+ 'tranType',
+ 'startDate',
+ 'endDate',
+ ])
+ ->setAllowedTypes('accountNumber', 'string')
+ ->setAllowedValues('tranType', ['Credit', 'Debit', ''])
+ ->setAllowedTypes('startDate', \DateTimeInterface::class)
+ ->setAllowedTypes('endDate', \DateTimeInterface::class)
+ ->setNormalizer('startDate', $datetimeNormalizer)
+ ->setNormalizer('endDate', $datetimeNormalizer)
+ ->setDefaults([
+ 'accountNumber' => $this->customer->accountNumber,
+ 'tranType' => 'Credit',
+ 'startDate' => new \DateTimeImmutable(),
+ 'endDate' => new \DateTimeImmutable(),
+ ])
+ ;
+
+ $parameters = $resolver->resolve($parameters);
+
+ $parameters['pageNumber'] = 0;
+ do {
+ $transactions = $this->post(
+ 'getHistTransactions',
+ $parameters
+ )['transactions'];
+ foreach ($this->objectMapper->hydrateObjects(
+ Transaction::class,
+ $transactions
+ )->getIterator() as $transaction) {
+ yield $transaction;
+ }
+ ++$parameters['pageNumber'];
+ } while (count($transactions) > 0);
+ }
+}
diff --git a/src/Api/AuthenticatedSession.php b/src/Api/AuthenticatedSession.php
new file mode 100644
index 0000000..edd8640
--- /dev/null
+++ b/src/Api/AuthenticatedSession.php
@@ -0,0 +1,15 @@
+ $this->id, ...parent::getRequestParameters()];
+ }
+}
diff --git a/src/Api/FieldBuilder.php b/src/Api/BodyBuilder.php
similarity index 65%
rename from src/Api/FieldBuilder.php
rename to src/Api/BodyBuilder.php
index f013b4c..42b3db2 100644
--- a/src/Api/FieldBuilder.php
+++ b/src/Api/BodyBuilder.php
@@ -4,12 +4,11 @@
use IPay\Encryption\Encrypter;
use Nette\Utils\Json;
-use Nette\Utils\Random;
/**
* @extends \ArrayObject
*/
-final class FieldBuilder extends \ArrayObject implements \Stringable, \JsonSerializable
+final class BodyBuilder extends \ArrayObject implements \Stringable, \JsonSerializable
{
/**
* @param string[] $data
@@ -22,17 +21,17 @@ private function __construct(array $data)
/**
* @param string[] $data
*/
- public static function with(array $data): static
+ public static function from(array $data): static
{
return new static($data);
}
- public function withRequiredFields(): self
+ /**
+ * @param string[] $parameters
+ */
+ public function enhance(array $parameters): static
{
- $this['lang'] = 'en';
- $this['requestId'] = Random::generate(12, '0-9A-Z').'|'.time();
-
- return $this;
+ return static::from($this->getArrayCopy() + $parameters);
}
public function build(): self
@@ -46,9 +45,7 @@ public function build(): self
public function encrypt(): string
{
- return new static([
- 'encrypted' => Encrypter::encrypt($this->build()),
- ]);
+ return static::from(['encrypted' => Encrypter::encrypt($this)]);
}
public function __toString(): string
diff --git a/src/Api/Session.php b/src/Api/Session.php
deleted file mode 100644
index e8ed17e..0000000
--- a/src/Api/Session.php
+++ /dev/null
@@ -1,31 +0,0 @@
-getObjectMapper()->hydrateObject(
- Customer::class,
- $this->post('/getCustomerDetails'),
- );
- }
-
- protected function configureFieldBuilder(FieldBuilder $builder): FieldBuilder
- {
- $builder['sessionId'] = $this->id;
-
- return parent::configureFieldBuilder($builder);
- }
-}
diff --git a/src/Api/SessionInterface.php b/src/Api/SessionInterface.php
new file mode 100644
index 0000000..bada074
--- /dev/null
+++ b/src/Api/SessionInterface.php
@@ -0,0 +1,11 @@
+
+ */
+final class UnauthenticatedApi extends AbstractApi
+{
+ /**
+ * @param array{
+ * userName: string,
+ * accessCode: string,
+ * } $credentials
+ *
+ * @throws \IPay\Exception\LoginException
+ */
+ public function login(array $credentials): AuthenticatedApi
+ {
+ $resolver = self::createOptionsResolver()
+ ->setRequired([
+ 'userName',
+ 'accessCode',
+ ])
+ ->setAllowedTypes('userName', 'string')
+ ->setAllowedTypes('accessCode', 'string')
+ ;
+
+ $parameters = $resolver->resolve($credentials) + $this->bypassCaptcha();
+
+ /** @var array{sessionId: string, ...} */
+ $result = $this->post('signIn', $parameters);
+
+ return new AuthenticatedApi(
+ $this->iPayClient,
+ new AuthenticatedSession($result['sessionId'])
+ );
+ }
+
+ /**
+ * @return array{captchaId:string,captchaCode:string}
+ */
+ private function bypassCaptcha(): array
+ {
+ $captchaId = Random::generate(9, '0-9a-zA-Z');
+ $svg = (string) $this->iPayClient->getClient()
+ ->get(sprintf('api/get-captcha/%s', $captchaId))
+ ->getBody();
+ $captchaCode = CaptchaSolver::solve($svg);
+
+ return compact('captchaId', 'captchaCode');
+ }
+}
diff --git a/src/Api/UnauthenticatedSession.php b/src/Api/UnauthenticatedSession.php
new file mode 100644
index 0000000..d6b5bd9
--- /dev/null
+++ b/src/Api/UnauthenticatedSession.php
@@ -0,0 +1,16 @@
+ 'en',
+ 'requestId' => Random::generate(12, '0-9A-Z').'|'.time(),
+ ];
+ }
+}
diff --git a/src/Captcha/CaptchaSolver.php b/src/Captcha/CaptchaSolver.php
new file mode 100644
index 0000000..2370241
--- /dev/null
+++ b/src/Captcha/CaptchaSolver.php
@@ -0,0 +1,42 @@
+ 0,
+ 49 => 1,
+ 53 => 2,
+ 70 => 3,
+ 76 => 4,
+ 58 => 5,
+ 64 => 6,
+ 50 => 7,
+ 75 => 8,
+ 69 => 9,
+ ];
+
+ public static function solve(string $svg): string
+ {
+ /** @var string[] */
+ $svgPathCommands = (new Crawler($svg))
+ ->filterXPath('//path[@fill!="none"]')
+ ->each(function (Crawler $node): string {
+ return (string) $node->attr('d');
+ });
+
+ sort($svgPathCommands, SORT_NATURAL);
+
+ $result = '';
+ foreach ($svgPathCommands as $svgPathCommand) {
+ /** @var string */
+ $symbolicPath = preg_replace('/[^A-Z]/', '', $svgPathCommand);
+ $result .= static::LENGTH_TO_NUMBER_MAPPING[strlen($symbolicPath)];
+ }
+
+ return $result;
+ }
+}
diff --git a/src/Encryption/Encrypter.php b/src/Encryption/Encrypter.php
index da4d9db..c20a8f1 100644
--- a/src/Encryption/Encrypter.php
+++ b/src/Encryption/Encrypter.php
@@ -2,38 +2,24 @@
namespace IPay\Encryption;
-const IPAY_PUBLIC_KEY = <<loadKey(static::IPAY_PUBLIC_KEY);
- return base64_encode($result);
+ return base64_encode($rsa->encrypt($message));
}
}
diff --git a/src/Entity/Account.php b/src/Entity/Account.php
new file mode 100644
index 0000000..a2153a3
--- /dev/null
+++ b/src/Entity/Account.php
@@ -0,0 +1,23 @@
+ 'name',
@@ -10,29 +10,13 @@
'jobTitle' => 'job',
'feeAcctNo' => 'accountNumber',
])]
-readonly class Info
+readonly class Customer
{
- /**
- * @internal
- */
public function __construct(
public string $name,
public string $phone,
public string $job,
- public string $accountNumber
- ) {
- }
-}
-
-readonly class Customer
-{
- /**
- * @internal
- */
- public function __construct(
- public string $userName,
- #[MapFrom('customerInfo')]
- public Info $info,
+ public string $accountNumber,
) {
}
}
diff --git a/src/Entity/Transaction.php b/src/Entity/Transaction.php
new file mode 100644
index 0000000..4e9d0d5
--- /dev/null
+++ b/src/Entity/Transaction.php
@@ -0,0 +1,21 @@
+then(function (ResponseInterface $response): ResponseInterface {
if (200 !== $response->getStatusCode()) {
- /** @var object{errorCode:string} */
$error = Json::decode((string) $response->getBody());
- throw self::createException($error->errorCode);
+ throw self::createException($error);
}
return $response;
});
}
- private static function createException(string $code): \Throwable
+ /**
+ * @param \stdClass&object{errorCode: string, errorMessage?: string} $error
+ */
+ private static function createException(\stdClass $error): \Throwable
{
- return match ($code) {
- '96', '99' => new SessionExpiredException('The session has expired.'),
- default => new \RuntimeException('Unknown error.'),
+ /** @var class-string<\Exception> */
+ $class = match ($error->errorCode) {
+ 'LOGON_CREDENTIALS_REJECTED' => LoginException::class,
+ '96', '99' => SessionException::class,
+ default => \RuntimeException::class,
};
+
+ return new $class($error->errorMessage ?? 'Unknown error.');
}
}
diff --git a/src/IPayClient.php b/src/IPayClient.php
index 960a79d..e1bdf9f 100644
--- a/src/IPayClient.php
+++ b/src/IPayClient.php
@@ -9,7 +9,10 @@
use Http\Client\Common\PluginClient;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Discovery\Psr18ClientDiscovery;
-use IPay\Api\Auth;
+use IPay\Api\AuthenticatedApi;
+use IPay\Api\AuthenticatedSession;
+use IPay\Api\UnauthenticatedApi;
+use IPay\Api\UnauthenticatedSession;
use IPay\Http\Plugin\ExceptionThrower;
final class IPayClient
@@ -20,7 +23,7 @@ public static function create(): static
new HttpMethodsClient(
new PluginClient(Psr18ClientDiscovery::find(), [
new BaseUriPlugin(Psr17FactoryDiscovery::findUriFactory()
- ->createUri('https://api-ipay.vietinbank.vn/ipay/wa')
+ ->createUri('https://api-ipay.vietinbank.vn')
),
new ContentTypePlugin(),
new ExceptionThrower(),
@@ -41,8 +44,13 @@ public function getClient(): HttpMethodsClientInterface
return $this->client;
}
- public function auth(): Auth
+ public function guest(): UnauthenticatedApi
{
- return new Auth($this);
+ return new UnauthenticatedApi($this, new UnauthenticatedSession());
+ }
+
+ public function session(string $id): AuthenticatedApi
+ {
+ return new AuthenticatedApi($this, new AuthenticatedSession($id));
}
}
diff --git a/src/functions.php b/src/functions.php
new file mode 100644
index 0000000..844e2b7
--- /dev/null
+++ b/src/functions.php
@@ -0,0 +1,10 @@
+toBe($result);
+})->with([
+ 'captcha 1' => [__DIR__.'/Fixture/1.svg', '540701'],
+ 'captcha 2' => [__DIR__.'/Fixture/2.svg', '826778'],
+ 'captcha 3' => [__DIR__.'/Fixture/3.svg', '835697'],
+]);
diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php
new file mode 100644
index 0000000..eba6be3
--- /dev/null
+++ b/tests/Feature/ExampleTest.php
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/tests/Feature/Fixture/2.svg b/tests/Feature/Fixture/2.svg
new file mode 100644
index 0000000..d261e9e
--- /dev/null
+++ b/tests/Feature/Fixture/2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/tests/Feature/Fixture/3.svg b/tests/Feature/Fixture/3.svg
new file mode 100644
index 0000000..80718fd
--- /dev/null
+++ b/tests/Feature/Fixture/3.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/tests/Pest.php b/tests/Pest.php
new file mode 100644
index 0000000..5949c61
--- /dev/null
+++ b/tests/Pest.php
@@ -0,0 +1,45 @@
+in('Feature');
+
+/*
+|--------------------------------------------------------------------------
+| Expectations
+|--------------------------------------------------------------------------
+|
+| When you're writing tests, you often need to check that values meet certain conditions. The
+| "expect()" function gives you access to a set of "expectations" methods that you can use
+| to assert different things. Of course, you may extend the Expectation API at any time.
+|
+*/
+
+expect()->extend('toBeOne', function () {
+ return $this->toBe(1);
+});
+
+/*
+|--------------------------------------------------------------------------
+| Functions
+|--------------------------------------------------------------------------
+|
+| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
+| project that you don't want to repeat in every file. Here you can also expose helpers as
+| global functions to help you to reduce the number of lines of code in your test files.
+|
+*/
+
+function something()
+{
+ // ..
+}
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 0000000..cfb05b6
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,10 @@
+