├── mail ├── src │ ├── README │ ├── new-invoice-admin-text.php │ ├── cancel-invoice-text.php │ ├── new-invoice-user-text.php │ ├── new-invoice-admin-html.php │ ├── cancel-invoice-html.php │ ├── new-invoice-user-html.php │ └── css │ │ └── mailgun.css ├── new-invoice-admin-text.php ├── cancel-invoice-text.php ├── new-invoice-user-text.php ├── new-invoice-admin-html.php ├── cancel-invoice-html.php └── new-invoice-user-html.php ├── views ├── address │ ├── manage.css │ ├── manage.scss │ ├── manage.css.map │ ├── create.php │ ├── update.php │ ├── _grid.php │ ├── _form.php │ ├── manage.php │ ├── _search.php │ ├── view.php │ └── index.php ├── bitcoin │ ├── _scan.php │ ├── _copy.php │ ├── payment.min.js │ ├── view.php │ ├── payment.js │ ├── index.php │ └── payment.php ├── bank │ ├── create.php │ ├── update.php │ ├── _search.php │ ├── _form.php │ ├── view.php │ └── index.php ├── coupon │ ├── create.php │ ├── update.php │ ├── _search.php │ ├── view.php │ ├── _form.php │ └── index.php ├── info │ ├── manage.php │ ├── update.php │ ├── create.php │ ├── _search.php │ ├── _form.php │ ├── view.php │ └── index.php ├── invoice │ ├── update.php │ ├── _shipping_address.php │ ├── _billing_address.php │ ├── _form.php │ ├── index.php │ └── manage.php ├── setting │ └── index.php └── paypal │ └── return.php ├── console └── README ├── urlManager.php ├── modules └── api │ ├── v1 │ ├── Module.php │ └── controllers │ │ ├── DefaultController.php │ │ └── InvoiceController.php │ ├── controllers │ └── DefaultController.php │ └── Module.php ├── memberArea.php ├── README.md ├── composer.json ├── components ├── Tax.php └── CurrencyLayer.php ├── widgets ├── AddAddress.php └── views │ └── add-address.php ├── memberMenu.php ├── migrations └── m170907_104245_init.php ├── cronjobs ├── TaskCancelInvoice.php ├── TaskReleaseBTCAddr.php ├── TaskCheckBTCTransaction.php └── TaskCheckBTCPayment.php ├── adminMenu.php ├── Module.php ├── models ├── BankSearch.php ├── BitcoinAddressSearch.php ├── BillingInfoSearch.php ├── Setting.php ├── CouponForm.php ├── InvoiceSearch.php ├── CouponSearch.php ├── Item.php ├── AddressSearch.php ├── Bank.php ├── Address.php └── Coupon.php ├── settings.php └── controllers └── SettingController.php /mail/src/README: -------------------------------------------------------------------------------- 1 | grunt --dist=../vendor/powerkernel/yii2-billing/mail -------------------------------------------------------------------------------- /views/address/manage.css: -------------------------------------------------------------------------------- 1 | .billing-address-manage .box-address .box-body{height:160px} 2 | /*# sourceMappingURL=manage.css.map */ 3 | -------------------------------------------------------------------------------- /views/address/manage.scss: -------------------------------------------------------------------------------- 1 | .billing-address-manage { 2 | .box-address { 3 | .box-body { 4 | height: 160px; 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /views/address/manage.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "AAEI,8CAAU,CACR,MAAM,CAAE,KAAK", 4 | "sources": ["manage.scss"], 5 | "names": [], 6 | "file": "manage.css" 7 | } -------------------------------------------------------------------------------- /console/README: -------------------------------------------------------------------------------- 1 | 2 | if(class_exists('yii\mongodb\console\controllers\MigrateController')){ 3 | $map=[ 4 | 'controllerMap' => [ 5 | 'mongodb-migrate' => 'yii\mongodb\console\controllers\MigrateController' 6 | ], 7 | ]; 8 | } -------------------------------------------------------------------------------- /urlManager.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | return [ 9 | 'ignoreLanguageUrlPatterns' => [ 10 | '#^billing/api#' => '#^billing/api#', 11 | ], 12 | ]; 13 | -------------------------------------------------------------------------------- /modules/api/v1/Module.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | namespace powerkernel\billing\modules\api\controllers; 9 | 10 | /** 11 | * Class DefaultController 12 | */ 13 | class DefaultController extends \yii\rest\Controller 14 | { 15 | /** 16 | * @return array 17 | */ 18 | public function actionIndex(){ 19 | return [ 20 | 'status'=>'ok' 21 | ]; 22 | } 23 | } -------------------------------------------------------------------------------- /mail/new-invoice-admin-text.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | getModule('billing')->t('Greetings from {APP},', ['APP' => Yii::$app->name]) ?> 9 | 10 | 11 | getModule('billing')->t('A new order has just been placed') ?> 12 | 13 | 14 | getModule('billing')->t('Invoice #{ID}', ['ID' => $model->id_invoice]) ?> 15 | 16 | 17 | getModule('billing')->t('View Invoice: {URL}', ['URL'=>$model->getAdminInvoiceUrl(true)]) ?> 18 | -------------------------------------------------------------------------------- /mail/src/new-invoice-admin-text.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | getModule('billing')->t('Greetings from {APP},', ['APP' => Yii::$app->name]) ?> 9 | 10 | 11 | getModule('billing')->t('A new order has just been placed') ?> 12 | 13 | 14 | getModule('billing')->t('Invoice #{ID}', ['ID' => $model->id_invoice]) ?> 15 | 16 | 17 | getModule('billing')->t('View Invoice: {URL}', ['URL'=>$model->getAdminInvoiceUrl(true)]) ?> 18 | -------------------------------------------------------------------------------- /views/bitcoin/_scan.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | /* @var $bitcoin [] */ 9 | 10 | use yii\helpers\Html; 11 | 12 | ?> 13 |
14 |
15 | 'img img-responsive img-thumbnail']) ?> 16 |
17 |
18 | -------------------------------------------------------------------------------- /modules/api/v1/controllers/DefaultController.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | namespace powerkernel\billing\modules\api\v1\controllers; 9 | 10 | /** 11 | * Class DefaultController 12 | */ 13 | class DefaultController extends \yii\rest\Controller 14 | { 15 | /** 16 | * @return array 17 | */ 18 | public function actionIndex(){ 19 | return [ 20 | 'status'=>'ok', 21 | 'version'=>'1.0' 22 | ]; 23 | } 24 | } -------------------------------------------------------------------------------- /mail/cancel-invoice-text.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | getModule('billing')->t('Hi {USER},', ['USER' => $model->account->fullname]) ?> 9 | 10 | getModule('billing')->t('We\'re sorry to let you know that your invoice has been canceled. You don\'t need to do anything else.') ?> 11 | 12 | getModule('billing')->t('Invoice #{ID}', ['ID' => $model->id_invoice]) ?> 13 | 14 | getModule('billing')->t('View Invoice: {URL}', ['URL'=>$model->getInvoiceUrl(true)]) ?> 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Yii2 Billing 2 | ============ 3 | Yii2 Billing 4 | 5 | Installation 6 | ------------ 7 | 8 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 9 | 10 | Either run 11 | 12 | ``` 13 | php composer.phar require --prefer-dist powerkernel/yii2-billing "*" 14 | ``` 15 | 16 | or add 17 | 18 | ``` 19 | "powerkernel/yii2-billing": "*" 20 | ``` 21 | 22 | to the require section of your `composer.json` file, then run 23 | 24 | ``` 25 | php yii mongodb-migrate --migrationPath=@vendor/powerkernel/yii2-billing/migrations/ --migrationCollection=billing_migration 26 | ``` -------------------------------------------------------------------------------- /mail/src/cancel-invoice-text.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | getModule('billing')->t('Hi {USER},', ['USER' => $model->account->fullname]) ?> 9 | 10 | getModule('billing')->t('We\'re sorry to let you know that your invoice has been canceled. You don\'t need to do anything else.') ?> 11 | 12 | getModule('billing')->t('Invoice #{ID}', ['ID' => $model->id_invoice]) ?> 13 | 14 | getModule('billing')->t('View Invoice: {URL}', ['URL'=>$model->getInvoiceUrl(true)]) ?> 15 | -------------------------------------------------------------------------------- /modules/api/Module.php: -------------------------------------------------------------------------------- 1 | user->enableSession = false; 18 | \Yii::$app->user->loginUrl = null; 19 | $this->modules = [ 20 | 'v1' => [ 21 | 'class' => 'powerkernel\billing\modules\api\v1\Module', 22 | ], 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "powerkernel/yii2-billing", 3 | "description": "Yii2 Billing", 4 | "type": "yii2-extension", 5 | "keywords": ["yii2","billing"], 6 | "license": "BSD-3-Clause", 7 | "authors": [ 8 | { 9 | "name": "Harry Tang", 10 | "email": "harry@powerkernel.com" 11 | } 12 | ], 13 | "require": { 14 | "yiisoft/yii2": "~2.0", 15 | "picqer/php-barcode-generator": "~0.2.0", 16 | "paypal/rest-api-sdk-php": "~1.0", 17 | "endroid/qr-code": "~3.1", 18 | "bitwasp/bitcoin": "*" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "powerkernel\\billing\\": "" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /components/Tax.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | namespace powerkernel\billing\components; 8 | 9 | 10 | /** 11 | * Class Tax 12 | * @package powerkernel\billing\components 13 | */ 14 | class Tax 15 | { 16 | 17 | public $tax = [ 18 | 'VN' => 0.1 19 | ]; 20 | 21 | /** 22 | * get tax value by country 23 | * @param $country 24 | * @return int|mixed 25 | */ 26 | public function getTaxValue($country) 27 | { 28 | if (isset($this->tax[$country])) { 29 | return $this->tax[$country]; 30 | } 31 | return 0; 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /views/bank/create.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | /* @var $this yii\web\View */ 9 | /* @var $model powerkernel\billing\models\Bank */ 10 | 11 | 12 | /* breadcrumbs */ 13 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Banks'), 'url' => ['index']]; 14 | $this->params['breadcrumbs'][] = $this->title; 15 | 16 | /* misc */ 17 | //$js=file_get_contents(__DIR__.'/index.min.js'); 18 | //$this->registerJs($js); 19 | //$css=file_get_contents(__DIR__.'/index.css'); 20 | //$this->registerCss($css); 21 | ?> 22 |
23 |
24 |
25 | render('_form', [ 26 | 'model' => $model, 27 | ]) ?> 28 |
29 |
30 |
31 | -------------------------------------------------------------------------------- /views/coupon/create.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | /* @var $this yii\web\View */ 9 | /* @var $model powerkernel\billing\models\Coupon */ 10 | 11 | 12 | /* breadcrumbs */ 13 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Coupons'), 'url' => ['index']]; 14 | $this->params['breadcrumbs'][] = $this->title; 15 | 16 | /* misc */ 17 | //$js=file_get_contents(__DIR__.'/index.min.js'); 18 | //$this->registerJs($js); 19 | //$css=file_get_contents(__DIR__.'/index.css'); 20 | //$this->registerCss($css); 21 | ?> 22 |
23 |
24 |
25 | render('_form', [ 26 | 'model' => $model, 27 | ]) ?> 28 |
29 |
30 |
31 | -------------------------------------------------------------------------------- /widgets/AddAddress.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | 9 | namespace powerkernel\billing\widgets; 10 | 11 | use powerkernel\billing\models\Address; 12 | use Yii; 13 | use yii\base\Widget; 14 | 15 | /** 16 | * Class AddAddress 17 | * @package powerkernel\billing\widgets 18 | */ 19 | class AddAddress extends Widget 20 | { 21 | 22 | /** 23 | * @inheritdoc 24 | * @return string 25 | */ 26 | public function run() 27 | { 28 | parent::run(); // TODO: Change the autogenerated stub 29 | $model = new Address(); 30 | if ($model->load(Yii::$app->request->post())) { 31 | $model->save(); 32 | Yii::$app->controller->refresh(); 33 | } 34 | return $this->render('add-address', ['model' => $model]); 35 | 36 | } 37 | } -------------------------------------------------------------------------------- /views/info/manage.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | /* @var $this yii\web\View */ 9 | /* @var $model powerkernel\billing\models\BillingInfo */ 10 | 11 | 12 | /* breadcrumbs */ 13 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Billing Information'), 'url' => ['manage']]; 14 | $this->params['breadcrumbs'][] = $this->title; 15 | 16 | /* misc */ 17 | //$js=file_get_contents(__DIR__.'/index.min.js'); 18 | //$this->registerJs($js); 19 | //$css=file_get_contents(__DIR__.'/index.css'); 20 | //$this->registerCss($css); 21 | ?> 22 |
23 |
24 |
25 | render('_form', [ 26 | 'model' => $model, 27 | ]) ?> 28 |
29 |
30 |
31 | -------------------------------------------------------------------------------- /views/address/create.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | /* @var $this yii\web\View */ 9 | /* @var $model powerkernel\billing\models\Address */ 10 | 11 | 12 | /* breadcrumbs */ 13 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Addresses'), 'url' => ['index']]; 14 | $this->params['breadcrumbs'][] = $this->title; 15 | 16 | /* misc */ 17 | //$js=file_get_contents(__DIR__.'/index.min.js'); 18 | //$this->registerJs($js); 19 | //$css=file_get_contents(__DIR__.'/index.css'); 20 | //$this->registerCss($css); 21 | $this->context->layout = Yii::$app->view->theme->basePath . '/account.php'; 22 | ?> 23 |
24 |
25 |
26 | render('_form', [ 27 | 'model' => $model, 28 | ]) ?> 29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /views/bank/update.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | /* @var $this yii\web\View */ 9 | /* @var $model powerkernel\billing\models\Bank */ 10 | 11 | 12 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Banks'), 'url' => ['index']]; 13 | $this->params['breadcrumbs'][] = ['label' => $model->title, 'url' => ['view', 'id' => $model->id]]; 14 | $this->params['breadcrumbs'][] = Yii::t('billing', 'Update'); 15 | 16 | /* misc */ 17 | //$js=file_get_contents(__DIR__.'/index.min.js'); 18 | //$this->registerJs($js); 19 | //$css=file_get_contents(__DIR__.'/index.css'); 20 | //$this->registerCss($css); 21 | ?> 22 |
23 |
24 |
25 | render('_form', [ 26 | 'model' => $model, 27 | ]) ?> 28 |
29 |
30 |
31 | -------------------------------------------------------------------------------- /memberMenu.php: -------------------------------------------------------------------------------- 1 | 5 | * @link https://powerkernel.com 6 | * @copyright Copyright (c) 2017 Power Kernel 7 | */ 8 | 9 | 10 | use common\Core; 11 | use common\widgets\SideMenu; 12 | 13 | $menu=[ 14 | 'title'=>Yii::$app->getModule('billing')->t('Billing'), 15 | 'icon'=> 'shopping-bag', 16 | 'items'=>[ 17 | ['icon' => 'info-square', 'label' => Yii::$app->getModule('billing')->t('Information'), 'url' => ['/billing/info'], 'active' => Core::checkMCA('billing', 'info', '*')], 18 | ['icon' => 'credit-card', 'label' => Yii::$app->getModule('billing')->t('Invoices'), 'url' => ['/billing/invoice'], 'active' => Core::checkMCA('billing', 'invoice', '*')], 19 | ['icon' => 'address-book', 'label' => Yii::$app->getModule('billing')->t('Addresses'), 'url' => ['/billing/address'], 'active' => Core::checkMCA('billing', 'address', '*')], 20 | ], 21 | ]; 22 | $menu['active']=SideMenu::isActive($menu['items']); 23 | return [$menu]; 24 | -------------------------------------------------------------------------------- /views/coupon/update.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | /* @var $this yii\web\View */ 9 | /* @var $model powerkernel\billing\models\Coupon */ 10 | 11 | 12 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Coupons'), 'url' => ['index']]; 13 | $this->params['breadcrumbs'][] = ['label' => $model->code, 'url' => ['view', 'id' => $model->code]]; 14 | $this->params['breadcrumbs'][] = Yii::t('billing', 'Update'); 15 | 16 | /* misc */ 17 | //$js=file_get_contents(__DIR__.'/index.min.js'); 18 | //$this->registerJs($js); 19 | //$css=file_get_contents(__DIR__.'/index.css'); 20 | //$this->registerCss($css); 21 | ?> 22 |
23 |
24 |
25 | render('_form', [ 26 | 'model' => $model, 27 | ]) ?> 28 |
29 |
30 |
31 | -------------------------------------------------------------------------------- /views/invoice/update.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | /* @var $this yii\web\View */ 9 | /* @var $model powerkernel\billing\models\Invoice */ 10 | 11 | 12 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Invoices'), 'url' => ['index']]; 13 | $this->params['breadcrumbs'][] = ['label' => $model->id, 'url' => ['view', 'id' => $model->id]]; 14 | $this->params['breadcrumbs'][] = Yii::t('billing', 'Update'); 15 | 16 | /* misc */ 17 | //$js=file_get_contents(__DIR__.'/index.min.js'); 18 | //$this->registerJs($js); 19 | //$css=file_get_contents(__DIR__.'/index.css'); 20 | //$this->registerCss($css); 21 | ?> 22 |
23 |
24 |
25 | render('_form', [ 26 | 'model' => $model, 27 | ]) ?> 28 |
29 |
30 |
31 | -------------------------------------------------------------------------------- /views/info/update.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | /* @var $this yii\web\View */ 9 | /* @var $model powerkernel\billing\models\BillingInfo */ 10 | 11 | 12 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Customers'), 'url' => ['index']]; 13 | $this->params['breadcrumbs'][] = ['label' => $model->id_account, 'url' => ['view', 'id' => $model->id_account]]; 14 | $this->params['breadcrumbs'][] = Yii::t('billing', 'Update'); 15 | 16 | /* misc */ 17 | //$js=file_get_contents(__DIR__.'/index.min.js'); 18 | //$this->registerJs($js); 19 | //$css=file_get_contents(__DIR__.'/index.css'); 20 | //$this->registerCss($css); 21 | ?> 22 |
23 |
24 |
25 | render('_form', [ 26 | 'model' => $model, 27 | ]) ?> 28 |
29 |
30 |
31 | -------------------------------------------------------------------------------- /views/address/update.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | /* @var $this yii\web\View */ 9 | /* @var $model powerkernel\billing\models\Address */ 10 | 11 | 12 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Addresses'), 'url' => ['index']]; 13 | $this->params['breadcrumbs'][] = ['label' => $model->_id, 'url' => ['view', 'id' => (string)$model->_id]]; 14 | $this->params['breadcrumbs'][] = Yii::t('billing', 'Update'); 15 | 16 | /* misc */ 17 | //$js=file_get_contents(__DIR__.'/index.min.js'); 18 | //$this->registerJs($js); 19 | //$css=file_get_contents(__DIR__.'/index.css'); 20 | //$this->registerCss($css); 21 | $this->context->layout = Yii::$app->view->theme->basePath . '/account.php'; 22 | ?> 23 |
24 |
25 |
26 | render('_form', [ 27 | 'model' => $model, 28 | ]) ?> 29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /views/invoice/_shipping_address.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2018 Power Kernel 6 | */ 7 | 8 | /* @var $info[] */ 9 | ?> 10 | 11 |
12 | 13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 |
22 | 23 |
getModule('billing')->t('Phone:') ?>
24 | -------------------------------------------------------------------------------- /views/info/create.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | /* @var $this yii\web\View */ 9 | /* @var $model powerkernel\billing\models\BillingInfo */ 10 | /* @var $account \common\models\Account */ 11 | 12 | 13 | /* breadcrumbs */ 14 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Customers'), 'url' => ['index']]; 15 | $this->params['breadcrumbs'][] = $this->title; 16 | 17 | /* misc */ 18 | //$js=file_get_contents(__DIR__.'/index.min.js'); 19 | //$this->registerJs($js); 20 | //$css=file_get_contents(__DIR__.'/index.css'); 21 | //$this->registerCss($css); 22 | ?> 23 |
24 |
25 |
26 |
27 | getModule('billing')->t('Adding billing information for {EMAIL}', ['EMAIL'=>$account->email]) ?> 28 |
29 | render('_form', [ 30 | 'model' => $model, 31 | ]) ?> 32 |
33 |
34 |
35 | -------------------------------------------------------------------------------- /views/bank/_search.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use yii\helpers\Html; 9 | use yii\widgets\ActiveForm; 10 | 11 | /* @var $this yii\web\View */ 12 | /* @var $model powerkernel\billing\models\BankSearch */ 13 | /* @var $form yii\widgets\ActiveForm */ 14 | ?> 15 | 16 | 47 | -------------------------------------------------------------------------------- /views/invoice/_billing_address.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2018 Power Kernel 6 | */ 7 | 8 | /* @var $info[] */ 9 | ?> 10 | 11 |
12 | 13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 |
22 | 23 |
getModule('billing')->t('Phone:') ?>
24 | 25 |
getModule('billing')->t('Email:') ?>
26 | 27 |
getModule('billing')->t('Tax:') ?>
28 | 29 | -------------------------------------------------------------------------------- /views/coupon/_search.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use yii\helpers\Html; 9 | use yii\widgets\ActiveForm; 10 | 11 | /* @var $this yii\web\View */ 12 | /* @var $model powerkernel\billing\models\CouponSearch */ 13 | /* @var $form yii\widgets\ActiveForm */ 14 | ?> 15 | 16 | 49 | -------------------------------------------------------------------------------- /mail/new-invoice-user-text.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | getModule('billing')->t('Dear {NAME},', ['NAME' => $model->account->fullname]) ?> 9 | 10 | 11 | getModule('billing')->t('Thank you for placing your order with us! Your total amount due is {TOTAL}. More details about your purchase are included below.', ['TOTAL'=>Yii::$app->formatter->asCurrency($model->total, $model->currency)]) ?> 12 | 13 | 14 | getModule('billing')->t('Invoice #{ID}', ['ID' => $model->id_invoice]) ?> 15 | 16 | items as $item):?> 17 | getModule('billing')->t(' - {ITEM}: {PRICE}', ['ITEM'=>$item->name, 'PRICE'=>Yii::$app->formatter->asCurrency($item->price, $model->currency)]) ?> 18 | 19 | 20 | 21 | getModule('billing')->t('Shipping: {SHIP}', ['SHIP' => Yii::$app->formatter->asCurrency($model->shipping, $model->currency)]) ?> 22 | 23 | getModule('billing')->t('Tax: {TAX}', ['TAX' => Yii::$app->formatter->asCurrency($model->tax, $model->currency)]) ?> 24 | 25 | getModule('billing')->t('Total: {TOTAL}', ['TOTAL' => Yii::$app->formatter->asCurrency($model->total, $model->currency)]) ?> 26 | 27 | 28 | getModule('billing')->t('Pay Now: {URL}', ['URL'=>$model->getInvoiceUrl(true)]) ?> 29 | -------------------------------------------------------------------------------- /mail/src/new-invoice-user-text.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | getModule('billing')->t('Dear {NAME},', ['NAME' => $model->account->fullname]) ?> 9 | 10 | 11 | getModule('billing')->t('Thank you for placing your order with us! Your total amount due is {TOTAL}. More details about your purchase are included below.', ['TOTAL'=>Yii::$app->formatter->asCurrency($model->total, $model->currency)]) ?> 12 | 13 | 14 | getModule('billing')->t('Invoice #{ID}', ['ID' => $model->id_invoice]) ?> 15 | 16 | items as $item):?> 17 | getModule('billing')->t(' - {ITEM}: {PRICE}', ['ITEM'=>$item->name, 'PRICE'=>Yii::$app->formatter->asCurrency($item->price, $model->currency)]) ?> 18 | 19 | 20 | 21 | getModule('billing')->t('Shipping: {SHIP}', ['SHIP' => Yii::$app->formatter->asCurrency($model->shipping, $model->currency)]) ?> 22 | 23 | getModule('billing')->t('Tax: {TAX}', ['TAX' => Yii::$app->formatter->asCurrency($model->tax, $model->currency)]) ?> 24 | 25 | getModule('billing')->t('Total: {TOTAL}', ['TOTAL' => Yii::$app->formatter->asCurrency($model->total, $model->currency)]) ?> 26 | 27 | 28 | getModule('billing')->t('Pay Now: {URL}', ['URL'=>$model->getInvoiceUrl(true)]) ?> 29 | -------------------------------------------------------------------------------- /migrations/m170907_104245_init.php: -------------------------------------------------------------------------------- 1 | mongodb->getCollection('billing_settings'); 14 | $col->createIndexes([ 15 | [ 16 | 'key'=>['key'], 17 | 'unique'=>true, 18 | ] 19 | ]); 20 | 21 | $col=Yii::$app->mongodb->getCollection('billing_invoice'); 22 | $col->createIndexes([ 23 | [ 24 | 'key'=>['id_invoice'], 25 | 'unique'=>true, 26 | ] 27 | ]); 28 | 29 | $col=Yii::$app->mongodb->getCollection('billing_coupon'); 30 | $col->createIndexes([ 31 | [ 32 | 'key'=>['code'], 33 | 'unique'=>true, 34 | ] 35 | ]); 36 | 37 | $col=Yii::$app->mongodb->getCollection('billing_bitcoin_payments'); 38 | $col->createIndexes([ 39 | [ 40 | 'key'=>['address'], 41 | 'unique'=>true, 42 | ] 43 | ]); 44 | 45 | 46 | } 47 | 48 | /** 49 | * @return bool 50 | */ 51 | public function down() 52 | { 53 | echo "m170907_104245_init cannot be reverted.\n"; 54 | 55 | return false; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /views/address/_grid.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use yii\helpers\Html; 9 | 10 | /* @var $this \yii\web\View */ 11 | /* @var $model \powerkernel\billing\models\Address */ 12 | 13 | ?> 14 |
15 |
16 |
17 |
contact_name ?>
18 |
street_address_1 ?>
19 | street_address_2)):?> 20 |
street_address_2 ?>
21 | 22 |
city ?>, state ?> zip_code ?>
23 |
country) ?>
24 |
getAttributeLabel('phone') ?>: phone ?>
25 |
26 | 29 |
30 |
-------------------------------------------------------------------------------- /views/bitcoin/_copy.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | /* @var $bitcoin [] */ 9 | 10 | ?> 11 |
12 |

getModule('billing')->t('To complete your payment, please send {BTC} BTC to the address below.', ['BTC' => $bitcoin['amount']]) ?>

13 | 14 |
15 |
16 |
17 |
18 |
getModule('billing')->t('AMOUNT') ?>
19 |
BTC
20 |
21 |

22 |
23 |
getModule('billing')->t('ADDRESS') ?>
24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 | 32 |
33 | -------------------------------------------------------------------------------- /views/bitcoin/payment.min.js: -------------------------------------------------------------------------------- 1 | function setAddress(){var addr=$("#btc-address");addr.html(addr.data("addr"))}function checkPayment(){var url=$("#check-payment-url").data("check-payment-url");$.getJSON(url).done(function(response){if(response.payment_received===true){$("#payment-waiting").addClass("hidden");$("#btc-info").addClass("hidden");$("#payment-received").removeClass("hidden")}})}checkPayment();$("#copy-tab").on("click","#btc-address",function(){var $temp=$("");$("body").append($temp);$temp.val($(this).data("addr")).select();document.execCommand("copy");$temp.remove();$(this).html($(this).data("copied"));setTimeout(setAddress,2e3)});setInterval(checkPayment,1e4);var $clock=$("#count-down"),addrTime=$("#btc-address").data("date"),currentTime=moment().unix(),diffTime=currentTime-addrTime,timeout=$("#btc-info").data("timeout"),duration=moment.duration((timeout-diffTime)*1e3,"milliseconds"),interval=1e3;var $m=$('--').appendTo($clock);$(':').appendTo($clock);var $s=$('--').appendTo($clock);setInterval(function(){duration=moment.duration(duration.asMilliseconds()-interval,"milliseconds");var m=moment.duration(duration).minutes(),s=moment.duration(duration).seconds();m=$.trim(m).length===1?"0"+m:m;s=$.trim(s).length===1?"0"+s:s;if(moment.duration(duration).asSeconds()>=0){if(moment.duration(duration).asSeconds()<120){$("#count-down").addClass("text-danger")}$m.text(m);$s.text(s)}else{$("#btc-info").hide();$("#btc-expired").removeClass("hidden")}},interval); -------------------------------------------------------------------------------- /views/address/_form.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use common\Core; 9 | use conquer\select2\Select2Widget; 10 | use yii\helpers\Html; 11 | use yii\widgets\ActiveForm; 12 | 13 | /* @var $this yii\web\View */ 14 | /* @var $model powerkernel\billing\models\Address */ 15 | /* @var $form yii\widgets\ActiveForm */ 16 | ?> 17 | 18 |
19 | 20 | 21 | 22 | field($model, 'country')->widget(Select2Widget::class, [ 23 | 'bootstrap' => false, 24 | 'items' => Core::getCountryList(), 25 | 'options' => ['prompt' => Yii::$app->getModule('billing')->t('Select Country')] 26 | ]) ?> 27 | 28 | field($model, 'contact_name') ?> 29 | 30 | field($model, 'street_address_1') ?> 31 | 32 | field($model, 'street_address_2') ?> 33 | 34 | field($model, 'city') ?> 35 | 36 | field($model, 'state') ?> 37 | 38 | field($model, 'zip_code') ?> 39 | 40 | field($model, 'phone') ?> 41 | 42 |
43 | $model->isNewRecord ? Yii::t('billing', 'Create') : Yii::t('billing', 'Update'), 'options'=>['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']]) ?> 44 |
45 | 46 | 47 | 48 |
49 | -------------------------------------------------------------------------------- /cronjobs/TaskCancelInvoice.php: -------------------------------------------------------------------------------- 1 | call(function (\yii\console\Application $app) { 13 | 14 | /* get pending invoice > 7 days */ 15 | $now = time(); 16 | $days = 7 * 24 * 3600; 17 | $point = $now - $days; 18 | 19 | 20 | $invoices = Invoice::find() 21 | ->where([ 22 | 'status' => Invoice::STATUS_PENDING, 23 | 'updated_at' => ['$lte' => new \MongoDB\BSON\UTCDateTime($point * 1000)] 24 | ])->all(); 25 | 26 | 27 | if ($invoices) { 28 | $obj = []; 29 | foreach ($invoices as $invoice) { 30 | $invoice->cancel(); 31 | $obj[] = $invoice->id; 32 | } 33 | $output = implode(', ', $obj); 34 | } 35 | 36 | /* Result */ 37 | if (!empty($output)) { 38 | $log = new \common\models\TaskLog(); 39 | $log->task = basename(__FILE__, '.php'); 40 | $log->result = $output; 41 | $log->save(); 42 | } 43 | 44 | 45 | /* delete old logs never bad */ 46 | $period = 30 * 24 * 60 * 60; // 30 days 47 | $point = time() - $period; 48 | \common\models\TaskLog::deleteAll([ 49 | 'task' => basename(__FILE__, '.php'), 50 | 'created_at' => ['$lte', new \MongoDB\BSON\UTCDateTime($point * 1000)] 51 | ]); 52 | 53 | 54 | unset($app); 55 | 56 | })->cron($time); 57 | -------------------------------------------------------------------------------- /views/address/manage.php: -------------------------------------------------------------------------------- 1 | params['breadcrumbs'][] = Yii::$app->getModule('billing')->t('Billing'); 15 | $this->params['breadcrumbs'][] = $this->title; 16 | 17 | /* misc */ 18 | $this->registerJs('$(document).on("pjax:send", function(){ $(".grid-view-overlay").removeClass("hidden");});$(document).on("pjax:complete", function(){ $(".grid-view-overlay").addClass("hidden");})'); 19 | //$js=file_get_contents(__DIR__.'/index.min.js'); 20 | //$this->registerJs($js); 21 | $css=file_get_contents(__DIR__.'/manage.css'); 22 | $this->registerCss($css); 23 | $this->context->layout = Yii::$app->view->theme->basePath . '/account.php'; 24 | ?> 25 |
26 | 27 | 28 | render('_search', ['model' => $searchModel]); ?> 29 | 30 | 31 | 32 | 33 | $dataProvider, 35 | 'itemOptions' => ['class' => 'item'], 36 | 'itemView' => '_grid', 37 | 'layout' => "
{items}
{pager}" 38 | ]) ?> 39 | 40 | 41 | 42 | 43 |

44 | 'btn btn-success']) ?> 45 |

