├── .gitignore
├── phpstan.common.neon
├── src
├── Exception
│ ├── CacheException.php
│ ├── SwapException.php
│ ├── UnsupportedDateException.php
│ ├── SwapRuntimeException.php
│ ├── UnsupportedCurrencyPairException.php
│ ├── UnsupportedExchangeQueryException.php
│ └── NonBreakingInvalidArgumentException.php
├── WalletSwapServiceProvider.php
├── CurrencyServiceInterface.php
├── SwapExchangeService.php
└── CurrencyService.php
├── .github
├── workflows
│ ├── changelog.yml
│ ├── stale.yml
│ ├── phpstan.yaml
│ ├── phpunits.yaml
│ └── fixer.yaml
└── dependabot.yml
├── infection.json.dist
├── .whitesource
├── phpstan.neon
├── README.md
├── ecs.php
├── phpunit.xml
├── rector.php
├── LICENSE
├── tests
├── TestCase.php
├── SwapTest.php
└── CurrencyTest.php
├── composer.json
└── changelog.md
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.phar
2 | /vendor/
3 | composer.lock
4 | .idea/
5 | build/
6 | .phpunit.result.cache
7 | .php_cs_cache
8 | .phpunit.cache/
9 |
--------------------------------------------------------------------------------
/phpstan.common.neon:
--------------------------------------------------------------------------------
1 | includes:
2 | - vendor/larastan/larastan/extension.neon
3 | - vendor/ergebnis/phpstan-rules/rules.neon
4 |
5 | parameters:
6 | level: 9
7 | fileExtensions:
8 | - php
--------------------------------------------------------------------------------
/src/Exception/CacheException.php:
--------------------------------------------------------------------------------
1 | app->singleton(CurrencyServiceInterface::class, CurrencyService::class);
16 | $this->app->singleton(ExchangeServiceInterface::class, SwapExchangeService::class);
17 | }
18 |
19 | /**
20 | * @return class-string[]
21 | */
22 | public function provides(): array
23 | {
24 | return [CurrencyServiceInterface::class];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://packagist.org/packages/bavix/laravel-wallet-swap)
2 | [](https://packagist.org/packages/bavix/laravel-wallet-swap)
3 | [](https://packagist.org/packages/bavix/laravel-wallet-swap)
4 |
5 | laravel-wallet-swap - Addition to the package laravel-wallet.
6 |
7 | * **Vendor**: bavix
8 | * **Package**: laravel-wallet-swap
9 | * **[Composer](https://getcomposer.org/):** `composer require bavix/laravel-wallet-swap`
10 |
11 | ---
12 | Supported by
13 |
14 | [](https://www.jetbrains.com/)
15 |
--------------------------------------------------------------------------------
/ecs.php:
--------------------------------------------------------------------------------
1 | paths([
11 | __DIR__ . '/src',
12 | __DIR__ . '/tests',
13 | ]);
14 |
15 | $config->skip([
16 | GeneralPhpdocAnnotationRemoveFixer::class,
17 | ]);
18 |
19 | $config->sets([
20 | SetList::CLEAN_CODE,
21 | SetList::SYMPLIFY,
22 | SetList::ARRAY,
23 | SetList::COMMON,
24 | SetList::PSR_12,
25 | SetList::CONTROL_STRUCTURES,
26 | SetList::NAMESPACES,
27 | SetList::STRICT,
28 | SetList::PHPUNIT,
29 | SetList::LARAVEL,
30 | ]);
31 | };
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | tests
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | src/
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | parallel();
12 | $config->paths([
13 | __DIR__ . '/src',
14 | __DIR__ . '/tests',
15 | ]);
16 |
17 | // Define what rule sets will be applied
18 | $config->import(PHPUnitSetList::ANNOTATIONS_TO_ATTRIBUTES);
19 | $config->import(LaravelLevelSetList::UP_TO_LARAVEL_110);
20 | $config->import(PHPUnitSetList::PHPUNIT_100);
21 | $config->import(SetList::STRICT_BOOLEANS);
22 | $config->import(SetList::PRIVATIZATION);
23 | $config->import(SetList::EARLY_RETURN);
24 | $config->import(SetList::INSTANCEOF);
25 | $config->import(SetList::CODE_QUALITY);
26 | $config->import(SetList::DEAD_CODE);
27 | $config->import(SetList::PHP_82);
28 | };
29 |
--------------------------------------------------------------------------------
/.github/workflows/phpstan.yaml:
--------------------------------------------------------------------------------
1 | name: phpstan
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | phpstan:
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v4
16 |
17 | - name: Setup PHP
18 | uses: shivammathur/setup-php@v2
19 | with:
20 | php-version: 8.2
21 |
22 | - name: Validate composer.json and composer.lock
23 | run: composer validate --strict
24 |
25 | - name: Cache Composer packages
26 | id: composer-cache
27 | uses: actions/cache@v4
28 | with:
29 | path: vendor
30 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
31 | restore-keys: |
32 | ${{ runner.os }}-php-
33 |
34 | - name: Install dependencies
35 | run: composer install --prefer-dist --no-progress
36 |
37 | - name: Run phpstan
38 | run: composer phpstan
39 |
--------------------------------------------------------------------------------
/src/CurrencyServiceInterface.php:
--------------------------------------------------------------------------------
1 | artisan('migrate')
22 | ->run();
23 | }
24 |
25 | /**
26 | * @param Application $app
27 | */
28 | protected function getPackageProviders($app): array
29 | {
30 | return [WalletServiceProvider::class, WalletSwapServiceProvider::class, SwapServiceProvider::class];
31 | }
32 |
33 | /**
34 | * Define environment setup.
35 | *
36 | * @param Application $app
37 | */
38 | protected function getEnvironmentSetUp($app): void
39 | {
40 | // swap
41 | $app['config']->set('swap.services', [
42 | 'array' => [
43 | [
44 | 'EUR/USD' => round(114.19 / 100, 3),
45 | 'USD/EUR' => round(100 / 114.19, 3),
46 | 'BTC/USD' => round(42766 / 1., 3),
47 | 'USD/BTC' => round(1. / 42766, 3),
48 | ],
49 | ],
50 | ]);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/SwapExchangeService.php:
--------------------------------------------------------------------------------
1 | mathService->mul($this->currencyService->rate($fromCurrency, $toCurrency), $amount);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.github/workflows/phpunits.yaml:
--------------------------------------------------------------------------------
1 | name: phpunits
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | units:
11 | runs-on: ubuntu-latest
12 |
13 | strategy:
14 | matrix:
15 | php-versions: [8.2, 8.3]
16 |
17 | steps:
18 | - name: Checkout
19 | id: git-checkout
20 | uses: actions/checkout@v4
21 |
22 | - name: Setup PHP
23 | id: php-install
24 | uses: shivammathur/setup-php@v2
25 | with:
26 | php-version: ${{ matrix.php-versions }}
27 | extensions: mbstring, pgsql, mysql, sqlite, redis, memcached
28 | coverage: pcov
29 |
30 | - name: Validate composer.json and composer.lock
31 | id: composer-validate
32 | run: composer validate --strict
33 |
34 | - name: Cache Composer packages
35 | id: composer-cache
36 | uses: actions/cache@v4
37 | with:
38 | path: vendor
39 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
40 | restore-keys: |
41 | ${{ runner.os }}-php-
42 |
43 | - name: Install dependencies
44 | id: composer-dependencies
45 | run: composer install --prefer-dist --no-progress
46 |
47 | - name: Prepare run test suite
48 | id: unit-prepare
49 | run: |
50 | mkdir build
51 |
52 | - name: Run test suite
53 | id: unit-run
54 | run: composer parabench
55 |
56 | - name: Run mutation test suite
57 | id: infect-run
58 | run: composer infect
59 |
--------------------------------------------------------------------------------
/tests/SwapTest.php:
--------------------------------------------------------------------------------
1 | convertTo('USD', 'EUR', 99);
22 |
23 | /** @var ExchangeRate $expected */
24 | $expected = Swap::latest('USD/EUR');
25 |
26 | self::assertSame(0, $mathService->compare($expected->getValue() * 99, $value));
27 | }
28 |
29 | public function testIdentical(): void
30 | {
31 | $mathService = app(MathServiceInterface::class);
32 | $value = app(ExchangeServiceInterface::class)
33 | ->convertTo('USD', 'USD', 128);
34 |
35 | self::assertSame(0, $mathService->compare(128, $value));
36 | }
37 |
38 | public function testCryptoBtcUsd(): void
39 | {
40 | $mathService = app(MathServiceInterface::class);
41 | $value = app(ExchangeServiceInterface::class)
42 | ->convertTo('BTC', 'USD', 20);
43 |
44 | /** @var ExchangeRate $expected */
45 | $expected = Swap::latest('BTC/USD');
46 |
47 | self::assertSame(0, $mathService->compare($expected->getValue() * 20, $value));
48 | }
49 |
50 | public function testCryptoUsdBtc(): void
51 | {
52 | $mathService = app(MathServiceInterface::class);
53 | $value = app(ExchangeServiceInterface::class)
54 | ->convertTo('USD', 'BTC', 100);
55 |
56 | /** @var ExchangeRate $expected */
57 | $expected = Swap::latest('USD/BTC');
58 | self::assertSame(0, $mathService->compare($expected->getValue() * 100, $value));
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bavix/laravel-wallet-swap",
3 | "description": "Addition to the package laravel-wallet.",
4 | "keywords": [
5 | "swap",
6 | "laravel",
7 | "credits",
8 | "bavix",
9 | "currency",
10 | "laravel-wallet",
11 | "virtual",
12 | "wallet",
13 | "payments"
14 | ],
15 | "minimum-stability": "stable",
16 | "homepage": "https://github.com/bavix/laravel-wallet",
17 | "license": "MIT",
18 | "authors": [
19 | {
20 | "name": "Babichev Maxim",
21 | "email": "info@babichev.net"
22 | }
23 | ],
24 | "require": {
25 | "php": "^8.2",
26 | "bavix/laravel-wallet": "^11.0",
27 | "florianv/laravel-swap": "^2.3",
28 | "nyholm/psr7": "^1.8",
29 | "php-http/curl-client": "^2.3",
30 | "php-http/message": "^1.16"
31 | },
32 | "require-dev": {
33 | "driftingly/rector-laravel": "^1.0",
34 | "ergebnis/phpstan-rules": "^2.1",
35 | "infection/infection": "~0.27",
36 | "larastan/larastan": "^2.8",
37 | "nunomaduro/collision": "^8.0",
38 | "orchestra/testbench": "^9.0",
39 | "phpstan/phpstan": "^1.10",
40 | "phpunit/phpunit": "^11.0",
41 | "rector/rector": "^1.0",
42 | "symplify/easy-coding-standard": "^12.1"
43 | },
44 | "autoload": {
45 | "psr-4": {
46 | "Bavix\\WalletSwap\\": "src/"
47 | }
48 | },
49 | "autoload-dev": {
50 | "psr-4": {
51 | "Bavix\\WalletSwap\\Test\\": "tests/"
52 | }
53 | },
54 | "extra": {
55 | "laravel": {
56 | "providers": [
57 | "Bavix\\WalletSwap\\WalletSwapServiceProvider"
58 | ]
59 | }
60 | },
61 | "scripts": {
62 | "parabench":"@php ./vendor/bin/testbench package:test --coverage-xml=build/coverage-xml --log-junit=build/junit.xml",
63 | "infect": "@php vendor/bin/infection --coverage=build --min-msi=50 -j$(nproc) --only-covering-test-cases",
64 | "phpstan": "@php vendor/bin/phpstan analyse -vvv --memory-limit 2G -c phpstan.neon",
65 | "phpstan-baseline": "@php vendor/bin/phpstan analyse -vvv --memory-limit 2G -c phpstan.neon --generate-baseline phpstan.baseline.neon",
66 | "ecs": "@php vendor/bin/ecs check",
67 | "ecs-fix": "@php vendor/bin/ecs check --fix",
68 | "ecs-cc": "@php vendor/bin/ecs --clear-cache",
69 | "rector": "@php vendor/bin/rector process --dry-run",
70 | "rector-fix": "@php vendor/bin/rector process"
71 | },
72 | "config": {
73 | "sort-packages": true,
74 | "allow-plugins": {
75 | "infection/extension-installer": true,
76 | "php-http/discovery": true
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/.github/workflows/fixer.yaml:
--------------------------------------------------------------------------------
1 | name: fixer
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | autofix:
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v4
16 |
17 | - name: Setup PHP
18 | uses: shivammathur/setup-php@v2
19 | with:
20 | php-version: 8.2
21 | extensions: mbstring, pgsql, mysql, sqlite, redis, memcached, bcmath
22 | coverage: pcov
23 | env:
24 | runner: self-hosted
25 |
26 | - name: Validate composer.json and composer.lock
27 | run: composer validate --strict
28 |
29 | - name: Cache Composer packages
30 | id: composer-cache
31 | uses: actions/cache@v4
32 | with:
33 | path: vendor
34 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
35 | restore-keys: |
36 | ${{ runner.os }}-php-
37 |
38 | - name: Install dependencies
39 | run: composer install --prefer-dist --no-progress
40 |
41 | - name: Run rector-fix
42 | run: composer rector-fix
43 |
44 | - name: Run ecs-fix
45 | run: composer ecs-fix
46 |
47 | - name: Run rector
48 | run: composer rector
49 |
50 | - name: Run ecs
51 | run: composer ecs
52 |
53 | - name: Run parabench
54 | run: composer parabench
55 |
56 | - name: "Check if build has changed"
57 | if: success()
58 | id: has-changes
59 | run: |
60 | echo "stdout<> $GITHUB_OUTPUT
61 | echo "$(git diff --stat)" >> $GITHUB_OUTPUT
62 | echo 'EOF' >> $GITHUB_OUTPUT
63 |
64 | - name: Import GPG key
65 | if: ${{ steps.has-changes.outputs.stdout }}
66 | uses: crazy-max/ghaction-import-gpg@v6
67 | with:
68 | gpg_private_key: ${{ secrets.GPG_BOT }}
69 | passphrase: ${{ secrets.GPG_PASSPHRASE }}
70 | fingerprint: ${{ secrets.GPG_FINGERPRINT }}
71 | git_config_global: true
72 | git_user_signingkey: true
73 | git_commit_gpgsign: true
74 | git_committer_name: Github bot
75 | git_committer_email: bot@babichev.net
76 |
77 | - name: "Commit files"
78 | if: ${{ steps.has-changes.outputs.stdout }}
79 | env:
80 | GH_TOKEN: ${{ secrets.BOT_TOKEN }}
81 | run: |
82 | gh pr checkout ${{ github.event.pull_request.number }}
83 | git commit -S -m "autofix" -a
84 |
85 | - name: "Push changes"
86 | if: ${{ steps.has-changes.outputs.stdout }}
87 | env:
88 | GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }}
89 | run: git push -u origin HEAD
--------------------------------------------------------------------------------
/tests/CurrencyTest.php:
--------------------------------------------------------------------------------
1 | expectException($expect);
39 |
40 | $mockSwapService = $this->createMock(Swap::class);
41 | $mockSwapService->method('latest')
42 | ->willThrowException($throwable);
43 |
44 | $currencyService = new CurrencyService($mockSwapService);
45 | $currencyService->rate('USD', 'RUB');
46 | }
47 |
48 | public static function exceptionDataProvider(): iterable
49 | {
50 | $currencyPair = new CurrencyPair('USD', 'EUR');
51 | $service = new PhpArray([]);
52 |
53 | yield [CacheException::class, new ExchangerCacheException()];
54 | yield [NonBreakingInvalidArgumentException::class, new ExchangerNonBreakingInvalidArgumentException()];
55 |
56 | yield [
57 | UnsupportedExchangeQueryException::class,
58 | new ExchangerUnsupportedExchangeQueryException(new ExchangeRateQuery($currencyPair), $service),
59 | ];
60 |
61 | yield [
62 | UnsupportedCurrencyPairException::class,
63 | new ExchangerUnsupportedCurrencyPairException($currencyPair, $service),
64 | ];
65 |
66 | yield [
67 | UnsupportedDateException::class,
68 | new ExchangerUnsupportedDateException(new DateTimeImmutable(), $service),
69 | ];
70 |
71 | yield [SwapException::class, new ExchangerException()];
72 |
73 | yield [SwapException::class, new ChainException([])];
74 |
75 | yield [SwapRuntimeException::class, new RuntimeException()];
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/CurrencyService.php:
--------------------------------------------------------------------------------
1 | swapService
46 | ->latest($fromCurrency.'/'.$toCurrency)
47 | ->getValue();
48 | } catch (ExchangerCacheException $exception) {
49 | throw new CacheException($exception->getMessage(), $exception->getCode(), $exception);
50 | } catch (ExchangerNonBreakingInvalidArgumentException $exception) {
51 | throw new NonBreakingInvalidArgumentException($exception->getMessage(), $exception->getCode(), $exception);
52 | } catch (ExchangerUnsupportedExchangeQueryException $exception) {
53 | throw new UnsupportedExchangeQueryException($exception->getMessage(), $exception->getCode(), $exception);
54 | } catch (ExchangerUnsupportedCurrencyPairException $exception) {
55 | throw new UnsupportedCurrencyPairException($exception->getMessage(), $exception->getCode(), $exception);
56 | } catch (ExchangerUnsupportedDateException $exception) {
57 | throw new UnsupportedDateException($exception->getMessage(), $exception->getCode(), $exception);
58 | } catch (ExchangerException $exception) {
59 | throw new SwapException($exception->getMessage(), $exception->getCode(), $exception);
60 | } catch (\Throwable $exception) {
61 | throw new SwapRuntimeException($exception->getMessage(), $exception->getCode(), $exception);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 7.0.1 - 2024-03-14
4 |
5 | ### What's Changed
6 |
7 | * Bump actions/checkout from 2 to 4 by [@dependabot](https://github.com/dependabot) in https://github.com/bavix/laravel-wallet-swap/pull/45
8 | * Bump actions/cache from 2 to 4 by [@dependabot](https://github.com/dependabot) in https://github.com/bavix/laravel-wallet-swap/pull/46
9 | * Bump bavix/.github from 0.1.6 to 0.2.4 by [@dependabot](https://github.com/dependabot) in https://github.com/bavix/laravel-wallet-swap/pull/47
10 | * Bump actions/stale from 1 to 9 by [@dependabot](https://github.com/dependabot) in https://github.com/bavix/laravel-wallet-swap/pull/48
11 |
12 | **Full Changelog**: https://github.com/bavix/laravel-wallet-swap/compare/7.0.0...7.0.1
13 |
14 | ## 7.0.0 - 2024-03-14
15 |
16 | ### What's Changed
17 |
18 | * Configure Mend Bolt for GitHub by [@mend-bolt-for-github](https://github.com/mend-bolt-for-github) in https://github.com/bavix/laravel-wallet-swap/pull/43
19 | * [7.x] add support laravel-wallet ^11.0 by [@rez1dent3](https://github.com/rez1dent3) in https://github.com/bavix/laravel-wallet-swap/pull/44
20 |
21 | ### New Contributors
22 |
23 | * [@mend-bolt-for-github](https://github.com/mend-bolt-for-github) made their first contribution in https://github.com/bavix/laravel-wallet-swap/pull/43
24 |
25 | **Full Changelog**: https://github.com/bavix/laravel-wallet-swap/compare/6.0.1...7.0.0
26 |
27 | ## 6.0.1 - 2024-01-17
28 |
29 | ## What's Changed
30 |
31 | * Update driftingly/rector-laravel requirement from ^0.21.0 to ^0.22.0 by [@dependabot](https://github.com/dependabot) in https://github.com/bavix/laravel-wallet-swap/pull/31
32 | * Update driftingly/rector-laravel requirement from ^0.22.0 to ^0.25.0 by [@dependabot](https://github.com/dependabot) in https://github.com/bavix/laravel-wallet-swap/pull/36
33 | * Update driftingly/rector-laravel requirement from ^0.25.0 to ^0.26.0 by [@dependabot](https://github.com/dependabot) in https://github.com/bavix/laravel-wallet-swap/pull/37
34 |
35 | **Full Changelog**: https://github.com/bavix/laravel-wallet-swap/compare/6.0.0...6.0.1
36 |
37 | ## 6.0.0 - 2023-07-09
38 |
39 | ## What's Changed
40 |
41 | * Update rector/rector requirement from ^0.12.5 to ^0.13.0 by [@dependabot](https://github.com/dependabot) in https://github.com/bavix/laravel-wallet-swap/pull/15
42 | * Update rector/rector requirement from ^0.13 to ^0.14 by [@dependabot](https://github.com/dependabot) in https://github.com/bavix/laravel-wallet-swap/pull/18
43 | * add support laravel-wallet:^10.0 by [@rez1dent3](https://github.com/rez1dent3) in https://github.com/bavix/laravel-wallet-swap/pull/28
44 |
45 | **Full Changelog**: https://github.com/bavix/laravel-wallet-swap/compare/5.0.0...6.0.0
46 |
47 | ## 5.0.0 - 2022-05-02
48 |
49 | ### Added
50 |
51 | - add support laravel-wallet ^9.0
52 |
53 | ## What's Changed
54 |
55 | * add support laravel-walelt:^9.0 by [@rez1dent3](https://github.com/rez1dent3) in https://github.com/bavix/laravel-wallet-swap/pull/14
56 |
57 | **Full Changelog**: https://github.com/bavix/laravel-wallet-swap/compare/4.2.0...5.0.0
58 |
59 | ## 4.2.0 - 2022-02-08
60 |
61 | ### Added
62 |
63 | - add support laravel-wallet ^8.0
64 |
65 | ## 4.1.0 - 2021-11-26
66 |
67 | ### Added
68 |
69 | - closed the third party library with its interface
70 |
71 | ## 4.0.0 - 2021-11-25
72 |
73 | ### Added
74 |
75 | - support laravel-wallet ^7.0
76 |
77 | ## 3.0.0 - 2020-11-13
78 |
79 | ### Added
80 |
81 | - support laravel-wallet v6.0
82 | - support php ^8.0
83 | - More unit tests
84 |
85 | ## 2.0.0 - 2020-03-13
86 |
87 | ### Added
88 |
89 | - support laravel-wallet v5.0
90 | - More unit tests
91 |
92 | ## 1.1.0 - 2019-10-21
93 |
94 | ### Added
95 |
96 | - support laravel-wallet v4.0
97 |
98 | ## 1.0.0 - 2019-07-29
99 |
100 | ### Added
101 |
102 | - support laravel-wallet v3.1.1
103 |
104 | ### Fixed
105 |
106 | - Unit-tests
107 | - method `rate`
108 |
--------------------------------------------------------------------------------