Completed
Push — master ( 552a80...bb7932 )
by Vuong
01:49
created

PaymentGateway::getMerchantData()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 5.024

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 3
cts 5
cp 0.6
rs 8.7972
c 0
b 0
f 0
cc 4
eloc 14
nc 3
nop 2
crap 5.024
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\baokim;
9
10
use yii\di\Instance;
11
use yii\helpers\ArrayHelper;
12
13
use yiiviet\payment\BasePaymentGateway;
14
15
use vxm\gatewayclients\RequestEvent;
16
use vxm\gatewayclients\DataInterface;
17
18
19
/**
20
 * Lớp PaymentGateway thực thi các phương thức trừu tượng dùng hổ trợ kết nối đến Bảo Kim.
21
 * Hiện tại nó hổ trợ 100% các tính năng từ cổng thanh toán Bảo Kim.
22
 *
23
 * @method ResponseData|DataInterface purchase(array $data, $clientId = null)
24
 * @method ResponseData|DataInterface queryDR(array $data, $clientId = null)
25
 * @method bool|VerifiedData verifyRequestPurchaseSuccess($clientId = null, \yii\web\Request $request = null)
26
 *
27
 * @property PaymentClient $client
28
 * @property PaymentClient $defaultClient
29
 *
30
 * @author Vuong Minh <[email protected]>
31
 * @since 1.0
32
 */