46 | 47 | 48 |
49 | -------------------------------------------------------------------------------- /cronjobs/TaskReleaseBTCAddr.php: -------------------------------------------------------------------------------- 1 | call(function (\yii\console\Application $app) { 13 | 14 | /* get used addresses, but no tx > 3 days */ 15 | $now = time(); 16 | $days = 3 * 24 * 3600; 17 | $point = $now - $days; 18 | 19 | $addresses = BitcoinAddress::find() 20 | ->where([ 21 | 'status' => BitcoinAddress::STATUS_USED, 22 | 'updated_at' => ['$lte' => new \MongoDB\BSON\UTCDateTime($point * 1000)], 23 | 'tx_id' => null 24 | ])->all(); 25 | 26 | 27 | if ($addresses) { 28 | $obj = []; 29 | foreach ($addresses as $address) { 30 | $address->release(); 31 | $obj[] = $address->address; 32 | } 33 | $output = implode(', ', $obj); 34 | } 35 | 36 | /* Result */ 37 | if (!empty($output)) { 38 | $log = new \common\models\TaskLog(); 39 | $log->task = basename(__FILE__, '.php'); 40 | $log->result = $output; 41 | $log->save(); 42 | } 43 | 44 | 45 | /* delete old logs never bad */ 46 | $period = 30 * 24 * 60 * 60; // 30 days 47 | $point = time() - $period; 48 | \common\models\TaskLog::deleteAll([ 49 | 'task' => basename(__FILE__, '.php'), 50 | 'created_at' => ['$lte', new \MongoDB\BSON\UTCDateTime($point * 1000)] 51 | ]); 52 | 53 | 54 | })->cron($time); 55 | -------------------------------------------------------------------------------- /views/address/_search.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use yii\helpers\Html; 9 | use yii\widgets\ActiveForm; 10 | 11 | /* @var $this yii\web\View */ 12 | /* @var $model powerkernel\billing\models\AddressSearch */ 13 | /* @var $form yii\widgets\ActiveForm */ 14 | ?> 15 | 16 | 57 | -------------------------------------------------------------------------------- /views/bank/_form.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use common\Core; 9 | use conquer\select2\Select2Widget; 10 | use powerkernel\billing\models\Bank; 11 | use yii\widgets\ActiveForm; 12 | 13 | /* @var $this yii\web\View */ 14 | /* @var $model powerkernel\billing\models\Bank */ 15 | /* @var $form yii\widgets\ActiveForm */ 16 | ?> 17 | 18 |
19 | 20 | 21 | 22 | field($model, 'country')->widget(Select2Widget::class, [ 23 | 'bootstrap' => false, 24 | 'items' => Core::getCountryList(), 25 | 'options' => ['prompt' => Yii::$app->getModule('billing')->t('Select Country')] 26 | ]) ?> 27 | 28 | field($model, 'title')->textInput(['maxlength' => true]) ?> 29 | 30 | field($model, 'info')->textarea(['rows' => 6]) ?> 31 | 32 | field($model, 'currency')->widget(Select2Widget::class, [ 33 | 'bootstrap' => false, 34 | 'items' => Core::getCurrencyList(), 35 | 'options' => ['prompt' => Yii::$app->getModule('billing')->t('Select Country')] 36 | ]) ?> 37 | 38 | field($model, 'status')->dropDownList(Bank::getStatusOption()) ?> 39 | 40 |
41 | $model->isNewRecord ? Yii::t('billing', 'Create') : Yii::t('billing', 'Update'), 'options'=>['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']]) ?> 42 |
43 | 44 | 45 | 46 |
47 | -------------------------------------------------------------------------------- /cronjobs/TaskCheckBTCTransaction.php: -------------------------------------------------------------------------------- 1 | call(function (\yii\console\Application $app) { 13 | 14 | /* update confirmations */ 15 | $addresses = BitcoinAddress::find()->where(['status'=>BitcoinAddress::STATUS_UNCONFIRMED])->all(); 16 | 17 | if ($addresses) { 18 | $obj = []; 19 | foreach ($addresses as $address) { 20 | $address->checkPayment(); 21 | $obj[] = $address->address; 22 | } 23 | $output = $app->getModule('billing')->t('Addresses checked: {ADDR}', ['ADDR' => implode(', ', $obj)]); 24 | } 25 | 26 | /* Result */ 27 | if (!empty($output)) { 28 | $log = new \common\models\TaskLog(); 29 | $log->task = basename(__FILE__, '.php'); 30 | $log->result = $output; 31 | $log->save(); 32 | } 33 | 34 | 35 | /* delete old logs never bad */ 36 | $period = 30* 24 * 60 * 60; // 30 day 37 | $point = time() - $period; 38 | if(Yii::$app->params['mongodb']['taskLog']){ 39 | \common\models\TaskLog::deleteAll([ 40 | 'task'=>basename(__FILE__, '.php'), 41 | 'created_at'=>['$lte', new \MongoDB\BSON\UTCDateTime($point*1000)] 42 | ]); 43 | } 44 | else { 45 | \common\models\TaskLog::deleteAll('task=:task AND created_at<=:point', [ 46 | ':task' => basename(__FILE__, '.php'), 47 | ':point' => $point 48 | ]); 49 | } 50 | })->cron($time); -------------------------------------------------------------------------------- /views/info/_search.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use yii\helpers\Html; 9 | use yii\widgets\ActiveForm; 10 | 11 | /* @var $this yii\web\View */ 12 | /* @var $model powerkernel\billing\models\BillingInfoSearch */ 13 | /* @var $form yii\widgets\ActiveForm */ 14 | ?> 15 | 16 | 59 | -------------------------------------------------------------------------------- /adminMenu.php: -------------------------------------------------------------------------------- 1 | 5 | * @link https://powerkernel.com 6 | * @copyright Copyright (c) 2017 Power Kernel 7 | */ 8 | 9 | 10 | use common\Core; 11 | use common\widgets\SideMenu; 12 | 13 | 14 | $menu=[ 15 | 'title'=>Yii::$app->getModule('billing')->t('Billing'), 16 | 'icon'=> 'shopping-bag', 17 | 'items'=>[ 18 | ['icon' => 'money-bill-alt', 'label' => Yii::$app->getModule('billing')->t('Invoices'), 'url' => ['billing/invoice/index'], 'active' => Core::checkMCA('billing', 'invoice', '*')], 19 | ['icon' => 'users', 'label' => Yii::$app->getModule('billing')->t('Customers'), 'url' => ['billing/info/index'], 'active' => Core::checkMCA('billing', 'info', '*')], 20 | ['icon' => 'address-book', 'label' => Yii::$app->getModule('billing')->t('Addresses'), 'url' => ['/billing/address/index'], 'active' => Core::checkMCA('billing', 'address', '*')], 21 | ['icon' => 'gift', 'label' => Yii::$app->getModule('billing')->t('Coupons'), 'url' => ['billing/coupon/index'], 'active' => Core::checkMCA('billing', 'coupon', '*')], 22 | ['icon' => 'university', 'label' => Yii::$app->getModule('billing')->t('Banks'), 'url' => ['billing/bank/index'], 'active' => Core::checkMCA('billing', 'bank', '*')], 23 | ['icon' => 'bitcoin', 'prefix'=>'fab fa-fw', 'label' => Yii::$app->getModule('billing')->t('Bitcoin'), 'url' => ['billing/bitcoin/index'], 'active' => Core::checkMCA('billing', 'bitcoin', '*')], 24 | ['icon' => 'cogs', 'label' => Yii::$app->getModule('billing')->t('Settings'), 'url' => ['billing/setting/index'], 'active' => Core::checkMCA('billing', 'setting', 'index')], 25 | ], 26 | ]; 27 | $menu['active']=SideMenu::isActive($menu['items']); 28 | return [$menu]; 29 | -------------------------------------------------------------------------------- /views/address/view.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use yii\helpers\Html; 9 | use yii\widgets\DetailView; 10 | 11 | /* @var $this yii\web\View */ 12 | /* @var $model powerkernel\billing\models\Address */ 13 | 14 | $this->params['breadcrumbs'][] = Yii::$app->getModule('billing')->t('Billing'); 15 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Addresses'), 'url' => ['index']]; 16 | $this->params['breadcrumbs'][] = Yii::$app->getModule('billing')->t('View'); 17 | 18 | /* misc */ 19 | //$js=file_get_contents(__DIR__.'/index.min.js'); 20 | //$this->registerJs($js); 21 | //$css=file_get_contents(__DIR__.'/index.css'); 22 | //$this->registerCss($css); 23 | ?> 24 |
25 |
26 |
27 |
28 | $model, 30 | 'attributes' => [ 31 | //'_id', 32 | ['attribute' => 'id_account', 'value' => Html::a($model->account->fullname, ['/account/view', 'id'=>(string)$model->account->id]), 'format'=>'raw'], 33 | 'contact_name', 34 | 'street_address_1', 35 | 'street_address_2', 36 | 'city', 37 | 'state', 38 | 'zip_code', 39 | 'country', 40 | 'phone', 41 | 'status', 42 | 'createdAt:dateTime', 43 | 'updatedAt:dateTime', 44 | //['attribute' => 'status', 'value' => $model->statusColorText, 'format'=>'raw'], 45 | ], 46 | ]) ?> 47 |
48 |
49 |
50 |
51 | -------------------------------------------------------------------------------- /views/bank/view.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use yii\helpers\Html; 9 | use yii\widgets\DetailView; 10 | 11 | /* @var $this yii\web\View */ 12 | /* @var $model powerkernel\billing\models\Bank */ 13 | 14 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Banks'), 'url' => ['index']]; 15 | $this->params['breadcrumbs'][] = $this->title; 16 | 17 | /* misc */ 18 | //$js=file_get_contents(__DIR__.'/index.min.js'); 19 | //$this->registerJs($js); 20 | //$css=file_get_contents(__DIR__.'/index.css'); 21 | //$this->registerCss($css); 22 | ?> 23 |
24 |
25 |
26 |
27 | $model, 29 | 'attributes' => [ 30 | 'id', 31 | 'country', 32 | 'title', 33 | 'info:ntext', 34 | 'currency', 35 | ['attribute' => 'status', 'value' => $model->statusColorText, 'format'=>'raw'], 36 | 'createdAt:dateTime', 37 | 'updatedAt:dateTime', 38 | ], 39 | ]) ?> 40 |
41 |

42 | (string)$model->id], ['class' => 'btn btn-primary']) ?> 43 | (string)$model->id], [ 44 | 'class' => 'btn btn-danger', 45 | 'data' => [ 46 | 'confirm' => Yii::t('billing', 'Are you sure you want to delete this item?'), 47 | 'method' => 'post', 48 | ], 49 | ]) ?> 50 |

