Completed
Push — master ( 4f7c7c...fdaf8f )
by Vuong
02:10
created

BasePaymentGateway::verifyRequestPurchaseSuccess()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
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 GatewayClients\DataInterface;
14
15
use yii\base\InvalidArgumentException;
16
use yii\base\InvalidConfigException;
17
18
use vxm\gatewayclients\BaseGateway;
19
use vxm\gatewayclients\RequestEvent;
20
use vxm\gatewayclients\ResponseData;
21
22
/**
23
 * 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.
24
 *
25
 * @property BasePaymentClient $client
26
 * @property BasePaymentClient $defaultClient
27
 *
28
 * @author Vuong Minh <[email protected]>
29
 * @since 1.0
30
 */
31
abstract class BasePaymentGateway extends BaseGateway implements PaymentGatewayInterface
32
{
33
    /**
34
     * Lệnh `purchase` sử dụng cho việc khởi tạo truy vấn thanh toán.
35
     */
36
    const RC_PURCHASE = 'purchase';
37
38
    /**
39
     * Lệnh `queryDR` sử dụng cho việc truy vấn thông tin giao dịch.
40
     */
41
    const RC_QUERY_DR = 'queryDR';
42
43
    /**
44
     * Lệnh `refund` sử dụng cho việc tạo [[request()]] yêu cầu hoàn trả tiền.
45
     */
46
    const RC_REFUND = 'refund';
47
48
    /**
49
     * Lệnh `queryRefund` sử dụng cho việc tạo [[request()]] để kiểm tra trang thái của lệnh `refund` đã tạo.
50
     */
51
    const RC_QUERY_REFUND = 'queryRefund';
52
53
    /**
54
     * Lệnh `purchaseSuccess` sử dụng cho việc yêu cấu xác thực tính hợp lệ
55
     * 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).
56
     */
57
    const VRC_PURCHASE_SUCCESS = 'purchaseSuccess';
58
59
    /**
60
     * Lệnh `IPN` sử dụng cho việc yêu cấu xác thực tính hợp lệ
61
     * 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).
62
     */
63
    const VRC_IPN = 'IPN';
64
65
    /**
66
     * @event RequestEvent được gọi khi dữ liệu truy vấn đã được xác thực.
67
     * 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.
68
     */
69
    const EVENT_VERIFIED_REQUEST = 'verifiedRequest';
70
71
    /**
72
     * @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,
73
     * được cổng thanh toán dẫn về hệ thống đã xác thực.
74
     */
75
    const EVENT_VERIFIED_REQUEST_PURCHASE_SUCCESS = 'verifiedRequestPurchaseSuccess';
76
77
    /**
78
     * @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,
79
     * được cổng thanh toán bắn `request` sang hệ thống đã xác thực.
80
     */
81
    const EVENT_VERIFIED_REQUEST_IPN = 'verifiedRequestIPN';
82
83
    /**
84
     * @event RequestEvent được gọi trước khi khởi tạo lệnh [[RC_PURCHASE]] ở phương thức [[request()]].
85
     */
86
    const EVENT_BEFORE_PURCHASE = 'beforePurchase';
87
88
    /**
89
     * @event RequestEvent được gọi sau khi khởi tạo lệnh [[RC_PURCHASE]] ở phương thức [[request()]].
90
     */
91
    const EVENT_AFTER_PURCHASE = 'afterPurchase';
92
93
    /**
94
     * @event RequestEvent được gọi trước khi khởi tạo lệnh [[RC_QUERY_DR]] ở phương thức [[request()]].
95
     */
96
    const EVENT_BEFORE_QUERY_DR = 'beforeQueryDR';
97
98
    /**
99
     * @event RequestEvent được gọi sau khi khởi tạo lệnh [[RC_QUERY_DR]] ở phương thức [[request()]].
100
     */
101
    const EVENT_AFTER_QUERY_DR = 'afterQueryDR';
102
103
    /**
104
     * @event RequestEvent được gọi trước khi khởi tạo lệnh [[RC_REFUND]] ở phương thức [[request()]].
105
     * @since 1.0.3
106
     */
107
    const EVENT_BEFORE_REFUND = 'beforeRefund';
108
109
    /**
110
     * @event RequestEvent được gọi sau khi khởi tạo lệnh [[RC_REFUND]] ở phương thức [[request()]].
111
     * @since 1.0.3
112
     */
113
    const EVENT_AFTER_REFUND = 'afterRefund';
114
115
    /**
116
     * @event RequestEvent được gọi trước khi khởi tạo lệnh [[RC_QUERY_REFUND]] ở phương thức [[request()]].
117
     * @since 1.0.3
118
     */
119
    const EVENT_BEFORE_QUERY_REFUND = 'beforeQueryRefund';
120
121
    /**
122
     * @event RequestEvent được gọi sau khi khởi tạo lệnh [[RC_QUERY_REFUND]] ở phương thức [[request()]].
123
     * @since 1.0.3
124
     */
125
    const EVENT_AFTER_QUERY_REFUND = 'afterQueryRefund';
126
127
    /**
128
     * @var bool nếu là môi trường test thì thiết lập là TRUE và ngược lại.
129
     */
130
    public $sandbox = false;
131
132
    /**
133
     * @var array
134
     */
135
    public $verifiedDataConfig = [];
136
137
    /**
138
     * @inheritdoc
139
     */
140 64
    public function init()
141
    {
142 64
        if ($this->sandbox) {
143 64
            $this->initSandboxEnvironment();
144
        }
145
146 64
        parent::init();
147 64
    }
148
149
    /**
150
     * Phương thức khởi tạo môi trường thử nghiệm.
151
     * Nó chỉ được gọi khi thuộc tính `sandbox` được thiết lập là TRUE.
152
     */
153
    abstract protected function initSandboxEnvironment();
154
155
    /**
156
     * 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.
157
     *
158
     * @see verifyRequestCommands
159
     * @var array
160
     */
161
    private $_verifyRequestCommands;
162
163
    /**
164
     * 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_`.
165
     * `VRC` có nghĩa là Verify Request Command.
166
     *
167
     * @inheritdoc
168
     * @throws \ReflectionException
169
     */
170 13
    public function verifyRequestCommands(): array
171
    {
172 13
        if ($this->_verifyRequestCommands === null) {
173 13
            $reflection = new ReflectionClass($this);
174
175 13
            $commands = [];
176 13
            foreach ($reflection->getConstants() as $name => $value) {
177 13
                if (strpos($name, 'VRC_') === 0) {
178 13
                    $commands[] = $value;
179
                }
180
            }
181
182 13
            return $this->_verifyRequestCommands = $commands;
183
        } else {
184
            return $this->_verifyRequestCommands;
185
        }
186
    }
187
188
    /**
189
     * 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.
190
     *
191
     * @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...
192
     * @param string|int $clientId PaymentClient id dùng để tạo yêu cầu thanh toán.
193
     * @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.
194
     * @throws InvalidConfigException|\ReflectionException
195
     */
196 2
    public function purchase(array $data, $clientId = null): DataInterface
197
    {
198 2
        return $this->request(self::RC_PURCHASE, $data, $clientId);
199
    }
200
201
    /**
202
     * 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.
203
     *
204
     * @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...
205
     * @param string|int $clientId PaymentClient id dùng để tạo yêu cầu truy vấn giao dịch.
206
     * @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.
207
     * @throws InvalidConfigException|\ReflectionException
208
     */
209
    public function queryDR(array $data, $clientId = null): DataInterface
210
    {
211
        return $this->request(self::RC_QUERY_DR, $data, $clientId);
212
    }
213
214
    /**
215
     * Phương thức này là phương thức ánh xạ của [[request()]] nó sẽ tạo lệnh [[RC_REFUND]] để tạo yêu cầu hoàn tiền tới cổng thanh toán.
216
     *
217
     * @param array $data dữ liệu yêu cầu hoàn trả.
218
     * @param null $clientId Client id sử dụng để tạo yêu cầu.
219
     * Nếu không thiết lập [[getDefaultClient()]] sẽ được gọi để xác định client.
220
     * @return ResponseData|DataInterface Trả về [[DataInterface]] là dữ liệu tổng hợp từ MOMO phản hồi.
221
     * @throws \ReflectionException|\yii\base\InvalidConfigException|\yii\base\InvalidArgumentException
222
     * @since 1.0.3
223
     */
224
    public function refund(array $data, $clientId = null): DataInterface
225
    {
226
        return $this->request(self::RC_REFUND, $data, $clientId);
227
    }
228
229
    /**
230
     * Phương thức này là phương thức ánh xạ của [[request()]] nó sẽ tạo lệnh [[RC_QUERY_REFUND]] để tạo yêu cầu truy vấn trạng thái hoàn tiền tới cổng thanh toán.
231
     *
232
     * @param array $data dữ liệu trạng thái hoàn tiền.
233
     * @param null $clientId Client id sử dụng để tạo yêu cầu truy vấn trạng thái.
234
     * Nếu không thiết lập [[getDefaultClient()]] sẽ được gọi để xác định client.
235
     * @return ResponseData|DataInterface Trả về [[DataInterface]] là dữ liệu tổng hợp từ MOMO phản hồi.
236
     * @throws \ReflectionException|\yii\base\InvalidConfigException|\yii\base\InvalidArgumentException
237
     * @since 1.0.3
238
     */
239
    public function queryRefund(array $data, $clientId = null): DataInterface
240
    {
241
        return $this->request(self::RC_QUERY_REFUND, $data, $clientId);
242
    }
243
244
    /**
245
     * @inheritdoc
246
     */
247 30 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...
248
    {
249 30
        switch ($event->command) {
250 30
            case self::RC_PURCHASE:
251 14
                $this->trigger(self::EVENT_BEFORE_PURCHASE, $event);
252 14
                break;
253 19
            case self::RC_QUERY_DR:
254 7
                $this->trigger(self::EVENT_BEFORE_QUERY_DR, $event);
255 7
                break;
256 12
            case self::RC_REFUND:
257 3
                $this->trigger(self::EVENT_BEFORE_REFUND, $event);
258 3
                break;
259 9
            case self::RC_QUERY_REFUND:
260 2
                $this->trigger(self::EVENT_BEFORE_QUERY_REFUND, $event);
261 2
                break;
262
            default:
263 7
                break;
264
        }
265
266 30
        parent::beforeRequest($event);
267 30
    }
268
269
    /**
270
     * @inheritdoc
271
     */
272 24 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...
273
    {
274 24
        switch ($event->command) {
275 24
            case self::RC_PURCHASE:
276 11
                $this->trigger(self::EVENT_AFTER_PURCHASE, $event);
277 11
                break;
278 16
            case self::RC_QUERY_DR:
279 6
                $this->trigger(self::EVENT_AFTER_QUERY_DR, $event);
280 6
                break;
281 10
            case self::RC_REFUND:
282 2
                $this->trigger(self::EVENT_AFTER_REFUND, $event);
283 2
                break;
284 8
            case self::RC_QUERY_REFUND:
285 1
                $this->trigger(self::EVENT_AFTER_QUERY_REFUND, $event);
286 1
                break;
287
            default:
288 7
                break;
289
        }
290
291 24
        parent::afterRequest($event);
292 24
    }
293
294
    /**
295
     * 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]]
296
     * để 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ủ.
297
     *
298
     * @param \yii\web\Request|null $request Đối tượng `request` thực hiện truy cập hệ thống.
299
     * @param null|int|string $clientId PaymentClient id dùng để xác thực tính hợp lệ của dữ liệu.
300
     * @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.
301
     * @throws InvalidConfigException|\ReflectionException
302
     */
303 1
    public function verifyRequestPurchaseSuccess(\yii\web\Request $request = null, $clientId = null)
304
    {
305 1
        return $this->verifyRequest(self::VRC_PURCHASE_SUCCESS, $request, $clientId);
306
    }
307
308
    /**
309
     * Phương thức này là phương thức ánh xạ của [[verifyRequest()]] nó sẽ tạo lệnh [[VRC_IPN]]
310
     * để 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ủ.
311
     *
312
     * @param \yii\web\Request|null $request Đối tượng `request` thực hiện truy cập hệ thống.
313
     * @param null|int|string $clientId PaymentClient id dùng để xác thực tính hợp lệ của dữ liệu.
314
     * @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.
315
     * @throws InvalidConfigException|\ReflectionException
316
     */
317 2
    public function verifyRequestIPN(\yii\web\Request $request = null, $clientId = null)
318
    {
319 2
        return $this->verifyRequest(self::VRC_IPN, $request, $clientId);
320
    }
321
322
    /**
323
     * @inheritdoc
324
     * @throws InvalidConfigException|\ReflectionException
325
     */
326 13
    public function verifyRequest($command, \yii\web\Request $request = null, $clientId = null)
327
    {
328 13
        if (in_array($command, $this->verifyRequestCommands(), true)) {
329 13
            $client = $this->getClient($clientId);
330
331 13 View Code Duplication
            if ($request === null) {
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...
332 11
                if (Yii::$app instanceof \yii\web\Application) {
333 11
                    $request = Yii::$app->getRequest();
334
                } else {
335
                    throw new InvalidArgumentException('Request instance arg must be set to verify return request is valid or not!');
336
                }
337
            }
338
339 13
            $data = $this->getVerifyRequestData($command, $request);
340
            /** @var VerifiedData $requestData */
341 13
            $verifyData = Yii::createObject($this->verifiedDataConfig, [$command, $data, $client]);
342 13
            if ($verifyData->validate()) {
343
                /** @var VerifiedRequestEvent $event */
344 2
                $event = Yii::createObject([
345 2
                    'class' => VerifiedRequestEvent::class,
346 2
                    'verifiedData' => $verifyData,
347 2
                    'client' => $client,
348 2
                    'command' => $command
349
                ]);
350 2
                $this->verifiedRequest($event);
351
352 2
                return $verifyData;
353
            } else {
354 11
                return false;
355
            }
356
        } else {
357
            throw new InvalidArgumentException("Verify request command: `$command` invalid in " . __CLASS__);
358
        }
359
    }
360
361
    /**
362
     * 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`.
363
     *
364
     * @param int|string $command Lệnh yêu cầu lấy dữ liệu cần được xác minh.
365
     * @param \yii\web\Request $request Đối tượng `request` được yêu cầu lấy dữ liệu.
366
     * @return array Trả về mảng dữ liệu cần được xác minh.
367
     */
368
    abstract protected function getVerifyRequestData($command, \yii\web\Request $request): array;
369
370
    /**
371
     * 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.
372
     * 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.
373
     *
374
     * @param VerifiedRequestEvent $event
375
     */
376 2
    public function verifiedRequest(VerifiedRequestEvent $event)
377
    {
378 2
        if ($event->command === self::VRC_IPN) {
379 2
            $this->trigger(self::EVENT_VERIFIED_REQUEST_IPN, $event);
380
        } elseif ($event->command === self::VRC_PURCHASE_SUCCESS) {
381
            $this->trigger(self::EVENT_VERIFIED_REQUEST_PURCHASE_SUCCESS, $event);
382
        }
383
384 2
        $this->trigger(self::EVENT_VERIFIED_REQUEST, $event);
385 2
    }
386
387
388
}
389