33
class PaymentGateway extends BasePaymentGateway
34
{
35
    /**
36
     * Lệnh `purchasePro` sử dụng cho việc tạo [[request()]] yêu cầu thanh toán PRO.
37
     */
38
    const RC_PURCHASE_PRO = 'purchasePro';
39
40
    /**
41
     * Lệnh `getMerchantData` sử dụng cho việc tạo [[request()]] yêu cầu thông tin merchant.
42
     */
43
    const RC_GET_MERCHANT_DATA = 'getMerchantData';
44
45
    /**
46
     * @event RequestEvent được gọi trước khi tạo yêu câu thanh toán PRO.
47
     */
48
    const EVENT_BEFORE_PURCHASE_PRO = 'beforePurchasePro';
49
50
    /**
51
     * @event RequestEvent được gọi sau khi tạo yêu câu thanh toán PRO.
52
     */
53
    const EVENT_AFTER_PURCHASE_PRO = 'afterPurchasePro';
54
55
    /**
56
     * @event RequestEvent được gọi sau khi tạo yêu câu thanh toán PRO.
57
     */
58
    const EVENT_VERIFIED_REQUEST_PURCHASE_PRO_SUCCESS = 'verifiedRequestPurchaseProSuccess';
59
60
    /**
61
     * @event RequestEvent được gọi trước khi tạo yêu câu lấy thông tin merchant.
62
     */
63
    const EVENT_BEFORE_GET_MERCHANT_DATA = 'beforeGetMerchantData';
64
65
    /**
66
     * @event RequestEvent được gọi sau khi tạo yêu câu lấy thông tin merchant.
67
     */
68
    const EVENT_AFTER_GET_MERCHANT_DATA = 'afterGetMerchantData';
69
70
    /**
71
     * Đường dẫn API của thanh toán Bảo Kim.
72
     */
73
    const PURCHASE_URL = '/payment/order/version11';
74
75
    /**
76
     * Đường dẫn API của thanh toán PRO.
77
     */
78
    const PURCHASE_PRO_URL = '/payment/rest/payment_pro_api/pay_by_card';
79
80
    /**
81
     * Đường dẫn API để lấy thông tin merchant.
82
     */
83
    const PRO_SELLER_INFO_URL = '/payment/rest/payment_pro_api/get_seller_info';
84
85
    /**
86
     * Đường dẫn API để truy vấn thông tin giao dịch.
87
     */
88
    const QUERY_DR_URL = '/payment/order/queryTransaction';
89
90
    /**
91
     * Đường dẫn API IPN của Bảo Kim để cập nhật và xác minh dữ liệu từ IPN request từ Bảo Kim bắn sang.
92
     * Nói cách khác là sẽ có 2 IPN, 1 cái nằm trên server của bạn và 1 cái là của Bảo Kim để cập nhật đơn hàng của họ.
93
     */
94
    const VERIFY_IPN_URL = '/bpn/verify';
95
96
    /**
97
     * MUI thuộc tính trong mảng data khi tạo thanh toán PRO, cho phép chỉ định giao diện hiển thị charge.
98
     */
99
    const MUI_CHARGE = 'charge';
100
101
    /**
102
     * MUI thuộc tính trong mảng data khi tạo thanh toán PRO, cho phép chỉ định giao diện hiển thị base.
103
     */
104
    const MUI_BASE = 'base';
105
106
    /**
107
     * MUI thuộc tính trong mảng data khi tạo thanh toán PRO, cho phép chỉ định giao diện hiển thị iframe.
108
     */
109
    const MUI_IFRAME = 'iframe';
110
111
    /**
112
     * Transaction mode direct là thuộc tính khi tạo thanh toán Bảo Kim và PRO, cho phép chỉ định giao dịch trực tiếp.
113
     */
114
    const DIRECT_TRANSACTION = 1;
115
116
    /**
117
     * Transaction mode safe là thuộc tính khi tạo thanh toán Bảo Kim và PRO, cho phép chỉ định giao dịch tạm giữ.
118
     */
119
    const SAFE_TRANSACTION = 2;
120
121
    /**
122
     * @see getMerchantData
123
     * @var bool|string|array|\yii\caching\Cache Hổ trợ cho việc cache lại dữ liệu merchant lấy từ Bảo Kim nhầm tối ưu hóa hệ thống
124
     * do dữ liệu này ít khi bị thay đổi.
125
     */
126
    public $merchantDataCache = 'cache';
127
128
    /**
129
     * @var int Thời gian cache dữ liệu của merchant lấy từ Bảo Kim.
130
     */
131
    public $merchantDataCacheDuration = 86400;
132
133
    /**
134
     * @inheritdoc
135
     */
136
    public $clientConfig = ['class' => PaymentClient::class];
137
138
    /**
139
     * @inheritdoc
140
     */
141
    public $requestDataConfig = ['class' => RequestData::class];
142
143
    /**
144
     * @inheritdoc
145
     */
146
    public $responseDataConfig = ['class' => ResponseData::class];
147
148
    /**
149
     * @inheritdoc
150
     */
151
    public $verifiedDataConfig = ['class' => VerifiedData::class];
152
153
    /**
154
     * @inheritdoc
155
     */
156
    public function getBaseUrl(): string
157
    {
158
        return $this->sandbox ? 'https://sandbox.baokim.vn' : 'https://www.baokim.vn';
159
    }
160
161
    /**
162
     * @throws \yii\base\InvalidConfigException
163
     * @inheritdoc
164
     */
165
    public function init()
166
    {
167
        if ($this->merchantDataCache) {
168
            $this->merchantDataCache = Instance::ensure($this->merchantDataCache, 'yii\caching\Cache');
169
        }
170
171
        parent::init();
172
    }
173
174
    /**
175
     * @inheritdoc
176
     * @throws \yii\base\InvalidConfigException
177
     */
178
    protected function initSandboxEnvironment()
179
    {
180
        $clientConfig = require(__DIR__ . '/sandbox-client.php');
181
        $this->setClient($clientConfig);
182
    }
183
184 5
    /**
185
     * Phương thức thanh toán pro (payment pro) hổ trợ tạo thanh toán với phương thức PRO của Bảo Kim.
186 5
     * Đây là phương thức ánh xạ của [[request()]] sử dụng lệnh [[RC_PURCHASE_PRO]].
187
     *
188
     * @param array $data Dữ liệu yêu cầu khởi tạo thanh toán PRO
189
     * @param null $clientId PaymentClient id sử dụng để tạo yêu cầu thanh toán.
190
     * Nếu không thiết lập [[getDefaultClient()]] sẽ được gọi để xác định client.
191
     * @return ResponseData|DataInterface Trả về [[ResponseData]] là dữ liệu từ Bảo Kim phản hồi.
192
     * @throws \ReflectionException|\yii\base\InvalidConfigException
193 8
     */
194
    public function purchasePro(array $data, $clientId = null): DataInterface
195 8
    {
196 8
        return $this->request(self::RC_PURCHASE_PRO, $data, $clientId);
197
    }
198
199 8
    /**
200 8
     * @inheritdoc
201
     */
202 View Code Duplication
    protected function getHttpClientConfig(): array
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...
203
    {
204
        return [
205
            'transport' => 'yii\httpclient\CurlTransport',
206 8
            'requestConfig' => [
207
                'format' => 'json',
208 8
                'options' => [
209 8
                    CURLOPT_SSL_VERIFYHOST => false,
210 8
                    CURLOPT_SSL_VERIFYPEER => false
211
                ]
212
            ]
213
        ];
214
    }
215
216
    /**
217
     * Phương thức hổ trợ lấy thông tin merchant thông qua email business.
218
     * Đây là phương thức ánh xạ của [[request()]] sử dụng lệnh [[RC_GET_MERCHANT_DATA]].
219
     *
220
     * @param string $emailBusiness Email muốn lấy thông tin từ Bảo Kim.
221
     * @param int|string|null $clientId PaymentClient id sử dụng để lấy thông tin.
222 1
     * Nếu không thiết lập [[getDefaultClient()]] sẽ được gọi để xác định client.
223
     * @throws \ReflectionException|\yii\base\InvalidConfigException
224 1
     * @return ResponseData|DataInterface Trả về [[ResponseData]] là dữ liệu của emailBusiness từ Bảo Kim phản hồi.
225
     */
226
    public function getMerchantData(string $emailBusiness = null, $clientId = null): DataInterface
227
    {
228
        /** @var PaymentClient $client */
229
        $client = $this->getClient($clientId);
230
        $cacheKey = [
231
            __METHOD__,
232
            get_class($client),
233
            $client->merchantId,
234
            $emailBusiness
235
        ];
236
237
        if (!$this->merchantDataCache || !$responseData = $this->merchantDataCache->get($cacheKey)) {
238
            $responseData = $this->request(self::RC_GET_MERCHANT_DATA, [
239
                'business' => $emailBusiness ?? $client->merchantEmail
240
            ], $clientId);
241
242
            if ($this->merchantDataCache) {
243 5
                $this->merchantDataCache->set($cacheKey, $responseData, $this->merchantDataCacheDuration);
244
            }
245
        }
246 5
247
        return $responseData;
248 5
    }
249
250 5
    /**
251 5
     * @inheritdoc
252 5
     */
253 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...
254
    {
255
        if ($event->command === self::RC_PURCHASE_PRO) {
256
            $this->trigger(self::EVENT_BEFORE_PURCHASE_PRO, $event);
257
        } elseif ($event->command === self::RC_GET_MERCHANT_DATA) {
258
            $this->trigger(self::EVENT_BEFORE_GET_MERCHANT_DATA, $event);
259
        }
260
261
        parent::beforeRequest($event);
262
    }
263
264
    /**
265
     * @inheritdoc
266
     */
267 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...
268
    {
269 1
        if ($event->command === self::RC_PURCHASE_PRO) {
270
            $this->trigger(self::EVENT_AFTER_PURCHASE_PRO, $event);
271
        } elseif ($event->command === self::RC_GET_MERCHANT_DATA) {
272 1
            $this->trigger(self::EVENT_AFTER_GET_MERCHANT_DATA, $event);
273
        }
274 1
275 1
        parent::afterRequest($event);
276 1
    }
277 1
278
    /**
279
     * @inheritdoc
280 1
     * @throws \yii\base\InvalidConfigException
281 1
     */
282 1
    protected function requestInternal(\vxm\gatewayclients\RequestData $requestData, \yii\httpclient\Client $httpClient): array
283 1
    {
284
        /** @var PaymentClient $client */
285 1
        $client = $requestData->getClient();
286 1
        $command = $requestData->getCommand();
287
        $data = $requestData->get();
288
        $httpMethod = 'POST';
289
        $options = [
290 1
            CURLOPT_HTTPAUTH => CURLAUTH_DIGEST | CURLAUTH_BASIC,
291
            CURLOPT_USERPWD => $client->apiUser . ':' . $client->apiPassword
292
        ];
293
294
        if (in_array($command, [self::RC_GET_MERCHANT_DATA, self::RC_QUERY_DR], true)) {
295
            if ($command === self::RC_GET_MERCHANT_DATA) {
296
                $url = self::PRO_SELLER_INFO_URL;
297
            } else {
298
                $url = self::QUERY_DR_URL;
299
            }
300
            $data[0] = $url;
301
            $url = $data;
302
            $data = null;
303
            $httpMethod = 'GET';
304
        } elseif ($command === self::RC_PURCHASE_PRO) {
305 1
            $url = [self::PURCHASE_PRO_URL, 'signature' => ArrayHelper::remove($data, 'signature')];
306
        } else {
307 1
            $data[0] = self::PURCHASE_URL;
308 1
            return ['redirect_url' => $httpClient->get($data)->getFullUrl()];
309
        }
310
311 1
        return $httpClient->createRequest()
312
            ->setUrl($url)
313
            ->setMethod($httpMethod)
314
            ->addOptions($options)
315
            ->addData($data)
316
            ->send()
317 5
            ->getData();
318
    }
319 5
320 1
    /**
321 4
     * @inheritdoc
322 1
     * @return bool|VerifiedData|DataInterface
323 3
     */
324 1
    public function verifyRequestIPN($clientId = null, \yii\web\Request $request = null)
325
    {
326
        if ($request === null) {
327 5
            $request = Instance::ensure('request', '\yii\web\Request');
328 5
        }
329
330
        $content = $this->getHttpClient()->post(self::VERIFY_IPN_URL, $request->post())->send()->getContent();
331
332
        if (strpos($content, 'VERIFIED') !== false) {
333 5
            return parent::verifyRequestIPN($clientId, $request);
334
        } else {
335 5
            return false;
336 1
        }
337 4
    }
338 1
339 3
    /**
340 1
     * @inheritdoc
341
     */
342
    protected function getVerifyRequestData($command, \yii\web\Request $request): array
343 5
    {
344 5
        $params = [
345
            'order_id', 'transaction_id', 'created_on', 'payment_type', 'transaction_status', 'total_amount', 'net_amount',
346
            'fee_amount', 'merchant_id', 'customer_name', 'customer_email', 'customer_phone', 'customer_address', 'checksum',
347
            'payer_name', 'payer_email', 'payer_phone_no', 'shipping_address', 'verify_sign', 'resend'
348
        ];
349
        $commandRequestMethods = [self::VRC_PURCHASE_SUCCESS => 'get', self::VRC_IPN => 'post'];
350
        $requestMethod = $commandRequestMethods[$command];
351
        $data = [];
352
353 View Code Duplication
        foreach ($params as $param) {
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...
354
            if (($value = call_user_func([$request, $requestMethod], $param)) !== null) {
355
                $data[$param] = $value;
356
            }
357
        }
358
359
        return $data;
360
    }
361
362
}
363