PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.15.4
GiveWP – Donation Plugin and Fundraising Platform v4.15.4
4.18.0 4.17.0 4.16.9 4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 All 257 releases
give / src / PaymentGateways / PayPalCommerce / Repositories / PayPalOrder.php

PayPalOrder.php in GiveWP – Donation Plugin and Fundraising Platform 4.15.4, at src/PaymentGateways/PayPalCommerce/Repositories/PayPalOrder.php

449 lines 13.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Give\PaymentGateways\PayPalCommerce\Repositories;
4
5 use Exception;
6 use Give\Donations\Models\Donation;
7 use Give\Framework\Exceptions\Primitives\InvalidArgumentException;
8 use Give\Framework\PaymentGateways\Log\PaymentGatewayLog;
9 use Give\Framework\Support\ValueObjects\Money;
10 use Give\PaymentGateways\PayPalCommerce\Models\MerchantDetail;
11 use Give\PaymentGateways\PayPalCommerce\Models\PayPalOrder as PayPalOrderModel;
12 use Give\PaymentGateways\PayPalCommerce\PayPalClient;
13 use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
14 use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
15 use PayPalCheckoutSdk\Orders\OrdersGetRequest;
16 use PayPalCheckoutSdk\Orders\OrdersPatchRequest;
17 use PayPalCheckoutSdk\Payments\CapturesRefundRequest;
18 use PayPalHttp\HttpException;
19 use PayPalHttp\IOException;
20
21 use function give_record_gateway_error as logError;
22
23 /**
24 * Class PayPalOrder
25 * @package Give\PaymentGateways\PayPalCommerce\Repositories
26 *
27 * @since 2.9.0
28 */
29 class PayPalOrder
30 {
31 /**
32 * @since 2.9.0
33 *
34 * @var PayPalClient
35 */
36 private $paypalClient;
37
38 /**
39 * @since 2.9.0
40 *
41 * @var MerchantDetail
42 */
43 private $merchantDetails;
44
45 /**
46 * @since 2.16.2
47 * @var Settings
48 */
49 private $settings;
50
51 /**
52 * PayPalOrder constructor.
53 *
54 * @since 2.9.0
55 * @since 2.16.2 Add third param "Settings" to function
56 *
57 * @param PayPalClient $paypalClient
58 * @param MerchantDetail $merchantDetails
59 * @param Settings $settings
60 */
61 public function __construct(PayPalClient $paypalClient, MerchantDetail $merchantDetails, Settings $settings)
62 {
63 $this->paypalClient = $paypalClient;
64 $this->merchantDetails = $merchantDetails;
65 $this->settings = $settings;
66 }
67
68 /**
69 * Approve order.
70 *
71 * @since 4.1.0 Add PayPal-Partner-Attribution-Id header
72 * @since 2.9.0
73 *
74 * @return object
75 * @throws Exception
76 * @see https://developer.paypal.com/docs/api/orders/v2/#orders_capture
77 *
78 */
79 public function approveOrder(string $orderId)
80 {
81 $request = new OrdersCaptureRequest($orderId);
82 $request->headers["PayPal-Partner-Attribution-Id"] = give('PAYPAL_COMMERCE_ATTRIBUTION_ID');
83
84 try {
85 return $this->paypalClient->getHttpClient()->execute($request)->result;
86 } catch (Exception $exception) {
87 PaymentGatewayLog::error(
88 'Capture PayPal Commerce payment failure',
89 [
90 'response' => $exception->getMessage()
91 ]
92 );
93
94 throw $exception;
95 }
96 }
97
98 /**
99 * Create order.
100 *
101 * @see https://developer.paypal.com/docs/api/orders/v2
102 *
103 * @since 4.1.0 updated to include 3d secure params for card payments
104 * @since 3.4.2 Extract the amount parameters to a separate method
105 * @since 3.1.0 "payer" argument is deprecated, using payment_source/paypal.
106 * @since 2.9.0
107 * @since 2.16.2 Conditionally set transaction as donation or standard transaction in PayPal.
108 *
109 * @throws Exception|HttpException|IOException
110 */
111 public function createOrder(array $array, string $intent = 'CAPTURE'): string
112 {
113 $this->validateCreateOrderArguments($array);
114
115 $request = new OrdersCreateRequest();
116 $request->payPalPartnerAttributionId(give('PAYPAL_COMMERCE_ATTRIBUTION_ID'));
117
118 $purchaseUnits = array_merge(
119 $this->getAmountParameters($array),
120 [
121 'description' => $array['formTitle'],
122 'payee' => [
123 'email_address' => $this->merchantDetails->merchantId,
124 'merchant_id' => $this->merchantDetails->merchantIdInPayPal,
125 ],
126 ]
127 );
128
129 if ($intent === 'CAPTURE') {
130 $purchaseUnits = array_merge($purchaseUnits, [
131 'payment_instruction' => [
132 'disbursement_mode' => 'INSTANT',
133 ]
134 ]);
135 }
136
137 $requestBody = [
138 'intent' => $intent,
139 'payment_source' => [
140 "paypal" => [
141 'name' => [
142 "given_name" => $array['payer']['firstName'],
143 "surname" => $array['payer']['lastName'],
144 ],
145 "email_address" => $array['payer']['email'],
146 ],
147 'card' => [
148 'attributes' => [
149 'verification' => [
150 'method' => 'SCA_WHEN_REQUIRED'
151 ]
152 ]
153 ]
154 ],
155 'purchase_units' => [
156 $purchaseUnits
157 ],
158 'application_context' => [
159 'shipping_preference' => 'NO_SHIPPING',
160 'user_action' => 'PAY_NOW',
161 ],
162 ];
163
164 if (!empty($array['payer']['address'])){
165 $requestBody['payment_source']['paypal']['address'] = $array['payer']['address'];
166 }
167
168 $request->body = $requestBody;
169
170 try {
171 return $this->paypalClient->getHttpClient()->execute($request)->result->id;
172 } catch (Exception $exception) {
173 PaymentGatewayLog::error(
174 'Create PayPal Commerce order failure',
175 [
176 'response' => $exception->getMessage()
177 ]
178 );
179
180 throw $exception;
181 }
182 }
183
184 /**
185 * @since 3.4.2
186 */
187 private function getAmountParameters($array): array
188 {
189 $formId = (int)$array['formId'];
190 $donationCurrency = give_get_currency($formId);
191 $donationAmount = give_maybe_sanitize_amount(
192 $array['donationAmount'],
193 ['currency' => give_get_currency($formId)]
194 );
195
196 /**
197 * To make an update, you must provide a reference_id. If you OMIT THIS VALUE WITH AN ORDER THAT CONTAINS
198 * ONLY ONE PURCHASE UNIT, PayPal sets the value to default which enables you to use the path:
199 * "/purchase_units/@reference_id=='default'/{attribute-or-object}".
200 *
201 * @see https://developer.paypal.com/docs/api/orders/v2/#orders_patch
202 */
203 $amountParameters = [
204 //'reference_id' => get_post_field('post_name', $formId),
205 'amount' => [
206 'value' => $donationAmount,
207 'currency_code' => $donationCurrency,
208 ],
209 ];
210
211 // Set PayPal transaction as donation.
212 if ($this->settings->isTransactionTypeDonation()) {
213 $amountParameters['items'] = [
214 [
215 'name' => get_post_field('post_name', $formId),
216 'unit_amount' => [
217 'value' => $donationAmount,
218 'currency_code' => $donationCurrency,
219 ],
220 'quantity' => 1,
221 'category' => 'DONATION',
222 ],
223 ];
224
225 $amountParameters['amount']['breakdown'] = [
226 'item_total' => [
227 'currency_code' => $donationCurrency,
228 'value' => $donationAmount,
229 ],
230 ];
231 }
232
233 return $amountParameters;
234 }
235
236 /**
237 * @since 4.1.0 Add PayPal-Partner-Attribution-Id header
238 * @since 3.4.2
239 *
240 * @return mixed
241 *
242 * @throws Exception|HttpException|IOException
243 * @see https://github.com/paypal/Checkout-PHP-SDK/blob/develop/samples/PatchOrder.php
244 *
245 */
246 public function updateOrderAmount($orderId, array $array)
247 {
248 $this->validateCreateOrderArguments($array);
249
250 $patchRequest = new OrdersPatchRequest($orderId);
251
252 $patchRequest->headers["PayPal-Partner-Attribution-Id"] = give('PAYPAL_COMMERCE_ATTRIBUTION_ID');
253
254 $patchRequest->body = [
255 0 => [
256 'op' => 'replace',
257 'path' => '/intent',
258 'value' => 'CAPTURE',
259 ],
260 1 => [
261 'op' => 'replace',
262 'path' => "/purchase_units/@reference_id=='default'",
263 'value' => $this->getAmountParameters($array),
264 ],
265 ];
266
267 try {
268 return $this->paypalClient->getHttpClient()->execute($patchRequest)->result->id;
269 } catch (Exception $ex) {
270 logError(
271 'Update PayPal Commerce order failure',
272 sprintf(
273 '<strong>Request</strong><pre>%1$s</pre><br><strong>Response</strong><pre>%2$s</pre>',
274 print_r($patchRequest->body, true),
275 print_r(json_decode($ex->getMessage(), true), true)
276 )
277 );
278
279 throw $ex;
280 }
281 }
282
283 /**
284 * Update order amount using the Donation model.
285 *
286 * @since 4.2.1 updated to support donation category
287 * @since 4.1.0 Add PayPal-Partner-Attribution-Id header
288 * @since 4.0.0
289 *
290 * @throws Exception|HttpException|IOException
291 * @see https://developer.paypal.com/docs/api/orders/v2/#orders_patch
292 *
293 */
294 public function updateOrderFromDonation(string $orderId, Donation $donation)
295 {
296 $patchRequest = new OrdersPatchRequest($orderId);
297
298 $patchRequest->headers["PayPal-Partner-Attribution-Id"] = give('PAYPAL_COMMERCE_ATTRIBUTION_ID');
299
300 $value = $donation->amount->formatToDecimal();
301 $currency = $donation->amount->getCurrency()->getCode();
302 $name = $donation->formTitle;
303
304 $amountParameters = [
305 'amount' => [
306 'value' => $value,
307 'currency_code' => $currency
308 ],
309 ];
310
311 if ($this->settings->isTransactionTypeDonation()) {
312 $amountParameters['items'] = [
313 [
314 'name' => $name,
315 'unit_amount' => [
316 'value' => $value,
317 'currency_code' => $currency,
318 ],
319 'quantity' => 1,
320 'category' => 'DONATION',
321 ],
322 ];
323
324 $amountParameters['amount']['breakdown'] = [
325 'item_total' => [
326 'currency_code' => $currency,
327 'value' => $value,
328 ],
329 ];
330 }
331
332 $patchRequest->body = [
333 [
334 'op' => 'replace',
335 'path' => "/purchase_units/@reference_id=='default'",
336 'value' => $amountParameters
337 ],
338 ];
339
340 try {
341 return $this->paypalClient->getHttpClient()->execute($patchRequest)->result->id;
342 } catch (Exception $exception) {
343 PaymentGatewayLog::error(
344 'Update PayPal Commerce order failure',
345 [
346 'response' => $exception->getMessage(),
347 'request' => $patchRequest->body
348 ]
349 );
350
351 throw $exception;
352 }
353 }
354
355 /**
356 * Refunds a processed payment
357 *
358 * @since 4.1.0 Add PayPal-Partner-Attribution-Id header
359 * @since 2.9.0
360 *
361 * @param $captureId
362 *
363 * @return string The id of the refund
364 * @throws Exception
365 */
366 public function refundPayment($captureId)
367 {
368 $refund = new CapturesRefundRequest($captureId);
369
370 $refund->headers["PayPal-Partner-Attribution-Id"] = give('PAYPAL_COMMERCE_ATTRIBUTION_ID');
371
372 try {
373 return $this->paypalClient->getHttpClient()->execute($refund)->result->id;
374 } catch (Exception $exception) {
375 logError(
376 'Create PayPal Commerce payment refund failure',
377 sprintf(
378 '<strong>Response</strong><pre>%1$s</pre>',
379 print_r(json_decode($exception->getMessage(), true), true)
380 )
381 );
382
383 throw $exception;
384 }
385 }
386
387 /**
388 * Validate argument given to create PayPal order.
389 *
390 * @since 2.9.0
391 *
392 * @param array $array
393 *
394 * @throws InvalidArgumentException
395 */
396 private function validateCreateOrderArguments($array)
397 {
398 $required = ['formId', 'donationAmount', 'payer'];
399 $array = array_filter($array); // Remove empty values.
400
401 if (array_diff($required, array_keys($array))) {
402 throw new InvalidArgumentException(
403 __(
404 'To create a paypal order, please provide formId, donationAmount and payer',
405 'give'
406 )
407 );
408 }
409 }
410
411 /**
412 * Get order details from paypal commerce.
413 *
414 * @since 4.1.0 Add PayPal-Partner-Attribution-Id header
415 * @since 2.19.0
416 *
417 * @param string $orderId
418 *
419 * @return PayPalOrderModel
420 * @throws HttpException | IOException
421 */
422 public function getOrder($orderId)
423 {
424 $orderDetailRequest = new OrdersGetRequest($orderId);
425 $orderDetailRequest->headers["PayPal-Partner-Attribution-Id"] = give('PAYPAL_COMMERCE_ATTRIBUTION_ID');
426 $orderDetails = (array)$this->paypalClient->getHttpClient()->execute($orderDetailRequest)->result;
427
428 return PayPalOrderModel::fromArray($orderDetails);
429 }
430
431 /**
432 * Get order details from PayPal commerce.
433 *
434 * @see https://developer.paypal.com/docs/api/orders/v2/#orders_get
435 *
436 * @since 4.1.0 Add PayPal-Partner-Attribution-Id header
437 * @since 4.0.0
438 *
439 * @throws HttpException | IOException
440 */
441 public function getApprovedOrder(string $orderId)
442 {
443 $orderDetailRequest = new OrdersGetRequest($orderId);
444 $orderDetailRequest->headers["PayPal-Partner-Attribution-Id"] = give('PAYPAL_COMMERCE_ATTRIBUTION_ID');
445
446 return $this->paypalClient->getHttpClient()->execute($orderDetailRequest)->result;
447 }
448 }
449