1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Yii2 extension for payment processing with Omnipay, Payum and more later. |
4
|
|
|
* |
5
|
|
|
* @link https://github.com/hiqdev/yii2-merchant |
6
|
|
|
* @package yii2-merchant |
7
|
|
|
* @license BSD-3-Clause |
8
|
|
|
* @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace hiqdev\yii2\merchant; |
12
|
|
|
|
13
|
|
|
use Closure; |
14
|
|
|
use hiqdev\php\merchant\AbstractMerchant; |
15
|
|
|
use hiqdev\php\merchant\Helper; |
16
|
|
|
use hiqdev\yii2\merchant\Collection; |
17
|
|
|
use hiqdev\yii2\merchant\models\Deposit; |
18
|
|
|
use hiqdev\yii2\merchant\controllers\PayController; |
19
|
|
|
use hiqdev\yii2\merchant\transactions\Transaction; |
20
|
|
|
use hiqdev\yii2\merchant\transactions\TransactionException; |
21
|
|
|
use hiqdev\yii2\merchant\transactions\TransactionRepositoryInterface; |
22
|
|
|
use Yii; |
23
|
|
|
use yii\base\InvalidConfigException; |
24
|
|
|
use yii\helpers\ArrayHelper; |
25
|
|
|
use yii\helpers\FileHelper; |
26
|
|
|
use yii\helpers\Json; |
27
|
|
|
use yii\helpers\Url; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Merchant Module. |
31
|
|
|
* |
32
|
|
|
* Example application configuration: |
33
|
|
|
* |
34
|
|
|
* ```php |
35
|
|
|
* 'modules' => [ |
36
|
|
|
* 'merchant' => [ |
37
|
|
|
* 'class' => 'hiqdev\yii2\merchant\Module', |
38
|
|
|
* 'notifyPage' => '/my/notify/page', |
39
|
|
|
* 'collection' => [ |
40
|
|
|
* 'PayPal' => [ |
41
|
|
|
* 'purse' => $params['paypal_purse'], |
42
|
|
|
* 'secret' => $params['paypal_secret'], /// NEVER keep secret in source control |
43
|
|
|
* ], |
44
|
|
|
* 'webmoney_usd' => [ |
45
|
|
|
* 'gateway' => 'WebMoney', |
46
|
|
|
* 'purse' => $params['webmoney_purse'], |
47
|
|
|
* 'secret' => $params['webmoney_secret'], /// NEVER keep secret in source control |
48
|
|
|
* ], |
49
|
|
|
* ], |
50
|
|
|
* ], |
51
|
|
|
* ], |
52
|
|
|
* ``` |
53
|
|
|
* |
54
|
|
|
* @var string returns username for usage in merchant |
55
|
|
|
*/ |
56
|
|
|
class Module extends \yii\base\Module |
57
|
|
|
{ |
58
|
|
|
/** |
59
|
|
|
* The URL prefix that will be used as a key to save current URL in the session. |
60
|
|
|
* |
61
|
|
|
* @see rememberUrl() |
62
|
|
|
* @see previousUrl() |
63
|
|
|
* @see \yii\helpers\BaseUrl::remember() |
64
|
|
|
* @see \yii\helpers\BaseUrl::previous() |
65
|
|
|
*/ |
66
|
|
|
const URL_PREFIX = 'merchant_url_'; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var string merchant library name. Defaults to `Omnipay` |
70
|
|
|
*/ |
71
|
|
|
public $merchantLibrary = 'Omnipay'; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @var string merchant collection class name. Defaults to [[Collection]] |
75
|
|
|
*/ |
76
|
|
|
public $collectionClass = Collection::class; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @var string Deposit model class name. Defaults to [[Deposit]] |
80
|
|
|
*/ |
81
|
|
|
public $depositClass = Deposit::class; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @var array|Closure list of merchants |
85
|
|
|
*/ |
86
|
|
|
protected $_collection = []; |
87
|
|
|
/** |
88
|
|
|
* @var TransactionRepositoryInterface |
89
|
|
|
*/ |
90
|
|
|
protected $transactionRepository; |
91
|
|
|
|
92
|
|
|
public function __construct($id, $parent = null, TransactionRepositoryInterface $transactionRepository, array $config = []) |
93
|
|
|
{ |
94
|
|
|
parent::__construct($id, $parent, $config); |
95
|
|
|
$this->transactionRepository = $transactionRepository; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @param array $params parameters for collection |
100
|
|
|
* @return AbstractMerchant[] list of merchants |
101
|
|
|
*/ |
102
|
|
|
public function getCollection(array $params = []) |
103
|
|
|
{ |
104
|
|
|
return Yii::createObject(array_merge([ |
105
|
|
|
'class' => $this->collectionClass, |
106
|
|
|
'module' => $this, |
107
|
|
|
'params' => $params, |
108
|
|
|
], (array) $this->_collection)); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* @param string $id merchant id |
113
|
|
|
* @param array $params parameters for collection |
114
|
|
|
* @return AbstractMerchant merchant instance |
115
|
|
|
*/ |
116
|
|
|
public function getMerchant($id, array $params = []) |
117
|
|
|
{ |
118
|
|
|
return $this->getCollection($params)->get($id); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Checks if merchant exists in the hub. |
123
|
|
|
* |
124
|
|
|
* @param string $id merchant id |
125
|
|
|
* @return bool whether merchant exist |
126
|
|
|
*/ |
127
|
|
|
public function hasMerchant($id) |
128
|
|
|
{ |
129
|
|
|
return $this->getCollection()->has($id); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Creates merchant instance from its array configuration. |
134
|
|
|
* |
135
|
|
|
* @param string $id ID |
136
|
|
|
* @param array $config merchant instance configuration |
137
|
|
|
* @return AbstractMerchant merchant instance |
138
|
|
|
*/ |
139
|
|
|
public function createMerchant($id, array $config) |
140
|
|
|
{ |
141
|
|
|
return Helper::create(array_merge([ |
142
|
|
|
'library' => $this->merchantLibrary, |
143
|
|
|
'gateway' => $id, |
144
|
|
|
'id' => $id, |
145
|
|
|
], $config)); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Method builds data for merchant request. |
150
|
|
|
* |
151
|
|
|
* @param string $merchant |
152
|
|
|
* @param array $data request data |
153
|
|
|
* @return array |
154
|
|
|
*/ |
155
|
|
|
public function prepareRequestData($merchant, array $data) |
156
|
|
|
{ |
157
|
|
|
$data = array_merge([ |
158
|
|
|
'merchant' => $merchant, |
159
|
|
|
'description' => Yii::$app->request->getServerName() . ' deposit: ' . $this->username, |
|
|
|
|
160
|
|
|
'transactionId' => uniqid(), |
161
|
|
|
], $data); |
162
|
|
|
|
163
|
|
|
return array_merge([ |
164
|
|
|
'notifyUrl' => $this->buildUrl('notify', $data), |
165
|
|
|
'returnUrl' => $this->buildUrl('return', $data), |
166
|
|
|
'cancelUrl' => $this->buildUrl('cancel', $data), |
167
|
|
|
'finishUrl' => $this->buildUrl('finish', $data), |
168
|
|
|
'returnMethod' => 'POST', |
169
|
|
|
'cancelMethod' => 'POST', |
170
|
|
|
], $data); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* @var string client login |
175
|
|
|
*/ |
176
|
|
|
protected $_username; |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Sets [[_username]]. |
180
|
|
|
* |
181
|
|
|
* @param $username |
182
|
|
|
*/ |
183
|
|
|
public function setUsername($username) |
184
|
|
|
{ |
185
|
|
|
$this->_username = $username; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Gets [[_username]] when defined, otherwise - `Yii::$app->user->identity->username`, |
190
|
|
|
* otherwise `Yii::$app->user->identity->getId()`. |
191
|
|
|
* @throws InvalidConfigException |
192
|
|
|
* @return string |
193
|
|
|
*/ |
194
|
|
|
public function getUsername() |
195
|
|
|
{ |
196
|
|
|
if (isset($this->_username)) { |
197
|
|
|
return $this->_username; |
198
|
|
|
} elseif (($identity = Yii::$app->user->identity) !== null) { |
199
|
|
|
if ($identity->hasProperty('username')) { |
200
|
|
|
$this->_username = $identity->username; |
201
|
|
|
} else { |
202
|
|
|
$this->_username = $identity->getId(); |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
return $this->_username; |
206
|
|
|
} |
207
|
|
|
throw new InvalidConfigException('Unable to determine username'); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* @var string|array the URL that will be used for payment system notifications. Will be passed through [[Url::to()]] |
212
|
|
|
*/ |
213
|
|
|
public $notifyPage = 'notify'; |
214
|
|
|
/** |
215
|
|
|
* @var string|array the URL that will be used to redirect client from the merchant after the success payment. |
216
|
|
|
* Will be passed through [[Url::to()]] |
217
|
|
|
*/ |
218
|
|
|
public $returnPage = 'return'; |
219
|
|
|
/** |
220
|
|
|
* @var string|array the URL that will be used to redirect client from the merchant after the failed payment. |
221
|
|
|
* Will be passed through [[Url::to()]] |
222
|
|
|
*/ |
223
|
|
|
public $cancelPage = 'cancel'; |
224
|
|
|
/** |
225
|
|
|
* @var string|array the URL that might be used to redirect used from the success or error page to the finish page. |
226
|
|
|
* Will be passed through [[Url::to()]] |
227
|
|
|
*/ |
228
|
|
|
public $finishPage = 'finish'; |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* Builds URLs that will be passed in the request to the merchant. |
232
|
|
|
* |
233
|
|
|
* @param string $destination `notify`, `return`, `cancel` |
234
|
|
|
* @param array $data data, that will be used to build URL. Only `merchant` and `transactionId` keys |
235
|
|
|
* will be used from the array |
236
|
|
|
* @return string URL |
237
|
|
|
*/ |
238
|
|
|
public function buildUrl($destination, array $data) |
239
|
|
|
{ |
240
|
|
|
$page = array_merge([ |
241
|
|
|
'username' => $this->username, |
|
|
|
|
242
|
|
|
'merchant' => $data['merchant'], |
243
|
|
|
'transactionId' => $data['transactionId'], |
244
|
|
|
], (array) $this->getPage($destination, $data)); |
245
|
|
|
|
246
|
|
|
if (is_array($page)) { |
247
|
|
|
$page[0] = $this->localizePage($page[0]); |
248
|
|
|
} else { |
249
|
|
|
$page = $this->localizePage($page); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
return Url::to($page, true); |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* Builds url to `this_module/pay/$page` if page is not /full/page. |
257
|
|
|
* @param mixed $page |
258
|
|
|
* @return mixed |
259
|
|
|
*/ |
260
|
|
|
public function localizePage($page) |
261
|
|
|
{ |
262
|
|
|
return is_string($page) && $page[0] !== '/' ? ('/' . $this->id . '/pay/' . $page) : $page; |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
public function getPage($destination, array $data) |
266
|
|
|
{ |
267
|
|
|
$name = $destination . 'Page'; |
268
|
|
|
if (isset($data[$name])) { |
269
|
|
|
return $data[$name]; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
return $this->hasProperty($name) ? $this->{$name} : $destination; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* Saves the $url to session with [[URL_PREFIX]] key, trailed with $name. |
277
|
|
|
* |
278
|
|
|
* @param array|string $url |
279
|
|
|
* @param string $name the trailing part for the URL save key. Defaults to `back` |
280
|
|
|
* @void |
281
|
|
|
*/ |
282
|
|
|
public function rememberUrl($url, $name = 'back') |
283
|
|
|
{ |
284
|
|
|
Url::remember($url, static::URL_PREFIX . $name); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Extracts the URL from session storage, saved with [[URL_PREFIX]] key, trailed with $name. |
289
|
|
|
* |
290
|
|
|
* @param string $name the trailing part for the URL save key. Defaults to `back` |
291
|
|
|
* @return string |
292
|
|
|
*/ |
293
|
|
|
public function previousUrl($name = 'back') |
294
|
|
|
{ |
295
|
|
|
return Url::previous(static::URL_PREFIX . $name); |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
/** |
299
|
|
|
* @var PayController The Payment controller |
300
|
|
|
*/ |
301
|
|
|
protected $_payController; |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* @throws InvalidConfigException |
305
|
|
|
* |
306
|
|
|
* @return PayController |
307
|
|
|
*/ |
308
|
|
|
public function getPayController() |
309
|
|
|
{ |
310
|
|
|
if ($this->_payController === null) { |
311
|
|
|
$this->_payController = $this->createControllerById('pay'); |
|
|
|
|
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
return $this->_payController; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Renders page, that contains list of payment systems, that might be choosen by user. |
319
|
|
|
* Should be implemented in `PayController`. |
320
|
|
|
* |
321
|
|
|
* @param array $params |
322
|
|
|
* @return \yii\web\Response |
323
|
|
|
*/ |
324
|
|
|
public function renderDeposit(array $params) |
325
|
|
|
{ |
326
|
|
|
return $this->getPayController()->renderDeposit($params); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* @param Transaction $transaction |
331
|
|
|
* @return Transaction |
332
|
|
|
*/ |
333
|
|
|
public function saveTransaction($transaction) |
334
|
|
|
{ |
335
|
|
|
if ($transaction->isCompleted()) { |
336
|
|
|
return $transaction; |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
return $this->transactionRepository->save($transaction); |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
public function createTransaction($data) |
343
|
|
|
{ |
344
|
|
|
$id = ArrayHelper::remove($data, 'transactionId', uniqid()); |
345
|
|
|
$merchant = ArrayHelper::remove($data, 'merchant'); |
346
|
|
|
if (empty($merchant)) { |
347
|
|
|
throw new InvalidConfigException('Merchant is required to create a transaction'); |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
return $this->transactionRepository->create($id, $merchant, $data); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
public function insertTransaction($data) |
354
|
|
|
{ |
355
|
|
|
$transaction = $this->createTransaction($data); |
356
|
|
|
|
357
|
|
|
return $this->transactionRepository->insert($transaction); |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
/** |
361
|
|
|
* @param string $id transaction ID |
362
|
|
|
* @return Transaction|null |
363
|
|
|
*/ |
364
|
|
|
public function findTransaction($id) |
365
|
|
|
{ |
366
|
|
|
try { |
367
|
|
|
return $this->transactionRepository->findById($id); |
368
|
|
|
} catch (TransactionException $e) { |
369
|
|
|
return null; |
370
|
|
|
} |
371
|
|
|
} |
372
|
|
|
} |
373
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.