Completed
Push — master ( 95b9de...99a495 )
by Dmitry
03:00
created

PayController::actionRequest()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 24
ccs 0
cts 19
cp 0
rs 8.6845
cc 4
eloc 15
nc 4
nop 0
crap 20
1
<?php
2
/**
3
 * Yii2 extension for payment processing with Omnipay, Payum and more later.
4
 *
5
 * @link      https://github.com/hiqdev/yii2-merchant
6
 * @package   yii2-merchant
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\yii2\merchant\controllers;
12
13
use hiqdev\yii2\merchant\Module;
14
use Yii;
15
use yii\base\InvalidCallException;
16
use yii\base\UserException;
17
use yii\helpers\Json;
18
use yii\web\BadRequestHttpException;
19
use yii\web\Response;
20
21
class PayController extends \yii\web\Controller
22
{
23
    /**
24
     * @return Module|\yii\base\Module
25
     */
26
    public function getMerchantModule()
27
    {
28
        return $this->module;
29
    }
30
31
    /**
32
     * Disable CSRF validation for POST requests we receive from outside
33
     * {@inheritdoc}
34
     */
35
    public function beforeAction($action)
36
    {
37
        if (in_array($this->action->id, ['notify', 'return', 'cancel'], true)) {
38
            Yii::$app->controller->enableCsrfValidation = false;
39
        }
40
41
        return parent::beforeAction($action);
42
    }
43
44
    /**
45
     * @return Response
46
     */
47
    public function actionCancel()
48
    {
49
        Yii::$app->session->addFlash('error', Yii::t('merchant', 'Payment failed or cancelled'));
50
51
        return $this->redirect($this->getMerchantModule()->previousUrl() ?: ['deposit']);
52
    }
53
54
    /**
55
     * @return string
56
     */
57
    public function actionReturn()
58
    {
59
        $this->checkNotify();
60
61
        return $this->render('return', [
62
            'transactionId' => Yii::$app->request->get('transactionId'),
63
        ]);
64
    }
65
66
    /**
67
     * @param string $transactionId
68
     * @throws BadRequestHttpException
69
     * @return array
70
     */
71
    public function actionCheckReturn($transactionId)
72
    {
73
        Yii::$app->response->format = Response::FORMAT_JSON;
74
        $data = $this->getMerchantModule()->readHistory($transactionId);
75
76
        if ($data['username'] !== $this->getMerchantModule()->username) {
77
            throw new BadRequestHttpException('Access denied', 403);
78
        }
79
80
        return [
81
            'status' => $data['_isCompleted'],
82
            'url'    => $data['_isCompleted'] ? $data['finishUrl'] : $data['cancelUrl'],
83
        ];
84
    }
85
86
    /**
87
     * Action is designed to get the system notification from payment system,
88
     * process it and report success or error for the payment system.
89
     * @return null|string
90
     */
91
    public function actionNotify()
92
    {
93
        $result = $this->checkNotify();
94
        Yii::$app->response->format = Response::FORMAT_RAW;
95
96
        return $result['_isCompleted'] ? 'OK' : $result['_error'];
97
    }
98
99
    /**
100
     * Check notifications.
101
     * TODO: implement actual request check and proper handling.
102
     * @return array
103
     */
104
    public function checkNotify()
0 ignored issues
show
Coding Style introduced by
checkNotify uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
105
    {
106
        $result = $_REQUEST;
107
108
        return $this->completeHistory($result);
0 ignored issues
show
Documentation Bug introduced by
The method completeHistory does not exist on object<hiqdev\yii2\merch...trollers\PayController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
109
    }
110
111
    public function actionDeposit()
112
    {
113
        $model   = Yii::createObject($this->getMerchantModule()->depositClass);
114
        $request = Yii::$app->request;
115
        $availableMerchants = $this->getMerchantModule()->getCollection()->getItems();
116
        if ($model->load($request->isPost ? $request->post() : $request->get()) && $model->validate()) {
117
            return $this->renderDeposit($model->getAttributes());
118
        }
119
120
        return $this->render('deposit-form', compact('model', 'availableMerchants'));
121
    }
122
123
    /**
124
     * Renders depositing buttons for given request data.
125
     *
126
     * @param array $data request data:
127
     *  - `sum` - the amount of payment without fees
128
     *  - `currency` - the currency of transaction
129
     *  - `finishPage/Url` - page or URL to redirect user after the payment
130
     *  - `returnPage/Url` - page or URL to return user from payment system on success
131
     *  - `cancelPage/Url` - page or URL to return user from payment system on fail
132
     *  - `notifyPage/Url` - page or URL used by payment system to notify us on successful payment
133
     * @return \yii\web\Response
134
     */
135
    public function renderDeposit(array $data)
136
    {
137
        $merchants = $this->getMerchantModule()->getCollection($data)->getItems();
138
        $requests = [];
139
        foreach ($merchants as $id => $merchant) {
140
            $requests[$id] = $merchant->request('purchase', $this->getMerchantModule()->prepareRequestData($id, $data));
141
        }
142
143
        return $this->render('deposit', compact('requests'));
144
    }
145
146
    /**
147
     * Performs purchase request.
148
     * @void
149
     */
150
    public function actionRequest()
151
    {
152
        $merchant   = Yii::$app->request->post('merchant');
153
        if (empty($merchant)) {
154
            throw new BadRequestHttpException('Merchant is missing');
155
        }
156
157
        $data       = Json::decode(Yii::$app->request->post('data', '{}'));
158
        $merchant   = $this->getMerchantModule()->getMerchant($merchant, $data);
159
        $request    = $merchant->request('purchase', $data);
160
161
        $this->getMerchantModule()->writeHistory(array_merge($data, ['username' => $this->getMerchantModule()->username]));
162
163
        $response   = $request->send();
164
165
        if ($response->isSuccessful()) {
166
            throw new InvalidCallException('Instant payment is not implemented yet');
167
//            $merchant->registerMoney($response);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
168
        } elseif ($response->isRedirect()) {
169
            $response->redirect();
170
        } else {
171
            throw new UserException('Merchant request failed');
172
        }
173
    }
174
}
175