├── version
├── src
├── transactions
│ ├── TransactionException.php
│ ├── TransactionRepositoryInterface.php
│ └── Transaction.php
├── Currencies.php
├── events
│ └── TransactionInsertEvent.php
├── MerchantAsset.php
├── actions
│ ├── ConfirmAction.php
│ └── RequestAction.php
├── Collection.php
├── models
│ ├── Currency.php
│ ├── DepositRequest.php
│ ├── MerchantTrait.php
│ ├── DepositForm.php
│ └── PurchaseRequest.php
├── views
│ └── pay
│ │ ├── return.php
│ │ ├── deposit.php
│ │ └── deposit-form.php
├── assets
│ └── css
│ │ └── selectPayment.css
├── widgets
│ ├── views
│ │ └── pay-button.php
│ └── PayButton.php
├── messages
│ └── ru
│ │ └── merchant.php
├── controllers
│ └── PayController.php
└── Module.php
├── .scrutinizer.yml
├── tests
├── unit
│ ├── FakeGateway.php
│ ├── FakeTransactionRepository.php
│ ├── FakeMerchant.php
│ └── ModuleTest.php
└── _bootstrap.php
├── .gitignore
├── hidev.yml
├── .travis.yml
├── phpunit.xml.dist
├── config
└── web.php
├── chkipper.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── .php_cs
├── composer.json
└── history.md
/version:
--------------------------------------------------------------------------------
1 | yii2-merchant 0.1.0 2017-09-28 16:52:32 +0200 e4c0085e667c292263694a3854e41c6f5b5d179c
2 |
--------------------------------------------------------------------------------
/src/transactions/TransactionException.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | abstract class Currencies
13 | {
14 | /**
15 | * @return Currency[]
16 | */
17 | abstract public function getList(): array;
18 | }
19 |
--------------------------------------------------------------------------------
/src/events/TransactionInsertEvent.php:
--------------------------------------------------------------------------------
1 | getAssetManager()->getPublishedUrl($this->sourcePath);
20 |
21 | return $path . '/' . $asset;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/hidev.yml:
--------------------------------------------------------------------------------
1 | package:
2 | type: yii2-extension
3 | name: yii2-merchant
4 | title: Yii2 extension for payment processing with Omnipay, Payum and more later
5 | headline: Yii2 Merchant
6 | keywords: yii2, merchant, payment, omnipay, payum
7 | namespace: hiqdev\yii2\merchant
8 | description: |
9 | This [Yii2] extension provides payment gateways integration with use of
10 | [Omnipay] and [Payum] payment processing libraries.
11 |
12 | [Yii2]: http://yiiframework.com/
13 | [Omnipay]: http://omnipay.thephpleague.com/
14 | [Payum]: http://payum.org/
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - 5.6
4 | - 7
5 | - 7.1
6 | - hhvm
7 | dist: trusty
8 | matrix:
9 | allow_failures:
10 | -
11 | php: hhvm
12 | cache:
13 | directories:
14 | - $HOME/.composer/cache
15 | before_install:
16 | - 'composer self-update'
17 | - 'composer --version'
18 | - 'wget http://hiqdev.com/hidev/hidev.phar -O hidev.phar && chmod a+x hidev.phar'
19 | - './hidev.phar --version'
20 | - './hidev.phar travis/before-install'
21 | sudo: false
22 | install:
23 | - './hidev.phar travis/install'
24 | script:
25 | - './hidev.phar travis/script'
26 | after_script:
27 | - './hidev.phar travis/after-script'
28 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ./tests/unit/
6 |
7 |
8 |
9 |
10 | ./src/
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/actions/ConfirmAction.php:
--------------------------------------------------------------------------------
1 | module->createMerchant($id, $config);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/config/web.php:
--------------------------------------------------------------------------------
1 | [
13 | 'themeManager' => [
14 | 'class' => \hiqdev\thememanager\ThemeManager::class,
15 | 'pathMap' => [
16 | dirname(__DIR__) . '/src/views' => '$themedViewPaths',
17 | ],
18 | ],
19 | 'i18n' => [
20 | 'translations' => [
21 | 'merchant' => [
22 | 'class' => \yii\i18n\PhpMessageSource::class,
23 | 'sourceLanguage' => 'en-US',
24 | 'basePath' => dirname(__DIR__) . '/src/messages',
25 | ],
26 | ],
27 | ],
28 | ],
29 | ];
30 |
--------------------------------------------------------------------------------
/tests/unit/FakeTransactionRepository.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class Currency extends Model
15 | {
16 | /** @var string */
17 | public $code;
18 |
19 | /**
20 | * Returns the symbol used for a currency.
21 | *
22 | * @param string|null $locale optional
23 | *
24 | * @return string|null the currency symbol or NULL if not found
25 | * @see https://stackoverflow.com/questions/13897516/get-currency-symbol-in-php
26 | */
27 | public function getSymbol($locale = null): string
28 | {
29 | if (null === $locale) {
30 | $locale = Yii::$app->formatter->locale;
31 | }
32 | $fmt = new NumberFormatter($locale . "@currency={$this->code}", NumberFormatter::CURRENCY);
33 | $symbol = $fmt->getSymbol(NumberFormatter::CURRENCY_SYMBOL);
34 |
35 | return $symbol;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/transactions/TransactionRepositoryInterface.php:
--------------------------------------------------------------------------------
1 | _worker === null) {
29 | $this->_worker = Yii::createObject([
30 | 'class' => FakeGateway::class,
31 | 'gateway' => $this->gateway,
32 | 'data' => $this->data,
33 | ]);
34 | }
35 |
36 | return $this->_worker;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/chkipper.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hiqdev/yii2-merchant",
3 | "authors": {
4 | "hiqsol": {
5 | "name": "Andrii Vasyliev",
6 | "role": "Project lead",
7 | "email": "sol@hiqdev.com",
8 | "github": "https://github.com/hiqsol",
9 | "homepage": "http://hipanel.com/"
10 | },
11 | "SilverFire": {
12 | "name": "Dmitry Naumenko",
13 | "role": "Lead backend developer",
14 | "email": "d.naumenko.a@gmail.com",
15 | "github": "https://github.com/SilverFire",
16 | "homepage": "http://silverfire.me/"
17 | },
18 | "tafid": {
19 | "name": "Andrey Klochok",
20 | "role": "Lead frontend developer",
21 | "email": "andreyklochok@gmail.com",
22 | "github": "https://github.com/tafid",
23 | "homepage": "http://hiqdev.com/"
24 | },
25 | "BladeRoot": {
26 | "name": "Yuriy Myronchuk",
27 | "role": "QA Lead",
28 | "email": "bladeroot@gmail.com",
29 | "github": "https://github.com/BladeRoot",
30 | "homepage": "http://hiqdev.com/"
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/models/DepositRequest.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('merchant', 'Payment result');
11 |
12 | ?>
13 |
14 |
15 | = Yii::t('merchant', 'Waiting for confirmation from the payment system...') ?>
16 |
17 |
18 | Url::to(['@pay/check-return']),
21 | 'data' => [
22 | 'transactionId' => $transactionId,
23 | ],
24 | ]);
25 |
26 | $this->registerJs(<<
55 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright © 2015-2017, HiQDev (http://hiqdev.com/)
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions
6 | are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in
12 | the documentation and/or other materials provided with the
13 | distribution.
14 | * Neither the name of HiQDev nor the names of its
15 | contributors may be used to endorse or promote products derived
16 | from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 | POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/src/views/pay/deposit.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('merchant', 'Select payment method');
16 | $this->params['breadcrumbs'][] = ['label' => Yii::t('merchant', 'Recharge account'), 'url' => ['deposit']];
17 | $this->params['breadcrumbs'][] = $this->title;
18 |
19 | ?>
20 |
21 |
22 | = Html::tag('div', Yii::t('merchant', 'There are no payments methods available'), [
23 | 'class' => 'alert alert-danger text-center',
24 | 'style' => 'width: 50rem; margin: 0 auto;',
25 | ]) ?>
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | -
34 | = PayButton::widget([
35 | 'request' => $request,
36 | 'depositForm' => $depositForm,
37 | ]) ?>
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/models/MerchantTrait.php:
--------------------------------------------------------------------------------
1 | Yii::t('merchant', 'Name'),
45 | 'system' => Yii::t('merchant', 'Payment System'),
46 | 'purse' => Yii::t('merchant', 'Purse'),
47 | 'amount' => Yii::t('merchant', 'Amount'),
48 | 'fee' => Yii::t('merchant', 'Fee'),
49 | 'currency' => Yii::t('merchant', 'Currency'),
50 | 'signature' => Yii::t('merchant', 'Signature'),
51 | 'commission' => Yii::t('merchant', 'Commission'),
52 | ];
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Yii2 Merchant
2 |
3 | **Yii2 extension for payment processing with Omnipay, Payum and more later**
4 |
5 | [](https://packagist.org/packages/hiqdev/yii2-merchant)
6 | [](https://packagist.org/packages/hiqdev/yii2-merchant)
7 | [](https://travis-ci.org/hiqdev/yii2-merchant)
8 | [](https://scrutinizer-ci.com/g/hiqdev/yii2-merchant/)
9 | [](https://scrutinizer-ci.com/g/hiqdev/yii2-merchant/)
10 | [](https://www.versioneye.com/php/hiqdev:yii2-merchant/dev-master)
11 |
12 | This [Yii2] extension provides payment gateways integration with use of
13 | [Omnipay] and [Payum] payment processing libraries.
14 |
15 | [Yii2]: http://yiiframework.com/
16 | [Omnipay]: http://omnipay.thephpleague.com/
17 | [Payum]: http://payum.org/
18 |
19 | ## Installation
20 |
21 | The preferred way to install this yii2-extension is through [composer](http://getcomposer.org/download/).
22 |
23 | Either run
24 |
25 | ```sh
26 | php composer.phar require "hiqdev/yii2-merchant"
27 | ```
28 |
29 | or add
30 |
31 | ```json
32 | "hiqdev/yii2-merchant": "*"
33 | ```
34 |
35 | to the require section of your composer.json.
36 |
37 | ## License
38 |
39 | This project is released under the terms of the BSD-3-Clause [license](LICENSE).
40 | Read more [here](http://choosealicense.com/licenses/bsd-3-clause).
41 |
42 | Copyright © 2015-2017, HiQDev (http://hiqdev.com/)
43 |
--------------------------------------------------------------------------------
/src/assets/css/selectPayment.css:
--------------------------------------------------------------------------------
1 | .products-list {
2 | list-style: none;
3 | margin: 0;
4 | padding: 0;
5 | }
6 |
7 | .products-list > .item {
8 | border-radius: 3px;
9 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
10 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
11 | padding: 10px 0;
12 | background: #fff;
13 | }
14 |
15 | .products-list > .item:before,
16 | .products-list > .item:after {
17 | content: ' ';
18 | display: table;
19 | }
20 |
21 | .products-list > .item:after {
22 | clear: both;
23 | }
24 |
25 | .products-list .product-img {
26 | float: left;
27 | }
28 |
29 | .products-list .product-img img {
30 | width: 50px;
31 | height: 50px;
32 | }
33 |
34 | .products-list .product-info {
35 | margin-left: 60px;
36 | }
37 |
38 | .products-list .product-title {
39 | font-weight: 600;
40 | }
41 |
42 | .products-list .product-description {
43 | display: block;
44 | color: #999;
45 | overflow: hidden;
46 | white-space: nowrap;
47 | text-overflow: ellipsis;
48 | }
49 |
50 | .product-list-in-box > .item {
51 | -webkit-box-shadow: none;
52 | box-shadow: none;
53 | border-radius: 0;
54 | border-bottom: 1px solid #f4f4f4;
55 | }
56 |
57 | .product-list-in-box > .item:last-of-type {
58 | border-bottom-width: 0;
59 | }
60 |
61 | .products-list .product-img {
62 | margin-right: 15px;
63 | }
64 |
65 | .products-list button {
66 | border: none;
67 | text-align: left;
68 | background: none;
69 | outline: none;
70 | padding: 1rem;
71 | }
72 |
73 | .products-list button:hover {
74 | background: #f7f7f7;
75 | }
76 |
77 | .products-list .product-description {
78 | overflow: visible;
79 | }
80 |
81 | @media screen and (max-device-width: 480px) and (orientation: portrait){
82 | .product-info > div > span {
83 | display: none;
84 | }
85 | .products-list .product-description {
86 | overflow: auto;
87 | white-space: normal;
88 | text-overflow: clip;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/models/DepositForm.php:
--------------------------------------------------------------------------------
1 | 9999999999999, 'whenClient' => 'function (attribute, value) {
44 | if (value.includes(",")) {
45 | var form = attribute.$form;
46 | $("#" + attribute.id).val(value.replace(/[,]/g, "."));
47 | form.yiiActiveForm("validate");
48 | }
49 | return true;
50 | }'],
51 | [['currency'], 'default', 'value' => 'usd'],
52 | [['amount', 'currency'], 'required'],
53 | [['amount'], 'compare', 'operator' => '>', 'compareValue' => 0],
54 | ];
55 | }
56 |
57 | public function attributes()
58 | {
59 | return ['amount', 'currency'];
60 | }
61 |
62 | /**
63 | * {@inheritdoc}
64 | */
65 | public function attributeLabels()
66 | {
67 | return [
68 | 'amount' => Yii::t('merchant', 'Amount'),
69 | 'currency' => Yii::t('merchant', 'Currency'),
70 | ];
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/.php_cs:
--------------------------------------------------------------------------------
1 | setUsingCache(true)
14 | ->setRiskyAllowed(true)
15 | ->setRules(array(
16 | '@Symfony' => true,
17 | 'header_comment' => [
18 | 'header' => $header,
19 | 'separate' => 'bottom',
20 | 'location' => 'after_declare_strict',
21 | 'commentType' => 'PHPDoc',
22 | ],
23 | 'binary_operator_spaces' => [
24 | 'align_double_arrow' => null,
25 | 'align_equals' => null,
26 | ],
27 | 'concat_space' => ['spacing' => 'one'],
28 | 'array_syntax' => ['syntax' => 'short'],
29 | 'blank_line_before_return' => false,
30 | 'phpdoc_align' => false,
31 | 'phpdoc_scalar' => false,
32 | 'phpdoc_separation' => false,
33 | 'phpdoc_to_comment' => false,
34 | 'method_argument_space' => false,
35 | 'ereg_to_preg' => true,
36 | 'blank_line_after_opening_tag' => true,
37 | 'single_blank_line_before_namespace' => true,
38 | 'ordered_imports' => true,
39 | 'phpdoc_order' => true,
40 | 'pre_increment' => true,
41 | 'strict_comparison' => true,
42 | 'strict_param' => true,
43 | 'no_multiline_whitespace_before_semicolons' => true,
44 | ))
45 | ->setFinder(
46 | PhpCsFixer\Finder::create()
47 | ->in(__DIR__)
48 | ->notPath('vendor')
49 | ->notPath('runtime')
50 | ->notPath('web/assets')
51 | )
52 | ;
53 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hiqdev/yii2-merchant",
3 | "type": "yii2-extension",
4 | "description": "Yii2 extension for payment processing with Omnipay, Payum and more later",
5 | "keywords": [
6 | "yii2",
7 | "merchant",
8 | "payment",
9 | "omnipay",
10 | "payum"
11 | ],
12 | "homepage": "https://github.com/hiqdev/yii2-merchant",
13 | "license": "BSD-3-Clause",
14 | "support": {
15 | "email": "support@hiqdev.com",
16 | "source": "https://github.com/hiqdev/yii2-merchant",
17 | "issues": "https://github.com/hiqdev/yii2-merchant/issues",
18 | "wiki": "https://github.com/hiqdev/yii2-merchant/wiki",
19 | "forum": "http://forum.hiqdev.com/"
20 | },
21 | "authors": [
22 | {
23 | "name": "Andrii Vasyliev",
24 | "role": "Project lead",
25 | "email": "sol@hiqdev.com",
26 | "homepage": "http://hipanel.com/"
27 | },
28 | {
29 | "name": "Dmitry Naumenko",
30 | "role": "Lead backend developer",
31 | "email": "d.naumenko.a@gmail.com",
32 | "homepage": "http://silverfire.me/"
33 | },
34 | {
35 | "name": "Andrey Klochok",
36 | "role": "Lead frontend developer",
37 | "email": "andreyklochok@gmail.com",
38 | "homepage": "http://hiqdev.com/"
39 | },
40 | {
41 | "name": "Yuriy Myronchuk",
42 | "role": "QA Lead",
43 | "email": "bladeroot@gmail.com",
44 | "homepage": "http://hiqdev.com/"
45 | }
46 | ],
47 | "require": {
48 | "php": "^7.4||^8.0",
49 | "hiqdev/yii2-collection": "*",
50 | "hiqdev/payment-icons": "*",
51 | "hiqdev/php-merchant": "*"
52 | },
53 | "require-dev": {
54 | "hiqdev/hidev-php": "*",
55 | "hiqdev/hidev-hiqdev": "*",
56 | "yiisoft/yii2": "*@dev"
57 | },
58 | "autoload": {
59 | "psr-4": {
60 | "hiqdev\\yii2\\merchant\\": "src"
61 | }
62 | },
63 | "autoload-dev": {
64 | "psr-4": {
65 | "hiqdev\\yii2\\merchant\\tests\\": "tests"
66 | }
67 | },
68 | "extra": {
69 | "config-plugin": {
70 | "web": "config/web.php"
71 | }
72 | },
73 | "repositories": [
74 | {
75 | "type": "composer",
76 | "url": "https://asset-packagist.org"
77 | }
78 | ]
79 | }
80 |
--------------------------------------------------------------------------------
/src/models/PurchaseRequest.php:
--------------------------------------------------------------------------------
1 |
21 | */
22 | class PurchaseRequest
23 | {
24 | public $id;
25 |
26 | public $merchant_name;
27 | public $system;
28 | public $label;
29 |
30 | public $amount;
31 | public $fee;
32 | public $commission_fee;
33 |
34 | public $vat_rate;
35 | public $vat_sum;
36 |
37 | public $currency;
38 | public $disableReason;
39 |
40 | public $paymentMethod;
41 |
42 | /** @var RedirectPurchaseResponse */
43 | public $form;
44 |
45 | public function setForm(RedirectPurchaseResponse $response)
46 | {
47 | $this->form = $response;
48 | }
49 |
50 | public function getFormInputs()
51 | {
52 | if (!isset($this->form)) {
53 | return [];
54 | }
55 |
56 | return $this->form->getRedirectData();
57 | }
58 |
59 | public function getFormAction()
60 | {
61 | if (!isset($this->form) || empty($this->form->getRedirectUrl())) {
62 | throw new InvalidConfigException('Form action for purchase request is missing');
63 | }
64 |
65 | return $this->form->getRedirectUrl();
66 | }
67 |
68 | public function getFormMethod()
69 | {
70 | if (!isset($this->form)) {
71 | return 'POST';
72 | }
73 |
74 | return $this->form->getMethod();
75 | }
76 |
77 | public function getPaymentMethodLabel(): ?string
78 | {
79 | if ($this->system !== 'yandexmoney') {
80 | return null;
81 | }
82 |
83 | if ($this->paymentMethod === null) {
84 | return null;
85 | }
86 |
87 | if ($this->paymentMethod === 'AC') {
88 | return Yii::t('merchant', 'via bank card');
89 | }
90 |
91 | if ($this->paymentMethod === 'PC') {
92 | return Yii::t('merchant', 'via account balance');
93 | }
94 |
95 | return Yii::t('merchant', 'via phone');
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/widgets/views/pay-button.php:
--------------------------------------------------------------------------------
1 |
14 | $widget->action]) ?>
15 | = Html::activeHiddenInput($depositRequest, 'id') ?>
16 | = Html::activeHiddenInput($depositRequest, 'amount') ?>
17 | = Html::activeHiddenInput($depositRequest, 'merchant') ?>
18 | = Html::activeHiddenInput($depositRequest, 'currency') ?>
19 | finishUrl) : ?>
20 | = Html::activeHiddenInput($depositRequest, 'finishUrl') ?>
21 |
22 |
23 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/src/transactions/Transaction.php:
--------------------------------------------------------------------------------
1 | id = $id;
35 | $this->merchant = $merchant;
36 | }
37 |
38 | public function getMerchant()
39 | {
40 | return $this->merchant;
41 | }
42 |
43 | /**
44 | * @return bool
45 | */
46 | public function isCompleted()
47 | {
48 | return $this->success !== null;
49 | }
50 |
51 | public function complete()
52 | {
53 | $this->success = true;
54 | }
55 |
56 | public function cancel($error = null)
57 | {
58 | $this->success = false;
59 | $this->addParameter('error', $error);
60 | }
61 |
62 | public function isConfirmed()
63 | {
64 | return $this->success === true;
65 | }
66 |
67 | /**
68 | * @return bool
69 | */
70 | public function getSuccess()
71 | {
72 | return $this->success;
73 | }
74 |
75 | /**
76 | * @return string
77 | */
78 | public function getId()
79 | {
80 | return $this->id;
81 | }
82 |
83 | /**
84 | * @return array
85 | */
86 | public function getParameters()
87 | {
88 | return $this->parameters;
89 | }
90 |
91 | /**
92 | * @param array $parameters
93 | */
94 | public function setParameters($parameters)
95 | {
96 | $this->parameters = $parameters;
97 | }
98 |
99 | /**
100 | * Adds parameter $parameter with value $value to the parameters
101 | *
102 | * @param string $parameter
103 | * @param mixed $value
104 | */
105 | public function addParameter($parameter, $value)
106 | {
107 | if (isset($this->parameters[$parameter])) {
108 | return;
109 | }
110 |
111 | $this->parameters[$parameter] = $value;
112 | }
113 |
114 | /**
115 | * @return array
116 | */
117 | public function toArray()
118 | {
119 | return [
120 | 'id' => $this->getId(),
121 | 'merchant' => $this->getMerchant(),
122 | 'parameters' => $this->getParameters(),
123 | 'success' => $this->getSuccess()
124 | ];
125 | }
126 |
127 | public function getParameter($parameter)
128 | {
129 | return isset($this->parameters[$parameter]) ? $this->parameters[$parameter] : null;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/messages/ru/merchant.php:
--------------------------------------------------------------------------------
1 | 'Сумма',
30 | 'Available payment methods' => 'Доступные методы оплаты',
31 | 'Enter the amount of the replenishment in {currency}. For example: 8.79' => 'Введите сумму пополнения в {currency}. Например: 8,79',
32 | 'Choose the purse currency you want to replenish and enter the payment amount.' => 'Выберите валюту счёта, который хотите пополнить, и укажите сумму платежа.',
33 | 'Important information' => 'Важная информация',
34 | 'Select payment method' => 'Выберите способ оплаты',
35 | 'We support fully automatic account depositing with the following payment systems: {merchants}' => 'Мы принимаем к оплате в автоматическом режиме следующие платежные системы: {merchants}',
36 | 'Remember to return to the site after successful payment!' => 'Не забудьте вернуться на сайт после успешной оплаты!',
37 | 'Pay {amount} with {merchantLabel}' => 'Оплатить {amount} через {merchantLabel}',
38 | 'Proceed' => 'Продолжить',
39 | 'Recharge account' => 'Пополнить счёт',
40 | 'Account recharging' => 'Пополнение счёта',
41 | 'Waiting for confirmation from the payment system...' => 'Ожидаем подтверждения из платежной системы...',
42 | 'Payment result' => 'Результат оплаты',
43 | 'There are no payments methods available' => 'Нет доступных методов оплаты',
44 | 'Amount' => 'Сумма',
45 | 'Name' => 'Название',
46 | 'Payment System' => 'Платежная система',
47 | 'Purse' => 'Кошелек',
48 | 'Fee' => 'Комиссия платежной системы',
49 | 'Currency' => 'Валюта',
50 | 'Signature' => 'Подпись',
51 | 'Commission' => 'Комиссия',
52 | 'Payment failed or cancelled' => 'Платеж прошел неуспешно или был отменён',
53 | 'including commission {commission}' => 'включая комиссию {commission}',
54 | 'including VAT {rate}% — {sum}' => 'включая НДС {rate}% — {sum}',
55 | 'via account balance' => 'с помощью перевода с баланса',
56 | 'via bank card' => 'с помощью банковской карты',
57 | 'via phone' => 'с помощью телефона',
58 | ];
59 |
--------------------------------------------------------------------------------
/tests/unit/ModuleTest.php:
--------------------------------------------------------------------------------
1 | [
31 | 'purse' => 'asd',
32 | 'secret' => '123',
33 | ],
34 | 'wmusd' => [
35 | 'gateway' => 'WebMoney',
36 | 'purse' => 'Z0000000',
37 | 'secret' => '123',
38 | ],
39 | 'fake' => [
40 | 'class' => 'hiqdev\yii2\merchant\tests\unit\FakeMerchant',
41 | 'purse' => 'SDF',
42 | 'secret' => '000',
43 | ],
44 | ];
45 |
46 | protected function setUp()
47 | {
48 | $application = new Application([
49 | 'id' => 'fake',
50 | 'basePath' => dirname(dirname(__DIR__)),
51 | 'modules' => [
52 | 'merchant' => [
53 | 'class' => Module::class,
54 | 'username' => 'fake',
55 | 'notifyPage' => '/my/notify/page',
56 | 'returnPage' => '/merchant/pay/return',
57 | 'cancelPage' => '/merchant/pay/cancel',
58 | 'collection' => $this->gateways,
59 | ],
60 | ],
61 | 'container' => [
62 | 'singletons' => [
63 | \hiqdev\yii2\merchant\transactions\TransactionRepositoryInterface::class => \hiqdev\yii2\merchant\tests\unit\FakeTransactionRepository::class,
64 | ],
65 | ],
66 | ]);
67 | $this->module = Yii::$app->getModule('merchant');
68 | }
69 |
70 | protected function tearDown()
71 | {
72 | }
73 |
74 | public function testGetCollection()
75 | {
76 | $this->assertInstanceOf(Collection::class, $this->module->collection);
77 | }
78 |
79 | public function testGetMerchant()
80 | {
81 | $this->assertInstanceOf(FakeMerchant::class, $this->module->collection->fake);
82 | $this->assertInstanceOf(OmnipayMerchant::class, $this->module->collection->wmusd);
83 | $this->assertSame($this->gateways['wmusd']['purse'], $this->module->collection->wmusd->data['purse']);
84 | $this->assertSame($this->gateways['fake']['secret'], $this->module->collection->fake->data['secret']);
85 | }
86 |
87 | public function testGetWorker()
88 | {
89 | $this->assertInstanceOf(FakeGateway::class, $this->module->collection->fake->getWorker());
90 | $this->assertInstanceOf('Omnipay\Stripe\Gateway', $this->module->collection->Stripe->getWorker());
91 | $this->assertInstanceOf('Omnipay\WebMoney\Gateway', $this->module->collection->wmusd->getWorker());
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/actions/RequestAction.php:
--------------------------------------------------------------------------------
1 | controller->getMerchantModule();
25 | }
26 |
27 | public function run()
28 | {
29 | $depositRequest = $this->loadDepositRequest();
30 | $this->registerTransaction($depositRequest);
31 | $request = $this->createPurchaseRequest($depositRequest);
32 |
33 | return $this->handlePurchaseRequest($request);
34 | }
35 |
36 | /**
37 | * @return DepositRequest
38 | * @throws BadRequestHttpException
39 | */
40 | protected function loadDepositRequest()
41 | {
42 | $depositRequest = new DepositRequest();
43 | $depositRequest->load(Yii::$app->request->post());
44 | if (!$depositRequest->validate()) {
45 | $errors = $depositRequest->getFirstErrors();
46 | throw new BadRequestHttpException('Deposit request is not loaded: ' . reset($errors));
47 | }
48 |
49 | $this->getMerchantModule()->prepareRequestData($depositRequest);
50 |
51 | return $depositRequest;
52 | }
53 |
54 | /**
55 | * @param DepositRequest $depositRequest
56 | * @return \hiqdev\yii2\merchant\models\PurchaseRequest
57 | */
58 | protected function createPurchaseRequest($depositRequest)
59 | {
60 | $request = $this->getMerchantModule()->getPurchaseRequest($depositRequest->merchant, $depositRequest);
61 |
62 | return $request;
63 | }
64 |
65 | /**
66 | * @param DepositRequest $depositRequest
67 | */
68 | protected function registerTransaction($depositRequest)
69 | {
70 | $this->trigger(self::EVENT_BEFORE_TRANSACTION_INSERT, new TransactionInsertEvent(['depositRequest' => $depositRequest]));
71 |
72 | $transaction = $this->getMerchantModule()->insertTransaction($depositRequest->id, $depositRequest->merchant, array_merge([
73 | 'username' => $depositRequest->username,
74 | 'currency' => $depositRequest->currency,
75 | ], $depositRequest->toArray()));
76 |
77 | $this->trigger(self::EVENT_AFTER_TRANSACTION_INSERT, new TransactionInsertEvent([
78 | 'depositRequest' => $depositRequest,
79 | 'transaction' => $transaction
80 | ]));
81 | }
82 |
83 | /**
84 | * @param PurchaseRequest $response
85 | * @return \yii\web\Response
86 | * @throws BadRequestHttpException
87 | */
88 | protected function handlePurchaseRequest($response)
89 | {
90 | if ('GET' === $response->getFormMethod()) {
91 | return $this->controller->redirect($response->getFormAction());
92 | } elseif ('POST' === $response->getFormMethod()) {
93 | $hiddenFields = '';
94 | foreach ($response->getFormInputs() as $key => $value) {
95 | $hiddenFields .= sprintf(
96 | '',
97 | htmlentities($key, ENT_QUOTES, 'UTF-8', false),
98 | htmlentities($value, ENT_QUOTES, 'UTF-8', false)
99 | )."\n";
100 | }
101 |
102 | $output = '
103 |
104 |
105 |
106 | Redirecting...
107 |
108 |
109 |
116 |
117 | ';
118 | $output = sprintf(
119 | $output,
120 | htmlentities($response->getFormAction(), ENT_QUOTES, 'UTF-8', false),
121 | $hiddenFields
122 | );
123 |
124 | return $output;
125 | }
126 |
127 | throw new BadRequestHttpException();
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/widgets/PayButton.php:
--------------------------------------------------------------------------------
1 | getView());
52 | $this->registerMerchantCss();
53 | }
54 |
55 | protected function registerMerchantCss()
56 | {
57 | $pathToCss = dirname(__DIR__) . '/assets/css/selectPayment.css';
58 | if (is_file($pathToCss)) {
59 | Yii::$app->assetManager->publish($pathToCss);
60 | $file = Yii::$app->assetManager->getPublishedUrl($pathToCss);
61 | $this->view->registerCssFile($file);
62 | }
63 | }
64 |
65 | /**
66 | * {@inheritdoc}
67 | */
68 | public function run()
69 | {
70 | return $this->renderButton();
71 | }
72 |
73 | /**
74 | * Renders the payment button.
75 | *
76 | * @return string
77 | */
78 | public function renderButton()
79 | {
80 | return $this->render('pay-button', [
81 | 'widget' => $this,
82 | 'request' => $this->request,
83 | 'depositRequest' => new DepositRequest([
84 | 'id' => $this->request->id,
85 | 'amount' => $this->depositForm->amount,
86 | 'currency' => $this->depositForm->currency,
87 | 'merchant' => $this->getMerchantName(),
88 | 'finishUrl' => $this->depositForm->finishUrl,
89 | ])
90 | ]);
91 | }
92 |
93 | /**
94 | * Extracts merchant name from the [[request]].
95 | * @return string
96 | */
97 | public function getMerchantName()
98 | {
99 | return $this->request->merchant_name;
100 | }
101 |
102 | /**
103 | * Extracts merchant system from the [[request]].
104 | * @return string
105 | */
106 | public function getMerchantSystem()
107 | {
108 | return $this->request->system;
109 | }
110 |
111 | public function getTotalCommission()
112 | {
113 | return $this->request->fee + $this->request->commission_fee;
114 | }
115 |
116 | public function includesVat(): bool
117 | {
118 | return $this->request->vat_rate !== null;
119 | }
120 |
121 | public function getVatRate(): float
122 | {
123 | return $this->request->vat_rate;
124 | }
125 |
126 | public function getVatSum(): float
127 | {
128 | return $this->request->vat_sum;
129 | }
130 |
131 | public function getButtonData()
132 | {
133 | return Json::encode($this->request->getFormInputs());
134 | }
135 |
136 | /**
137 | * Renders the button comment. Normally triggers [[EVENT_RENDER_COMMENT]] event.
138 | */
139 | public function renderButtonComment()
140 | {
141 | $this->trigger(self::EVENT_RENDER_COMMENT);
142 | }
143 |
144 | public function formatMoney($sum)
145 | {
146 | $currency = $this->request->currency;
147 | $formatter = new NumberFormatter(Yii::$app->language, NumberFormatter::CURRENCY);
148 |
149 | switch ($currency) {
150 | case 'BTC':
151 | $formatter->setAttribute(NumberFormatter::MAX_SIGNIFICANT_DIGITS, 6);
152 | break;
153 | case 'RUB':
154 | $formatter->setSymbol(NumberFormatter::CURRENCY_SYMBOL, '₽');
155 | break;
156 | }
157 |
158 | return $formatter->formatCurrency($sum, $currency);
159 | }
160 |
161 | public function isDisabled()
162 | {
163 | return $this->request->disableReason !== null;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/views/pay/deposit-form.php:
--------------------------------------------------------------------------------
1 | title = Yii::t('merchant', 'Account recharging');
15 | $this->params['breadcrumbs'][] = $this->title;
16 | PaymentIconsAsset::register(Yii::$app->getView());
17 |
18 | $merchantNames = [];
19 | foreach ($availableMerchants as $merchant) {
20 | $merchantNames[$merchant->system] = $merchant->label;
21 | }
22 |
23 | $primaryCurrency = reset($availableCurrencies);
24 | $primaryCurrencySymbol = $primaryCurrency->getSymbol();
25 |
26 | ?>
27 |
28 |
90 |
91 |
92 |
93 | context->module->cashewOnly === false) : ?>
94 |
95 |
96 |
99 |
100 | = Yii::t('merchant', 'We support fully automatic account depositing with the following payment systems: {merchants}', [
101 | 'merchants' => implode(', ', $merchantNames)
102 | ]) ?>
103 |
104 |
105 | $label) : ?>
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
= Yii::t('merchant', 'Important information') ?>
115 |
= Yii::t('merchant', 'Remember to return to the site after successful payment!') ?>
116 |
117 |
118 |
119 |
120 |
121 |
122 | registerJs(<<
131 |
--------------------------------------------------------------------------------
/src/controllers/PayController.php:
--------------------------------------------------------------------------------
1 | [
36 | 'class' => RequestAction::class
37 | ]
38 | ]);
39 | }
40 |
41 | /**
42 | * @return Module|\yii\base\Module
43 | */
44 | public function getMerchantModule()
45 | {
46 | return $this->module;
47 | }
48 |
49 | /**
50 | * Disable CSRF validation for POST requests we receive from outside
51 | * {@inheritdoc}
52 | */
53 | public function beforeAction($action)
54 | {
55 | if (in_array($this->action->id, ['notify', 'return', 'cancel', 'proxy-notification'], true)) {
56 | Yii::$app->controller->enableCsrfValidation = false;
57 | }
58 |
59 | return parent::beforeAction($action);
60 | }
61 |
62 | /**
63 | * @return Response
64 | */
65 | public function actionCancel()
66 | {
67 | Yii::$app->session->addFlash('error', Yii::t('merchant', 'Payment failed or cancelled'));
68 |
69 | return $this->redirect($this->getMerchantModule()->previousUrl() ?: ['deposit']);
70 | }
71 |
72 | /**
73 | * @param string|null $transactionId
74 | *
75 | * Parameters are NOT required because some merchants may NOT send them, or send in POST or JSON bode.
76 | * The main purpose of these parameters is handling of special routes using UrlManager
77 | *
78 | * @return string
79 | * @throws InvalidConfigException
80 | */
81 | public function actionReturn(string $transactionId = null)
82 | {
83 | $transaction = $this->checkNotify($transactionId);
84 | if ($transaction === null) {
85 | return $this->actionCancel();
86 | }
87 |
88 | return $this->render('return', [
89 | 'transactionId' => $transaction->getId(),
90 | ]);
91 | }
92 |
93 | /**
94 | * @param string $transactionId
95 | * @throws BadRequestHttpException
96 | * @return array
97 | */
98 | public function actionCheckReturn($transactionId)
99 | {
100 | Yii::$app->response->format = Response::FORMAT_JSON;
101 | $transaction = $this->getMerchantModule()->findTransaction($transactionId);
102 |
103 | if ($transaction === null) {
104 | throw new NotFoundHttpException('Transaction does not exist');
105 | }
106 |
107 | if ($transaction->getParameter('username') !== $this->getMerchantModule()->getUsername()) {
108 | throw new BadRequestHttpException('Access denied', 403);
109 | }
110 |
111 | return [
112 | 'status' => $transaction->getSuccess(),
113 | 'url' => $transaction->isConfirmed()
114 | ? Url::toRoute($this->successPaymentRedirectRoute)
115 | : Url::toRoute($this->failedPaymentRedirectRoute),
116 | ];
117 | }
118 |
119 | /**
120 | * Action handles notifications from payment systems,
121 | * processes them and report success or error for the payment system.
122 | *
123 | * @param string|null $transactionId Parameters is NOT required because some merchants may NOT send it, or send in POST or JSON body.
124 | * The main purpose of these parameters is handling of special routes using UrlManager
125 | *
126 | * @return null|string
127 | * @throws InvalidConfigException
128 | */
129 | public function actionNotify(string $transactionId = null)
130 | {
131 | $transaction = $this->checkNotify($transactionId);
132 | if ($transaction === null) {
133 | return 'Unknown transaction';
134 | }
135 |
136 | Yii::$app->response->format = Response::FORMAT_RAW;
137 |
138 | return $transaction->isConfirmed() ? 'OK' : $transaction->getParameter('error');
139 | }
140 |
141 | /**
142 | * Check notifications.
143 | * TODO: implement actual request check and proper handling.
144 | *
145 | * @param string|null $transactionId Parameters is NOT required because some merchants may NOT send it, or send in POST or JSON body.
146 | * The main purpose of these parameters is handling of special routes using UrlManager
147 | *
148 | * @return Transaction|null
149 | * @throws InvalidConfigException
150 | */
151 | public function checkNotify(string $transactionId = null): ?Transaction
152 | {
153 | throw new InvalidConfigException('Method checkNotify must be implemented');
154 | }
155 |
156 | public function actionProxyNotification()
157 | {
158 | throw new InvalidConfigException('Method actionProxyNotification must be implemented');
159 | }
160 |
161 | public function actionDeposit()
162 | {
163 | $user = Yii::$app->user;
164 | if (!$user->isGuest && !$user->can('deposit')) {
165 | throw new ForbiddenHttpException(Yii::t('yii', 'You are not allowed to perform this action.'));
166 | }
167 |
168 | $merchantModule = $this->getMerchantModule();
169 |
170 | $model = Yii::createObject($merchantModule->depositFromClass);
171 | $request = Yii::$app->request;
172 | if ($model->load($request->isPost ? $request->post() : $request->get()) && $model->validate()) {
173 | return $this->renderDeposit($model);
174 | }
175 |
176 | return $this->render('deposit-form', [
177 | 'model' => $model,
178 | 'availableMerchants' => $this->getMerchantModule()->getPurchaseRequestCollection()->getItems(),
179 | 'availableCurrencies' => $this->getMerchantModule()->getAvailableCurrenciesCollection()->getList(),
180 | ]);
181 | }
182 |
183 | /**
184 | * Renders depositing buttons for given request data.
185 | *
186 | * @param DepositForm $form request data
187 | * @return Response|string
188 | * @throws InvalidConfigException
189 | */
190 | public function renderDeposit(Model $form)
191 | {
192 | if ($this->getMerchantModule()->cashewOnly) {
193 | $request = $this->createDepositRequestWithForm($form);
194 | $request->id = bin2hex(random_bytes(8));
195 | $request->merchant = sprintf('cashew_%s', strtolower($request->currency));
196 | $this->getMerchantModule()->prepareRequestData($request);
197 |
198 | $requests = $this->getMerchantModule()->getPurchaseRequestCollection($request)->getItems();
199 | if (isset($requests[$request->merchant])) {
200 | return $this->redirect($requests[$request->merchant]->form->getRedirectUrl());
201 | }
202 | }
203 | $request = $this->createDepositRequestWithForm($form);
204 | $requests = $this->getMerchantModule()->getPurchaseRequestCollection($request)->getItems();
205 |
206 | return $this->render('deposit', [
207 | 'requests' => $requests,
208 | 'depositForm' => $form
209 | ]);
210 | }
211 |
212 | private function createDepositRequestWithForm(Model $form): DepositRequest
213 | {
214 | $request = new DepositRequest();
215 | $request->amount = $form->amount;
216 | $request->currency = $form->currency;
217 | $request->finishUrl = $form->finishUrl;
218 |
219 | return $request;
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/src/Module.php:
--------------------------------------------------------------------------------
1 | [
31 | * 'merchant' => [
32 | * 'class' => 'hiqdev\yii2\merchant\Module',
33 | * 'notifyPage' => '/my/notify/page',
34 | * 'collection' => [
35 | * 'PayPal' => [
36 | * 'purse' => $params['paypal_purse'],
37 | * 'secret' => $params['paypal_secret'], /// NEVER keep secret in source control
38 | * ],
39 | * 'webmoney_usd' => [
40 | * 'gateway' => 'WebMoney',
41 | * 'purse' => $params['webmoney_purse'],
42 | * 'secret' => $params['webmoney_secret'], /// NEVER keep secret in source control
43 | * ],
44 | * ],
45 | * ],
46 | * ],
47 | * ```
48 | *
49 | * @var string returns username for usage in merchant
50 | */
51 | class Module extends \yii\base\Module
52 | {
53 | /**
54 | * The URL prefix that will be used as a key to save current URL in the session.
55 | *
56 | * @see rememberUrl()
57 | * @see previousUrl()
58 | * @see \yii\helpers\BaseUrl::remember()
59 | * @see \yii\helpers\BaseUrl::previous()
60 | */
61 | const URL_PREFIX = 'merchant_url_';
62 |
63 | /**
64 | * @var string|class-string merchant collection class name. Defaults to [[Collection]]
65 | */
66 | public $purchaseRequestCollectionClass = Collection::class;
67 | /**
68 | * @var string currencies collection class name. Defaults to [[Collection]]
69 | */
70 | public $currenciesCollectionClass;
71 | /**
72 | * @var string Deposit model class name. Defaults to [[DepositForm]]
73 | */
74 | public $depositFromClass = DepositForm::class;
75 | /**
76 | * @var bool Whether to use payment processing only through Cashew
77 | */
78 | public bool $cashewOnly = false;
79 | /**
80 | * @var TransactionRepositoryInterface
81 | */
82 | protected $transactionRepository;
83 |
84 | public function __construct($id, $parent = null, TransactionRepositoryInterface $transactionRepository, array $config = [])
85 | {
86 | parent::__construct($id, $parent, $config);
87 |
88 | $this->transactionRepository = $transactionRepository;
89 | }
90 |
91 | public function setCollection(array $collection)
92 | {
93 | $this->_collection = $collection;
94 | }
95 |
96 | /**
97 | * @param DepositRequest $depositRequest
98 | * @return Collection
99 | * @throws InvalidConfigException
100 | */
101 | public function getPurchaseRequestCollection($depositRequest = null)
102 | {
103 | return Yii::createObject([
104 | 'class' => $this->purchaseRequestCollectionClass,
105 | 'module' => $this,
106 | 'depositRequest' => $depositRequest,
107 | ]);
108 | }
109 |
110 | /**
111 | * @return Currencies
112 | * @throws InvalidConfigException
113 | */
114 | public function getAvailableCurrenciesCollection(): Currencies
115 | {
116 | return Yii::createObject([
117 | 'class' => $this->currenciesCollectionClass,
118 | 'module' => $this,
119 | ]);
120 | }
121 |
122 | /**
123 | * @param string $merchant_name merchant id
124 | * @param DepositRequest $depositRequest
125 | * @return PurchaseRequest merchant instance
126 | */
127 | public function getPurchaseRequest($merchant_name, DepositRequest $depositRequest)
128 | {
129 | return $this->getPurchaseRequestCollection($depositRequest)->get($merchant_name);
130 | }
131 |
132 | /**
133 | * Checks if merchant exists in the hub.
134 | *
135 | * @param string $id merchant id
136 | * @return bool whether merchant exist
137 | */
138 | public function hasPurchaseRequest($id)
139 | {
140 | return $this->getPurchaseRequestCollection()->has($id);
141 | }
142 |
143 | /**
144 | * Method builds data for merchant request.
145 | *
146 | * @param DepositRequest $depositRequest
147 | */
148 | public function prepareRequestData($depositRequest): void
149 | {
150 | $depositRequest->username = $this->getUsername();
151 | $depositRequest->notifyUrl = $this->buildUrl('notify', $depositRequest);
152 | $depositRequest->returnUrl = $this->buildUrl('return', $depositRequest);
153 | $depositRequest->cancelUrl = $this->buildUrl('cancel', $depositRequest);
154 | $depositRequest->finishUrl = $this->buildUrl('finish', $depositRequest);
155 | }
156 |
157 | /**
158 | * @var string client login
159 | */
160 | protected $_username;
161 |
162 | /**
163 | * Sets [[_username]].
164 | *
165 | * @param $username
166 | */
167 | public function setUsername($username)
168 | {
169 | $this->_username = $username;
170 | }
171 |
172 | /**
173 | * Gets [[_username]] when defined, otherwise - `Yii::$app->user->identity->username`,
174 | * otherwise `Yii::$app->user->identity->getId()`.
175 | * @throws InvalidConfigException
176 | * @return string
177 | */
178 | public function getUsername()
179 | {
180 | if (isset($this->_username)) {
181 | return $this->_username;
182 | } elseif (($identity = Yii::$app->user->identity) !== null) {
183 | if ($identity->hasProperty('username')) {
184 | $this->_username = $identity->username;
185 | } else {
186 | $this->_username = $identity->getId();
187 | }
188 |
189 | return $this->_username;
190 | }
191 | throw new InvalidConfigException('Unable to determine username');
192 | }
193 |
194 | /**
195 | * @var string|array the URL that will be used for payment system notifications. Will be passed through [[Url::to()]]
196 | */
197 | public $notifyPage = 'notify';
198 | /**
199 | * @var string|array the URL that will be used to redirect client from the merchant after the success payment.
200 | * Will be passed through [[Url::to()]]
201 | */
202 | public $returnPage = 'return';
203 | /**
204 | * @var string|array the URL that will be used to redirect client from the merchant after the failed payment.
205 | * Will be passed through [[Url::to()]]
206 | */
207 | public $cancelPage = 'cancel';
208 | /**
209 | * @var string|array the URL that might be used to redirect used from the success or error page to the finish page.
210 | * Will be passed through [[Url::to()]]
211 | */
212 | public $finishPage = 'finish';
213 |
214 | /**
215 | * Builds URLs that will be passed in the request to the merchant.
216 | *
217 | * @param string $destination `notify`, `return`, `cancel`
218 | * @param DepositRequest $depositRequest
219 | * @return string URL
220 | */
221 | public function buildUrl($destination, DepositRequest $depositRequest)
222 | {
223 | $page = [
224 | $this->getPage($destination, $depositRequest),
225 | 'username' => $depositRequest->username,
226 | 'merchant' => $depositRequest->merchant,
227 | 'transactionId' => $depositRequest->id,
228 | ];
229 |
230 | if (is_array($page)) {
231 | $page[0] = $this->localizePage($page[0]);
232 | } else {
233 | $page = $this->localizePage($page);
234 | }
235 |
236 | return Url::to($page, true);
237 | }
238 |
239 | /**
240 | * Builds url to `this_module/pay/$page` if page is not /full/page.
241 | * @param mixed $page
242 | * @return mixed
243 | */
244 | public function localizePage($page)
245 | {
246 | return is_string($page) && $page[0] !== '/' ? ('/' . $this->id . '/pay/' . $page) : $page;
247 | }
248 |
249 | public function getPage($destination, DepositRequest $depositRequest)
250 | {
251 | $property = $destination . 'Url';
252 | if ($depositRequest->$property) {
253 | return $depositRequest->$property;
254 | }
255 |
256 | $name = $destination . 'Page';
257 |
258 | return $this->hasProperty($name) ? $this->{$name} : $destination;
259 | }
260 |
261 | /**
262 | * Saves the $url to session with [[URL_PREFIX]] key, trailed with $name.
263 | *
264 | * @param array|string $url
265 | * @param string $name the trailing part for the URL save key. Defaults to `back`
266 | * @void
267 | */
268 | public function rememberUrl($url, $name = 'back')
269 | {
270 | Url::remember($url, static::URL_PREFIX . $name);
271 | }
272 |
273 | /**
274 | * Extracts the URL from session storage, saved with [[URL_PREFIX]] key, trailed with $name.
275 | *
276 | * @param string $name the trailing part for the URL save key. Defaults to `back`
277 | * @return string
278 | */
279 | public function previousUrl($name = 'back')
280 | {
281 | return Url::previous(static::URL_PREFIX . $name);
282 | }
283 |
284 | /**
285 | * @var PayController The Payment controller
286 | */
287 | protected $_payController;
288 |
289 | /**
290 | * @throws InvalidConfigException
291 | *
292 | * @return PayController
293 | */
294 | public function getPayController()
295 | {
296 | if ($this->_payController === null) {
297 | $this->_payController = $this->createControllerById('pay');
298 | }
299 |
300 | return $this->_payController;
301 | }
302 |
303 | /**
304 | * Renders page, that contains list of payment systems, that might be choosen by user.
305 | * Should be implemented in `PayController`.
306 | *
307 | * @param DepositForm $form
308 | * @return \yii\web\Response
309 | */
310 | public function renderDeposit($form)
311 | {
312 | return $this->getPayController()->renderDeposit($form);
313 | }
314 |
315 | /**
316 | * @param Transaction $transaction
317 | * @return Transaction
318 | */
319 | public function saveTransaction($transaction)
320 | {
321 | return $this->transactionRepository->save($transaction);
322 | }
323 |
324 | public function insertTransaction($id, $merchant, $data)
325 | {
326 | $transaction = $this->transactionRepository->create($id, $merchant, $data);
327 |
328 | return $this->transactionRepository->insert($transaction);
329 | }
330 |
331 | /**
332 | * @param string $id transaction ID
333 | * @return Transaction|null
334 | */
335 | public function findTransaction($id)
336 | {
337 | try {
338 | return $this->transactionRepository->findById($id);
339 | } catch (TransactionException $e) {
340 | return null;
341 | }
342 | }
343 | }
344 |
--------------------------------------------------------------------------------
/history.md:
--------------------------------------------------------------------------------
1 | # hiqdev/yii2-merchant
2 |
3 | ## [0.1.0] - 2017-09-28
4 |
5 | - Refactored transactions storage
6 | - [e4c0085] 2017-09-28 fixed tests [@hiqsol]
7 | - [9723af5] 2017-09-28 csfixed [@hiqsol]
8 | - [47379d4] 2017-07-31 Updated Module to use hostname instead of servername [@SilverFire]
9 | - [2ee5644] 2017-06-20 renamed `web` config <- hisite [@hiqsol]
10 | - [ee8ca60] 2017-06-20 renamed `hidev.yml` [@hiqsol]
11 | - [7e76575] 2017-03-31 Removed redundant check in Module::saveTransaction() [@SilverFire]
12 | - [80f5213] 2017-03-31 Fixed case when tansaction is not found [@SilverFire]
13 | - [60eecd6] 2017-03-31 Renamed `status` to `success` in Transaction table [@SilverFire]
14 | - [d2116fd] 2017-03-30 Refactored transactions storage [@SilverFire]
15 | - [9d55e1d] 2017-03-29 fixed getting available merchants in `pay/deposit` [@hiqsol]
16 | - [35326fe] 2017-03-28 Updated PayButton and Deposit form to follow php-merchant API changes [@SilverFire]
17 | - [99a4950] 2017-03-28 Enhanced PayController::actionRequest() to check merchant presence [@SilverFire]
18 | - [2fd82c1] 2017-03-27 redone `Module::getCollection` function to fetch collection every time again [@hiqsol]
19 | - [f443293] 2017-03-27 csfixed [@SilverFire]
20 | - [9c8aaea] 2017-03-27 Added PHPUnit 6 compatibility [@SilverFire]
21 | - [250ba1e] 2017-03-23 Removed commented code [@tafid]
22 | - [a47e48a] 2017-03-22 Added to obtain available merchants to Deposit action [@tafid]
23 | - [8e34c11] 2017-02-16 Added extra config-plugin hisite config [@tafid]
24 | - [0455e49] 2017-02-01 PayButton refactord to trigger local event instead of a global one [@SilverFire]
25 | - [2e5cbd8] 2017-02-01 Implemented button comment rendering [@SilverFire]
26 | - Changed design
27 | - [56a4365] 2017-03-31 Removed box header from deposit view [@tafid]
28 | - [6e55765] 2017-03-31 Changed deposit and deposit-form views markup [@tafid]
29 | - [efaad49] 2017-03-27 Added responsive styles to merchant select [@tafid]
30 | - [37fbf3b] 2017-03-27 Removed inline css for deposit view and placed it to ccs file, call it from PayButton widget [@tafid]
31 | - [b338802] 2017-03-27 Changed box size in deposit view [@tafid]
32 | - [7a14b5d] 2017-03-22 Changed design [@tafid]
33 | - [937be79] 2017-02-16 Added extra css to PayButton marckup [@tafid]
34 | - [820df72] 2017-02-16 Added class clearly [@tafid]
35 | - [4ed4be1] 2017-02-16 Added Theme Manager settings [@tafid]
36 | - Added commision support
37 | - [4f1c26c] 2017-01-20 Added commission support [@SilverFire]
38 | - Fixed translations
39 | - [e1a9c7e] 2017-03-27 added i18n hisite config [@hiqsol]
40 | - [c17c7e0] 2017-03-27 Updated translations, fixed presentation logic [@SilverFire]
41 | - [a20b8c2] 2017-03-22 translations [@tafid]
42 | - [c8145a4] 2017-01-13 Translations updated [@SilverFire]
43 | - Fixed eCoin and Paxum
44 | - [33e91a1] 2016-07-13 rehideved [@hiqsol]
45 | - [9dc45c6] 2016-04-18 fixed comleting history [@hiqsol]
46 | - [a0213a0] 2016-03-25 fixed History functions [@hiqsol]
47 | - [77afc6c] 2016-03-25 fixed eCoin to work, it makes no notify just return [@hiqsol]
48 | - Fixed bugs
49 | - [87b761f] 2016-08-05 changed bumping to use `chkipper` [@hiqsol]
50 | - [e4fa5f9] 2016-07-13 csfixed [@hiqsol]
51 | - [0be4d2e] 2016-07-12 csfixed [@hiqsol]
52 | - [e367e59] 2016-06-30 Removed dependency on Err class [@SilverFire]
53 | - [d16a1ec] 2016-03-25 redone history functions: - id argument [@hiqsol]
54 | - [7e9b380] 2016-03-11 Added translations [@SilverFire]
55 | - [99618c7] 2016-02-11 Minor fix. Add Box view [@tafid]
56 | - [4254b94] 2016-02-04 phpcsfixed [@hiqsol]
57 | - [c66c63c] 2016-02-03 + localizePage to build proper url to /merchant/pay/return [@hiqsol]
58 | - [bca7393] 2016-02-03 pay/deposit - added the message for a case when no payments methods available [@SilverFire]
59 | - Fixed Travis build
60 | - [10c6932] 2016-01-30 fixed tests [@hiqsol]
61 | - [f8fa4aa] 2016-01-26 rehideved [@hiqsol]
62 | - [2616a6e] 2015-12-15 php-cs-fixed [@hiqsol]
63 | - [cae2de5] 2015-12-15 PayController - back ~> returnUrl, PHPDoc updated [@SilverFire]
64 | - [f394b3a] 2015-12-15 fixed tests [@hiqsol]
65 | - [f1ecb98] 2015-12-14 fixed travis: dont build 5.4, dont allow fail 7 [@hiqsol]
66 | - [72c57a8] 2015-12-14 trying hidev.phar [@hiqsol]
67 | - Changed: redone with `php-merchant`
68 | - [d914e86] 2015-12-16 + finishUrl [@hiqsol]
69 | - [099a0a7] 2015-12-14 Action check-return implemented [@SilverFire]
70 | - [eb568af] 2015-12-14 php-cs-fixed [@hiqsol]
71 | - [e3e8e2a] 2015-12-14 Deposit - added validation rules for sum attribute [@SilverFire]
72 | - [15dc2d9] 2015-12-14 Module tidied up more [@SilverFire]
73 | - [25703a6] 2015-12-14 Module tidied up [@SilverFire]
74 | - [d66f028] 2015-12-11 + return/cancel method set to POST [@hiqsol]
75 | - [b57d0fb] 2015-12-11 renamed `transactionId` <- `internalid` [@hiqsol]
76 | - [1d0f00e] 2015-12-11 + return/cancel method set to POST [@hiqsol]
77 | - [cbba2e2] 2015-12-11 renamed `transactionId` <- `internalid` [@hiqsol]
78 | - [278543e] 2015-12-11 + internalid and history [@hiqsol]
79 | - [957f3aa] 2015-12-09 simplified merchant creating [@hiqsol]
80 | - [fce8fd4] 2015-12-08 finishing redoing to php-merchant [@hiqsol]
81 | - [ff73135] 2015-12-08 redone with php-merchant [@hiqsol]
82 | - Fixed redirection to payment systems
83 | - [a2b0337] 2015-12-04 fixed bulidUrl to pass proper merchant [@hiqsol]
84 | - [294da33] 2015-12-03 fixed tests [@hiqsol]
85 | - [2befc31] 2015-12-03 fixed tests [@hiqsol]
86 | - [bc7177c] 2015-12-03 fixed redirection to payment systems [@hiqsol]
87 | - [9379eb2] 2015-12-03 + preparing data for Omnipay merchant unification [@hiqsol]
88 | - [247c544] 2015-12-03 fixed payment icons [@hiqsol]
89 | - Added `renderDeposit` facility
90 | - [c366ba7] 2015-12-01 + `renderDeposit` facility [@hiqsol]
91 | - Added use of Payment Icons
92 | - [79c0ce8] 2015-11-30 used Payment Icons [@hiqsol]
93 | - Added tests and Travis CI
94 | - [995394c] 2015-11-30 improved tests [@hiqsol]
95 | - [4f3ceb1] 2015-11-25 added tests and Travis CI [@hiqsol]
96 | - [277f5e6] 2015-11-25 fixed Module::createMerchant to properly pass data [@hiqsol]
97 | - Chnaged: redone with Omnipay
98 | - [bf5d311] 2015-11-23 Changed namespace to yii2-collection [@SilverFire]
99 | - [b6400ea] 2015-11-12 php-cs-fixed [@hiqsol]
100 | - [ef52743] 2015-11-12 php-cs-fixed [@hiqsol]
101 | - [024f9a1] 2015-11-12 still redoing to omnipay [@hiqsol]
102 | - [b941ed3] 2015-11-09 + require yii2-collection [@hiqsol]
103 | - [1d6f98b] 2015-11-09 started redoing to omnipay [@hiqsol]
104 | - [67aa249] 2015-11-09 improved package description [@hiqsol]
105 | - Added basics
106 | - [ce5afe7] 2015-11-07 added basics [@hiqsol]
107 | - [cf31f89] 2015-10-30 php-cs-fixed [@hiqsol]
108 | - [1b82630] 2015-10-30 finished translation [@hiqsol]
109 | - [961f39d] 2015-10-30 fixed composer type to yii2-extension [@hiqsol]
110 | - [66b9ec7] 2015-10-30 fixed composer type to yii2-extension; adding translations [@hiqsol]
111 | - [124f377] 2015-10-26 adding deposit views, model and action [@hiqsol]
112 | - [234edfc] 2015-10-26 + pay button rendering with merchant [@hiqsol]
113 | - [0614bd9] 2015-10-22 improved with `_loadMerchant` [@hiqsol]
114 | - [26d44b4] 2015-10-21 php-cs-fixed [@hiqsol]
115 | - [f538d37] 2015-10-21 hideved [@hiqsol]
116 | - [dfadf30] 2015-10-21 inited [@hiqsol]
117 |
118 | ## [Development started] - 2015-10-21
119 |
120 | [@hiqsol]: https://github.com/hiqsol
121 | [sol@hiqdev.com]: https://github.com/hiqsol
122 | [@SilverFire]: https://github.com/SilverFire
123 | [d.naumenko.a@gmail.com]: https://github.com/SilverFire
124 | [@tafid]: https://github.com/tafid
125 | [andreyklochok@gmail.com]: https://github.com/tafid
126 | [@BladeRoot]: https://github.com/BladeRoot
127 | [bladeroot@gmail.com]: https://github.com/BladeRoot
128 | [a0213a0]: https://github.com/hiqdev/yii2-merchant/commit/a0213a0
129 | [77afc6c]: https://github.com/hiqdev/yii2-merchant/commit/77afc6c
130 | [d16a1ec]: https://github.com/hiqdev/yii2-merchant/commit/d16a1ec
131 | [7e9b380]: https://github.com/hiqdev/yii2-merchant/commit/7e9b380
132 | [99618c7]: https://github.com/hiqdev/yii2-merchant/commit/99618c7
133 | [4254b94]: https://github.com/hiqdev/yii2-merchant/commit/4254b94
134 | [c66c63c]: https://github.com/hiqdev/yii2-merchant/commit/c66c63c
135 | [bca7393]: https://github.com/hiqdev/yii2-merchant/commit/bca7393
136 | [10c6932]: https://github.com/hiqdev/yii2-merchant/commit/10c6932
137 | [f8fa4aa]: https://github.com/hiqdev/yii2-merchant/commit/f8fa4aa
138 | [2616a6e]: https://github.com/hiqdev/yii2-merchant/commit/2616a6e
139 | [cae2de5]: https://github.com/hiqdev/yii2-merchant/commit/cae2de5
140 | [f394b3a]: https://github.com/hiqdev/yii2-merchant/commit/f394b3a
141 | [f1ecb98]: https://github.com/hiqdev/yii2-merchant/commit/f1ecb98
142 | [72c57a8]: https://github.com/hiqdev/yii2-merchant/commit/72c57a8
143 | [d914e86]: https://github.com/hiqdev/yii2-merchant/commit/d914e86
144 | [099a0a7]: https://github.com/hiqdev/yii2-merchant/commit/099a0a7
145 | [eb568af]: https://github.com/hiqdev/yii2-merchant/commit/eb568af
146 | [e3e8e2a]: https://github.com/hiqdev/yii2-merchant/commit/e3e8e2a
147 | [15dc2d9]: https://github.com/hiqdev/yii2-merchant/commit/15dc2d9
148 | [25703a6]: https://github.com/hiqdev/yii2-merchant/commit/25703a6
149 | [d66f028]: https://github.com/hiqdev/yii2-merchant/commit/d66f028
150 | [b57d0fb]: https://github.com/hiqdev/yii2-merchant/commit/b57d0fb
151 | [1d0f00e]: https://github.com/hiqdev/yii2-merchant/commit/1d0f00e
152 | [cbba2e2]: https://github.com/hiqdev/yii2-merchant/commit/cbba2e2
153 | [278543e]: https://github.com/hiqdev/yii2-merchant/commit/278543e
154 | [957f3aa]: https://github.com/hiqdev/yii2-merchant/commit/957f3aa
155 | [fce8fd4]: https://github.com/hiqdev/yii2-merchant/commit/fce8fd4
156 | [ff73135]: https://github.com/hiqdev/yii2-merchant/commit/ff73135
157 | [a2b0337]: https://github.com/hiqdev/yii2-merchant/commit/a2b0337
158 | [294da33]: https://github.com/hiqdev/yii2-merchant/commit/294da33
159 | [2befc31]: https://github.com/hiqdev/yii2-merchant/commit/2befc31
160 | [bc7177c]: https://github.com/hiqdev/yii2-merchant/commit/bc7177c
161 | [9379eb2]: https://github.com/hiqdev/yii2-merchant/commit/9379eb2
162 | [247c544]: https://github.com/hiqdev/yii2-merchant/commit/247c544
163 | [c366ba7]: https://github.com/hiqdev/yii2-merchant/commit/c366ba7
164 | [79c0ce8]: https://github.com/hiqdev/yii2-merchant/commit/79c0ce8
165 | [995394c]: https://github.com/hiqdev/yii2-merchant/commit/995394c
166 | [4f3ceb1]: https://github.com/hiqdev/yii2-merchant/commit/4f3ceb1
167 | [277f5e6]: https://github.com/hiqdev/yii2-merchant/commit/277f5e6
168 | [bf5d311]: https://github.com/hiqdev/yii2-merchant/commit/bf5d311
169 | [b6400ea]: https://github.com/hiqdev/yii2-merchant/commit/b6400ea
170 | [ef52743]: https://github.com/hiqdev/yii2-merchant/commit/ef52743
171 | [024f9a1]: https://github.com/hiqdev/yii2-merchant/commit/024f9a1
172 | [b941ed3]: https://github.com/hiqdev/yii2-merchant/commit/b941ed3
173 | [1d6f98b]: https://github.com/hiqdev/yii2-merchant/commit/1d6f98b
174 | [67aa249]: https://github.com/hiqdev/yii2-merchant/commit/67aa249
175 | [ce5afe7]: https://github.com/hiqdev/yii2-merchant/commit/ce5afe7
176 | [cf31f89]: https://github.com/hiqdev/yii2-merchant/commit/cf31f89
177 | [1b82630]: https://github.com/hiqdev/yii2-merchant/commit/1b82630
178 | [961f39d]: https://github.com/hiqdev/yii2-merchant/commit/961f39d
179 | [66b9ec7]: https://github.com/hiqdev/yii2-merchant/commit/66b9ec7
180 | [124f377]: https://github.com/hiqdev/yii2-merchant/commit/124f377
181 | [234edfc]: https://github.com/hiqdev/yii2-merchant/commit/234edfc
182 | [0614bd9]: https://github.com/hiqdev/yii2-merchant/commit/0614bd9
183 | [26d44b4]: https://github.com/hiqdev/yii2-merchant/commit/26d44b4
184 | [f538d37]: https://github.com/hiqdev/yii2-merchant/commit/f538d37
185 | [dfadf30]: https://github.com/hiqdev/yii2-merchant/commit/dfadf30
186 | [33e91a1]: https://github.com/hiqdev/yii2-merchant/commit/33e91a1
187 | [e4fa5f9]: https://github.com/hiqdev/yii2-merchant/commit/e4fa5f9
188 | [0be4d2e]: https://github.com/hiqdev/yii2-merchant/commit/0be4d2e
189 | [e367e59]: https://github.com/hiqdev/yii2-merchant/commit/e367e59
190 | [9dc45c6]: https://github.com/hiqdev/yii2-merchant/commit/9dc45c6
191 | [87b761f]: https://github.com/hiqdev/yii2-merchant/commit/87b761f
192 | [e4c0085]: https://github.com/hiqdev/yii2-merchant/commit/e4c0085
193 | [9723af5]: https://github.com/hiqdev/yii2-merchant/commit/9723af5
194 | [47379d4]: https://github.com/hiqdev/yii2-merchant/commit/47379d4
195 | [2ee5644]: https://github.com/hiqdev/yii2-merchant/commit/2ee5644
196 | [ee8ca60]: https://github.com/hiqdev/yii2-merchant/commit/ee8ca60
197 | [56a4365]: https://github.com/hiqdev/yii2-merchant/commit/56a4365
198 | [6e55765]: https://github.com/hiqdev/yii2-merchant/commit/6e55765
199 | [7e76575]: https://github.com/hiqdev/yii2-merchant/commit/7e76575
200 | [80f5213]: https://github.com/hiqdev/yii2-merchant/commit/80f5213
201 | [60eecd6]: https://github.com/hiqdev/yii2-merchant/commit/60eecd6
202 | [d2116fd]: https://github.com/hiqdev/yii2-merchant/commit/d2116fd
203 | [9d55e1d]: https://github.com/hiqdev/yii2-merchant/commit/9d55e1d
204 | [35326fe]: https://github.com/hiqdev/yii2-merchant/commit/35326fe
205 | [99a4950]: https://github.com/hiqdev/yii2-merchant/commit/99a4950
206 | [2fd82c1]: https://github.com/hiqdev/yii2-merchant/commit/2fd82c1
207 | [e1a9c7e]: https://github.com/hiqdev/yii2-merchant/commit/e1a9c7e
208 | [c17c7e0]: https://github.com/hiqdev/yii2-merchant/commit/c17c7e0
209 | [f443293]: https://github.com/hiqdev/yii2-merchant/commit/f443293
210 | [9c8aaea]: https://github.com/hiqdev/yii2-merchant/commit/9c8aaea
211 | [efaad49]: https://github.com/hiqdev/yii2-merchant/commit/efaad49
212 | [b338802]: https://github.com/hiqdev/yii2-merchant/commit/b338802
213 | [37fbf3b]: https://github.com/hiqdev/yii2-merchant/commit/37fbf3b
214 | [250ba1e]: https://github.com/hiqdev/yii2-merchant/commit/250ba1e
215 | [a47e48a]: https://github.com/hiqdev/yii2-merchant/commit/a47e48a
216 | [7a14b5d]: https://github.com/hiqdev/yii2-merchant/commit/7a14b5d
217 | [a20b8c2]: https://github.com/hiqdev/yii2-merchant/commit/a20b8c2
218 | [937be79]: https://github.com/hiqdev/yii2-merchant/commit/937be79
219 | [820df72]: https://github.com/hiqdev/yii2-merchant/commit/820df72
220 | [8e34c11]: https://github.com/hiqdev/yii2-merchant/commit/8e34c11
221 | [4ed4be1]: https://github.com/hiqdev/yii2-merchant/commit/4ed4be1
222 | [0455e49]: https://github.com/hiqdev/yii2-merchant/commit/0455e49
223 | [2e5cbd8]: https://github.com/hiqdev/yii2-merchant/commit/2e5cbd8
224 | [4f1c26c]: https://github.com/hiqdev/yii2-merchant/commit/4f1c26c
225 | [c8145a4]: https://github.com/hiqdev/yii2-merchant/commit/c8145a4
226 | [Under development]: https://github.com/hiqdev/yii2-merchant/releases
227 | [0.1.0]: https://github.com/hiqdev/yii2-merchant/releases/tag/0.1.0
228 |
--------------------------------------------------------------------------------