Completed
Push — master ( 02e31a...ea780f )
by Vuong
06:21
created

PaymentGateway::getHttpClientConfig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 8

Duplication

Lines 13
Ratio 100 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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