Completed
Push — master ( 2671f8...fde375 )
by Vuong
02:05
created

BasePaymentGateway::verifyRequestIPN()   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 `purchaseSuccess` sử dụng cho việc yêu cấu xác thực tính hợp lệ
45
     * 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).
46
     */
47
    const VRC_PURCHASE_SUCCESS = 'purchaseSuccess';
48
49
    /**
50
     * Lệnh `IPN` sử dụng cho việc yêu cấu xác thực tính hợp lệ
51
     * 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).
52
     */
53
    const VRC_IPN = 'IPN';
54
55
    /**
56
     * @event RequestEvent được gọi khi dữ liệu truy vấn đã được xác thực.
57
     * 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.
58
     */
59
    const EVENT_VERIFIED_REQUEST = 'verifiedRequest';
60
61
    /**
62
     * @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,
63
     * được cổng thanh toán dẫn về hệ thống đã xác thực.
64
     */
65
    const EVENT_VERIFIED_REQUEST_PURCHASE_SUCCESS = 'verifiedRequestPurchaseSuccess';
66
67
    /**
68
     * @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,
69
     * được cổng thanh toán bắn `request` sang hệ thống đã xác thực.
70
     */
71
    const EVENT_VERIFIED_REQUEST_IPN = 'verifiedRequestIPN';
72
73
    /**
74
     * @event RequestEvent được gọi trước khi khởi tạo lệnh [[RC_PURCHASE]] ở phương thức [[request()]].
75
     */
76
    const EVENT_BEFORE_PURCHASE = 'beforePurchase';
77
78
    /**
79
     * @event RequestEvent được gọi sau khi khởi tạo lệnh [[RC_PURCHASE]] ở phương thức [[request()]].
80
     */
81
    const EVENT_AFTER_PURCHASE = 'afterPurchase';
82
83
    /**
84
     * @event RequestEvent được gọi trước khi khởi tạo lệnh [[RC_QUERY_DR]] ở phương thức [[request()]].
85
     */
86
    const EVENT_BEFORE_QUERY_DR = 'beforeQueryDR';
87
88
    /**
89
     * @event RequestEvent được gọi sau khi khởi tạo lệnh [[RC_QUERY_DR]] ở phương thức [[request()]].
90
     */
91
    const EVENT_AFTER_QUERY_DR = 'afterQueryDR';
92
93
    /**
94
     * @var bool nếu là môi trường test thì thiết lập là TRUE và ngược lại.
95
     */
96
    public $sandbox = false;
97
98
    /**
99
     * @var array
100
     */
101
    public $verifiedDataConfig = [];
102
103
    /**
104
     * @inheritdoc
105
     */
106 34
    public function init()
107
    {
108 34
        if ($this->sandbox) {
109 34
            $this->initSandboxEnvironment();
110
        }
111
112 34
        parent::init();
113 34
    }
114
115
    /**
116
     * Phương thức khởi tạo môi trường thử nghiệm.
117
     * Nó chỉ được gọi khi thuộc tính `sandbox` được thiết lập là TRUE.
118
     */
119
    abstract protected function initSandboxEnvironment();
120
121
    /**
122
     * 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.
123
     *
124
     * @see verifyRequestCommands
125
     * @var array
126
     */
127
    private $_verifyRequestCommands;
128
129
    /**
130
     * 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_`.
131
     * `VRC` có nghĩa là Verify Request Command.
132
     *
133
     * @inheritdoc
134
     * @throws \ReflectionException
135
     */
136 9
    public function verifyRequestCommands(): array
137
    {
138 9
        if ($this->_verifyRequestCommands === null) {
139 9
            $reflection = new ReflectionClass($this);
140
141 9
            $commands = [];
142 9
            foreach ($reflection->getConstants() as $name => $value) {
143 9
                if (strpos($name, 'VRC_') === 0) {
144 9
                    $commands[] = $value;
145
                }
146
            }
147
148 9
            return $this->_verifyRequestCommands = $commands;
149
        } else {
150 1
            return $this->_verifyRequestCommands;
151
        }
152
    }
153
154
    /**
155
     * 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.
156
     *
157
     * @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...
158
     * @param string|int $clientId PaymentClient id dùng để tạo yêu cầu thanh toán.
159
     * @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.
160
     * @throws InvalidConfigException|\ReflectionException
161
     */
162 2
    public function purchase(array $data, $clientId = null): DataInterface
163
    {
164 2
        return $this->request(self::RC_PURCHASE, $data, $clientId);
165
    }
166
167
    /**
168
     * 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.
169
     *
170
     * @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...
171
     * @param string|int $clientId PaymentClient id dùng để tạo yêu cầu truy vấn giao dịch.
172
     * @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.
173
     * @throws InvalidConfigException|\ReflectionException
174
     */
175
    public function queryDR(array $data, $clientId = null): DataInterface
176
    {
177
        return $this->request(self::RC_QUERY_DR, $data, $clientId);
178
    }
