Completed
Push — master ( ac749f...8181ea )
by Vuong
01:54
created

BasePaymentGateway::verifyRequestPurchaseSuccess()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
crap 2
1
<?php
2
/**
3
 * @link https://github.com/yiiviet/yii2-payment
4
 * @copyright Copyright (c) 2017 Yii Viet
5
 * @license [New BSD License](http://www.opensource.org/licenses/bsd-license.php)
6
 */
7
8
namespace yiiviet\payment;
9
10
use Yii;
11
use ReflectionClass;
12
13
use yii\base\InvalidArgumentException;
14
use yii\base\InvalidConfigException;
15
use yii\helpers\ArrayHelper;
16
use yii\httpclient\Client as HttpClient;
17
18
use vxm\gatewayclients\BaseGateway;
19
use vxm\gatewayclients\DataInterface;
20
use vxm\gatewayclients\RequestEvent;
21
use vxm\gatewayclients\ResponseData;
22
23
/**
24
 * Lớp BasePaymentGateway thực thi mẫu trừu tượng [[PaymentGatewayInterface]] giúp cho việc xây dựng các lợp thực thi được tối giản.
25
 *
26
 * @property BasePaymentClient $client
27
 * @property BasePaymentClient $defaultClient
28
 *
29
 * @author Vuong Minh <[email protected]>
30
 * @since 1.0
31
 */