51 |
52 |
53 |
54 | -------------------------------------------------------------------------------- /views/bitcoin/view.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use yii\helpers\Html; 9 | use yii\widgets\DetailView; 10 | 11 | /* @var $this yii\web\View */ 12 | /* @var $model powerkernel\billing\models\BitcoinAddress */ 13 | 14 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Bitcoin Addresses'), 'url' => ['index']]; 15 | $this->params['breadcrumbs'][] = $this->title; 16 | 17 | /* misc */ 18 | //$js=file_get_contents(__DIR__.'/index.min.js'); 19 | //$this->registerJs($js); 20 | //$css=file_get_contents(__DIR__.'/index.css'); 21 | //$this->registerCss($css); 22 | ?> 23 |
24 |
25 |
26 |
27 | $model, 29 | 'attributes' => [ 30 | ['attribute' => 'address', 'value' => Html::a($model->address, 'https://blockchain.info/address/' . $model->address, ['target' => '_blank']), 'format' => 'raw'], 31 | ['attribute' => 'id_invoice', 'value' => empty($model->invoice) ? null : Html::a($model->id_invoice, Yii::$app->urlManager->createUrl(['billing/invoice/view', 'id' => (string)$model->invoice->id]), ['target' => '_blank']), 'format' => 'raw'], 32 | 'id_account', 33 | 'request_balance', 34 | 'total_received', 35 | 'final_balance', 36 | ['attribute' => 'tx_id', 'value' => empty($model->tx_id) ? null : Html::a($model->tx_id, 'https://blockchain.info/tx/' . $model->tx_id, ['target' => '_blank']), 'format' => 'raw'], 37 | 'tx_confirmed:decimal', 38 | //'tx_date:dateTime', 39 | //'tx_check_date:dateTime', 40 | 'updatedAt:dateTime', 41 | ['attribute' => 'status', 'value' => $model->statusColorText, 'format' => 'raw'], 42 | ], 43 | ]) ?> 44 |
45 |
46 |
47 |
48 | -------------------------------------------------------------------------------- /Module.php: -------------------------------------------------------------------------------- 1 | registerTranslations(); 25 | $this->registerMailer(); 26 | /* api module */ 27 | $this->modules = [ 28 | 'api' => [ 29 | 'class' => 'powerkernel\billing\modules\api\Module', 30 | ], 31 | ]; 32 | } 33 | 34 | /** 35 | * Config Mailer for the Module 36 | */ 37 | public function registerMailer() 38 | { 39 | Yii::$app->mailer->setViewPath($this->basePath . '/mail'); 40 | Yii::$app->mailer->htmlLayout = '@common/mail/layouts/html'; 41 | Yii::$app->mailer->textLayout = '@common/mail/layouts/text'; 42 | } 43 | 44 | /** 45 | * Register translation for the Module 46 | */ 47 | public function registerTranslations() 48 | { 49 | $class = 'common\components\MongoDbMessageSource'; 50 | Yii::$app->i18n->translations['billing'] = [ 51 | 'class' => $class, 52 | 'on missingTranslation' => function ($event) { 53 | $event->sender->handleMissingTranslation($event); 54 | }, 55 | ]; 56 | } 57 | 58 | /** 59 | * Translate message 60 | * @param $message 61 | * @param array $params 62 | * @param null $language 63 | * @return mixed 64 | */ 65 | public static function t($message, $params = [], $language = null) 66 | { 67 | return Yii::$app->getModule('billing')->translate($message, $params, $language); 68 | } 69 | 70 | /** 71 | * Translate message 72 | * @param $message 73 | * @param array $params 74 | * @param null $language 75 | * @return mixed 76 | */ 77 | public static function translate($message, $params = [], $language = null) 78 | { 79 | return Yii::t('billing', $message, $params, $language); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /cronjobs/TaskCheckBTCPayment.php: -------------------------------------------------------------------------------- 1 | call(function (\yii\console\Application $app) { 13 | 14 | /* check payment within 15 min */ 15 | $now = time(); 16 | $period = 999; // margin is always better :) 17 | $point = $now - $period; 18 | 19 | if(Yii::$app->getModule('billing')->params['db']==='mongodb'){ 20 | $addresses = BitcoinAddress::find() 21 | ->where([ 22 | 'status'=>BitcoinAddress::STATUS_USED, 23 | 'updated_at'=>['$gte'=>new \MongoDB\BSON\UTCDateTime($point*1000)] 24 | ])->all(); 25 | } 26 | else { 27 | $addresses = BitcoinAddress::find() 28 | ->where('status=:used AND updated_at>=:point', 29 | [ 30 | ':used' => BitcoinAddress::STATUS_USED, 31 | ':point' => $point 32 | ])->all(); 33 | } 34 | 35 | 36 | if ($addresses) { 37 | $obj = []; 38 | foreach ($addresses as $address) { 39 | $address->checkPayment(); 40 | $obj[] = $address->address; 41 | } 42 | $output = $app->getModule('billing')->t('Addresses checked: {ADDR}', ['ADDR' => implode(', ', $obj)]); 43 | } 44 | 45 | /* Result */ 46 | if (!empty($output)) { 47 | $log = new \common\models\TaskLog(); 48 | $log->task = basename(__FILE__, '.php'); 49 | $log->result = $output; 50 | $log->save(); 51 | } 52 | 53 | 54 | /* delete old logs never bad */ 55 | $period = 30 * 24 * 60 * 60; // 30 days 56 | $point = time() - $period; 57 | if(Yii::$app->params['mongodb']['taskLog']){ 58 | \common\models\TaskLog::deleteAll([ 59 | 'task'=>basename(__FILE__, '.php'), 60 | 'created_at'=>['$lte', new \MongoDB\BSON\UTCDateTime($point*1000)] 61 | ]); 62 | } 63 | else { 64 | \common\models\TaskLog::deleteAll('task=:task AND created_at<=:point', [ 65 | ':task' => basename(__FILE__, '.php'), 66 | ':point' => $point 67 | ]); 68 | } 69 | })->cron($time); -------------------------------------------------------------------------------- /views/info/_form.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use common\Core; 9 | use conquer\select2\Select2Widget; 10 | use yii\bootstrap\ActiveForm; 11 | use yii\helpers\Html; 12 | 13 | 14 | /* @var $this yii\web\View */ 15 | /* @var $model powerkernel\billing\models\BillingInfo */ 16 | /* @var $form yii\widgets\ActiveForm */ 17 | ?> 18 | 19 |
20 | 21 | 22 | 23 |
24 |
25 | field($model, 'company')->textInput(['maxlength' => true]) ?> 26 | 27 | field($model, 'tax_id')->textInput(['maxlength' => true]) ?> 28 | 29 | field($model, 'f_name')->textInput(['maxlength' => true]) ?> 30 | 31 | field($model, 'l_name')->textInput(['maxlength' => true]) ?> 32 | 33 | field($model, 'address')->textInput(['maxlength' => true]) ?> 34 | 35 | field($model, 'address2')->textInput(['maxlength' => true]) ?> 36 |
37 |
38 | 39 | 40 | field($model, 'city')->textInput(['maxlength' => true]) ?> 41 | 42 | field($model, 'state')->textInput(['maxlength' => true]) ?> 43 | 44 | field($model, 'zip')->textInput(['maxlength' => true]) ?> 45 | 46 | field($model, 'country')->widget(Select2Widget::class, [ 47 | 'bootstrap' => false, 48 | 'items' => Core::getCountryList(), 49 | 'options' => ['prompt' => Yii::$app->getModule('billing')->t('Select Country')] 50 | ]) ?> 51 | 52 | field($model, 'phone')->textInput(['maxlength' => true])->hint(Yii::$app->getModule('billing')->t('Add the ‘+’ prefix and the country code before the number. Like +84123123123'), ['class'=>'hint-block text-muted']) ?> 53 |
54 |
55 | 56 |
57 | Yii::t('billing', 'Save'), 'options'=>['class' => 'btn btn-primary']]) ?> 58 |
59 | 60 | 61 | 62 |
63 | -------------------------------------------------------------------------------- /models/BankSearch.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | 9 | namespace powerkernel\billing\models; 10 | 11 | use yii\base\Model; 12 | use yii\data\ActiveDataProvider; 13 | 14 | /** 15 | * BankSearch represents the model behind the search form about `powerkernel\billing\models\Bank`. 16 | */ 17 | class BankSearch extends Bank 18 | { 19 | /** 20 | * @inheritdoc 21 | */ 22 | public function rules() 23 | { 24 | return [ 25 | [['status', 'created_at', 'updated_at'], 'safe'], 26 | [['country', 'title', 'info', 'currency'], 'safe'], 27 | ]; 28 | } 29 | 30 | /** 31 | * @inheritdoc 32 | */ 33 | public function scenarios() 34 | { 35 | // bypass scenarios() implementation in the parent class 36 | return Model::scenarios(); 37 | } 38 | 39 | /** 40 | * Creates data provider instance with search query applied 41 | * 42 | * @param array $params 43 | * 44 | * @return ActiveDataProvider 45 | */ 46 | public function search($params) 47 | { 48 | $query = Bank::find(); 49 | 50 | // add conditions that should always apply here 51 | 52 | $dataProvider = new ActiveDataProvider([ 53 | 'query' => $query, 54 | //'sort'=> ['defaultOrder' => ['id'=>SORT_DESC]] 55 | ]); 56 | 57 | $this->load($params); 58 | 59 | if (!$this->validate()) { 60 | // uncomment the following line if you do not want to return any records when validation fails 61 | // $query->where('0=1'); 62 | return $dataProvider; 63 | } 64 | 65 | // grid filtering conditions 66 | $query->andFilterWhere([ 67 | 'id' => $this->id, 68 | 'status' => $this->status, 69 | 'created_at' => $this->created_at, 70 | 'updated_at' => $this->updated_at, 71 | ]); 72 | 73 | $query->andFilterWhere(['like', 'country', $this->country]) 74 | ->andFilterWhere(['like', 'title', $this->title]) 75 | ->andFilterWhere(['like', 'info', $this->info]) 76 | ->andFilterWhere(['like', 'currency', $this->currency]); 77 | 78 | //$query->andFilterWhere([ 79 | // 'DATE(FROM_UNIXTIME(`created_at`))' => $this->created_at, 80 | //]); 81 | 82 | return $dataProvider; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /views/info/view.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use yii\helpers\Html; 9 | use yii\widgets\DetailView; 10 | 11 | /* @var $this yii\web\View */ 12 | /* @var $model powerkernel\billing\models\BillingInfo */ 13 | 14 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Customers'), 'url' => ['index']]; 15 | $this->params['breadcrumbs'][] = $this->title; 16 | 17 | /* misc */ 18 | //$js=file_get_contents(__DIR__.'/index.min.js'); 19 | //$this->registerJs($js); 20 | //$css=file_get_contents(__DIR__.'/index.css'); 21 | //$this->registerCss($css); 22 | ?> 23 |
24 |
25 |
26 |
27 | $model, 29 | 'attributes' => [ 30 | //'id_account', 31 | 'company', 32 | 'tax_id', 33 | 'f_name', 34 | 'l_name', 35 | 'account.email', 36 | 'address', 37 | 'address2', 38 | 'city', 39 | 'state', 40 | 'zip', 41 | 'country', 42 | 'phone', 43 | ['attribute' => 'status', 'value' => $model->statusColorText, 'format'=>'raw'], 44 | 'createdAt:date', 45 | 'updatedAt:date', 46 | ], 47 | ]) ?> 48 |
49 |

50 | urlManager->createUrl(['account/view', 'id'=>(string)$model->id_account]), ['class' => 'btn btn-info']) ?> 51 | (string)$model->id], ['class' => 'btn btn-primary']) ?> 52 | (string)$model->id], [ 53 | 'class' => 'btn btn-danger', 54 | 'data' => [ 55 | 'confirm' => Yii::t('billing', 'Are you sure you want to delete this item?'), 56 | 'method' => 'post', 57 | ], 58 | ]) ?> 59 |

60 |
61 |
62 |
63 | -------------------------------------------------------------------------------- /views/coupon/view.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use powerkernel\billing\models\Coupon; 9 | use yii\helpers\Html; 10 | use yii\widgets\DetailView; 11 | 12 | /* @var $this yii\web\View */ 13 | /* @var $model powerkernel\billing\models\Coupon */ 14 | 15 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Coupons'), 'url' => ['index']]; 16 | $this->params['breadcrumbs'][] = $model->code; 17 | 18 | /* misc */ 19 | //$js=file_get_contents(__DIR__.'/index.min.js'); 20 | //$this->registerJs($js); 21 | //$css=file_get_contents(__DIR__.'/index.css'); 22 | //$this->registerCss($css); 23 | ?> 24 |
25 |
26 |
27 |
28 | $model, 30 | 'attributes' => [ 31 | 'code', 32 | 'currency', 33 | [ 34 | 'attribute' => 'discount', 35 | 'value' => $model->discount_type == Coupon::DISCOUNT_TYPE_PERCENT ? Yii::$app->formatter->asPercent($model->discount/100):Yii::$app->formatter->asCurrency($model->discount, $model->currency) 36 | ], 37 | 'beginAt:date', 38 | 'endAt:date', 39 | [ 40 | 'attribute' => 'quantity', 41 | 'value' => $model->quantity==-1?Yii::$app->getModule('billing')->t('Unlimited'):Yii::$app->formatter->asDecimal($model->quantity), 42 | ], 43 | 'reuse:boolean', 44 | ['attribute' => 'status', 'value' => $model->statusColorText, 'format' => 'raw'], 45 | 'createdAt:dateTime', 46 | 'updatedAt:dateTime', 47 | ], 48 | ]) ?> 49 |
50 |

51 | (string)$model->id], ['class' => 'btn btn-primary']) ?> 52 | (string)$model->id], [ 53 | 'class' => 'btn btn-danger', 54 | 'data' => [ 55 | 'confirm' => Yii::t('billing', 'Are you sure you want to delete this item?'), 56 | 'method' => 'post', 57 | ], 58 | ]) ?> 59 |