179
180
    /**
181
     * @inheritdoc
182
     */
183 17 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...
184
    {
185 17
        if ($event->command === self::RC_PURCHASE) {
186 9
            $this->trigger(self::EVENT_BEFORE_PURCHASE, $event);
187 8
        } elseif ($event->command === self::RC_QUERY_DR) {
188 5
            $this->trigger(self::EVENT_BEFORE_QUERY_DR, $event);
189
        }
190
191 17
        parent::beforeRequest($event);
192 17
    }
193
194
    /**
195
     * @inheritdoc
196
     */
197 14 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...
198
    {
199 14
        if ($event->command === self::RC_PURCHASE) {
200 6
            $this->trigger(self::EVENT_AFTER_PURCHASE, $event);
201 8
        } elseif ($event->command === self::RC_QUERY_DR) {
202 5
            $this->trigger(self::EVENT_AFTER_QUERY_DR, $event);
203
        }
204
205 14
        parent::afterRequest($event);
206 14
    }
207
208
    /**
209
     * 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]]
210
     * để 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ủ.
211
     *
212
     * @param \yii\web\Request|null $request Đối tượng `request` thực hiện truy cập hệ thống.
213
     * @param null|int|string $clientId PaymentClient id dùng để xác thực tính hợp lệ của dữ liệu.
214
     * @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.
215
     * @throws InvalidConfigException|\ReflectionException
216
     */
217 1
    public function verifyRequestPurchaseSuccess(\yii\web\Request $request = null, $clientId = null)
218
    {
219 1
        return $this->verifyRequest(self::VRC_PURCHASE_SUCCESS, $request, $clientId);
220
    }
221
222
    /**
223
     * Phương thức này là phương thức ánh xạ của [[verifyRequest()]] nó sẽ tạo lệnh [[VRC_IPN]]
224
     * để 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ủ.
225
     *
226
     * @param \yii\web\Request|null $request Đối tượng `request` thực hiện truy cập hệ thống.
227
     * @param null|int|string $clientId PaymentClient id dùng để xác thực tính hợp lệ của dữ liệu.
228
     * @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.
229
     * @throws InvalidConfigException|\ReflectionException
230
     */
231 2
    public function verifyRequestIPN(\yii\web\Request $request = null, $clientId = null)
232
    {
233 2
        return $this->verifyRequest(self::VRC_IPN, $request, $clientId);
234
    }
235
236
    /**
237
     * @inheritdoc
238
     * @throws InvalidConfigException|\ReflectionException
239
     */
240 9
    public function verifyRequest($command, \yii\web\Request $request = null, $clientId = null)
241
    {
242 9
        if (in_array($command, $this->verifyRequestCommands(), true)) {
243 9
            $client = $this->getClient($clientId);
244
245 9 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...
246 9
                if (Yii::$app instanceof \yii\web\Application) {
247 9
                    $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
253 9
            $data = $this->getVerifyRequestData($command, $request);
254
            /** @var VerifiedData $requestData */
255 9
            $verifyData = Yii::createObject($this->verifiedDataConfig, [$command, $data, $client]);
256 9
            if ($verifyData->validate()) {
257
                /** @var VerifiedRequestEvent $event */
258 2
                $event = Yii::createObject([
259 2
                    'class' => VerifiedRequestEvent::class,
260 2
                    'verifiedData' => $verifyData,
261 2
                    'client' => $client,
262 2
                    'command' => $command
263
                ]);
264 2
                $this->verifiedRequest($event);
265
266 2
                return $verifyData;
267
            } else {
268 8
                return false;
269
            }
270
        } else {
271
            throw new InvalidArgumentException("Unknown verify request command: `$command`");
272
        }
273
    }
274
275
    /**
276
     * 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`.
277
     *
278
     * @param int|string $command Lệnh yêu cầu lấy dữ liệu cần được xác minh.
279
     * @param \yii\web\Request $request Đối tượng `request` được yêu cầu lấy dữ liệu.
280
     * @return array Trả về mảng dữ liệu cần được xác minh.
281
     */
282
    abstract protected function getVerifyRequestData($command, \yii\web\Request $request): array;
283
284
    /**
285
     * 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.
286
     * 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.
287
     *
288
     * @param VerifiedRequestEvent $event
289
     */
290 2
    public function verifiedRequest(VerifiedRequestEvent $event)
291
    {
292 2
        if ($event->command === self::VRC_IPN) {
293 1
            $this->trigger(self::EVENT_VERIFIED_REQUEST_IPN, $event);
294 1
        } elseif ($event->command === self::VRC_PURCHASE_SUCCESS) {
295 1
            $this->trigger(self::EVENT_VERIFIED_REQUEST_PURCHASE_SUCCESS, $event);
296
        }
297
298 2
        $this->trigger(self::EVENT_VERIFIED_REQUEST, $event);
299 2
    }
300
301
302
}
303