32
abstract class BasePaymentGateway extends BaseGateway implements PaymentGatewayInterface
33
{
34
    /**
35
     * Lệnh `purchase` sử dụng cho việc khởi tạo truy vấn thanh toán.
36
     */
37
    const RC_PURCHASE = 'purchase';
38
39
    /**
40
     * Lệnh `queryDR` sử dụng cho việc truy vấn thông tin giao dịch.
41
     */
42
    const RC_QUERY_DR = 'queryDR';
43
44
    /**
45
     * Lệnh `purchaseSuccess` sử dụng cho việc yêu cấu xác thực tính hợp lệ
46
     * của dữ liệu khi khách hàng thanh toán thành công (cổng thanh toán redirect khách hàng về server).
47
     */
48
    const VRC_PURCHASE_SUCCESS = 'purchaseSuccess';
49
50
    /**
51
     * Lệnh `IPN` sử dụng cho việc yêu cấu xác thực tính hợp lệ
52
     * của dữ liệu khi khách hàng thanh toán thành công (cổng thanh toán bắn request về server).
53
     */
54
    const VRC_IPN = 'IPN';
55
56
    /**
57
     * @event RequestEvent được gọi khi dữ liệu truy vấn đã được xác thực.
58
     * Lưu ý sự kiện này luôn luôn được gọi khi xác thực dữ liệu truy vấn.
59
     */
60
    const EVENT_VERIFIED_REQUEST = 'verifiedRequest';
61
62
    /**
63
     * @event VerifiedRequestEvent được gọi khi dữ liệu truy vấn sau khi khách hàng thanh toán thành công,
64
     * được cổng thanh toán dẫn về hệ thống đã xác thực.
65
     */
66
    const EVENT_VERIFIED_REQUEST_PURCHASE_SUCCESS = 'verifiedRequestPurchaseSuccess';
67
68
    /**
69
     * @event VerifiedRequestEvent được gọi khi dữ liệu truy vấn sau khi khách hàng thanh toán thành công,
70
     * được cổng thanh toán bắn `request` sang hệ thống đã xác thực.
71
     */
72
    const EVENT_VERIFIED_REQUEST_IPN = 'verifiedRequestIPN';
73
74
    /**
75
     * @event RequestEvent được gọi trước khi khởi tạo lệnh [[RC_PURCHASE]] ở phương thức [[request()]].
76
     */
77
    const EVENT_BEFORE_PURCHASE = 'beforePurchase';
78
79
    /**
80
     * @event RequestEvent được gọi sau khi khởi tạo lệnh [[RC_PURCHASE]] ở phương thức [[request()]].
81
     */
82
    const EVENT_AFTER_PURCHASE = 'afterPurchase';
83
84
    /**
85
     * @event RequestEvent được gọi trước khi khởi tạo lệnh [[RC_QUERY_DR]] ở phương thức [[request()]].
86
     */
87
    const EVENT_BEFORE_QUERY_DR = 'beforeQueryDR';
88
89
    /**
90
     * @event RequestEvent được gọi sau khi khởi tạo lệnh [[RC_QUERY_DR]] ở phương thức [[request()]].
91
     */
92
    const EVENT_AFTER_QUERY_DR = 'afterQueryDR';
93
94
    /**
95
     * @var bool nếu là môi trường test thì thiết lập là TRUE và ngược lại.
96
     */
97
    public $sandbox = false;
98
99
    /**
100
     * @var array
101
     */
102
    public $verifiedDataConfig = [];
103
104
    /**
105
     * @inheritdoc
106
     */
107 26
    public function init()
108
    {
109 26
        if ($this->sandbox) {
110 26
            $this->initSandboxEnvironment();
111
        }
112
113 26
        parent::init();
114 26
    }
115
116
    /**
117
     * Phương thức khởi tạo môi trường thử nghiệm.
118
     * Nó chỉ được gọi khi thuộc tính `sandbox` được thiết lập là TRUE.
119
     */
120
    abstract protected function initSandboxEnvironment();
121
122
    /**
123
     * Thuộc tính để cache lại tiến trình của [[verifyRequestCommands()]] nhầm tối ưu tốc độ khi gọi nhiều lần.
124
     *
125
     * @see verifyRequestCommands
126
     * @var array
127
     */
128
    private $_verifyRequestCommands;
129
130
    /**
131
     * Phương thức này tự động thu thập các lệnh xác thực thông qua các hằng được khai bảo bằng tiền tố `VRC_`.
132
     * `VRC` có nghĩa là Verify Request Command.
133
     *
134
     * @inheritdoc
135
     * @throws \ReflectionException
136
     */
137 5
    public function verifyRequestCommands(): array
138
    {
139 5
        if ($this->_verifyRequestCommands === null) {
140 5
            $reflection = new ReflectionClass($this);
141
142 5
            $commands = [];
143 5
            foreach ($reflection->getConstants() as $name => $value) {
144 5
                if (strpos($name, 'VRC_') === 0) {
145 5
                    $commands[] = $value;
146
                }
147
            }
148
149 5
            return $this->_verifyRequestCommands = $commands;
150
        } else {
151
            return $this->_verifyRequestCommands;
152
        }
153
    }
154
155
    /**
156
     * Phương thức này là phương thức ánh xạ của [[request()]] nó sẽ tạo lệnh [[RC_PURCHASE]] để tạo yêu cầu giao dịch tới cổng thanh toán.
157
     *
158
     * @param array $data Dữ liệu dùng để yêu cầu tạo giao dịch thanh toán bên trong thường có giá tiền, địa chỉ giao hàng...
159
     * @param string|int $clientId PaymentClient id dùng để tạo yêu cầu thanh toán.
160
     * @return ResponseData|DataInterface Phương thức sẽ trả về mẫu trừu tượng [[DataInterface]] để lấy thông tin trả về từ cổng thanh toán.
161
     * @throws InvalidConfigException|\ReflectionException
162
     */
163
    public function purchase(array $data, $clientId = null): DataInterface
164
    {
165
        return $this->request(self::RC_PURCHASE, $data, $clientId);
166
    }
167
168
    /**
169
     * Phương thức này là phương thức ánh xạ của [[request()]] nó sẽ tạo lệnh [[RC_QUERY_DR]] để tạo yêu cầu truy vấn giao dịch tới cổng thanh toán.
170
     *
171
     * @param array $data Dữ liệu dùng để truy vấn thông tin giao dịch bên trong thường có mã giao dịch từ cổng thanh toán...
172
     * @param string|int $clientId PaymentClient id dùng để tạo yêu cầu truy vấn giao dịch.
173
     * @return ResponseData|DataInterface Phương thức sẽ trả về mẫu trừu tượng [[DataInterface]] để lấy thông tin trả về từ cổng thanh toán.
174
     * @throws InvalidConfigException|\ReflectionException
175
     */
176
    public function queryDR(array $data, $clientId = null): DataInterface
177
    {
178
        return $this->request(self::RC_QUERY_DR, $data, $clientId);
179
    }
180
181
    /**
182
     * @inheritdoc
183
     */
184 15 View Code Duplication
    public function beforeRequest(RequestEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
185
    {
186 15
        if ($event->command === self::RC_PURCHASE) {
187 7
            $this->trigger(self::EVENT_BEFORE_PURCHASE, $event);
188 8
        } elseif ($event->command === self::RC_QUERY_DR) {
189 5
            $this->trigger(self::EVENT_BEFORE_QUERY_DR, $event);
190
        }
191
192 15
        parent::beforeRequest($event);
193 15
    }
194
195
    /**
196
     * @inheritdoc
197
     */
198 13 View Code Duplication
    public function afterRequest(RequestEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
199
    {
200 13
        if ($event->command === self::RC_PURCHASE) {
201 5
            $this->trigger(self::EVENT_AFTER_PURCHASE, $event);
202 8
        } elseif ($event->command === self::RC_QUERY_DR) {
203 5
            $this->trigger(self::EVENT_AFTER_QUERY_DR, $event);
204
        }
205
206 13
        parent::afterRequest($event);
207 13
    }
208
209
    /**
210
     * Phương thức này là phương thức ánh xạ của [[verifyRequest()]] nó sẽ tạo lệnh [[VRC_PURCHASE_SUCCESS]]
211
     * để tạo yêu cầu xác minh tính hợp lệ của dữ liệu trả về từ máy khách đến máy chủ.
212
     *
213
     * @param \yii\web\Request|null $request Đối tượng `request` thực hiện truy cập hệ thống.
214
     * @param null|int|string $clientId PaymentClient id dùng để xác thực tính hợp lệ của dữ liệu.
215
     * @return bool|VerifiedData|DataInterface Sẽ trả về FALSE nếu như dữ liệu không hợp lệ ngược lại sẽ trả về thông tin đơn hàng đã được xác thực.
216
     * @throws InvalidConfigException|\ReflectionException
217
     */
218
    public function verifyRequestPurchaseSuccess(\yii\web\Request $request = null, $clientId = null)
219
    {
220
        return $this->verifyRequest(self::VRC_PURCHASE_SUCCESS, $request, $clientId);
221
    }
222
223
    /**
224
     * Phương thức này là phương thức ánh xạ của [[verifyRequest()]] nó sẽ tạo lệnh [[VRC_IPN]]
225
     * để tạo yêu cầu xác minh tính hợp lệ của dữ liệu trả về từ cổng thanh toán đến máy chủ.
226
     *
227
     * @param \yii\web\Request|null $request Đối tượng `request` thực hiện truy cập hệ thống.
228
     * @param null|int|string $clientId PaymentClient id dùng để xác thực tính hợp lệ của dữ liệu.
229
     * @return bool|VerifiedData|DataInterface Sẽ trả về FALSE nếu như dữ liệu không hợp lệ ngược lại sẽ trả về thông tin đơn hàng đã được xác thực.
230
     * @throws InvalidConfigException|\ReflectionException
231
     */
232
    public function verifyRequestIPN(\yii\web\Request $request = null, $clientId = null)
233
    {
234
        return $this->verifyRequest(self::VRC_IPN, $request, $clientId);
235
    }
236
237
    /**
238
     * @inheritdoc
239
     * @throws InvalidConfigException|\ReflectionException
240
     */
241 6
    public function verifyRequest($command, \yii\web\Request $request = null, $clientId = null)
242
    {
243 6
        if (in_array($command, $this->verifyRequestCommands(), true)) {
244 6
            $client = $this->getClient($clientId);
245
246 6 View Code Duplication
            if ($request === null && Yii::$app instanceof \yii\web\Application) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
247 6
                $request = Yii::$app->getRequest();
248
            } else {
249
                throw new InvalidArgumentException('Request instance arg must be set to verify return request is valid or not!');
250
            }
251
252 6
            $data = $this->getVerifyRequestData($command, $request);
253
            /** @var VerifiedData $requestData */
254 6
            $verifyData = Yii::createObject($this->verifiedDataConfig, [$command, $data, $client]);
255 6
            if ($verifyData->validate()) {
256
                /** @var VerifiedRequestEvent $event */
257 1
                $event = Yii::createObject([
258 1
                    'class' => VerifiedRequestEvent::class,
259 1
                    'verifiedData' => $verifyData,
260 1
                    'client' => $client,
261 1
                    'command' => $command
262
                ]);
263 1
                $this->verifiedRequest($event);
264
265 1
                return $verifyData;
266
            } else {
267 6
                return false;
268
            }
269
        } else {
270
            throw new InvalidArgumentException("Unknown verify request command: `$command`");
271
        }
272
    }
273
274
    /**
275
     * Phương thúc lấy dữ liệu cần xác minh từ lệnh yêu cầu và đối tượng `request`.
276
     *
277
     * @param int|string $command Lệnh yêu cầu lấy dữ liệu cần được xác minh.
278
     * @param \yii\web\Request $request Đối tượng `request` được yêu cầu lấy dữ liệu.
279
     * @return array Trả về mảng dữ liệu cần được xác minh.
280
     */
281
    abstract protected function getVerifyRequestData($command, \yii\web\Request $request): array;
282
283
    /**
284
     * Phương thức được gọi sau khi việc xác minh tính hợp hệ của dữ liệu thành công.
285
     * Nó được xây dựng để kích hoạt các sự kiện liên quan khi dữ liệu đã được xác minh.
286
     *
287
     * @param VerifiedRequestEvent $event
288
     */
289 1 View Code Duplication
    public function verifiedRequest(VerifiedRequestEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
290
    {
291 1
        if ($event->command === self::VRC_IPN) {
292
            $this->trigger(self::EVENT_VERIFIED_REQUEST_IPN, $event);
293 1
        } elseif ($event->command === self::VRC_PURCHASE_SUCCESS) {
294 1
            $this->trigger(self::EVENT_VERIFIED_REQUEST_PURCHASE_SUCCESS, $event);
295
        }
296
297 1
        $this->trigger(self::EVENT_VERIFIED_REQUEST, $event);
298 1
    }
299
300
301
}
302