1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @author Anton Tuyakhov <[email protected]> |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
namespace tuyakhov\braintree; |
8
|
|
|
|
9
|
|
|
use Braintree\Address; |
10
|
|
|
use Braintree\ClientToken; |
11
|
|
|
use Braintree\Configuration; |
12
|
|
|
use Braintree\CreditCard; |
13
|
|
|
use Braintree\Customer; |
14
|
|
|
use Braintree\MerchantAccount; |
15
|
|
|
use Braintree\PaymentMethod; |
16
|
|
|
use Braintree\Plan; |
17
|
|
|
use Braintree\ResourceCollection; |
18
|
|
|
use Braintree\Result\Error; |
19
|
|
|
use Braintree\Result\Successful; |
20
|
|
|
use Braintree\Subscription; |
21
|
|
|
use Braintree\Transaction; |
22
|
|
|
use Braintree\WebhookNotification; |
23
|
|
|
use yii\base\Component; |
24
|
|
|
use yii\base\InvalidConfigException; |
25
|
|
|
use yii\helpers\ArrayHelper; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Class Braintree. |
29
|
|
|
* @package tuyakhov\braintree |
30
|
|
|
* |
31
|
|
|
* @property UrlManager $urlManager provides methods for creating of URLs to Braintree; this property is read-only |
32
|
|
|
*/ |
33
|
|
|
class Braintree extends Component |
34
|
|
|
{ |
35
|
|
|
public $environment = 'sandbox'; |
36
|
|
|
public $merchantId; |
37
|
|
|
public $publicKey; |
38
|
|
|
public $privateKey; |
39
|
|
|
|
40
|
|
|
protected $clientToken; |
41
|
|
|
protected $options; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var UrlManager |
45
|
|
|
*/ |
46
|
|
|
protected $urlManager; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var Plan[] cached plans from Braintree |
50
|
|
|
*/ |
51
|
|
|
protected $plans; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Sets up Braintree configuration from config file. |
55
|
|
|
* @throws InvalidConfigException |
56
|
|
|
*/ |
57
|
9 |
|
public function init() |
58
|
|
|
{ |
59
|
9 |
|
foreach (['merchantId', 'publicKey', 'privateKey', 'environment'] as $attribute) { |
60
|
9 |
|
if ($this->$attribute === null) { |
61
|
|
|
throw new InvalidConfigException( |
62
|
|
|
strtr( |
63
|
|
|
'"{class}::{attribute}" cannot be empty.', |
64
|
|
|
[ |
65
|
|
|
'{class}' => static::class, |
66
|
|
|
'{attribute}' => '$' . $attribute, |
67
|
|
|
] |
68
|
|
|
) |
69
|
|
|
); |
70
|
|
|
} |
71
|
9 |
|
Configuration::$attribute($this->$attribute); |
72
|
|
|
} |
73
|
|
|
|
74
|
9 |
|
parent::init(); |
75
|
9 |
|
} |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Returns the URL manager object. |
79
|
|
|
* @return UrlManager |
80
|
|
|
*/ |
81
|
1 |
|
public function getUrlManager(): UrlManager |
82
|
|
|
{ |
83
|
1 |
|
if (!isset($this->urlManager)) { |
84
|
1 |
|
$this->urlManager = new UrlManager(); |
85
|
|
|
} |
86
|
|
|
|
87
|
1 |
|
return $this->urlManager; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
public function getClientToken($params = []): string |
91
|
|
|
{ |
92
|
|
|
if (!isset($this->clientToken)) { |
93
|
|
|
$this->clientToken = ClientToken::generate($params); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
return $this->clientToken; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Braintree sale function. |
101
|
|
|
* @param bool $submitForSettlement |
102
|
|
|
* @param bool $storeInVaultOnSuccess |
103
|
|
|
* @return array |
104
|
|
|
*/ |
105
|
3 |
|
public function sale(bool $submitForSettlement = true, bool $storeInVaultOnSuccess = true): array |
106
|
|
|
{ |
107
|
3 |
|
$this->options['options']['submitForSettlement'] = $submitForSettlement; |
108
|
3 |
|
$this->options['options']['storeInVaultOnSuccess'] = $storeInVaultOnSuccess; |
109
|
3 |
|
$result = Transaction::sale($this->options); |
110
|
|
|
|
111
|
3 |
|
return ['status' => $result->success, 'result' => $result]; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
public function saleWithServiceFee($merchantAccountId, $amount, $paymentMethodNonce, $serviceFeeAmount): array |
115
|
|
|
{ |
116
|
|
|
$result = Transaction::sale( |
117
|
|
|
[ |
118
|
|
|
'merchantAccountId' => $merchantAccountId, |
119
|
|
|
'amount' => $amount, |
120
|
|
|
'paymentMethodNonce' => $paymentMethodNonce, |
121
|
|
|
'serviceFeeAmount' => $serviceFeeAmount, |
122
|
|
|
] |
123
|
|
|
); |
124
|
|
|
|
125
|
|
|
return ['status' => $result->success, 'result' => $result]; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
public function saleWithPaymentNonce($amount, $paymentMethodNonce): array |
129
|
|
|
{ |
130
|
|
|
$result = Transaction::sale( |
131
|
|
|
[ |
132
|
|
|
'amount' => $amount, |
133
|
|
|
'paymentMethodNonce' => $paymentMethodNonce, |
134
|
|
|
'options' => [ |
135
|
|
|
'submitForSettlement' => true, |
136
|
|
|
'storeInVaultOnSuccess' => true, |
137
|
|
|
], |
138
|
|
|
] |
139
|
|
|
); |
140
|
|
|
|
141
|
|
|
return ['status' => $result->success, 'result' => $result]; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
public function savePaymentMethod(): array |
145
|
|
|
{ |
146
|
|
|
$result = PaymentMethod::create($this->options['paymentMethod']); |
147
|
|
|
|
148
|
|
|
return ['status' => $result->success, 'result' => $result]; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
public function updatePaymentMethod(): array |
152
|
|
|
{ |
153
|
|
|
$result = PaymentMethod::update($this->options['paymentMethodToken'], $this->options['paymentMethod']); |
154
|
|
|
|
155
|
|
|
return ['status' => $result->success, 'result' => $result]; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
public function deletePaymentMethod(): array |
159
|
|
|
{ |
160
|
|
|
$result = PaymentMethod::delete($this->options['paymentMethodToken']); |
161
|
|
|
|
162
|
|
|
return ['status' => $result->success, 'result' => $result]; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* This method saves the customer to the Braintree and returns the resulting array. |
167
|
|
|
* @return array |
168
|
|
|
*/ |
169
|
1 |
|
public function saveCustomer(): array |
170
|
|
|
{ |
171
|
1 |
|
if (isset($this->options['customerId'])) { |
172
|
|
|
$this->options['customer']['id'] = $this->options['customerId']; |
173
|
|
|
} |
174
|
1 |
|
$result = Customer::create($this->options['customer']); |
175
|
|
|
|
176
|
1 |
|
return ['status' => $result->success, 'result' => $result]; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* This method saves the credit card to the Braintree and returns the resulting array. |
181
|
|
|
* @return array |
182
|
|
|
*/ |
183
|
1 |
|
public function saveCreditCard(): array |
184
|
|
|
{ |
185
|
1 |
|
$sendArray = $this->options['creditCard']; |
186
|
1 |
|
if (isset($this->options['billing'])) { |
187
|
|
|
$sendArray['billingAddress'] = $this->options['billing']; |
188
|
|
|
} |
189
|
1 |
|
if (isset($this->options['customerId'])) { |
190
|
1 |
|
$sendArray['customerId'] = $this->options['customerId']; |
191
|
|
|
} |
192
|
1 |
|
$result = CreditCard::create($sendArray); |
193
|
|
|
|
194
|
1 |
|
return ['status' => $result->success, 'result' => $result]; |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
public function saveAddress(): array |
198
|
|
|
{ |
199
|
|
|
$sendArray = $this->options['billing']; |
200
|
|
|
if (isset($this->options['customerId'])) { |
201
|
|
|
$sendArray['customerId'] = $this->options['customerId']; |
202
|
|
|
} |
203
|
|
|
$result = Address::create($sendArray); |
204
|
|
|
|
205
|
|
|
return ['status' => $result->success, 'result' => $result]; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Constructs the array of credit card data for payment. |
210
|
|
|
* @param array $values credit card data, an array with structure: |
211
|
|
|
* [ |
212
|
|
|
* 'number' => int (required) credit card number |
213
|
|
|
* 'cvv' => int (optional) credit card security code |
214
|
|
|
* 'expirationMonth' => int (optional) format: MM |
215
|
|
|
* (use 'expirationMonth' and 'expirationYear' or 'expirationDate', not all at once) |
216
|
|
|
* 'expirationYear' => int (optional) format: YYYY |
217
|
|
|
* (use 'expirationMonth' and 'expirationYear' or 'expirationDate', not all at once) |
218
|
|
|
* 'expirationDate' => string (optional) format: MM/YYYY |
219
|
|
|
* (use 'expirationMonth' and 'expirationYear' or 'expirationDate', not all at once) |
220
|
|
|
* 'cardholderName' => string (optional) the cardholder name associated with the credit card |
221
|
|
|
* ] |
222
|
|
|
*/ |
223
|
3 |
|
public function setCreditCard(array $values) |
224
|
|
|
{ |
225
|
3 |
|
$creditCard = ['number' => $values['number']]; |
226
|
3 |
|
$optionalParamNames = ['cvv', 'expirationMonth', 'expirationYear', 'expirationDate', 'cardholderName']; |
227
|
3 |
|
foreach ($optionalParamNames as $optionalParamName) { |
228
|
3 |
|
$optionalValue = ArrayHelper::getValue($values, $optionalParamName); |
229
|
3 |
|
if (isset($optionalValue)) { |
230
|
3 |
|
$creditCard[$optionalParamName] = $optionalValue; |
231
|
|
|
} |
232
|
|
|
} |
233
|
3 |
|
$this->options['creditCard'] = $creditCard; |
234
|
3 |
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* @param array $values |
238
|
|
|
* @return $this |
239
|
|
|
*/ |
240
|
5 |
|
public function setOptions(array $values): Braintree |
241
|
|
|
{ |
242
|
5 |
|
if (!empty($values)) { |
243
|
5 |
|
foreach ($values as $key => $value) { |
244
|
5 |
|
if ($key === 'amount') { |
245
|
3 |
|
$this->setAmount($values['amount']); |
246
|
5 |
|
} elseif ($key === 'creditCard') { |
247
|
3 |
|
$this->setCreditCard($values['creditCard']); |
248
|
|
|
} else { |
249
|
3 |
|
$this->options[$key] = $value; |
250
|
|
|
} |
251
|
|
|
} |
252
|
|
|
} |
253
|
|
|
|
254
|
5 |
|
return $this; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Set the amount to charge. |
259
|
|
|
* @param float $amount no currency sign needed |
260
|
|
|
*/ |
261
|
3 |
|
public function setAmount(float $amount) |
262
|
|
|
{ |
263
|
3 |
|
$this->options['amount'] = round($amount, 2); |
264
|
3 |
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* @param bool $allowCaching whether to allow caching the result of retrieving of data from Braintree; |
268
|
|
|
* when this parameter is true (default), if data was retrieved before, |
269
|
|
|
* result will be directly returned when calling this method; |
270
|
|
|
* if this parameter is false, this method will always perform request to Braintree to obtain the up-to-date data; |
271
|
|
|
* note that this caching is effective only within the same HTTP request |
272
|
|
|
* @return Plan[] |
273
|
|
|
*/ |
274
|
1 |
|
public function getAllPlans(bool $allowCaching = true): array |
275
|
|
|
{ |
276
|
1 |
|
if (!$allowCaching || !isset($this->plans)) { |
277
|
1 |
|
$this->plans = Plan::all(); |
278
|
|
|
} |
279
|
|
|
|
280
|
1 |
|
return $this->plans; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* @param bool $allowCaching whether to allow caching the result of retrieving of data from Braintree; |
285
|
|
|
* when this parameter is true (default), if data was retrieved before, |
286
|
|
|
* result will be directly returned when calling this method; |
287
|
|
|
* if this parameter is false, this method will always perform request to Braintree to obtain the up-to-date data; |
288
|
|
|
* note that this caching is effective only within the same HTTP request |
289
|
|
|
* @return array |
290
|
|
|
*/ |
291
|
|
|
public function getPlanIds(bool $allowCaching = true): array |
292
|
|
|
{ |
293
|
|
|
$plans = $this->getAllPlans($allowCaching); |
294
|
|
|
$planIds = []; |
295
|
|
|
foreach ($plans as $plan) { |
296
|
|
|
$planIds[] = $plan->id; |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
return $planIds; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* @param string $planId |
304
|
|
|
* @param bool $allowCaching whether to allow caching the result of retrieving of data from Braintree; |
305
|
|
|
* when this parameter is true (default), if data was retrieved before, |
306
|
|
|
* result will be directly returned when calling this method; |
307
|
|
|
* if this parameter is false, this method will always perform request to Braintree to obtain the up-to-date data; |
308
|
|
|
* note that this caching is effective only within the same HTTP request |
309
|
|
|
* @return null|Plan |
310
|
|
|
*/ |
311
|
|
|
public function getPlanById(string $planId, bool $allowCaching = true): ?Plan |
312
|
|
|
{ |
313
|
|
|
$plans = $this->getAllPlans($allowCaching); |
314
|
|
|
foreach ($plans as $plan) { |
315
|
|
|
if ($plan->id === $planId) { |
316
|
|
|
return $plan; |
317
|
|
|
} |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
return null; |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
/** |
324
|
|
|
* Finds transaction by id. |
325
|
|
|
* @param string $id |
326
|
|
|
* @return Transaction |
327
|
|
|
*/ |
328
|
|
|
public function findTransaction(string $id): Transaction |
329
|
|
|
{ |
330
|
|
|
return Transaction::find($id); |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
public function searchTransaction($params = []): ResourceCollection |
334
|
|
|
{ |
335
|
|
|
return Transaction::search($params); |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* @param string $merchantId |
340
|
|
|
* @return MerchantAccount |
341
|
|
|
*/ |
342
|
1 |
|
public function findMerchant(string $merchantId): MerchantAccount |
343
|
|
|
{ |
344
|
1 |
|
return MerchantAccount::find($merchantId); |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* @param string $customerId |
349
|
|
|
* @return Customer |
350
|
|
|
*/ |
351
|
|
|
public function findCustomer(string $customerId): Customer |
352
|
|
|
{ |
353
|
|
|
return Customer::find($customerId); |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
public function findSubscription(string $subscriptionId): Subscription |
357
|
|
|
{ |
358
|
|
|
return Subscription::find($subscriptionId); |
359
|
|
|
} |
360
|
|
|
|
361
|
1 |
|
public function searchSubscription($params = []): ResourceCollection |
362
|
|
|
{ |
363
|
1 |
|
return Subscription::search($params); |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* Create subscription. |
368
|
|
|
* @param array $params |
369
|
|
|
* @return Error|Successful |
370
|
|
|
*/ |
371
|
|
|
public function createSubscription(array $params) |
372
|
|
|
{ |
373
|
|
|
return Subscription::create($params); |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
/** |
377
|
|
|
* Update subscription. |
378
|
|
|
* @param string $subscriptionId |
379
|
|
|
* @param array $params |
380
|
|
|
* @return Error|Successful |
381
|
|
|
*/ |
382
|
|
|
public function updateSubscription(string $subscriptionId, array $params) |
383
|
|
|
{ |
384
|
|
|
return Subscription::update($subscriptionId, $params); |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
/** |
388
|
|
|
* Cancel subscription. |
389
|
|
|
* @param string $subscriptionId |
390
|
|
|
* @return Error|Successful |
391
|
|
|
*/ |
392
|
|
|
public function cancelSubscription(string $subscriptionId) |
393
|
|
|
{ |
394
|
|
|
return Subscription::cancel($subscriptionId); |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
public function retryChargeSubscription(string $subscriptionId, $amount) |
398
|
|
|
{ |
399
|
|
|
$retryResult = Subscription::retryCharge($subscriptionId, $amount); |
400
|
|
|
|
401
|
|
|
if ($retryResult->success) { |
402
|
|
|
$result = Transaction::submitForSettlement($retryResult->transaction->id); |
403
|
|
|
|
404
|
|
|
return $result; |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
return $retryResult; |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
public function parseWebhookNotification(string $signature, $payload): WebhookNotification |
411
|
|
|
{ |
412
|
|
|
return WebhookNotification::parse($signature, $payload); |
413
|
|
|
} |
414
|
|
|
} |
415
|
|
|
|