60 |
61 |
62 |
63 | -------------------------------------------------------------------------------- /components/CurrencyLayer.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | 9 | namespace powerkernel\billing\components; 10 | 11 | 12 | use common\components\CurrencyFraction; 13 | use Yii; 14 | use yii\httpclient\Client; 15 | 16 | /** 17 | * Class CurrencyLayer 18 | * @package powerkernel\billing\components 19 | */ 20 | class CurrencyLayer 21 | { 22 | CONST END_POINT = 'http://www.apilayer.net/api/live'; 23 | public $quotes; 24 | 25 | /** 26 | * CurrencyLayer constructor. 27 | */ 28 | public function __construct() 29 | { 30 | $access_key=\powerkernel\billing\models\Setting::getValue('currencyLayerAPI'); 31 | if(!empty($access_key)){ 32 | $this->init($access_key); 33 | } 34 | } 35 | 36 | /** 37 | * @param $access_key 38 | * @throws \yii\base\InvalidConfigException 39 | */ 40 | protected function init($access_key){ 41 | $cache='currency-layer'; 42 | $client = new Client(); 43 | $response = Yii::$app->cache->get($cache); 44 | if ($response === false) { 45 | $response = $client->createRequest() 46 | ->setMethod('get') 47 | ->setUrl(self::END_POINT) 48 | ->setData(['access_key' => $access_key]) 49 | ->send(); 50 | Yii::$app->cache->set($cache, $response, 43200); // 12 hours 51 | } 52 | if ($response->isOk && !empty($response->data['quotes'])) { 53 | $this->quotes = $response->data['quotes']; 54 | } 55 | } 56 | 57 | 58 | /** 59 | * @param $from 60 | * @param $to 61 | * @param $amount 62 | * @return bool|float|int 63 | */ 64 | public function convert($from, $to, $amount) 65 | { 66 | if($from!='USD'){ 67 | $amount=$this->convertToUSD($from, $amount); 68 | } 69 | if (!empty($this->quotes['USD' . $to])) { 70 | $result=$amount*$this->quotes['USD' . $to]; 71 | if($to=='VND'){ 72 | $result=ceil($result/1000)*1000; // round up 73 | } 74 | return round($result, CurrencyFraction::getFraction($to)); 75 | } 76 | return false; 77 | } 78 | 79 | 80 | /** 81 | * @param $from 82 | * @param $amount 83 | * @return bool|float|int 84 | */ 85 | public function convertToUSD($from, $amount) 86 | { 87 | //if($amount==0) return $amount; 88 | 89 | if (!empty($this->quotes['USD' . $from])) { 90 | return round($amount / $this->quotes['USD' . $from], CurrencyFraction::getFraction('USD')); 91 | } 92 | return false; 93 | } 94 | 95 | } -------------------------------------------------------------------------------- /views/bitcoin/payment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Harry Tang 3 | * @link https://powerkernel.com 4 | * @copyright Copyright (c) 2017 Power Kernel 5 | */ 6 | 7 | /** 8 | * set BTC address back again 9 | */ 10 | function setAddress() { 11 | var addr = $("#btc-address"); 12 | addr.html(addr.data('addr')); 13 | } 14 | 15 | /** 16 | * check payment status 17 | */ 18 | function checkPayment() { 19 | var url = $("#check-payment-url").data("check-payment-url"); 20 | $.getJSON(url) 21 | .done(function (response) { 22 | /** @namespace response.payment_received */ 23 | if(response.payment_received===true){ 24 | $("#payment-waiting").addClass("hidden"); 25 | $("#btc-info").addClass("hidden"); 26 | $("#payment-received").removeClass("hidden"); 27 | } 28 | }); 29 | } 30 | checkPayment(); 31 | 32 | /** 33 | * copy BTC address on click 34 | */ 35 | $("#copy-tab").on("click", "#btc-address", function () { 36 | var $temp = $(""); 37 | $("body").append($temp); 38 | $temp.val($(this).data('addr')).select(); 39 | document.execCommand("copy"); 40 | $temp.remove(); 41 | $(this).html($(this).data('copied')); 42 | setTimeout(setAddress, 2000); 43 | }); 44 | 45 | /** 46 | * check payment every 10s 47 | */ 48 | setInterval(checkPayment, 10000); 49 | 50 | /* count down */ 51 | var $clock = $("#count-down"), 52 | addrTime = $("#btc-address").data('date'), // seconds 53 | currentTime = moment().unix(), // seconds 54 | diffTime = currentTime - addrTime, // seconds 55 | timeout = $("#btc-info").data('timeout'), // seconds 56 | duration = moment.duration((timeout - diffTime) * 1000, 'milliseconds'), 57 | interval = 1000; 58 | 59 | var $m = $('--').appendTo($clock); 60 | $(':').appendTo($clock); 61 | var $s = $('--').appendTo($clock); 62 | 63 | setInterval(function () { 64 | duration = moment.duration(duration.asMilliseconds() - interval, 'milliseconds'); 65 | var m = moment.duration(duration).minutes(), 66 | s = moment.duration(duration).seconds(); 67 | 68 | m = $.trim(m).length === 1 ? '0' + m : m; 69 | s = $.trim(s).length === 1 ? '0' + s : s; 70 | 71 | // show how many hours, minutes and seconds are left 72 | if (moment.duration(duration).asSeconds() >= 0) { 73 | if (moment.duration(duration).asSeconds() < 120) { 74 | $("#count-down").addClass("text-danger"); 75 | } 76 | $m.text(m); 77 | $s.text(s); 78 | } 79 | else { 80 | $("#btc-info").hide(); 81 | $("#btc-expired").removeClass('hidden'); 82 | } 83 | 84 | }, interval); 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /views/invoice/_form.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use powerkernel\billing\models\Invoice; 9 | use yii\widgets\ActiveForm; 10 | 11 | /* @var $this yii\web\View */ 12 | /* @var $model powerkernel\billing\models\Invoice */ 13 | /* @var $form yii\widgets\ActiveForm */ 14 | ?> 15 | 16 |
17 | 18 |
19 |
20 | field($model, 'id')->textInput(['disabled' => true]) ?> 21 | 22 | field($model, 'shipping')->textInput(['maxlength' => true]) ?> 23 | info, true); if(empty($info['tax_id'])):?> 24 | field($model, 'tax')->textInput(['maxlength' => true]) ?> 25 | 26 | field($model, 'subtotal')->textInput(['maxlength' => true, 'disabled' => true]) ?> 27 | field($model, 'total')->textInput(['maxlength' => true, 'disabled' => true]) ?> 28 | field($model, 'refund')->textInput(['maxlength' => true]) ?> 29 | 30 | 31 |
32 |
33 | field($model, 'currency')->textInput(['maxlength' => true, 'disabled' => true]) ?> 34 | field($model, 'transaction')->textInput(['maxlength' => true]) ?> 35 | field($model, 'payment_method')->dropDownList(Invoice::getPaymentMethodOption(), ['prompt'=>'']) ?> 36 | field($model, 'payment_date')->hiddenInput()->label(false) 38 | ?> 39 | field($model, 'payment_date_picker')->widget(DatePicker::class, [ 41 | // 'options' => ['class' => 'form-control'], 42 | // 'clientOptions' => [ 43 | // 'altField' => '#invoice-payment_date', 44 | // 'altFormat' => 'mm/dd/yy', 45 | // 'changeYear' => true, 46 | // 'changeMonth' => true, 47 | // //'onSelect' => new \yii\web\JsExpression('function(){$("#invoice-payment_date").val($("#invoice-payment_date").val()/1000);}') 48 | // ], 49 | // ]) 50 | ?> 51 | field($model, 'status')->dropDownList(Invoice::getStatusOption()) ?> 52 |
53 |
54 |
55 | $model->isNewRecord ? Yii::t('billing', 'Create') : Yii::t('billing', 'Update'), 'options'=>['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']]) ?> 56 |
57 | 58 |
59 | -------------------------------------------------------------------------------- /widgets/views/add-address.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | /* @var $model \powerkernel\billing\models\Address */ 9 | 10 | use common\Core; 11 | use yii\bootstrap\ActiveForm; 12 | use yii\bootstrap\Modal; 13 | 14 | ?> 15 |
16 | 'modal-add-address', 19 | 'header' => '

' . Yii::$app->getModule('billing')->t('New address') . '

', 20 | ]); 21 | ?> 22 | 23 |
24 | 25 | 26 | 27 |
28 |
29 | field($model, 'country')->dropDownList(Core::getCountryList())->label(false) ?> 30 |
31 |
32 | field($model, 'contact_name', ['inputOptions' => ['placeholder' => $model->getAttributeLabel('contact_name')]])->label(false) ?> 33 |
34 |
35 | 36 |
37 |
38 | field($model, 'street_address_1', ['inputOptions' => ['placeholder' => $model->getAttributeLabel('street_address_1')]])->label(false) ?> 39 |
40 |
41 | field($model, 'street_address_2', ['inputOptions' => ['placeholder' => $model->getAttributeLabel('street_address_2')]])->label(false) ?> 42 |
43 |
44 | 45 |
46 |
47 | field($model, 'city', ['inputOptions' => ['placeholder' => $model->getAttributeLabel('city')]])->label(false) ?> 48 |
49 |
50 | field($model, 'state', ['inputOptions' => ['placeholder' => $model->getAttributeLabel('state')]])->label(false) ?> 51 |
52 |
53 | 54 |
55 |
56 | field($model, 'zip_code', ['inputOptions' => ['placeholder' => $model->getAttributeLabel('zip_code')]])->label(false) ?> 57 |
58 |
59 | field($model, 'phone', ['inputOptions' => ['placeholder' => $model->getAttributeLabel('phone')]])->label(false) ?> 60 |
61 |
62 | 63 | 64 |
65 | Yii::t('billing', 'Add'), 'options'=>['class' => 'btn btn-primary']]) ?> 66 |
67 | 68 | 69 | 70 |
71 | 72 | 75 |
-------------------------------------------------------------------------------- /views/info/index.php: -------------------------------------------------------------------------------- 1 | params['breadcrumbs'][] = $this->title; 13 | 14 | /* misc */ 15 | $this->registerJs('$(document).on("pjax:send", function(){ $(".grid-view-overlay").removeClass("hidden");});$(document).on("pjax:complete", function(){ $(".grid-view-overlay").addClass("hidden");})'); 16 | //$js=file_get_contents(__DIR__.'/index.min.js'); 17 | //$this->registerJs($js); 18 | //$css=file_get_contents(__DIR__.'/index.css'); 19 | //$this->registerCss($css); 20 | ?> 21 |
22 |
23 |
24 | 25 | 26 | render('_search', ['model' => $searchModel]); ?> 27 | 28 | 29 | 30 |
31 | $dataProvider, 33 | 'filterModel' => $searchModel, 34 | 'columns' => [ 35 | ['class' => 'yii\grid\SerialColumn'], 36 | 37 | //'id_account', 38 | 'company', 39 | 'f_name', 40 | 'l_name', 41 | 'address', 42 | // 'address2', 43 | // 'city', 44 | // 'state', 45 | // 'zip', 46 | 'phone', 47 | 'country', 48 | 49 | // 'status', 50 | // 'created_at', 51 | // 'updated_at', 52 | //['attribute' => 'created_at', 'value' => 'created_at', 'format' => 'dateTime', 'filter' => DatePicker::widget(['model' => $searchModel, 'attribute' => 'created_at', 'dateFormat' => 'yyyy-MM-dd', 'options' => ['class' => 'form-control']])], 53 | //['attribute' => 'status', 'value' => function ($model){return $model->statusText;}, 'filter'=>''], 54 | [ 55 | 'class' => 'yii\grid\ActionColumn', 56 | 'contentOptions' => ['style' => 'min-width: 70px'] 57 | ], 58 | ], 59 | ]); ?> 60 |
61 | 62 | 63 | 64 |
65 | 66 | 69 | 70 |
71 |
72 | -------------------------------------------------------------------------------- /models/BitcoinAddressSearch.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | 9 | namespace powerkernel\billing\models; 10 | 11 | use yii\base\Model; 12 | use yii\data\ActiveDataProvider; 13 | 14 | /** 15 | * BitcoinAddressSearch represents the model behind the search form about `powerkernel\billing\models\BitcoinAddress`. 16 | */ 17 | class BitcoinAddressSearch extends BitcoinAddress 18 | { 19 | /** 20 | * @inheritdoc 21 | */ 22 | public function rules() 23 | { 24 | return [ 25 | [['address', 'id_invoice', 'tx_id', 'status'], 'safe'], 26 | [['request_balance', 'total_received'], 'number'], 27 | ]; 28 | } 29 | 30 | /** 31 | * @inheritdoc 32 | */ 33 | public function scenarios() 34 | { 35 | // bypass scenarios() implementation in the parent class 36 | return Model::scenarios(); 37 | } 38 | 39 | /** 40 | * Creates data provider instance with search query applied 41 | * 42 | * @param array $params 43 | * 44 | * @return ActiveDataProvider 45 | */ 46 | public function search($params) 47 | { 48 | $query = BitcoinAddress::find(); 49 | 50 | // add conditions that should always apply here 51 | 52 | $dataProvider = new ActiveDataProvider([ 53 | 'query' => $query, 54 | //'sort'=> ['defaultOrder' => ['id'=>SORT_DESC]], 55 | //'pagination'=>['pageSize'=>20], 56 | ]); 57 | 58 | $this->load($params); 59 | 60 | if (!$this->validate()) { 61 | // uncomment the following line if you do not want to return any records when validation fails 62 | // $query->where('0=1'); 63 | return $dataProvider; 64 | } 65 | 66 | // grid filtering conditions 67 | $query->andFilterWhere([ 68 | 'id' => $this->id, 69 | 'id_account' => $this->id_account, 70 | 'request_balance' => $this->request_balance, 71 | 'total_received' => $this->total_received, 72 | 'final_balance' => $this->final_balance, 73 | 'tx_date' => $this->tx_date, 74 | 'tx_confirmed' => $this->tx_confirmed, 75 | 'tx_check_date' => $this->tx_check_date, 76 | 'status' => $this->status, 77 | 'created_at' => $this->created_at, 78 | 'updated_at' => $this->updated_at, 79 | ]); 80 | 81 | $query->andFilterWhere(['like', 'address', $this->address]) 82 | ->andFilterWhere(['like', 'id_invoice', $this->id_invoice]) 83 | ->andFilterWhere(['like', 'tx_id', $this->tx_id]); 84 | 85 | //$query->andFilterWhere([ 86 | // 'DATE(FROM_UNIXTIME(`created_at`))' => $this->created_at, 87 | //]); 88 | 89 | return $dataProvider; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /views/bank/index.php: -------------------------------------------------------------------------------- 1 | params['breadcrumbs'][] = $this->title; 14 | 15 | /* misc */ 16 | $this->registerJs('$(document).on("pjax:send", function(){ $(".grid-view-overlay").removeClass("hidden");});$(document).on("pjax:complete", function(){ $(".grid-view-overlay").addClass("hidden");})'); 17 | //$js=file_get_contents(__DIR__.'/index.min.js'); 18 | //$this->registerJs($js); 19 | //$css=file_get_contents(__DIR__.'/index.css'); 20 | //$this->registerCss($css); 21 | ?> 22 |
23 |
24 |
25 | 26 | 27 | render('_search', ['model' => $searchModel]); ?> 28 | 29 | 30 | 31 |
32 | $dataProvider, 34 | 'filterModel' => $searchModel, 35 | 'columns' => [ 36 | ['class' => 'yii\grid\SerialColumn'], 37 | 38 | //'id', 39 | 'country', 40 | 'title', 41 | //'info:ntext', 42 | 'currency', 43 | // 'status', 44 | // 'created_at', 45 | // 'updated_at', 46 | //['attribute' => 'created_at', 'value' => 'created_at', 'format' => 'dateTime', 'filter' => DatePicker::widget(['model' => $searchModel, 'attribute' => 'created_at', 'dateFormat' => 'yyyy-MM-dd', 'options' => ['class' => 'form-control']])], 47 | ['attribute' => 'status', 'value' => function ($model) { 48 | return $model->statusColorText; 49 | }, 'filter' => \powerkernel\billing\models\Bank::getStatusOption(), 'format' => 'raw'], 50 | [ 51 | 'class' => 'yii\grid\ActionColumn', 52 | 'contentOptions' => ['style' => 'min-width: 70px'] 53 | ], 54 | ], 55 | ]); ?> 56 |
57 | 58 |

59 | 'btn btn-success']) ?> 60 |

61 | 62 |
63 | 64 | 67 | 68 |
69 |
70 | -------------------------------------------------------------------------------- /models/BillingInfoSearch.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | 9 | namespace powerkernel\billing\models; 10 | 11 | use yii\base\Model; 12 | use yii\data\ActiveDataProvider; 13 | 14 | /** 15 | * BillingInfoSearch represents the model behind the search form about `powerkernel\billing\models\BillingInfo`. 16 | */ 17 | class BillingInfoSearch extends BillingInfo 18 | { 19 | /** 20 | * @inheritdoc 21 | */ 22 | public function rules() 23 | { 24 | return [ 25 | [['id_account', 'status', 'created_at', 'updated_at'], 'integer'], 26 | [['company', 'f_name', 'l_name', 'address', 'address2', 'city', 'state', 'zip', 'country', 'phone'], 'safe'], 27 | ]; 28 | } 29 | 30 | /** 31 | * @inheritdoc 32 | */ 33 | public function scenarios() 34 | { 35 | // bypass scenarios() implementation in the parent class 36 | return Model::scenarios(); 37 | } 38 | 39 | /** 40 | * Creates data provider instance with search query applied 41 | * 42 | * @param array $params 43 | * 44 | * @return ActiveDataProvider 45 | */ 46 | public function search($params) 47 | { 48 | $query = BillingInfo::find(); 49 | 50 | // add conditions that should always apply here 51 | 52 | $dataProvider = new ActiveDataProvider([ 53 | 'query' => $query, 54 | //'sort'=> ['defaultOrder' => ['id'=>SORT_DESC]] 55 | ]); 56 | 57 | $this->load($params); 58 | 59 | if (!$this->validate()) { 60 | // uncomment the following line if you do not want to return any records when validation fails 61 | // $query->where('0=1'); 62 | return $dataProvider; 63 | } 64 | 65 | // grid filtering conditions 66 | $query->andFilterWhere([ 67 | 'id_account' => $this->id_account, 68 | 'status' => $this->status, 69 | 'created_at' => $this->created_at, 70 | 'updated_at' => $this->updated_at, 71 | ]); 72 | 73 | $query->andFilterWhere(['like', 'company', $this->company]) 74 | ->andFilterWhere(['like', 'f_name', $this->f_name]) 75 | ->andFilterWhere(['like', 'l_name', $this->l_name]) 76 | ->andFilterWhere(['like', 'address', $this->address]) 77 | ->andFilterWhere(['like', 'address2', $this->address2]) 78 | ->andFilterWhere(['like', 'city', $this->city]) 79 | ->andFilterWhere(['like', 'state', $this->state]) 80 | ->andFilterWhere(['like', 'zip', $this->zip]) 81 | ->andFilterWhere(['like', 'country', $this->country]) 82 | ->andFilterWhere(['like', 'phone', $this->phone]); 83 | 84 | //$query->andFilterWhere([ 85 | // 'DATE(FROM_UNIXTIME(`created_at`))' => $this->created_at, 86 | //]); 87 | 88 | return $dataProvider; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /models/Setting.php: -------------------------------------------------------------------------------- 1 | _id; 56 | } 57 | 58 | /** 59 | * @inheritdoc 60 | */ 61 | public function rules() 62 | { 63 | return [ 64 | [['key', 'title'], 'required'], 65 | [['key_order'], 'integer'], 66 | [['key', 'title', 'value', 'type', 'default', 'data', 'rules'], 'string', 'max' => 255] 67 | ]; 68 | } 69 | 70 | /** 71 | * @inheritdoc 72 | */ 73 | public function attributeLabels() 74 | { 75 | return [ 76 | 'key' => Yii::t('billing', 'Key'), 77 | 'title' => Yii::t('billing', 'Title'), 78 | 'value' => Yii::t('billing', 'Value'), 79 | 'type' => Yii::t('billing', 'Type'), 80 | 'default' => Yii::t('billing', 'Default'), 81 | 'data' => Yii::t('billing', 'Data'), 82 | 'rules' => Yii::t('billing', 'Rules'), 83 | 'key_order' => Yii::t('billing', 'Order'), 84 | ]; 85 | } 86 | 87 | /** 88 | * load as array 89 | * @return array 90 | */ 91 | public static function loadAsArray() 92 | { 93 | $settings = self::find()->all(); 94 | $a = []; 95 | foreach ($settings as $setting) { 96 | $a[$setting->key] = $setting->value; 97 | } 98 | return $a; 99 | } 100 | 101 | /** 102 | * @param $key 103 | * @return mixed|null 104 | */ 105 | public static function findTitle($key) 106 | { 107 | $model = self::find()->where(['key' => $key])->one(); 108 | if ($model) { 109 | return $model->title; 110 | } 111 | return null; 112 | } 113 | 114 | /** 115 | * get setting value 116 | * @param $key 117 | * @return mixed|null 118 | */ 119 | public static function getValue($key) 120 | { 121 | $model = self::find()->where(['key' => $key])->one(); 122 | if ($model) { 123 | return $model->value; 124 | } 125 | return null; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /views/setting/index.php: -------------------------------------------------------------------------------- 1 | title = Yii::t('billing', 'Settings'); 14 | $this->params['breadcrumbs'][] = ['label' => Yii::t('billing', 'Billing'), 'url' => ['index']]; 15 | $this->params['breadcrumbs'][] = $this->title; 16 | 17 | 18 | ?> 19 |
20 | 'horizontal', 22 | 'fieldConfig' => ['horizontalCssClasses' => [ 23 | 'offset' => '', 24 | 'label' => 'col-sm-2', 25 | 'wrapper' => 'col-sm-6', 26 | 'error' => '', 27 | 'hint' => 'col-sm-4', 28 | ]], 29 | ]); ?> 30 | 75 | 76 |
-------------------------------------------------------------------------------- /views/address/index.php: -------------------------------------------------------------------------------- 1 | params['breadcrumbs'][] = Yii::$app->getModule('billing')->t('Billing'); 14 | $this->params['breadcrumbs'][] = $this->title; 15 | 16 | /* misc */ 17 | $this->registerJs('$(document).on("pjax:send", function(){ $(".grid-view-overlay").removeClass("hidden");});$(document).on("pjax:complete", function(){ $(".grid-view-overlay").addClass("hidden");})'); 18 | //$js=file_get_contents(__DIR__.'/index.min.js'); 19 | //$this->registerJs($js); 20 | //$css=file_get_contents(__DIR__.'/index.css'); 21 | //$this->registerCss($css); 22 | ?> 23 |
24 |
25 |
26 | 27 | 28 | render('_search', ['model' => $searchModel]); ?> 29 | 30 | 31 | 32 |
33 | $dataProvider, 35 | 'filterModel' => $searchModel, 36 | 'columns' => [ 37 | ['class' => 'yii\grid\SerialColumn'], 38 | ['attribute' => 'id_account', 'value' => function ($model){return Html::a($model->account->fullname, ['/account/view', 'id'=>(string)$model->account->id], ['data-pjax'=>0]);}, 'format'=>'raw'], 39 | 40 | 'contact_name', 41 | //'street_address_1', 42 | // 'street_address_2', 43 | 'city', 44 | 'state', 45 | 'zip_code', 46 | 'country', 47 | 'phone', 48 | // 'status', 49 | // 'created_at', 50 | // 'updated_at', 51 | //[ 52 | // 'attribute' => 'created_at', 53 | // 'value' => 'created_at', 54 | // 'format' => 'dateTime', 55 | // 'filter' => DatePicker::widget(['model' => $searchModel, 'attribute' => 'created_at', 'dateFormat' => 'yyyy-MM-dd', 'options' => ['class' => 'form-control']]), 56 | // 'contentOptions'=>['style'=>'min-width: 80px'] 57 | //], 58 | //['attribute' => 'status', 'value' => function ($model){return $model->statusText;}, 'filter'=>''], 59 | [ 60 | 'class' => 'yii\grid\ActionColumn', 61 | //'contentOptions' => ['style' => 'min-width: 70px'], 62 | 'template'=>'{view}' 63 | ], 64 | ], 65 | ]); ?> 66 |
67 | 68 | 69 |
70 | 71 | 74 | 75 |
76 |
77 | -------------------------------------------------------------------------------- /views/invoice/index.php: -------------------------------------------------------------------------------- 1 | params['breadcrumbs'][] = $this->title; 15 | 16 | /* misc */ 17 | $this->registerJs('$(document).on("pjax:send", function(){ $(".grid-view-overlay").removeClass("hidden");});$(document).on("pjax:complete", function(){ $(".grid-view-overlay").addClass("hidden");})'); 18 | //$js=file_get_contents(__DIR__.'/index.min.js'); 19 | //$this->registerJs($js); 20 | //$css=file_get_contents(__DIR__.'/index.css'); 21 | //$this->registerCss($css); 22 | ?> 23 |
24 |
25 |
26 | 27 |
28 | $dataProvider, 30 | 'filterModel' => $searchModel, 31 | 'columns' => [ 32 | //['class' => 'yii\grid\SerialColumn'], 33 | 34 | 'id_invoice', 35 | //'account.fullname', 36 | ['attribute' => 'id_account', 'value' => 'account.fullname'], 37 | //['attribute' => 'subtotal', 'value' => function ($model){return Yii::$app->formatter->asCurrency($model->subtotal, $model->currency);}], 38 | //['attribute' => 'discount', 'value' => function ($model){return Yii::$app->formatter->asCurrency($model->discount, $model->currency);}], 39 | //['attribute' => 'tax', 'value' => function ($model){return Yii::$app->formatter->asCurrency($model->tax, $model->currency);}], 40 | ['attribute' => 'total', 'value' => function ($model) { 41 | return Yii::$app->formatter->asCurrency($model->total, $model->currency); 42 | }], 43 | // 'currency', 44 | // 'status', 45 | // 'created_at', 46 | // 'updated_at', 47 | ['attribute' => 'created_at', 'value' => 'createdAt', 'format' => 'dateTime', 'filter' => DatePicker::widget(['model' => $searchModel, 'attribute' => 'created_at', 'dateFormat' => 'yyyy-MM-dd', 'options' => ['class' => 'form-control']])], 48 | ['attribute' => 'status', 'value' => function ($model) { 49 | return $model->getStatusColorText(); 50 | }, 'filter' => Invoice::getStatusOption(), 'format' => 'raw'], 51 | [ 52 | 'class' => 'yii\grid\ActionColumn', 53 | 'template' => '{view} {update}', 54 | 'contentOptions'=>['style'=>'min-width: 50px'] 55 | ], 56 | ], 57 | ]); ?> 58 |
59 | 60 |
61 | 62 | 65 | 66 |
67 |
68 | -------------------------------------------------------------------------------- /views/bitcoin/index.php: -------------------------------------------------------------------------------- 1 | params['breadcrumbs'][] = $this->title; 15 | 16 | /* misc */ 17 | $this->registerJs('$(document).on("pjax:send", function(){ $(".grid-view-overlay").removeClass("hidden");});$(document).on("pjax:complete", function(){ $(".grid-view-overlay").addClass("hidden");})'); 18 | //$js=file_get_contents(__DIR__.'/index.min.js'); 19 | //$this->registerJs($js); 20 | //$css=file_get_contents(__DIR__.'/index.css'); 21 | //$this->registerCss($css); 22 | ?> 23 |
24 |
25 |
26 | 27 | 28 | render('_search', ['model' => $searchModel]); ?> 29 | 30 | 31 | 32 |
33 | $dataProvider, 35 | 'filterModel' => $searchModel, 36 | 'columns' => [ 37 | ['class' => 'yii\grid\SerialColumn'], 38 | //'id', 39 | ['attribute' => 'address', 'value' => function ($model){return Html::a($model->address, Yii::$app->urlManager->createUrl(['/billing/bitcoin/view', 'id'=>(string)$model->id]), ['title'=>Yii::$app->getModule('billing')->t('View BTC Address'), 'data-pjax'=>0]) ;}, 'format'=>'raw'], 40 | 'id_invoice', 41 | //'id_account', 42 | 43 | 'request_balance', 44 | 'total_received', 45 | // 'tx_id', 46 | // 'tx_date', 47 | // 'tx_confirmed', 48 | // 'tx_check_date', 49 | // 'status', 50 | // 'created_at', 51 | // 'updated_at', 52 | //[ 53 | // 'attribute' => 'created_at', 54 | // 'value' => 'created_at', 55 | // 'format' => 'dateTime', 56 | // 'filter' => DatePicker::widget(['model' => $searchModel, 'attribute' => 'created_at', 'dateFormat' => 'yyyy-MM-dd', 'options' => ['class' => 'form-control']]), 57 | // 'contentOptions'=>['style'=>'min-width: 80px'] 58 | //], 59 | ['attribute' => 'status', 'value' => function ($model){return $model->statusColorText;}, 'filter'=> BitcoinAddress::getStatusOption(), 'format'=>'raw'], 60 | //['class' => 'yii\grid\ActionColumn'], 61 | ], 62 | ]); ?> 63 |
64 | 65 |

66 | 'btn btn-success']) ?> 67 |

68 | 69 |
70 | 71 | 74 | 75 |
76 |
77 | -------------------------------------------------------------------------------- /mail/src/new-invoice-admin-html.php: -------------------------------------------------------------------------------- 1 | 7 |
8 |
9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 | 17 | 18 | 66 | 67 | 68 |
19 |
20 | 21 | 22 | 60 | 61 |
23 | 24 | 25 | 26 | 29 | 30 | 31 | 34 | 35 | 36 | 56 | 57 | 58 |
27 | getModule('billing')->t('Greetings from {APP},', ['APP'=>Yii::$app->name]) ?> 28 |
32 | getModule('billing')->t('A new order has just been placed') ?> 33 |
37 | 38 | 39 | 42 | 43 | 44 | 53 | 54 |
40 | getModule('billing')->t('Invoice #{ID}', ['ID'=>$model->id_invoice]) ?>
41 |
45 | 46 | 47 | 50 | 51 |
48 | getModule('billing')->t('View Invoice') ?> 49 |
52 |
55 |
59 |
62 | 63 |
64 | 65 |
69 | -------------------------------------------------------------------------------- /mail/src/cancel-invoice-html.php: -------------------------------------------------------------------------------- 1 | 7 |
8 |
9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 | 17 | 18 | 66 | 67 | 68 |
19 |
20 | 21 | 22 | 60 | 61 |
23 | 24 | 25 | 26 | 29 | 30 | 31 | 34 | 35 | 36 | 39 | 40 | 41 | 56 | 57 | 58 |
27 | getModule('billing')->t('Hi {USER},', ['USER' => $model->account->fullname]) ?> 28 |
32 | getModule('billing')->t('We\'re sorry to let you know that your invoice has been canceled. You don\'t need to do anything else.') ?> 33 |
37 | getModule('billing')->t('Invoice #{ID}', ['ID'=>$model->id_invoice]) ?>
38 |
42 | 43 | 44 | 53 | 54 |
45 | 46 | 47 | 50 | 51 |
48 | getModule('billing')->t('View Invoice') ?> 49 |
52 |
55 |
59 |
62 | 63 |
64 | 65 |
69 | -------------------------------------------------------------------------------- /models/CouponForm.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | namespace powerkernel\billing\models; 9 | 10 | use MongoDB\BSON\UTCDateTime; 11 | use Yii; 12 | use yii\base\Model; 13 | 14 | /** 15 | * Class CouponForm 16 | * @package powerkernel\billing\models 17 | */ 18 | class CouponForm extends Model 19 | { 20 | public $coupon; 21 | public $invoice; 22 | 23 | /** 24 | * @inheritdoc 25 | */ 26 | public function rules() 27 | { 28 | 29 | return [ 30 | [['coupon', 'invoice'], 'required'], 31 | [['coupon'], 'string', 'min' => 3, 'max' => 50], 32 | [['coupon'], 'checkCoupon'] 33 | ]; 34 | } 35 | 36 | /** 37 | * check coupon 38 | * @param $attribute 39 | * @param $params 40 | * @param $validator 41 | * @return bool 42 | */ 43 | public function checkCoupon($attribute, $params, $validator) 44 | { 45 | $query = Coupon::find()->where([ 46 | 'code' => $this->$attribute, 47 | 'status' => Coupon::STATUS_ACTIVE, 48 | ]); 49 | /* exist */ 50 | 51 | $code = $query->andFilterWhere([ 52 | 'begin_at' => ['$lte' => new UTCDateTime()], 53 | 'end_at' => ['$gte' => new UTCDateTime()] 54 | ]) 55 | ->one(); 56 | 57 | 58 | if (!$code) { 59 | $this->addError($attribute, Yii::$app->getModule('billing')->t('The code you entered is invalid or expired.')); 60 | return false; 61 | } 62 | 63 | /* currency */ 64 | if ($code->currency != $this->invoice->currency) { 65 | $this->addError($attribute, Yii::$app->getModule('billing')->t('The invoice is not eligible for this promotion.')); 66 | return false; 67 | } 68 | 69 | /* Quantity */ 70 | if ($code->quantity != -1) { 71 | $used = Invoice::find()->where([ 72 | 'coupon' => $code, 73 | 'status' => [ 74 | Invoice::STATUS_PENDING, 75 | Invoice::STATUS_PAID, 76 | Invoice::STATUS_PAID_UNCONFIRMED, 77 | Invoice::STATUS_REFUNDED 78 | ] 79 | ])->count(); 80 | if ($code->quantity <= $used) { 81 | $this->addError($attribute, Yii::$app->getModule('billing')->t('The code you entered is ended.')); 82 | return false; 83 | } 84 | } 85 | 86 | /* can reuse? */ 87 | if (!$code->reuse) { 88 | $reused = Invoice::find()->where([ 89 | 'coupon' => $code, 90 | 'status' => [ 91 | Invoice::STATUS_PENDING, 92 | Invoice::STATUS_PAID, 93 | Invoice::STATUS_PAID_UNCONFIRMED, 94 | Invoice::STATUS_REFUNDED 95 | ], 96 | 'id_account' => Yii::$app->user->id 97 | ])->count(); 98 | if ($reused) { 99 | $this->addError($attribute, Yii::$app->getModule('billing')->t('You already used this code once!')); 100 | return false; 101 | } 102 | } 103 | 104 | } 105 | 106 | /** 107 | * @inheritdoc 108 | */ 109 | public function attributeLabels() 110 | { 111 | return [ 112 | 'coupon' => Yii::$app->getModule('billing')->t('Coupon Code'), 113 | ]; 114 | } 115 | 116 | 117 | } 118 | -------------------------------------------------------------------------------- /models/InvoiceSearch.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | 9 | namespace powerkernel\billing\models; 10 | 11 | use common\models\Account; 12 | use MongoDB\BSON\UTCDateTime; 13 | use Yii; 14 | use yii\base\Model; 15 | use yii\data\ActiveDataProvider; 16 | 17 | /** 18 | * InvoiceSearch represents the model behind the search form about `powerkernel\billing\models\Invoice`. 19 | */ 20 | class InvoiceSearch extends Invoice 21 | { 22 | 23 | public $fullname; 24 | public $manage = false; 25 | 26 | /** 27 | * @inheritdoc 28 | */ 29 | public function rules() 30 | { 31 | return [ 32 | [['id_invoice', 'total', 'created_at', 'id_account', 'status'], 'safe'], 33 | ]; 34 | } 35 | 36 | /** 37 | * @inheritdoc 38 | */ 39 | public function scenarios() 40 | { 41 | // bypass scenarios() implementation in the parent class 42 | return Model::scenarios(); 43 | } 44 | 45 | /** 46 | * Creates data provider instance with search query applied 47 | * 48 | * @param array $params 49 | * 50 | * @return ActiveDataProvider 51 | */ 52 | public function search($params) 53 | { 54 | $query = Invoice::find(); 55 | //$query->joinWith(['account']); 56 | 57 | // add conditions that should always apply here 58 | 59 | $dataProvider = new ActiveDataProvider([ 60 | 'query' => $query, 61 | 'sort' => ['defaultOrder' => ['created_at' => SORT_DESC]] 62 | ]); 63 | 64 | $dataProvider->sort->attributes['fullname'] = [ 65 | // The tables are the ones our relation are configured to 66 | // in my case they are prefixed with "tbl_" 67 | //'asc' => ['{{%core_account}}.fullname' => SORT_ASC], 68 | //'desc' => ['{{%core_account}}.fullname' => SORT_DESC], 69 | ]; 70 | 71 | if ($this->manage) { 72 | $query->andWhere(['id_account' => \Yii::$app->user->id]); 73 | } 74 | 75 | $this->load($params); 76 | 77 | if (!$this->validate()) { 78 | // uncomment the following line if you do not want to return any records when validation fails 79 | // $query->where('0=1'); 80 | return $dataProvider; 81 | } 82 | 83 | // grid filtering conditions 84 | $query->andFilterWhere([ 85 | 'total' => in_array($this->total, ['', null], true) ? null : (float)$this->total, 86 | 'status' => $this->status, 87 | ]); 88 | $query->andFilterWhere(['like', 'id_invoice', $this->id_invoice]); 89 | 90 | /* account */ 91 | if (!empty($this->id_account)) { 92 | $key = '_id'; 93 | $ids = []; 94 | $owners = Account::find()->select([$key])->where(['like', 'fullname', $this->id_account])->asArray()->all(); 95 | foreach ($owners as $owner) { 96 | $ids[] = (string)$owner[$key]; 97 | } 98 | $query->andFilterWhere(['id_account' => empty($ids) ? '0' : $ids]); 99 | } 100 | 101 | /* created_at */ 102 | if (!empty($this->created_at)) { 103 | 104 | $query->andFilterWhere([ 105 | 'updated_at' => ['$gte' => new UTCDateTime(strtotime($this->created_at) * 1000)], 106 | ])->andFilterWhere([ 107 | 'updated_at' => ['$lt' => new UTCDateTime((strtotime($this->created_at) + 86400) * 1000)], 108 | ]); 109 | 110 | } 111 | 112 | return $dataProvider; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /models/CouponSearch.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | 9 | namespace powerkernel\billing\models; 10 | 11 | use yii\base\Model; 12 | use yii\data\ActiveDataProvider; 13 | 14 | /** 15 | * CouponSearch represents the model behind the search form about `powerkernel\billing\models\Coupon`. 16 | */ 17 | class CouponSearch extends Coupon 18 | { 19 | /** 20 | * @inheritdoc 21 | */ 22 | public function rules() 23 | { 24 | return [ 25 | [['code', 'begin_at', 'end_at', 'status', 'discount', 'quantity'], 'safe'], 26 | [['discount'], 'number'], 27 | //[['quantity', 'reuse', 'status', 'created_at', 'updated_at'], 'integer'], 28 | ]; 29 | } 30 | 31 | /** 32 | * @inheritdoc 33 | */ 34 | public function scenarios() 35 | { 36 | // bypass scenarios() implementation in the parent class 37 | return Model::scenarios(); 38 | } 39 | 40 | /** 41 | * Creates data provider instance with search query applied 42 | * 43 | * @param array $params 44 | * 45 | * @return ActiveDataProvider 46 | */ 47 | public function search($params) 48 | { 49 | $query = Coupon::find(); 50 | 51 | // add conditions that should always apply here 52 | 53 | $dataProvider = new ActiveDataProvider([ 54 | 'query' => $query, 55 | 'sort' => ['defaultOrder' => ['created_at' => SORT_DESC]], 56 | //'pagination'=>['pageSize'=>20], 57 | ]); 58 | 59 | $this->load($params); 60 | 61 | if (!$this->validate()) { 62 | // uncomment the following line if you do not want to return any records when validation fails 63 | // $query->where('0=1'); 64 | return $dataProvider; 65 | } 66 | 67 | // grid filtering conditions 68 | $query->andFilterWhere(['like', 'code', $this->code]); 69 | $query->andFilterWhere([ 70 | 'discount' => in_array($this->discount, ['', null], true) ? null : (float)$this->discount, 71 | 'quantity' => in_array($this->quantity, ['', null], true) ? null : (float)$this->quantity, 72 | 'status' => $this->status, 73 | ]); 74 | if (!empty($this->begin_at)) { 75 | 76 | $query->andFilterWhere([ 77 | 'begin_at' => ['$gte' => new \MongoDB\BSON\UTCDateTime(strtotime($this->begin_at) * 1000)], 78 | ])->andFilterWhere([ 79 | 'begin_at' => ['$lt' => new \MongoDB\BSON\UTCDateTime((strtotime($this->begin_at) + 86400) * 1000)], 80 | ]); 81 | 82 | } 83 | if (!empty($this->end_at)) { 84 | 85 | $query->andFilterWhere([ 86 | 'end_at' => ['$gte' => new \MongoDB\BSON\UTCDateTime(strtotime($this->end_at) * 1000)], 87 | ])->andFilterWhere([ 88 | 'end_at' => ['$lt' => new \MongoDB\BSON\UTCDateTime((strtotime($this->end_at) + 86400) * 1000)], 89 | ]); 90 | 91 | } 92 | 93 | // if(!empty($this->begin_at)){ 94 | // $query->andFilterWhere([ 95 | // 'DATE(CONVERT_TZ(FROM_UNIXTIME(`begin_at`), :UTC, :ATZ))' => $this->begin_at, 96 | // ])->params([ 97 | // ':UTC'=>'+00:00', 98 | // ':ATZ'=>date('P') 99 | // ]); 100 | // } 101 | // if(!empty($this->end_at)){ 102 | // $query->andFilterWhere([ 103 | // 'DATE(CONVERT_TZ(FROM_UNIXTIME(`end_at`), :UTC, :ATZ))' => $this->end_at, 104 | // ])->params([ 105 | // ':UTC'=>'+00:00', 106 | // ':ATZ'=>date('P') 107 | // ]); 108 | // } 109 | 110 | return $dataProvider; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /views/invoice/manage.php: -------------------------------------------------------------------------------- 1 | params['breadcrumbs'][] = $this->title; 16 | 17 | /* misc */ 18 | $this->registerJs('$(document).on("pjax:send", function(){ $(".grid-view-overlay").removeClass("hidden");});$(document).on("pjax:complete", function(){ $(".grid-view-overlay").addClass("hidden");})'); 19 | //$js=file_get_contents(__DIR__.'/index.min.js'); 20 | //$this->registerJs($js); 21 | //$css=file_get_contents(__DIR__.'/index.css'); 22 | //$this->registerCss($css); 23 | ?> 24 |
25 |
26 |
27 | 28 |
29 | $dataProvider, 31 | 'filterModel' => $searchModel, 32 | 'columns' => [ 33 | //['class' => 'yii\grid\SerialColumn'], 34 | 35 | 'id_invoice', 36 | //'account.fullname', 37 | //['attribute' => 'fullname', 'value' => 'account.fullname'], 38 | //['attribute' => 'subtotal', 'value' => function ($model){return Yii::$app->formatter->asCurrency($model->subtotal, $model->currency);}], 39 | //['attribute' => 'discount', 'value' => function ($model){return Yii::$app->formatter->asCurrency($model->discount, $model->currency);}], 40 | //['attribute' => 'tax', 'value' => function ($model){return Yii::$app->formatter->asCurrency($model->tax, $model->currency);}], 41 | ['attribute' => 'total', 'value' => function ($model) { 42 | return Yii::$app->formatter->asCurrency($model->total, $model->currency); 43 | }], 44 | // 'currency', 45 | // 'status', 46 | // 'created_at', 47 | // 'updated_at', 48 | ['attribute' => 'created_at', 'value' => 'createdAt', 'format' => 'dateTime', 'filter' => DatePicker::widget(['model' => $searchModel, 'attribute' => 'created_at', 'dateFormat' => 'yyyy-MM-dd', 'options' => ['class' => 'form-control']])], 49 | ['attribute' => 'status', 'value' => function ($model) { 50 | return $model->getStatusColorText(); 51 | }, 'filter' => Invoice::getStatusOption(), 'format' => 'raw'], 52 | [ 53 | 'class' => 'yii\grid\ActionColumn', 54 | 'template' => '{show}', 55 | 'buttons' => [ 56 | 'show' => function ($url) { 57 | $show = Html::a('', $url, [ 58 | 'title' => Yii::t('yii', 'View'), 59 | 'data-pjax' => '0', 60 | ]); 61 | return $show; 62 | } 63 | ] 64 | ], 65 | ], 66 | ]); ?> 67 |
68 | 69 |
70 | 71 | 74 | 75 |
76 |
77 | -------------------------------------------------------------------------------- /views/coupon/_form.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use common\Core; 9 | use conquer\select2\Select2Widget; 10 | use powerkernel\billing\models\Coupon; 11 | use yii\jui\DatePicker; 12 | use yii\widgets\ActiveForm; 13 | 14 | /* @var $this yii\web\View */ 15 | /* @var $model powerkernel\billing\models\Coupon */ 16 | /* @var $form yii\widgets\ActiveForm */ 17 | ?> 18 | 19 |
20 | 21 | 22 | 23 |
24 |
25 | field($model, 'code')->textInput(['maxlength' => true]) ?> 26 |
27 |
28 | field($model, 'currency')->widget(Select2Widget::class, [ 29 | 'bootstrap' => false, 30 | 'items' => Core::getCurrencyList(), 31 | 'options' => ['prompt' => Yii::$app->getModule('billing')->t('Select Currency')] 32 | ]) ?> 33 |
34 |
35 | 36 |
37 |
38 | field($model, 'discount')->textInput(['maxlength' => true]) ?> 39 |
40 |
41 | field($model, 'discount_type')->radioList(Coupon::getDiscountOption()) ?> 42 |
43 |
44 | 45 |
46 |
47 | field($model, 'begin_date_picker')->widget(DatePicker::class, [ 48 | 'options'=>['class'=>'form-control'], 49 | 'clientOptions'=>[ 50 | 'altField'=>'#coupon-begin_at', 51 | 'altFormat'=>'mm/dd/yy', 52 | //'altFormat'=>'@', 53 | 'changeYear'=>true, 54 | 'changeMonth'=>true, 55 | //'onSelect'=>new \yii\web\JsExpression('function(){$("#coupon-begin_at").val($("#coupon-begin_at").val()/1000);}') 56 | ], 57 | ]) ?> 58 | field($model, 'begin_at')->hiddenInput()->label(false)->error(['class'=>'hidden']) ?> 59 | 60 |
61 |
62 | field($model, 'end_date_picker')->widget(DatePicker::class, [ 63 | 'options'=>['class'=>'form-control'], 64 | 'clientOptions'=>[ 65 | 'altField'=>'#coupon-end_at', 66 | 'altFormat'=>'mm/dd/yy', 67 | //'altFormat'=>'@', 68 | 'changeYear'=>true, 69 | 'changeMonth'=>true, 70 | //'onSelect'=>new \yii\web\JsExpression('function(){$("#coupon-end_at").val($("#coupon-end_at").val()/1000);}') 71 | ], 72 | ]) ?> 73 | field($model, 'end_at')->hiddenInput()->label(false)->error(['class'=>'hidden']) ?> 74 |
75 |
76 | 77 |
78 |
79 | field($model, 'quantity')->textInput()->hint(Yii::$app->getModule('billing')->t('Enter -1 for unlimited')) ?> 80 |
81 |
82 | field($model, 'reuse')->radioList(\common\Core::getYesNoOption()) ?> 83 |
84 |
85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | field($model, 'status')->dropDownList(Coupon::getStatusOption()) ?> 102 | 103 |
104 | $model->isNewRecord ? Yii::t('billing', 'Create') : Yii::t('billing', 'Update'), 'options'=>['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']]) ?> 105 |
106 | 107 | 108 | 109 |
110 | -------------------------------------------------------------------------------- /modules/api/v1/controllers/InvoiceController.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | 9 | namespace powerkernel\billing\modules\api\v1\controllers; 10 | 11 | 12 | use powerkernel\billing\models\Invoice; 13 | use Yii; 14 | use yii\filters\AccessControl; 15 | use yii\filters\auth\HttpBasicAuth; 16 | use yii\filters\VerbFilter; 17 | 18 | 19 | /** 20 | * Class InvoiceController 21 | * @package powerkernel\billing\modules\api\v1\controllers 22 | */ 23 | class InvoiceController extends \yii\rest\Controller 24 | { 25 | /** 26 | * @return array 27 | */ 28 | public function behaviors() 29 | { 30 | $behaviors = parent::behaviors(); 31 | $behaviors['verbs'] = [ 32 | 'class' => VerbFilter::class, 33 | 'actions' => [ 34 | 'vcb-sms-check' => ['POST'], 35 | ], 36 | ]; 37 | 38 | 39 | $behaviors['authenticator'] = [ 40 | 'class' => HttpBasicAuth::class, 41 | ]; 42 | $behaviors['access'] = 43 | [ 44 | 'class' => AccessControl::class, 45 | 'rules' => [ 46 | [ 47 | 'roles' => ['admin'], 48 | 'allow' => true, 49 | ], 50 | ], 51 | ]; 52 | 53 | return $behaviors; 54 | } 55 | 56 | 57 | /** 58 | * check sms VCB account in VND 59 | * test: curl -k -i -H "Accept:application/json" -H "Authorization: Basic base64 token" url 60 | */ 61 | public function actionVcbSmsCheck() 62 | { 63 | if (!empty(Yii::$app->request->post('sms'))) { 64 | $sms = \Yii::$app->request->post('sms'); 65 | /* check invoice id */ 66 | $id = null; 67 | $model = null; 68 | $unpaidInvoices = Invoice::find()->where([ 69 | 'currency' => 'VND', 70 | 'status' => Invoice::STATUS_PENDING, 71 | ])->all(); 72 | foreach ($unpaidInvoices as $invoice) { 73 | if (preg_match("/({$invoice->id_invoice})/", $sms, $invoiceId)) { 74 | $id = $invoice->id_invoice; 75 | $model = $invoice; 76 | break; 77 | } 78 | } 79 | 80 | if ($id != null) { 81 | $amount = number_format($model->total, 0, '.', $thousands_sep = ","); 82 | /* amount */ 83 | if (!preg_match('/(\+' . $amount . ' VND)/', $sms, $creditAmt)) { 84 | return [ 85 | 'success' => false, 86 | 'data' => [ 87 | 'message' => 'Wrong amount in WIRE Transfer' 88 | ] 89 | ]; 90 | } 91 | 92 | /* set invoice paid if we are here*/ 93 | $model->status = Invoice::STATUS_PAID; 94 | $model->payment_method = 'Bank Wire'; 95 | //$model->save(); 96 | 97 | return [ 98 | 'success' => true, 99 | 'data' => [ 100 | 'invoice' => $model->id_invoice, 101 | 'total' => \Yii::$app->formatter->asDecimal($model->total), 102 | 'bank' => $creditAmt[1], 103 | 'save' => $model->save() 104 | ] 105 | ]; 106 | } 107 | return [ 108 | 'success' => false, 109 | 'data' => [ 110 | 'message' => 'Cannot find invoice ID' 111 | ] 112 | ]; 113 | } 114 | return [ 115 | 'success' => false, 116 | 'data' => [ 117 | 'message' => 'Missing required parameters: sms' 118 | ] 119 | ]; 120 | } 121 | } 122 | 123 | 124 | -------------------------------------------------------------------------------- /views/paypal/return.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | use powerkernel\fontawesome\Icon; 9 | use yii\helpers\Html; 10 | 11 | /* @var $this yii\web\View */ 12 | /* @var $invoice \powerkernel\billing\models\Invoice */ 13 | 14 | $this->title = Yii::$app->getModule('billing')->t('Complete Payment'); 15 | $keywords = ''; 16 | $description = ''; 17 | 18 | $this->registerMetaTag(['name' => 'keywords', 'content' => $keywords]); 19 | $this->registerMetaTag(['name' => 'description', 'content' => $description]); 20 | //$this->registerMetaTag(['name' => 'robots', 'content' => 'noindex, nofollow, nosnippet, noodp, noarchive, noimageindex']); 21 | 22 | /* Facebook */ 23 | //$this->registerMetaTag(['property' => 'og:title', 'content' => $this->title]); 24 | //$this->registerMetaTag(['property' => 'og:description', 'content' => $description]); 25 | //$this->registerMetaTag(['property' => 'og:type', 'content' => '']); 26 | //$this->registerMetaTag(['property' => 'og:image', 'content' => '']); 27 | //$this->registerMetaTag(['property' => 'og:url', 'content' => '']); 28 | //$this->registerMetaTag(['property' => 'fb:app_id', 'content' => '']); 29 | //$this->registerMetaTag(['property' => 'fb:admins', 'content' => '']); 30 | 31 | /* Twitter */ 32 | //$this->registerMetaTag(['name'=>'twitter:title', 'content'=>$this->title]); 33 | //$this->registerMetaTag(['name'=>'twitter:description', 'content'=>$description]); 34 | //$this->registerMetaTag(['name'=>'twitter:card', 'content'=>'summary']); 35 | //$this->registerMetaTag(['name'=>'twitter:site', 'content'=>'']); 36 | //$this->registerMetaTag(['name'=>'twitter:image', 'content'=>'']); 37 | //$this->registerMetaTag(['name'=>'twitter:data1', 'content'=>'']); 38 | //$this->registerMetaTag(['name'=>'twitter:label1', 'content'=>'']); 39 | //$this->registerMetaTag(['name'=>'twitter:data2', 'content'=>'']); 40 | //$this->registerMetaTag(['name'=>'twitter:label2', 'content'=>'']); 41 | 42 | /* breadcrumbs */ 43 | $this->params['breadcrumbs'][] = ['label' => 'label', 'url' => '#']; 44 | 45 | /* layout */ 46 | // Yii::$app->controller->layout = '@vendor/harrytang/yii2-theme/views/layouts/page'; 47 | ?> 48 |
49 |
50 |
51 |
52 |
53 |

title ?>

54 |
55 | 'times']), $invoice->getInvoiceUrl(), ['btn btn-box-tool']) ?> 56 |
57 |
58 |
59 |
60 |

'check-circle-o']) ?> 61 | getModule('billing')->t('Payment Authorized') ?>

62 |
63 |
64 |
65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 |
Invoiceid_invoice; ?>
Totalformatter->asCurrency($invoice->total, $invoice->currency); ?>
75 |
76 | 77 |
78 |
79 | 80 | $this->title, 'options'=>['name'=>'complete', 'class'=>'btn btn-success', 'value'=>'true']]) ?> 81 | 82 |
83 |
84 |
85 |
86 |
87 |
-------------------------------------------------------------------------------- /settings.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | use common\Core; 8 | 9 | $s = [ 10 | /* Billing */ 11 | ['key' => 'merchantName', 'value' => '', 'title' => 'Merchant Name', 'description' => 'Merchant name', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['required' => [], 'string' => []])], 12 | ['key' => 'merchantAddress', 'value' => '', 'title' => 'Merchant Address', 'description' => 'Merchant address', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['required' => [], 'string' => []])], 13 | ['key' => 'merchantCity', 'value' => '', 'title' => 'Merchant City', 'description' => 'Merchant city', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['required' => [], 'string' => []])], 14 | ['key' => 'merchantState', 'value' => '', 'title' => 'Merchant State', 'description' => 'Merchant state', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 15 | ['key' => 'merchantZip', 'value' => '', 'title' => 'Merchant Zip', 'description' => 'Merchant zip code', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 16 | ['key' => 'merchantCountry', 'value' => '', 'title' => 'Merchant Country', 'description' => 'Merchant country', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['required'=>[], 'string' => []])], 17 | ['key' => 'merchantPhone', 'value' => '', 'title' => 'Merchant Phone', 'description' => 'Merchant phone', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['required' => [], 'string' => []])], 18 | ['key' => 'merchantEmail', 'value' => '', 'title' => 'Merchant Email', 'description' => 'Merchant email', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['required' => [], 'email' => []])], 19 | 20 | /* Paypal */ 21 | ['key' => 'paypalSandbox', 'value' => '', 'title' => 'Paypal Sandbox Mode', 'description' => 'Set yes to enable sandbox', 'group' => 'Paypal', 'type' => 'dropDownList', 'data' => json_encode(Core::getYesNoOption()), 'default' => '1', 'rules' => json_encode(['required' => [], 'boolean' => []])], 22 | ['key' => 'paypalClientID', 'value' => '', 'title' => 'Paypal Client ID', 'description' => 'Live client ID', 'group' => 'Paypal', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 23 | ['key' => 'paypalSecret', 'value' => '', 'title' => 'Paypal Secret', 'description' => 'Live secret string', 'group' => 'Paypal', 'type' => 'passwordInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 24 | ['key' => 'paypalSandboxClientID', 'value' => '', 'title' => 'Paypal Sandbox Client ID', 'description' => 'Sandbox client ID', 'group' => 'Paypal', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 25 | ['key' => 'paypalSandboxSecret', 'value' => '', 'title' => 'Paypal Sandbox Secret', 'description' => 'Sandbox secret string', 'group' => 'Paypal', 'type' => 'passwordInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 26 | 27 | /* Bitcoin */ 28 | ['key' => 'btcPaymentTime', 'value' => '', 'title' => 'BTC Payment Timeout', 'description' => 'Payment timeout in seconds', 'group' => 'Bitcoin', 'type' => 'textInput', 'data' => '[]', 'default' => '900', 'rules' => json_encode(['integer' => ['min'=>300, 'max'=>3600]])], 29 | ['key' => 'btcWalletXPub', 'value' => '', 'title' => 'BTC Wallet xPub', 'description' => 'Wallet extended public key', 'group' => 'Bitcoin', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 30 | 31 | /* order API */ 32 | ['key' => 'currencyLayerAPI', 'value' => '', 'title' => 'Currency Layer API', 'description' => 'Access API key for currency conversion', 'group' => 'MISC', 'type' => 'passwordInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 33 | 34 | 35 | ]; 36 | return []; -------------------------------------------------------------------------------- /models/Item.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | namespace powerkernel\billing\models; 9 | 10 | use Yii; 11 | 12 | /** 13 | * This is the model class for Item. 14 | * 15 | * @property \MongoDB\BSON\ObjectID|string $id 16 | * @property string $id_invoice 17 | * @property string $name 18 | * @property integer $quantity 19 | * @property double $price 20 | * @property string $details 21 | * @property string $status 22 | */ 23 | class Item extends \yii\mongodb\ActiveRecord 24 | { 25 | 26 | const STATUS_NOT_SHIPPED = 'STATUS_NOT_SHIPPED'; //10; 27 | const STATUS_SHIPPED = 'STATUS_SHIPPED'; //10; 28 | const STATUS_RETURNED = 'STATUS_RETURNED'; //20; 29 | 30 | /** 31 | * @inheritdoc 32 | */ 33 | public static function collectionName() 34 | { 35 | return 'billing_item'; 36 | } 37 | 38 | /** 39 | * @return array 40 | */ 41 | public function attributes() 42 | { 43 | return [ 44 | '_id', 45 | 'id_invoice', 46 | 'name', 47 | 'quantity', 48 | 'price', 49 | 'details', 50 | 'status' 51 | ]; 52 | } 53 | 54 | /** 55 | * get id 56 | * @return \MongoDB\BSON\ObjectID|string 57 | */ 58 | public function getId() 59 | { 60 | return $this->_id; 61 | } 62 | 63 | 64 | /** 65 | * get status list 66 | * @param null $e 67 | * @return array 68 | */ 69 | public static function getStatusOption($e = null) 70 | { 71 | $option = [ 72 | self::STATUS_NOT_SHIPPED => Yii::$app->getModule('billing')->t('Not Shipped'), 73 | self::STATUS_SHIPPED => Yii::$app->getModule('billing')->t('Shipped'), 74 | self::STATUS_RETURNED => Yii::$app->getModule('billing')->t('Returned'), 75 | ]; 76 | if (is_array($e)) 77 | foreach ($e as $i) 78 | unset($option[$i]); 79 | return $option; 80 | } 81 | 82 | /** 83 | * get status text 84 | * @return string 85 | */ 86 | public function getStatusText() 87 | { 88 | $status = $this->status; 89 | $list = self::getStatusOption(); 90 | if (!empty($status) && in_array($status, array_keys($list))) { 91 | return $list[$status]; 92 | } 93 | return Yii::$app->getModule('billing')->t('Unknown'); 94 | } 95 | 96 | /** 97 | * @inheritdoc 98 | */ 99 | public function rules() 100 | { 101 | return [ 102 | ['status', 'default', 'value'=>null], 103 | [['id_invoice', 'name'], 'required'], 104 | [['quantity'], 'integer'], 105 | [['price'], 'number'], 106 | [['details'], 'string'], 107 | [['id_invoice'], 'string', 'max' => 23], 108 | [['name', 'status'], 'string', 'max' => 255], 109 | [['id_invoice'], 'exist', 'skipOnError' => true, 'targetClass' => Invoice::class, 'targetAttribute' => ['id_invoice' => 'id_invoice']], 110 | ]; 111 | } 112 | 113 | /** 114 | * @inheritdoc 115 | */ 116 | public function attributeLabels() 117 | { 118 | return [ 119 | 'id' => Yii::$app->getModule('billing')->t('ID'), 120 | 'id_invoice' => Yii::$app->getModule('billing')->t('Invoice'), 121 | 'name' => Yii::$app->getModule('billing')->t('Name'), 122 | 'quantity' => Yii::$app->getModule('billing')->t('Quantity'), 123 | 'price' => Yii::$app->getModule('billing')->t('Price'), 124 | 'details' => Yii::$app->getModule('billing')->t('Details'), 125 | 'status' => Yii::$app->getModule('billing')->t('Status'), 126 | ]; 127 | } 128 | 129 | /** 130 | * @return \yii\db\ActiveQueryInterface 131 | */ 132 | public function getInvoice() 133 | { 134 | return $this->hasOne(Invoice::class, ['id_invoice' => 'id_invoice']); 135 | } 136 | 137 | /** 138 | * @inheritdoc 139 | * @param bool $insert 140 | * @param array $changedAttributes 141 | */ 142 | public function afterSave($insert, $changedAttributes) 143 | { 144 | $this->invoice->calculate(); 145 | $this->invoice->save(false); 146 | parent::afterSave($insert, $changedAttributes); // TODO: Change the autogenerated stub 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /models/AddressSearch.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | 9 | namespace powerkernel\billing\models; 10 | 11 | use common\Core; 12 | use common\models\Account; 13 | use Yii; 14 | use yii\base\Model; 15 | use yii\data\ActiveDataProvider; 16 | 17 | /** 18 | * AddressSearch represents the model behind the search form about `powerkernel\billing\models\Address`. 19 | */ 20 | class AddressSearch extends Address 21 | { 22 | /** 23 | * @inheritdoc 24 | */ 25 | public function rules() 26 | { 27 | return [ 28 | [['id_account', 'country', 'contact_name', 'street_address_1', 'street_address_2', 'city', 'state', 'zip_code', 'phone', 'status', 'created_at', 'updated_at'], 'safe'], 29 | ]; 30 | } 31 | 32 | /** 33 | * @inheritdoc 34 | */ 35 | public function scenarios() 36 | { 37 | // bypass scenarios() implementation in the parent class 38 | return Model::scenarios(); 39 | } 40 | 41 | /** 42 | * Creates data provider instance with search query applied 43 | * 44 | * @param array $params 45 | * 46 | * @return ActiveDataProvider 47 | */ 48 | public function search($params) 49 | { 50 | $query = Address::find(); 51 | $pageSize = 20; 52 | if (Core::checkMCA('billing', 'address', 'manage')) { 53 | $pageSize = 9; 54 | } 55 | 56 | // add conditions that should always apply here 57 | 58 | 59 | $dataProvider = new ActiveDataProvider([ 60 | 'query' => $query, 61 | //'sort'=> ['defaultOrder' => ['id'=>SORT_DESC]], 62 | 'pagination' => ['pageSize' => $pageSize], 63 | ]); 64 | 65 | $this->load($params); 66 | 67 | /* manage action */ 68 | if (Core::checkMCA('billing', 'address', 'manage')) { 69 | $query->andFilterWhere(['like', 'id_account', Yii::$app->user->id]); 70 | } 71 | 72 | if (!$this->validate()) { 73 | // uncomment the following line if you do not want to return any records when validation fails 74 | // $query->where('0=1'); 75 | return $dataProvider; 76 | } 77 | 78 | // account 79 | if (!in_array($this->id_account, ['', null], true)) { 80 | $key = '_id'; 81 | $ids = []; 82 | $owners = Account::find()->select([$key])->where(['like', 'fullname', $this->id_account])->asArray()->all(); 83 | foreach ($owners as $owner) { 84 | $ids[] = (string)$owner[$key]; 85 | } 86 | if (empty($ids)) { 87 | $ids[] = '0'; 88 | } 89 | $query->andFilterWhere(['id_account' => $ids]); 90 | } 91 | 92 | // grid filtering conditions 93 | $query->andFilterWhere(['like', '_id', $this->_id]) 94 | //->andFilterWhere(['like', 'id_account', $this->id_account]) 95 | ->andFilterWhere(['like', 'country', $this->country]) 96 | ->andFilterWhere(['like', 'contact_name', $this->contact_name]) 97 | ->andFilterWhere(['like', 'street_address_1', $this->street_address_1]) 98 | ->andFilterWhere(['like', 'street_address_2', $this->street_address_2]) 99 | ->andFilterWhere(['like', 'city', $this->city]) 100 | ->andFilterWhere(['like', 'state', $this->state]) 101 | ->andFilterWhere(['like', 'zip_code', $this->zip_code]) 102 | ->andFilterWhere(['like', 'phone', $this->phone]) 103 | ->andFilterWhere(['like', 'status', $this->status]) 104 | ->andFilterWhere(['like', 'created_at', $this->created_at]) 105 | ->andFilterWhere(['like', 'updated_at', $this->updated_at]); 106 | 107 | //if(!empty($this->created_at)){ 108 | // $query->andFilterWhere([ 109 | // 'DATE(CONVERT_TZ(FROM_UNIXTIME(`created_at`), :UTC, :ATZ))' => $this->created_at, 110 | // ])->params([ 111 | // ':UTC'=>'+00:00', 112 | // ':ATZ'=>date('P') 113 | // ]); 114 | //} 115 | 116 | //if(!empty($this->created_by)) { 117 | // $owners=Account::find()->select('id')->where(['like', 'fullname', $this->created_by])->asArray()->all(); 118 | // $ids = [0]; 119 | // foreach ($owners as $owner) { 120 | // $ids[] = (integer)$owner['id']; 121 | // } 122 | // $query->andFilterWhere(['created_by' => $ids]); 123 | //} 124 | 125 | return $dataProvider; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /mail/new-invoice-admin-html.php: -------------------------------------------------------------------------------- 1 | 7 |
8 |
9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 | 17 | 18 | 66 | 67 | 68 |
19 |
20 | 21 | 22 | 60 | 61 |
23 | 24 | 25 | 26 | 29 | 30 | 31 | 34 | 35 | 36 | 56 | 57 | 58 |
27 | getModule('billing')->t('Greetings from {APP},', ['APP'=>Yii::$app->name]) ?> 28 |
32 | getModule('billing')->t('A new order has just been placed') ?> 33 |
37 | 38 | 39 | 42 | 43 | 44 | 53 | 54 |
40 | getModule('billing')->t('Invoice #{ID}', ['ID'=>$model->id_invoice]) ?>
41 |
45 | 46 | 47 | 50 | 51 |
48 | getModule('billing')->t('View Invoice') ?> 49 |
52 |
55 |
59 |
62 | 63 |
64 | 65 |
69 | -------------------------------------------------------------------------------- /mail/cancel-invoice-html.php: -------------------------------------------------------------------------------- 1 | 7 |
8 |
9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 | 17 | 18 | 66 | 67 | 68 |
19 |
20 | 21 | 22 | 60 | 61 |
23 | 24 | 25 | 26 | 29 | 30 | 31 | 34 | 35 | 36 | 39 | 40 | 41 | 56 | 57 | 58 |
27 | getModule('billing')->t('Hi {USER},', ['USER' => $model->account->fullname]) ?> 28 |
32 | getModule('billing')->t('We\'re sorry to let you know that your invoice has been canceled. You don\'t need to do anything else.') ?> 33 |
37 | getModule('billing')->t('Invoice #{ID}', ['ID'=>$model->id_invoice]) ?>
38 |
42 | 43 | 44 | 53 | 54 |
45 | 46 | 47 | 50 | 51 |
48 | getModule('billing')->t('View Invoice') ?> 49 |
52 |
55 |
59 |
62 | 63 |
64 | 65 |
69 | -------------------------------------------------------------------------------- /mail/src/new-invoice-user-html.php: -------------------------------------------------------------------------------- 1 | 7 |
8 |
9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 | 17 | 18 | 77 | 78 | 79 |
19 |
20 | 21 | 22 | 71 | 72 |
23 | 24 | 25 | 26 | 29 | 30 | 31 | 34 | 35 | 36 | 67 | 68 | 69 |
27 | getModule('billing')->t('Dear {NAME},', ['NAME'=>$model->account->fullname]) ?> 28 |
32 | getModule('billing')->t('Thank you for placing your order with us! Your total amount due is {TOTAL}. More details about your purchase are included below.', ['TOTAL'=>Yii::$app->formatter->asCurrency($model->total, $model->currency)]) ?> 33 |
37 | 38 | 39 | 42 | 43 | items as $item):?> 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 64 | 65 |
40 | getModule('billing')->t('Invoice #{ID}', ['ID'=>$model->id_invoice]) ?>
41 |
46 | getModule('billing')->t(' - {ITEM}: {PRICE}', ['ITEM'=>$item->name, 'PRICE'=>Yii::$app->formatter->asCurrency($item->price, $model->currency)]) ?> 47 |
getModule('billing')->t('Shipping: {SHIP}', ['SHIP' => Yii::$app->formatter->asCurrency($model->shipping, $model->currency)]) ?>
getModule('billing')->t('Tax: {TAX}', ['TAX' => Yii::$app->formatter->asCurrency($model->tax, $model->currency)]) ?>
getModule('billing')->t('Total: {TOTAL}', ['TOTAL' => Yii::$app->formatter->asCurrency($model->total, $model->currency)]) ?>
56 | 57 | 58 | 61 | 62 |
59 | getModule('billing')->t('Pay Now') ?> 60 |
63 |
66 |
70 |
73 | 74 |
75 | 76 |
80 | -------------------------------------------------------------------------------- /mail/src/css/mailgun.css: -------------------------------------------------------------------------------- 1 | /* Let's make sure all tables have defaults */ 2 | table td { 3 | vertical-align: top; 4 | } 5 | 6 | /* ------------------------------------- 7 | BODY & CONTAINER 8 | ------------------------------------- */ 9 | 10 | 11 | .body-wrap { 12 | background-color: #f6f6f6; 13 | width: 100%; 14 | } 15 | 16 | .container { 17 | display: block !important; 18 | max-width: 600px !important; 19 | margin: 0 auto !important; 20 | /* makes it centered */ 21 | clear: both !important; 22 | } 23 | 24 | .content { 25 | max-width: 600px; 26 | margin: 0 auto; 27 | display: block; 28 | padding: 20px; 29 | } 30 | 31 | /* ------------------------------------- 32 | HEADER, FOOTER, MAIN 33 | ------------------------------------- */ 34 | .main { 35 | background-color: #fff; 36 | border: 1px solid #e9e9e9; 37 | border-radius: 3px; 38 | } 39 | 40 | .content-wrap { 41 | padding: 20px; 42 | } 43 | 44 | .content-block { 45 | padding: 0 0 20px; 46 | } 47 | 48 | .header { 49 | width: 100%; 50 | margin-bottom: 20px; 51 | } 52 | 53 | .footer { 54 | width: 100%; 55 | clear: both; 56 | color: #999; 57 | padding: 20px; 58 | } 59 | .footer p, .footer a, .footer td { 60 | color: #999; 61 | font-size: 12px; 62 | } 63 | 64 | /* ------------------------------------- 65 | TYPOGRAPHY 66 | ------------------------------------- */ 67 | h1, h2, h3 { 68 | font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; 69 | color: #000; 70 | margin: 40px 0 0; 71 | line-height: 1.2em; 72 | font-weight: 400; 73 | } 74 | 75 | h1 { 76 | font-size: 32px; 77 | font-weight: 500; 78 | /* 1.2em * 32px = 38.4px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ 79 | /*line-height: 38px;*/ 80 | } 81 | 82 | h2 { 83 | font-size: 24px; 84 | /* 1.2em * 24px = 28.8px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ 85 | /*line-height: 29px;*/ 86 | } 87 | 88 | h3 { 89 | font-size: 18px; 90 | /* 1.2em * 18px = 21.6px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ 91 | /*line-height: 22px;*/ 92 | } 93 | 94 | h4 { 95 | font-size: 14px; 96 | font-weight: 600; 97 | } 98 | 99 | p, ul, ol { 100 | margin-bottom: 10px; 101 | font-weight: normal; 102 | } 103 | p li, ul li, ol li { 104 | margin-left: 5px; 105 | list-style-position: inside; 106 | } 107 | 108 | /* ------------------------------------- 109 | LINKS & BUTTONS 110 | ------------------------------------- */ 111 | a { 112 | color: #348eda; 113 | text-decoration: underline; 114 | } 115 | 116 | .btn-primary { 117 | text-decoration: none; 118 | color: #FFF; 119 | background-color: #348eda; 120 | border: solid #348eda; 121 | border-width: 10px 20px; 122 | line-height: 2em; 123 | /* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ 124 | /*line-height: 28px;*/ 125 | font-weight: bold; 126 | text-align: center; 127 | cursor: pointer; 128 | display: inline-block; 129 | border-radius: 5px; 130 | text-transform: capitalize; 131 | } 132 | 133 | /* ------------------------------------- 134 | OTHER STYLES THAT MIGHT BE USEFUL 135 | ------------------------------------- */ 136 | .last { 137 | margin-bottom: 0; 138 | } 139 | 140 | .first { 141 | margin-top: 0; 142 | } 143 | 144 | .aligncenter { 145 | text-align: center; 146 | } 147 | 148 | .alignright { 149 | text-align: right; 150 | } 151 | 152 | .alignleft { 153 | text-align: left; 154 | } 155 | 156 | .clear { 157 | clear: both; 158 | } 159 | 160 | /* ------------------------------------- 161 | ALERTS 162 | Change the class depending on warning email, good email or bad email 163 | ------------------------------------- */ 164 | .alert { 165 | font-size: 16px; 166 | color: #fff; 167 | font-weight: 500; 168 | padding: 20px; 169 | text-align: center; 170 | border-radius: 3px 3px 0 0; 171 | } 172 | .alert a { 173 | color: #fff; 174 | text-decoration: none; 175 | font-weight: 500; 176 | font-size: 16px; 177 | } 178 | .alert.alert-warning { 179 | background-color: #FF9F00; 180 | } 181 | .alert.alert-bad { 182 | background-color: #D0021B; 183 | } 184 | .alert.alert-good { 185 | background-color: #68B90F; 186 | } 187 | 188 | /* ------------------------------------- 189 | INVOICE 190 | Styles for the billing table 191 | ------------------------------------- */ 192 | .invoice { 193 | margin: 20px auto; 194 | text-align: left; 195 | width: 100%; 196 | } 197 | .invoice td { 198 | padding: 5px 0; 199 | } 200 | .invoice .invoice-items { 201 | width: 100%; 202 | } 203 | .invoice .invoice-items td { 204 | border-top: #eee 1px solid; 205 | } 206 | .invoice .invoice-items .total td { 207 | border-top: 2px solid #333; 208 | border-bottom: 2px solid #333; 209 | font-weight: 700; 210 | } 211 | -------------------------------------------------------------------------------- /models/Bank.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | namespace powerkernel\billing\models; 9 | 10 | use common\behaviors\UTCDateTimeBehavior; 11 | use Yii; 12 | 13 | /** 14 | * This is the model class for Bank. 15 | * 16 | * @property \MongoDB\BSON\ObjectID|string $id 17 | * @property string $country 18 | * @property string $title 19 | * @property string $info 20 | * @property string $currency 21 | * @property string $status 22 | * @property \MongoDB\BSON\UTCDateTime $created_at 23 | * @property \MongoDB\BSON\UTCDateTime $updated_at 24 | */ 25 | class Bank extends \yii\mongodb\ActiveRecord 26 | { 27 | 28 | const STATUS_ACTIVE = 'STATUS_ACTIVE'; //10 29 | const STATUS_INACTIVE = 'STATUS_INACTIVE'; //20 30 | 31 | /** 32 | * @inheritdoc 33 | */ 34 | public static function collectionName() 35 | { 36 | return 'billing_bank'; 37 | } 38 | 39 | /** 40 | * @return array 41 | */ 42 | public function attributes() 43 | { 44 | return [ 45 | '_id', 46 | 'country', 47 | 'title', 48 | 'info', 49 | 'currency', 50 | 'status', 51 | 'created_at', 52 | 'updated_at', 53 | ]; 54 | } 55 | 56 | /** 57 | * get id 58 | * @return \MongoDB\BSON\ObjectID|string 59 | */ 60 | public function getId() 61 | { 62 | return $this->_id; 63 | } 64 | 65 | /** 66 | * @inheritdoc 67 | */ 68 | public function behaviors() 69 | { 70 | return [ 71 | UTCDateTimeBehavior::class, 72 | ]; 73 | } 74 | 75 | /** 76 | * @return int timestamp 77 | */ 78 | public function getUpdatedAt() 79 | { 80 | return $this->updated_at->toDateTime()->format('U'); 81 | } 82 | 83 | /** 84 | * @return int timestamp 85 | */ 86 | public function getCreatedAt() 87 | { 88 | return $this->created_at->toDateTime()->format('U'); 89 | } 90 | 91 | /** 92 | * get status list 93 | * @param null $e 94 | * @return array 95 | */ 96 | public static function getStatusOption($e = null) 97 | { 98 | $option = [ 99 | self::STATUS_ACTIVE => Yii::$app->getModule('billing')->t('Active'), 100 | self::STATUS_INACTIVE => Yii::$app->getModule('billing')->t('Inactive'), 101 | ]; 102 | if (is_array($e)) 103 | foreach ($e as $i) 104 | unset($option[$i]); 105 | return $option; 106 | } 107 | 108 | /** 109 | * get status text 110 | * @return string 111 | */ 112 | public function getStatusText() 113 | { 114 | $status = $this->status; 115 | $list = self::getStatusOption(); 116 | if (!empty($status) && in_array($status, array_keys($list))) { 117 | return $list[$status]; 118 | } 119 | return Yii::$app->getModule('billing')->t('Unknown'); 120 | } 121 | 122 | 123 | /** 124 | * color status text 125 | * @return mixed|string 126 | */ 127 | public function getStatusColorText() 128 | { 129 | $status = $this->status; 130 | if ($status == self::STATUS_ACTIVE) { 131 | return '' . $this->statusText . ''; 132 | } 133 | if ($status == self::STATUS_INACTIVE) { 134 | return '' . $this->statusText . ''; 135 | } 136 | return $this->statusText; 137 | } 138 | 139 | 140 | /** 141 | * @inheritdoc 142 | */ 143 | public function rules() 144 | { 145 | return [ 146 | [['country', 'title', 'info', 'currency'], 'required'], 147 | [['info', 'status'], 'string'], 148 | [['country'], 'string', 'max' => 2], 149 | [['title'], 'string', 'max' => 255], 150 | [['currency'], 'string', 'max' => 3], 151 | [['created_at', 'updated_at'], 'yii\mongodb\validators\MongoDateValidator'] 152 | ]; 153 | } 154 | 155 | /** 156 | * @inheritdoc 157 | */ 158 | public function attributeLabels() 159 | { 160 | return [ 161 | 'id' => Yii::$app->getModule('billing')->t('ID'), 162 | 'country' => Yii::$app->getModule('billing')->t('Country'), 163 | 'title' => Yii::$app->getModule('billing')->t('Title'), 164 | 'info' => Yii::$app->getModule('billing')->t('Info'), 165 | 'currency' => Yii::$app->getModule('billing')->t('Currency'), 166 | 'status' => Yii::$app->getModule('billing')->t('Status'), 167 | 'created_at' => Yii::$app->getModule('billing')->t('Created At'), 168 | 'updated_at' => Yii::$app->getModule('billing')->t('Updated At'), 169 | ]; 170 | } 171 | 172 | 173 | } 174 | -------------------------------------------------------------------------------- /views/coupon/index.php: -------------------------------------------------------------------------------- 1 | params['breadcrumbs'][] = $this->title; 16 | 17 | /* misc */ 18 | $this->registerJs('$(document).on("pjax:send", function(){ $(".grid-view-overlay").removeClass("hidden");});$(document).on("pjax:complete", function(){ $(".grid-view-overlay").addClass("hidden");})'); 19 | //$js=file_get_contents(__DIR__.'/index.min.js'); 20 | //$this->registerJs($js); 21 | //$css=file_get_contents(__DIR__.'/index.css'); 22 | //$this->registerCss($css); 23 | ?> 24 |
25 |
26 |
27 | 28 | 29 | render('_search', ['model' => $searchModel]); ?> 30 | 31 | 32 | 33 |
34 | $dataProvider, 36 | 'filterModel' => $searchModel, 37 | 'columns' => [ 38 | ['class' => 'yii\grid\SerialColumn'], 39 | 'code', 40 | [ 41 | 'attribute' => 'discount', 42 | 'value' => function ($model) { 43 | return $model->discount_type == Coupon::DISCOUNT_TYPE_PERCENT ? Yii::$app->formatter->asPercent($model->discount / 100) : Yii::$app->formatter->asCurrency($model->discount, $model->currency); 44 | } 45 | ], 46 | [ 47 | 'attribute' => 'begin_at', 48 | 'value' => 'beginAt', 49 | 'format' => 'date', 50 | 'filter' => DatePicker::widget(['model' => $searchModel, 'attribute' => 'begin_at', 'dateFormat' => 'yyyy-MM-dd', 'options' => ['class' => 'form-control']]), 51 | 'contentOptions' => ['style' => 'min-width: 80px'] 52 | ], 53 | [ 54 | 'attribute' => 'end_at', 55 | 'value' => 'endAt', 56 | 'format' => 'date', 57 | 'filter' => DatePicker::widget(['model' => $searchModel, 'attribute' => 'end_at', 'dateFormat' => 'yyyy-MM-dd', 'options' => ['class' => 'form-control']]), 58 | 'contentOptions' => ['style' => 'min-width: 80px'] 59 | ], 60 | [ 61 | 'attribute' => 'quantity', 62 | 'value' => function ($model) { 63 | return $model->quantity == -1 ? Yii::$app->getModule('billing')->t('Unlimited') : Yii::$app->formatter->asDecimal($model->quantity); 64 | }, 65 | ], 66 | //'', 67 | // 'reuse', 68 | // 'status', 69 | // 'created_at', 70 | // 'updated_at', 71 | //[ 72 | // 'attribute' => 'created_at', 73 | // 'value' => 'created_at', 74 | // 'format' => 'dateTime', 75 | // 'filter' => DatePicker::widget(['model' => $searchModel, 'attribute' => 'created_at', 'dateFormat' => 'yyyy-MM-dd', 'options' => ['class' => 'form-control']]), 76 | // 'contentOptions'=>['style'=>'min-width: 80px'] 77 | //], 78 | ['attribute' => 'status', 'value' => function ($model) { 79 | return $model->statusColorText; 80 | }, 'filter' => Coupon::getStatusOption(), 'format' => 'raw'], 81 | [ 82 | 'class' => 'yii\grid\ActionColumn', 83 | 'contentOptions' => ['style' => 'min-width: 70px'] 84 | ], 85 | ], 86 | ]); ?> 87 |
88 | 89 |

90 | 'btn btn-success']) ?> 91 |

92 | 93 |
94 | 95 | 98 | 99 |
100 |
101 | -------------------------------------------------------------------------------- /views/bitcoin/payment.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | use powerkernel\fontawesome\Icon; 8 | use yii\bootstrap\Tabs; 9 | use yii\helpers\Html; 10 | \common\plugins\moment\MomentAsset::register($this); 11 | 12 | /* @var $this yii\web\View */ 13 | /* @var $invoice \powerkernel\billing\models\Invoice */ 14 | /* @var $bitcoin [] */ 15 | 16 | $this->params['breadcrumbs'][] = $this->title; 17 | 18 | /* misc */ 19 | $js = file_get_contents(__DIR__ . '/payment.min.js'); 20 | $this->registerJs($js); 21 | //$css=file_get_contents(__DIR__.'/index.css'); 22 | //$this->registerCss($css); 23 | ?> 24 |
25 |
26 |
27 |
28 |
29 |

title ?>

30 |
31 | 'times']), $invoice->getInvoiceUrl(), ['btn btn-box-tool']) ?> 32 |
33 |
34 |
35 |
36 |
37 | getModule('billing')->t('Pay for invoice #{INVOICE}', ['INVOICE' => $invoice->id_invoice]) ?> 38 |
39 |
40 |
formatter->asDecimal($invoice->total) ?> currency ?>
41 |
BTC
42 |
43 |
44 | 45 |
46 | 'refresh fa-spin']) ?> 47 | getModule('billing')->t('Waiting payment...') ?> 48 |
49 |
50 | 51 | 60 | 61 | 62 | 63 |
64 |
65 | ['class' => 'nav nav-tabs nav-justified'], 68 | 'items' => [ 69 | [ 70 | 71 | 'active' => true, 72 | 'label' => Yii::$app->getModule('billing')->t('Scan'), 73 | 'content' => $this->render('_scan', ['bitcoin' => $bitcoin]) 74 | 75 | ], 76 | [ 77 | 78 | 'label' => Yii::$app->getModule('billing')->t('Copy'), 79 | 'content' => $this->render('_copy', ['bitcoin' => $bitcoin]) 80 | ], 81 | 82 | ], 83 | ]); 84 | ?> 85 |
86 | 87 |
88 | getModule('billing')->t('Open in Wallet').' '. Icon::widget(['icon'=>'external-link']), $bitcoin['url'], ['title' => Yii::$app->getModule('billing')->t('Open in Wallet'), 'class' => 'btn btn-lg btn-primary']) ?> 89 |
90 |
91 | 92 | 95 | 96 |
97 |
98 |
99 |
100 | 101 |
102 | 103 | 104 | -------------------------------------------------------------------------------- /mail/new-invoice-user-html.php: -------------------------------------------------------------------------------- 1 | 7 |
8 |
9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 | 17 | 18 | 77 | 78 | 79 |
19 |
20 | 21 | 22 | 71 | 72 |
23 | 24 | 25 | 26 | 29 | 30 | 31 | 34 | 35 | 36 | 67 | 68 | 69 |
27 | getModule('billing')->t('Dear {NAME},', ['NAME'=>$model->account->fullname]) ?> 28 |
32 | getModule('billing')->t('Thank you for placing your order with us! Your total amount due is {TOTAL}. More details about your purchase are included below.', ['TOTAL'=>Yii::$app->formatter->asCurrency($model->total, $model->currency)]) ?> 33 |
37 | 38 | 39 | 42 | 43 | items as $item):?> 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 64 | 65 |
40 | getModule('billing')->t('Invoice #{ID}', ['ID'=>$model->id_invoice]) ?>
41 |
46 | getModule('billing')->t(' - {ITEM}: {PRICE}', ['ITEM'=>$item->name, 'PRICE'=>Yii::$app->formatter->asCurrency($item->price, $model->currency)]) ?> 47 |
getModule('billing')->t('Shipping: {SHIP}', ['SHIP' => Yii::$app->formatter->asCurrency($model->shipping, $model->currency)]) ?>
getModule('billing')->t('Tax: {TAX}', ['TAX' => Yii::$app->formatter->asCurrency($model->tax, $model->currency)]) ?>
getModule('billing')->t('Total: {TOTAL}', ['TOTAL' => Yii::$app->formatter->asCurrency($model->total, $model->currency)]) ?>
56 | 57 | 58 | 61 | 62 |
59 | getModule('billing')->t('Pay Now') ?> 60 |
63 |
66 |
70 |
73 | 74 |
75 | 76 |
80 | -------------------------------------------------------------------------------- /models/Address.php: -------------------------------------------------------------------------------- 1 | _id; 69 | } 70 | 71 | /** 72 | * @inheritdoc 73 | */ 74 | public function behaviors() 75 | { 76 | return [ 77 | UTCDateTimeBehavior::class, 78 | ]; 79 | } 80 | 81 | /** 82 | * @return int timestamp 83 | */ 84 | public function getUpdatedAt() 85 | { 86 | return $this->updated_at->toDateTime()->format('U'); 87 | } 88 | 89 | /** 90 | * @return int timestamp 91 | */ 92 | public function getCreatedAt() 93 | { 94 | return $this->created_at->toDateTime()->format('U'); 95 | } 96 | 97 | /** 98 | * get status list 99 | * @param null $e 100 | * @return array 101 | */ 102 | public static function getStatusOption($e = null) 103 | { 104 | $option = [ 105 | self::STATUS_ACTIVE => Yii::$app->getModule('billing')->t('Active'), 106 | self::STATUS_INACTIVE => Yii::$app->getModule('billing')->t('Inactive'), 107 | ]; 108 | if (is_array($e)) 109 | foreach ($e as $i) 110 | unset($option[$i]); 111 | return $option; 112 | } 113 | 114 | /** 115 | * get status text 116 | * @return string 117 | */ 118 | public function getStatusText() 119 | { 120 | $status = $this->status; 121 | $list = self::getStatusOption(); 122 | if (!empty($status) && in_array($status, array_keys($list))) { 123 | return $list[$status]; 124 | } 125 | return Yii::$app->getModule('billing')->t('Unknown'); 126 | } 127 | 128 | 129 | /** 130 | * color status text 131 | * @return mixed|string 132 | */ 133 | public function getStatusColorText() 134 | { 135 | $status = $this->status; 136 | if ($status == self::STATUS_ACTIVE) { 137 | return '' . $this->statusText . ''; 138 | } 139 | if ($status == self::STATUS_INACTIVE) { 140 | return '' . $this->statusText . ''; 141 | } 142 | return $this->statusText; 143 | } 144 | 145 | 146 | /** 147 | * @inheritdoc 148 | */ 149 | public function rules() 150 | { 151 | return [ 152 | [['street_address_2', 'zip_code'], 'default', 'value' => null], 153 | [['country', 'contact_name', 'street_address_1', 'city', 'phone'], 'required'], 154 | 155 | [['country', 'contact_name', 'street_address_1', 'street_address_2', 'city', 'state', 'zip_code', 'phone', 'status'], 'string'], 156 | [['street_address_2', 'zip_code', 'state'], 'safe'], 157 | [['created_at', 'updated_at'], 'yii\mongodb\validators\MongoDateValidator'], 158 | [['id_account'], 'string'] 159 | ]; 160 | } 161 | 162 | /** 163 | * @inheritdoc 164 | */ 165 | public function attributeLabels() 166 | { 167 | return [ 168 | '_id' => 'ID', 169 | 'id_account' => Yii::$app->getModule('billing')->t('Account'), 170 | 'country' => Yii::$app->getModule('billing')->t('Country'), 171 | 'contact_name' => Yii::$app->getModule('billing')->t('Contact Name'), 172 | 'street_address_1' => Yii::$app->getModule('billing')->t('Street Address 1'), 173 | 'street_address_2' => Yii::$app->getModule('billing')->t('Street Address 2'), 174 | 'city' => Yii::$app->getModule('billing')->t('City'), 175 | 'state' => Yii::$app->getModule('billing')->t('State'), 176 | 'zip_code' => Yii::$app->getModule('billing')->t('Zip Code'), 177 | 'phone' => Yii::$app->getModule('billing')->t('Phone'), 178 | 'status' => Yii::$app->getModule('billing')->t('Status'), 179 | 'created_at' => Yii::$app->getModule('billing')->t('Created At'), 180 | 'updated_at' => Yii::$app->getModule('billing')->t('Updated At'), 181 | ]; 182 | } 183 | 184 | /** 185 | * @inheritdoc 186 | * @param bool $insert 187 | * @return bool 188 | */ 189 | public function beforeSave($insert) 190 | { 191 | if ($insert) { 192 | if (empty($this->id_account)) { 193 | $this->id_account = Yii::$app->user->id; 194 | } 195 | } 196 | return parent::beforeSave($insert); // TODO: Change the autogenerated stub 197 | } 198 | 199 | /** 200 | * @return \yii\db\ActiveQueryInterface 201 | */ 202 | public function getAccount() 203 | { 204 | return $this->hasOne(Account::class, ['_id' => 'id_account']); 205 | } 206 | 207 | /** 208 | * get user shipping addresses 209 | * @param $id_account 210 | * @return array 211 | */ 212 | public static function getAddressDataList($id_account) 213 | { 214 | $addresses = self::find()->where(['id_account' => $id_account])->all(); 215 | $data = []; 216 | foreach ($addresses as $address) { 217 | $data[(string)$address->id] = '' . $address->contact_name . ''; 218 | $data[(string)$address->id] .= '
' . $address->street_address_1; 219 | if (!empty($address->street_address_2)) { 220 | $data[(string)$address->id] .= '
' . $address->street_address_2; 221 | } 222 | $data[(string)$address->id] .= '
' . $address->city . ', ' . $address->state . ' ' . $address->zip_code; 223 | $data[(string)$address->id] .= '
' . Core::getCountryText($address->country); 224 | $data[(string)$address->id] .= '
' . $address->phone; 225 | } 226 | return $data; 227 | } 228 | 229 | } 230 | -------------------------------------------------------------------------------- /models/Coupon.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | namespace powerkernel\billing\models; 9 | 10 | use common\behaviors\UTCDateTimeBehavior; 11 | use Yii; 12 | 13 | /** 14 | * This is the model class for Coupon. 15 | * 16 | * @property \MongoDB\BSON\ObjectID|string $id 17 | * @property string $code 18 | * @property string $currency 19 | * @property float $discount 20 | * @property string $discount_type 21 | * @property \MongoDB\BSON\UTCDateTime $begin_at 22 | * @property \MongoDB\BSON\UTCDateTime $end_at 23 | * @property integer $quantity 24 | * @property integer $reuse 25 | * @property string $status 26 | * @property \MongoDB\BSON\UTCDateTime $created_at 27 | * @property \MongoDB\BSON\UTCDateTime $updated_at 28 | */ 29 | class Coupon extends \yii\mongodb\ActiveRecord 30 | { 31 | const STATUS_ACTIVE = 'STATUS_ACTIVE'; // 10; 32 | const STATUS_INACTIVE = 'STATUS_INACTIVE'; // 20; 33 | 34 | const DISCOUNT_TYPE_PERCENT = 'DISCOUNT_TYPE_PERCENT'; // 10; 35 | const DISCOUNT_TYPE_VALUE = 'DISCOUNT_TYPE_VALUE'; // 20 36 | 37 | public $begin_date_picker; 38 | public $end_date_picker; 39 | 40 | /** 41 | * @inheritdoc 42 | */ 43 | public static function collectionName() 44 | { 45 | return 'billing_coupon'; 46 | } 47 | 48 | /** 49 | * @return array 50 | */ 51 | public function attributes() 52 | { 53 | return [ 54 | '_id', 55 | 'code', 56 | 'currency', 57 | 'discount', 58 | 'discount_type', 59 | 'begin_at', 60 | 'end_at', 61 | 'quantity', 62 | 'reuse', 63 | 'status', 64 | 'created_at', 65 | 'updated_at', 66 | ]; 67 | } 68 | 69 | /** 70 | * get id 71 | * @return \MongoDB\BSON\ObjectID|string 72 | */ 73 | public function getId() 74 | { 75 | return $this->_id; 76 | } 77 | 78 | /** 79 | * @inheritdoc 80 | */ 81 | public function behaviors() 82 | { 83 | return [ 84 | UTCDateTimeBehavior::class, 85 | ]; 86 | } 87 | 88 | /** 89 | * @return int timestamp 90 | */ 91 | public function getUpdatedAt() 92 | { 93 | return $this->updated_at->toDateTime()->format('U'); 94 | } 95 | 96 | /** 97 | * @return int timestamp 98 | */ 99 | public function getCreatedAt() 100 | { 101 | return $this->created_at->toDateTime()->format('U'); 102 | } 103 | 104 | /** 105 | * @return int timestamp 106 | */ 107 | public function getBeginAt() 108 | { 109 | return $this->begin_at->toDateTime()->format('U'); 110 | } 111 | 112 | /** 113 | * @return int timestamp 114 | */ 115 | public function getEndAt() 116 | { 117 | return $this->end_at->toDateTime()->format('U'); 118 | } 119 | 120 | /** 121 | * get discount options 122 | * @param null $e 123 | * @return array 124 | */ 125 | public static function getDiscountOption($e = null) 126 | { 127 | $option = [ 128 | self::DISCOUNT_TYPE_PERCENT => Yii::$app->getModule('billing')->t('Percent'), 129 | self::DISCOUNT_TYPE_VALUE => Yii::$app->getModule('billing')->t('Value'), 130 | ]; 131 | if (is_array($e)) 132 | foreach ($e as $i) 133 | unset($option[$i]); 134 | return $option; 135 | } 136 | 137 | /** 138 | * get discount text 139 | * @return mixed 140 | */ 141 | public function getDiscountText() 142 | { 143 | $type = $this->discount_type; 144 | $list = self::getDiscountOption(); 145 | if (!empty($type) && in_array($type, array_keys($list))) { 146 | return $list[$type]; 147 | } 148 | return Yii::$app->getModule('billing')->t('Unknown'); 149 | } 150 | 151 | /** 152 | * get status list 153 | * @param null $e 154 | * @return array 155 | */ 156 | public static function getStatusOption($e = null) 157 | { 158 | $option = [ 159 | self::STATUS_ACTIVE => Yii::$app->getModule('billing')->t('Active'), 160 | self::STATUS_INACTIVE => Yii::$app->getModule('billing')->t('Inactive'), 161 | ]; 162 | if (is_array($e)) 163 | foreach ($e as $i) 164 | unset($option[$i]); 165 | return $option; 166 | } 167 | 168 | /** 169 | * get status text 170 | * @return string 171 | */ 172 | public function getStatusText() 173 | { 174 | $status = $this->status; 175 | $list = self::getStatusOption(); 176 | if (!empty($status) && in_array($status, array_keys($list))) { 177 | return $list[$status]; 178 | } 179 | return Yii::$app->getModule('billing')->t('Unknown'); 180 | } 181 | 182 | /** 183 | * get status color text 184 | * @return string 185 | */ 186 | public function getStatusColorText() 187 | { 188 | $status = $this->status; 189 | $list = self::getStatusOption(); 190 | 191 | $color = 'default'; 192 | if ($status == self::STATUS_ACTIVE) { 193 | $color = 'primary'; 194 | } 195 | if ($status == self::STATUS_INACTIVE) { 196 | $color = 'danger'; 197 | } 198 | 199 | if (!empty($status) && in_array($status, array_keys($list))) { 200 | return '' . $list[$status] . ''; 201 | } 202 | return '' . Yii::$app->getModule('billing')->t('Unknown') . ''; 203 | } 204 | 205 | 206 | /** 207 | * @inheritdoc 208 | */ 209 | public function rules() 210 | { 211 | return [ 212 | [['code', 'currency', 'discount', 'discount_type', 'begin_at', 'end_at', 'quantity', 'reuse', 'status'], 'required'], 213 | [['code'], 'unique'], 214 | //[['discount'], 'number'], 215 | [['quantity', 'reuse'], 'integer'], 216 | [['code'], 'string', 'max' => 50], 217 | [['begin_date_picker', 'end_date_picker'], 'required', 'on' => ['create']], 218 | [['end_at'], 'compare', 'compareAttribute' => 'begin_at', 'operator' => '>='], 219 | [['created_at', 'updated_at'], 'yii\mongodb\validators\MongoDateValidator'], 220 | [['begin_at'], 'yii\mongodb\validators\MongoDateValidator', 'format' => 'MM/dd/yyyy', 'mongoDateAttribute' => 'begin_at'], 221 | [['end_at'], 'yii\mongodb\validators\MongoDateValidator', 'format' => 'MM/dd/yyyy', 'mongoDateAttribute' => 'end_at'] 222 | ]; 223 | } 224 | 225 | /** 226 | * @inheritdoc 227 | */ 228 | public function attributeLabels() 229 | { 230 | return [ 231 | 'code' => Yii::$app->getModule('billing')->t('Code'), 232 | 'currency' => Yii::$app->getModule('billing')->t('Currency'), 233 | 'discount' => Yii::$app->getModule('billing')->t('Discount'), 234 | 'discount_type' => Yii::$app->getModule('billing')->t('Discount Type'), 235 | 'begin_at' => Yii::$app->getModule('billing')->t('Begin Date'), 236 | 'end_at' => Yii::$app->getModule('billing')->t('End Date'), 237 | 'quantity' => Yii::$app->getModule('billing')->t('Quantity'), 238 | 'reuse' => Yii::$app->getModule('billing')->t('Reuse'), 239 | 'status' => Yii::$app->getModule('billing')->t('Status'), 240 | 'created_at' => Yii::$app->getModule('billing')->t('Created At'), 241 | 'updated_at' => Yii::$app->getModule('billing')->t('Updated At'), 242 | /* custom */ 243 | 'begin_date_picker' => Yii::$app->getModule('billing')->t('Begin Date'), 244 | 'end_date_picker' => Yii::$app->getModule('billing')->t('End Date'), 245 | ]; 246 | } 247 | 248 | /** 249 | * @inheritdoc 250 | * @return bool 251 | */ 252 | public function beforeSave($insert) 253 | { 254 | $this->discount = (float)$this->discount; 255 | $this->quantity = (int)$this->quantity; 256 | $this->reuse = (int)$this->reuse; 257 | return parent::beforeSave($insert); // TODO: Change the autogenerated stub 258 | } 259 | 260 | 261 | } 262 | -------------------------------------------------------------------------------- /controllers/SettingController.php: -------------------------------------------------------------------------------- 1 | 4 | * @link https://powerkernel.com 5 | * @copyright Copyright (c) 2017 Power Kernel 6 | */ 7 | 8 | namespace powerkernel\billing\controllers; 9 | 10 | use backend\controllers\BackendController; 11 | use common\Core; 12 | use powerkernel\billing\models\Setting; 13 | use Yii; 14 | use yii\base\DynamicModel; 15 | 16 | /** 17 | * SettingController 18 | */ 19 | class SettingController extends BackendController 20 | { 21 | /** 22 | * setting page 23 | * @return string 24 | */ 25 | public function actionIndex(){ 26 | $attributes = Setting::loadAsArray(); 27 | $tabs=Setting::find()->asArray()->distinct('group'); 28 | 29 | $model=new DynamicModel($attributes); 30 | $settings = []; 31 | 32 | foreach ($attributes as $key=>$value) { 33 | $setting = Setting::find()->where(['key' => $key])->asArray()->one(); 34 | $settings[$setting['group']][$key] = $setting; 35 | $model->$key = $setting['value']; 36 | 37 | if (!empty($rules = json_decode($setting['rules'], true))) { 38 | foreach ($rules as $rule => $conf) { 39 | $model->addRule($key, $rule, $conf); 40 | } 41 | } else { 42 | $model->addRule($key, 'required'); 43 | } 44 | 45 | } 46 | 47 | 48 | 49 | if ($model->load(Yii::$app->request->post()) && $model->validate()) { 50 | foreach ($attributes as $key=>$oldValue) { 51 | $s = Setting::find()->where(['key'=>$key])->one(); 52 | $s->value = $model->$key; 53 | if (!$s->save(false)) { 54 | Yii::$app->session->setFlash('error', Yii::t('billing', 'Sorry, something went wrong. {ERRORS}.', ['ERRORS' => json_encode($s->errors)])); 55 | break; 56 | } 57 | } 58 | Yii::$app->session->setFlash('success', Yii::t('billing', 'Settings saved successfully.')); 59 | } 60 | 61 | 62 | return $this->render('index', [ 63 | 'model' => $model, 64 | 'settings' => $settings, 65 | 'tabs' => $tabs 66 | ]); 67 | } 68 | 69 | /** 70 | * update settings 71 | * @return \yii\web\Response 72 | */ 73 | public function actionUpdate(){ 74 | $s = [ 75 | /* Merchant */ 76 | ['key' => 'merchantName', 'value' => '', 'title' => 'Merchant Name', 'description' => 'Merchant name', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['required' => [], 'string' => []])], 77 | ['key' => 'merchantAddress', 'value' => '', 'title' => 'Merchant Address', 'description' => 'Merchant address', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['required' => [], 'string' => []])], 78 | ['key' => 'merchantCity', 'value' => '', 'title' => 'Merchant City', 'description' => 'Merchant city', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['required' => [], 'string' => []])], 79 | ['key' => 'merchantState', 'value' => '', 'title' => 'Merchant State', 'description' => 'Merchant state', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 80 | ['key' => 'merchantZip', 'value' => '', 'title' => 'Merchant Zip', 'description' => 'Merchant zip code', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 81 | ['key' => 'merchantCountry', 'value' => '', 'title' => 'Merchant Country', 'description' => 'Merchant country', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['required'=>[], 'string' => []])], 82 | ['key' => 'merchantPhone', 'value' => '', 'title' => 'Merchant Phone', 'description' => 'Merchant phone', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['required' => [], 'string' => []])], 83 | ['key' => 'merchantEmail', 'value' => '', 'title' => 'Merchant Email', 'description' => 'Merchant email', 'group' => 'Merchant', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['required' => [], 'email' => []])], 84 | 85 | /* Paypal */ 86 | ['key' => 'paypalEmail', 'value' => '', 'title' => 'Paypal Email', 'description' => 'Paypal email address', 'group' => 'Paypal', 'type' => 'textInput', 'data' => json_encode(Core::getYesNoOption()), 'default' => '', 'rules' => json_encode(['string' => [], 'email' => []])], 87 | ['key' => 'paypalSandbox', 'value' => '', 'title' => 'Paypal Sandbox Mode', 'description' => 'Set yes to enable sandbox', 'group' => 'Paypal', 'type' => 'dropDownList', 'data' => json_encode(Core::getYesNoOption()), 'default' => '1', 'rules' => json_encode(['required' => [], 'boolean' => []])], 88 | ['key' => 'paypalClientID', 'value' => '', 'title' => 'Paypal Client ID', 'description' => 'Live client ID', 'group' => 'Paypal', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 89 | ['key' => 'paypalSecret', 'value' => '', 'title' => 'Paypal Secret', 'description' => 'Live secret string', 'group' => 'Paypal', 'type' => 'passwordInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 90 | ['key' => 'paypalSandboxClientID', 'value' => '', 'title' => 'Paypal Sandbox Client ID', 'description' => 'Sandbox client ID', 'group' => 'Paypal', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 91 | ['key' => 'paypalSandboxSecret', 'value' => '', 'title' => 'Paypal Sandbox Secret', 'description' => 'Sandbox secret string', 'group' => 'Paypal', 'type' => 'passwordInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 92 | 93 | /* Bitcoin */ 94 | ['key' => 'btcPaymentTime', 'value' => '', 'title' => 'BTC Payment Timeout', 'description' => 'Payment timeout in seconds', 'group' => 'Bitcoin', 'type' => 'textInput', 'data' => '[]', 'default' => '900', 'rules' => json_encode(['integer' => ['min'=>300, 'max'=>3600]])], 95 | ['key' => 'btcWalletXPub', 'value' => '', 'title' => 'BTC Wallet xPub', 'description' => 'Wallet extended public key', 'group' => 'Bitcoin', 'type' => 'textInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 96 | 97 | /* order API */ 98 | ['key' => 'currencyLayerAPI', 'value' => '', 'title' => 'Currency Layer API', 'description' => 'Access API key for currency conversion', 'group' => 'MISC', 'type' => 'passwordInput', 'data' => '[]', 'default' => '', 'rules' => json_encode(['string' => []])], 99 | 100 | 101 | ]; 102 | $settings = Setting::find()->all(); 103 | foreach ($settings as $setting) { 104 | if (!in_array($setting->key, array_column($s, 'key'))) { 105 | $setting->delete(); 106 | } 107 | } 108 | 109 | /* sync */ 110 | $unsave=[]; 111 | foreach ($s as $i => $setting) { 112 | $conf = Setting::find()->where(['key'=>$setting['key']])->one(); 113 | if (!$conf) { 114 | $conf = new Setting(); 115 | $conf->key = $setting['key']; 116 | $conf->value = $setting['value']; 117 | } 118 | 119 | $conf->title = $setting['title']; 120 | $conf->group = $setting['group']; 121 | $conf->type = $setting['type']; 122 | $conf->data = $setting['data']; 123 | $conf->default = $setting['default']; 124 | $conf->rules = $setting['rules']; 125 | $conf->key_order = (int)$i; 126 | if(!$conf->save()){ 127 | $unsave[]=$conf->key; 128 | //var_dump($conf->errors); 129 | } 130 | } 131 | 132 | if(is_a(Yii::$app, '\yii\web\Application')){ 133 | if(empty($unsave)){ 134 | Yii::$app->session->setFlash('success', Yii::t('app', 'All settings has been updated.')); 135 | } 136 | else { 137 | Yii::$app->session->setFlash('warning', Yii::t('app', 'Some setting(s) can not be updated: {SETTINGS}', ['SETTINGS'=>implode(', ', $unsave)])); 138 | } 139 | } 140 | 141 | return $this->redirect(['index']); 142 | } 143 | } 144 | --------------------------------------------------------------------------------