Completed
Push — master ( 22f05f...6ee38e )
by Vuong
02:09
created

PaymentGateway::getBaseUrl()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

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