1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Srmklive\PayPal\Traits; |
4
|
|
|
|
5
|
|
|
use GuzzleHttp\Client as HttpClient; |
6
|
|
|
use GuzzleHttp\Exception\BadResponseException as HttpBadResponseException; |
7
|
|
|
use GuzzleHttp\Exception\ClientException as HttpClientException; |
8
|
|
|
use GuzzleHttp\Exception\ServerException as HttpServerException; |
9
|
|
|
use Illuminate\Support\Collection; |
10
|
|
|
|
11
|
|
|
trait PayPalRequest |
12
|
|
|
{ |
13
|
|
|
/** |
14
|
|
|
* Http Client class object. |
15
|
|
|
* |
16
|
|
|
* @var HttpClient |
17
|
|
|
*/ |
18
|
|
|
private $client; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* PayPal API mode to be used. |
22
|
|
|
* |
23
|
|
|
* @var string |
24
|
|
|
*/ |
25
|
|
|
public $mode; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Request data to be sent to PayPal. |
29
|
|
|
* |
30
|
|
|
* @var \Illuminate\Support\Collection |
31
|
|
|
*/ |
32
|
|
|
protected $post; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* PayPal API configuration. |
36
|
|
|
* |
37
|
|
|
* @var array |
38
|
|
|
*/ |
39
|
|
|
private $config; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Default currency for PayPal. |
43
|
|
|
* |
44
|
|
|
* @var string |
45
|
|
|
*/ |
46
|
|
|
private $currency; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Additional options for PayPal API request. |
50
|
|
|
* |
51
|
|
|
* @var array |
52
|
|
|
*/ |
53
|
|
|
private $options; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Default payment action for PayPal. |
57
|
|
|
* |
58
|
|
|
* @var string |
59
|
|
|
*/ |
60
|
|
|
private $paymentAction; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Default locale for PayPal. |
64
|
|
|
* |
65
|
|
|
* @var string |
66
|
|
|
*/ |
67
|
|
|
private $locale; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* PayPal API Endpoint. |
71
|
|
|
* |
72
|
|
|
* @var string |
73
|
|
|
*/ |
74
|
|
|
private $apiUrl; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* IPN notification url for PayPal. |
78
|
|
|
* |
79
|
|
|
* @var string |
80
|
|
|
*/ |
81
|
|
|
private $notifyUrl; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Http Client request body parameter name. |
85
|
|
|
* |
86
|
|
|
* @var string |
87
|
|
|
*/ |
88
|
|
|
private $httpBodyParam; |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Function To Set PayPal API Configuration. |
92
|
|
|
* |
93
|
|
|
* @param array $config |
94
|
|
|
* |
95
|
|
|
* @return void |
96
|
|
|
*/ |
97
|
|
|
private function setConfig(array $config = []) |
98
|
|
|
{ |
99
|
|
|
// Setting Http Client |
100
|
|
|
$this->client = $this->setClient(); |
101
|
|
|
|
102
|
|
|
// Set Api Credentials |
103
|
|
|
if (function_exists('config')) { |
104
|
|
|
$this->setApiCredentials( |
105
|
|
|
config('paypal') |
106
|
|
|
); |
107
|
|
|
} elseif (!empty($config)) { |
108
|
|
|
$this->setApiCredentials($config); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
// Set options to be empty. |
112
|
|
|
$this->options = []; |
113
|
|
|
|
114
|
|
|
$this->setRequestData(); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Function to initialize Http Client. |
119
|
|
|
* |
120
|
|
|
* @return HttpClient |
121
|
|
|
*/ |
122
|
|
|
protected function setClient() |
123
|
|
|
{ |
124
|
|
|
return new HttpClient([ |
125
|
|
|
'curl' => [ |
126
|
|
|
CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2, |
127
|
|
|
], |
128
|
|
|
]); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Set PayPal API Credentials. |
133
|
|
|
* |
134
|
|
|
* @param array $credentials |
135
|
|
|
* |
136
|
|
|
* @throws \Exception |
137
|
|
|
* |
138
|
|
|
* @return void |
139
|
|
|
*/ |
140
|
|
|
public function setApiCredentials($credentials) |
141
|
|
|
{ |
142
|
|
|
// Setting Default PayPal Mode If not set |
143
|
|
|
$this->setApiEnvironment($credentials); |
144
|
|
|
|
145
|
|
|
// Set API configuration for the PayPal provider |
146
|
|
|
$this->setApiProviderConfiguration($credentials); |
147
|
|
|
|
148
|
|
|
// Set default currency. |
149
|
|
|
$this->setCurrency($credentials['currency']); |
150
|
|
|
|
151
|
|
|
// Set default payment action. |
152
|
|
|
$this->paymentAction = $this->config['payment_action']; |
153
|
|
|
|
154
|
|
|
// Set default locale. |
155
|
|
|
$this->locale = $this->config['locale']; |
156
|
|
|
|
157
|
|
|
// Set PayPal API Endpoint. |
158
|
|
|
$this->apiUrl = $this->config['api_url']; |
159
|
|
|
|
160
|
|
|
// Set PayPal IPN Notification URL |
161
|
|
|
$this->notifyUrl = $credentials['notify_url']; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Set API environment to be used by PayPal. |
166
|
|
|
* |
167
|
|
|
* @param array $credentials |
168
|
|
|
* |
169
|
|
|
* @return void |
170
|
|
|
*/ |
171
|
|
|
private function setApiEnvironment($credentials) |
172
|
|
|
{ |
173
|
|
|
if (empty($credentials['mode']) || !in_array($credentials['mode'], ['sandbox', 'live'])) { |
174
|
|
|
$this->mode = 'live'; |
175
|
|
|
} else { |
176
|
|
|
$this->mode = $credentials['mode']; |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* Set configuration details for the provider. |
182
|
|
|
* |
183
|
|
|
* @param array $credentials |
184
|
|
|
* |
185
|
|
|
* @throws \Exception |
186
|
|
|
* |
187
|
|
|
* @return void |
188
|
|
|
*/ |
189
|
|
|
private function setApiProviderConfiguration($credentials) |
190
|
|
|
{ |
191
|
|
|
// Setting PayPal API Credentials |
192
|
|
|
collect($credentials[$this->mode])->map(function ($value, $key) { |
193
|
|
|
$this->config[$key] = $value; |
194
|
|
|
}); |
195
|
|
|
|
196
|
|
|
// Setup PayPal API Signature value to use. |
197
|
|
|
$this->config['signature'] = empty($this->config['certificate']) ? |
198
|
|
|
$this->config['secret'] : file_get_contents($this->config['certificate']); |
199
|
|
|
|
200
|
|
|
if ($this instanceof \Srmklive\PayPal\Services\AdaptivePayments) { |
201
|
|
|
$this->setAdaptivePaymentsOptions(); |
202
|
|
|
} elseif ($this instanceof \Srmklive\PayPal\Services\ExpressCheckout) { |
203
|
|
|
$this->setExpressCheckoutOptions($credentials); |
|
|
|
|
204
|
|
|
} else { |
205
|
|
|
throw new \Exception('Invalid api credentials provided for PayPal!. Please provide the right api credentials.'); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
if (empty($credentials['payment_action'])) { |
209
|
|
|
$credentials['payment_action'] = 'Sale'; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
if (empty($credentials['locale'])) { |
213
|
|
|
$credentials['locale'] = 'en_US'; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
// Adding params outside sandbox / live array |
217
|
|
|
$this->config['payment_action'] = $credentials['payment_action']; |
218
|
|
|
$this->config['notify_url'] = $credentials['notify_url']; |
219
|
|
|
$this->config['locale'] = $credentials['locale']; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* Setup request data to be sent to PayPal. |
224
|
|
|
* |
225
|
|
|
* @param array $data |
226
|
|
|
* |
227
|
|
|
* @return \Illuminate\Support\Collection |
228
|
|
|
*/ |
229
|
|
|
protected function setRequestData(array $data = []) |
230
|
|
|
{ |
231
|
|
|
if (($this->post instanceof Collection) && ($this->post->isNotEmpty())) { |
232
|
|
|
unset($this->post); |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
$this->post = new Collection($data); |
236
|
|
|
|
237
|
|
|
return $this->post; |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* Set other/override PayPal API parameters. |
242
|
|
|
* |
243
|
|
|
* @param array $options |
244
|
|
|
* |
245
|
|
|
* @return $this |
246
|
|
|
*/ |
247
|
|
|
public function addOptions(array $options) |
248
|
|
|
{ |
249
|
|
|
$this->options = $options; |
250
|
|
|
|
251
|
|
|
return $this; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* Function to set currency. |
256
|
|
|
* |
257
|
|
|
* @param string $currency |
258
|
|
|
* |
259
|
|
|
* @throws \Exception |
260
|
|
|
* |
261
|
|
|
* @return $this |
262
|
|
|
*/ |
263
|
|
|
public function setCurrency($currency = 'USD') |
264
|
|
|
{ |
265
|
|
|
$allowedCurrencies = ['AUD', 'BRL', 'CAD', 'CZK', 'DKK', 'EUR', 'HKD', 'HUF', 'ILS', 'JPY', 'MYR', 'MXN', 'NOK', 'NZD', 'PHP', 'PLN', 'GBP', 'SGD', 'SEK', 'CHF', 'TWD', 'THB', 'USD', 'RUB']; |
266
|
|
|
|
267
|
|
|
// Check if provided currency is valid. |
268
|
|
|
if (!in_array($currency, $allowedCurrencies)) { |
269
|
|
|
throw new \Exception('Currency is not supported by PayPal.'); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
$this->currency = $currency; |
273
|
|
|
|
274
|
|
|
return $this; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* Retrieve PayPal IPN Response. |
279
|
|
|
* |
280
|
|
|
* @param array $post |
281
|
|
|
* |
282
|
|
|
* @return array |
283
|
|
|
*/ |
284
|
|
|
public function verifyIPN($post) |
285
|
|
|
{ |
286
|
|
|
$this->setRequestData($post); |
287
|
|
|
|
288
|
|
|
$this->apiUrl = $this->config['gateway_url'].'/cgi-bin/webscr'; |
289
|
|
|
|
290
|
|
|
return $this->doPayPalRequest('verifyipn'); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* Refund PayPal Transaction. |
295
|
|
|
* |
296
|
|
|
* @param string $transaction |
297
|
|
|
* @param float $amount |
298
|
|
|
* |
299
|
|
|
* @return array |
300
|
|
|
*/ |
301
|
|
|
public function refundTransaction($transaction, $amount = 0.00) |
302
|
|
|
{ |
303
|
|
|
$this->setRequestData([ |
304
|
|
|
'TRANSACTIONID' => $transaction, |
305
|
|
|
]); |
306
|
|
|
|
307
|
|
|
if ($amount > 0) { |
308
|
|
|
$this->post->merge([ |
309
|
|
|
'REFUNDTYPE' => 'Partial', |
310
|
|
|
'AMT' => $amount, |
311
|
|
|
]); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
return $this->doPayPalRequest('RefundTransaction'); |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Search Transactions On PayPal. |
319
|
|
|
* |
320
|
|
|
* @param array $post |
321
|
|
|
* |
322
|
|
|
* @return array |
323
|
|
|
*/ |
324
|
|
|
public function searchTransactions($post) |
325
|
|
|
{ |
326
|
|
|
$this->setRequestData($post); |
327
|
|
|
|
328
|
|
|
return $this->doPayPalRequest('TransactionSearch'); |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
/** |
332
|
|
|
* Create request payload to be sent to PayPal. |
333
|
|
|
* |
334
|
|
|
* @param string $method |
335
|
|
|
*/ |
336
|
|
|
private function createRequestPayload($method) |
337
|
|
|
{ |
338
|
|
|
$config = array_merge([ |
339
|
|
|
'USER' => $this->config['username'], |
340
|
|
|
'PWD' => $this->config['password'], |
341
|
|
|
'SIGNATURE' => $this->config['signature'], |
342
|
|
|
'VERSION' => 123, |
343
|
|
|
'METHOD' => $method, |
344
|
|
|
], $this->options); |
345
|
|
|
|
346
|
|
|
$this->post = $this->post->merge($config) |
347
|
|
|
->filter(function ($value, $key) use ($method) { |
348
|
|
|
return (($method === 'verifyipn') && ($key === 'METHOD')) ?: $value; |
349
|
|
|
}); |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
/** |
353
|
|
|
* Perform PayPal API request & return response. |
354
|
|
|
* |
355
|
|
|
* @throws \Exception |
356
|
|
|
* |
357
|
|
|
* @return \Psr\Http\Message\StreamInterface |
358
|
|
|
*/ |
359
|
|
|
private function makeHttpRequest() |
360
|
|
|
{ |
361
|
|
|
try { |
362
|
|
|
return $this->client->post($this->apiUrl, [ |
363
|
|
|
$this->httpBodyParam => $this->post->toArray(), |
364
|
|
|
])->getBody(); |
365
|
|
|
} catch (HttpClientException $e) { |
366
|
|
|
throw new \Exception($e->getRequest().' '.$e->getResponse()); |
367
|
|
|
} catch (HttpServerException $e) { |
368
|
|
|
throw new \Exception($e->getRequest().' '.$e->getResponse()); |
369
|
|
|
} catch (HttpBadResponseException $e) { |
370
|
|
|
throw new \Exception($e->getRequest().' '.$e->getResponse()); |
371
|
|
|
} |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
/** |
375
|
|
|
* Function To Perform PayPal API Request. |
376
|
|
|
* |
377
|
|
|
* @param string $method |
378
|
|
|
* |
379
|
|
|
* @throws \Exception |
380
|
|
|
* |
381
|
|
|
* @return array|\Psr\Http\Message\StreamInterface |
382
|
|
|
*/ |
383
|
|
|
private function doPayPalRequest($method) |
384
|
|
|
{ |
385
|
|
|
// Setup PayPal API Request Payload |
386
|
|
|
$this->createRequestPayload($method); |
387
|
|
|
|
388
|
|
|
try { |
389
|
|
|
// Perform PayPal HTTP API request. |
390
|
|
|
$response = $this->makeHttpRequest(); |
391
|
|
|
|
392
|
|
|
return $this->retrieveData($method, $response); |
393
|
|
|
} catch (\Exception $e) { |
394
|
|
|
$message = collect($e->getTrace())->implode('\n'); |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
return [ |
398
|
|
|
'type' => 'error', |
399
|
|
|
'message' => $message, |
400
|
|
|
]; |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
/** |
404
|
|
|
* Parse PayPal NVP Response. |
405
|
|
|
* |
406
|
|
|
* @param string $method |
407
|
|
|
* @param mixed $response |
408
|
|
|
* |
409
|
|
|
* @return array |
410
|
|
|
*/ |
411
|
|
|
private function retrieveData($method, $response) |
412
|
|
|
{ |
413
|
|
|
if ($method === 'verifyipn') { |
414
|
|
|
return $response; |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
parse_str($response, $output); |
418
|
|
|
|
419
|
|
|
return $output; |
420
|
|
|
} |
421
|
|
|
} |
422
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.