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

PaymentGateway::getVerifyRequestData()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 12

Duplication

Lines 5
Ratio 26.32 %

Code Coverage

Tests 9
CRAP Score 3

Importance

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