PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 4.3.2
GiveWP – Donation Plugin and Fundraising Platform v4.3.2
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 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / src / PaymentGateways / PayPalCommerce / AjaxRequestHandler.php
give / src / PaymentGateways / PayPalCommerce Last commit date
Banners 2 years ago DataTransferObjects 1 year ago Exceptions 3 years ago Migrations 3 years ago Models 2 years ago PayPalCheckoutSdk 1 year ago Repositories 1 year ago Webhooks 2 years ago AccountAdminNotices.php 4 years ago AdminSettingFields.php 1 year ago AdvancedCardFields.php 4 years ago AjaxRequestHandler.php 1 year ago DonationDetailsPage.php 4 years ago DonationFormPaymentMethod.php 2 years ago PayPalClient.php 2 years ago PayPalCommerce.php 1 year ago RefreshToken.php 3 years ago RefundPaymentHandler.php 4 years ago ScriptLoader.php 1 year ago Utils.php 2 years ago onBoardingRedirectHandler.php 1 year ago
AjaxRequestHandler.php
458 lines
1 <?php
2
3 namespace Give\PaymentGateways\PayPalCommerce;
4
5 use Give\Framework\Http\ConnectServer\Client\ConnectClient;
6 use Give\PaymentGateways\PayPalCommerce\Models\MerchantDetail;
7 use Give\PaymentGateways\PayPalCommerce\PayPalCheckoutSdk\ProcessorResponseError;
8 use Give\PaymentGateways\PayPalCommerce\Repositories\MerchantDetails;
9 use Give\PaymentGateways\PayPalCommerce\Repositories\PayPalAuth;
10 use Give\PaymentGateways\PayPalCommerce\Repositories\PayPalOrder;
11 use Give\PaymentGateways\PayPalCommerce\Repositories\Settings;
12 use Give\PaymentGateways\PayPalCommerce\Repositories\Webhooks;
13 use Give\Helpers\Form\Utils as FormUtils;
14
15 /**
16 * Class AjaxRequestHandler
17 * @package Give\PaymentGateways\PaypalCommerce
18 *
19 * @sicne 2.9.0
20 */
21 class AjaxRequestHandler
22 {
23 /**
24 * @since 2.9.0
25 *
26 * @var Webhooks
27 */
28 private $webhooksRepository;
29
30 /**
31 * @since 2.9.0
32 *
33 * @var MerchantDetail
34 */
35 private $merchantDetails;
36
37 /**
38 * @since 2.9.0
39 *
40 * @var PayPalAuth
41 */
42 private $payPalAuth;
43
44 /**
45 * @since 2.9.0
46 *
47 * @var MerchantDetails
48 */
49 private $merchantRepository;
50
51 /**
52 * @since 2.9.0
53 *
54 * @var ConnectClient
55 */
56 private $refreshToken;
57
58 /**
59 * @since 2.9.0
60 *
61 * @var Settings
62 */
63 private $settings;
64
65 /**
66 * AjaxRequestHandler constructor.
67 *
68 * @since 2.9.0
69 *
70 * @param Webhooks $webhooksRepository
71 * @param MerchantDetail $merchantDetails
72 * @param MerchantDetails $merchantRepository
73 * @param RefreshToken $refreshToken
74 * @param Settings $settings
75 * @param PayPalAuth $payPalAuth
76 */
77 public function __construct(
78 Webhooks $webhooksRepository,
79 MerchantDetail $merchantDetails,
80 MerchantDetails $merchantRepository,
81 RefreshToken $refreshToken,
82 Settings $settings,
83 PayPalAuth $payPalAuth
84 ) {
85 $this->webhooksRepository = $webhooksRepository;
86 $this->merchantDetails = $merchantDetails;
87 $this->merchantRepository = $merchantRepository;
88 $this->refreshToken = $refreshToken;
89 $this->settings = $settings;
90 $this->payPalAuth = $payPalAuth;
91 }
92
93 /**
94 * give_paypal_commerce_user_onboarded ajax action handler
95 *
96 * @since 2.32.0 Return error response on exception when fetch access token from authorization code.
97 * @since 2.9.0
98 */
99 public function onBoardedUserAjaxRequestHandler()
100 {
101 $this->validateAdminRequest();
102
103 if (empty($_GET['mode']) || ! in_array($_GET['mode'], ['sandbox', 'live'])) {
104 wp_send_json_error('Must include valid mode');
105 }
106
107 $mode = sanitize_text_field(wp_unslash($_GET['mode']));
108
109 // Set PayPal client mode.
110 give(PayPalClient::class)->setMode($mode);
111
112 $partnerLinkInfo = $this->settings->getPartnerLinkDetails();
113
114 try {
115 $payPalResponse = $this->payPalAuth->getTokenFromAuthorizationCode(
116 give_clean($_GET['authCode']),
117 give_clean($_GET['sharedId']),
118 $partnerLinkInfo['nonce']
119 );
120 } catch (\Exception $exception) {
121 wp_send_json_error();
122 }
123
124 $this->settings->updateAccessToken($payPalResponse);
125
126 // Set cron job to refresh token.
127 $refreshToken = give(RefreshToken::class);
128 $refreshToken->setMode($mode);
129 $refreshToken->registerCronJobToRefreshToken($payPalResponse['expiresIn']);
130
131 wp_send_json_success();
132 }
133
134 /**
135 * This function handle ajax request with give_paypal_commerce_get_partner_url action.
136 *
137 * @since 3.0.0 Add support for accountType. This param is required to get partner link.
138 * @since 2.30.0 Add support for mode param.
139 * @since 2.9.0
140 */
141 public function onGetPartnerUrlAjaxRequestHandler()
142 {
143 $this->validateAdminRequest();
144
145 if (empty($accountType = $_GET['accountType']) || ! in_array($accountType, ScriptLoader::$accountTypes, true)) {
146 wp_send_json_error('Must include valid account type');
147 }
148
149 if (empty($country = $_GET['countryCode']) || ! isset(give_get_country_list()[$country])) {
150 wp_send_json_error('Must include valid 2-character country code');
151 }
152
153 if (empty($_GET['mode']) || ! in_array($_GET['mode'], ['sandbox', 'live'])) {
154 wp_send_json_error('Must include valid mode');
155 }
156
157 $country = sanitize_text_field(wp_unslash($_GET['countryCode']));
158 $accountType = sanitize_text_field(wp_unslash($_GET['accountType']));
159 $mode = sanitize_text_field(wp_unslash($_GET['mode']));
160 $redirectUrl = add_query_arg(
161 [
162 'tab' => 'gateways',
163 'section' => 'paypal',
164 'group' => 'paypal-commerce',
165 'mode' => $mode,
166 ],
167 admin_url('edit.php?post_type=give_forms&page=give-settings')
168 );
169
170 // Set PayPal client mode.
171 give(PayPalClient::class)->setMode($mode);
172
173 $data = $this->payPalAuth->getSellerPartnerLink($redirectUrl, $accountType);
174
175 if (! $data) {
176 wp_send_json_error();
177 }
178
179 $this->settings->updateAccountCountry($country);
180 $this->settings->updatePartnerLinkDetails($data);
181
182 wp_send_json_success($data);
183 }
184
185 /**
186 * give_paypal_commerce_disconnect_account ajax request handler.
187 *
188 * @since 3.16.0 added security nonce check
189 * @since 3.13.0 Add new $keepWebhooks option
190 * @since 2.30.0 Add support for mode param.
191 * @since 2.25.0 Remove merchant seller token.
192 * @since 2.9.0
193 */
194 public function removePayPalAccount()
195 {
196 check_ajax_referer( 'give_paypal_commerce_disconnect_account');
197
198 if (! current_user_can('manage_give_settings')) {
199 wp_send_json_error(['error' => esc_html__('You are not allowed to perform this action.', 'give')]);
200 }
201
202 try {
203 $mode = give_clean($_POST['mode']);
204 $keepWebhooks = rest_sanitize_boolean($_POST['keep-webhooks']);
205 $this->webhooksRepository->setMode($mode);
206 $this->merchantRepository->setMode($mode);
207 $this->refreshToken->setMode($mode);
208 $this->settings->setMode($mode);
209
210 $this->validateAdminRequest();
211
212 // Remove the webhook from PayPal if there is one
213 if ( ! $keepWebhooks && $webhookConfig = $this->webhooksRepository->getWebhookConfig()) {
214 $this->webhooksRepository->deleteWebhook($this->merchantDetails->accessToken, $webhookConfig->id);
215 $this->webhooksRepository->deleteWebhookConfig();
216 }
217
218 $this->merchantRepository->delete();
219 $this->merchantRepository->deleteAccountErrors();
220 $this->merchantRepository->deleteClientToken();
221 $this->settings->deleteSellerAccessToken();
222 $this->refreshToken->deleteRefreshTokenCronJob();
223
224 wp_send_json_success();
225 } catch (\Exception $exception) {
226 wp_send_json_error(['error' => $exception->getMessage()]);
227 }
228 }
229
230 /**
231 * Create order.
232 *
233 * @todo: handle payment create error on frontend.
234 *
235 * @since 3.1.0 Remove unused variable from createOrder argument.
236 * @since 2.9.0
237 */
238 public function createOrder()
239 {
240 $this->validateFrontendRequest();
241 $data = $this->getOrderData();
242
243 try {
244 $result = give(PayPalOrder::class)->createOrder($data);
245
246 wp_send_json_success(
247 [
248 'id' => $result,
249 ]
250 );
251 } catch (\Exception $ex) {
252 wp_send_json_error(
253 [
254 'error' => json_decode($ex->getMessage(), true),
255 ]
256 );
257 }
258 }
259
260 /**
261 * @since 4.2.1 only filter amount for v2 forms
262 * @since 3.4.2
263 */
264 private function getOrderData(): array
265 {
266 $postData = give_clean($_POST);
267 $formId = absint($postData['give-form-id']);
268 $donorAddress = $this->getDonorAddressFromPostedDataForPaypalOrder($postData);
269 $isV3Form = FormUtils::isV3Form($formId);
270
271 if ($isV3Form) {
272 // if coming from v3 forms, fee recovery is already included in the amount and should not be filtered.
273 $amount = isset($postData['give-amount']) ? give_clean($postData['give-amount']) : '0.00';
274 } else {
275 $amount = isset($postData['give-amount']) ?
276 (float)apply_filters(
277 'give_donation_total',
278 give_maybe_sanitize_amount(
279 $postData['give-amount'],
280 ['currency' => give_get_currency($formId)]
281 )
282 ) :
283 '0.00';
284 }
285
286 return [
287 'formId' => $formId,
288 'formTitle' => give_payment_gateway_item_title(['post_data' => $postData], 127),
289 'donationAmount' => $amount,
290 'payer' => [
291 'firstName' => $postData['give_first'],
292 'lastName' => $postData['give_last'],
293 'email' => $postData['give_email'],
294 'address' => $donorAddress,
295 ],
296 ];
297 }
298
299 /**
300 * Approve order.
301 *
302 * @todo: handle payment capture error on frontend.
303 *
304 * @since 3.2.0 Discover error by checking capture status.
305 * @since 2.9.0
306 */
307 public function approveOrder()
308 {
309 $this->validateFrontendRequest();
310
311 $orderId = give_clean($_GET['order']);
312 $updateAmount = filter_var(give_clean($_GET['update_amount']), FILTER_VALIDATE_BOOLEAN);
313
314 try {
315 if ($updateAmount) {
316 give(PayPalOrder::class)->updateOrderAmount($orderId, $this->getOrderData());
317 }
318
319 $result = give(PayPalOrder::class)->approveOrder($orderId);
320 // PayPal does not return error in case of invalid cvv. So we need to check capture status and return error.
321 // ref - https://feedback.givewp.com/bug-reports/p/paypal-credit-card-donations-can-generate-a-fatal-error
322 $this->returnErrorOnFailedApproveOrderResponse($result);
323 wp_send_json_success(['order' => $result,]);
324 } catch (\Exception $ex) {
325 wp_send_json_error(['error' => json_decode($ex->getMessage(), true),]);
326 }
327 }
328
329 /**
330 * @since 3.4.2
331 */
332 public function updateOrderAmount()
333 {
334 $this->validateFrontendRequest();
335
336 $orderId = give_clean($_GET['order']);
337
338 try {
339 give(PayPalOrder::class)->updateOrderAmount($orderId, $this->getOrderData());
340
341 wp_send_json_success(['order' => $orderId,]);
342 } catch (\Exception $ex) {
343 wp_send_json_error(['error' => json_decode($ex->getMessage(), true),]);
344 }
345 }
346
347 /**
348 * Return on boarding trouble notice.
349 *
350 * @since 2.9.6
351 */
352 public function onBoardingTroubleNotice()
353 {
354 if (! current_user_can('manage_give_settings')) {
355 wp_die();
356 }
357
358 /* @var AdminSettingFields $adminSettingFields */
359 $adminSettingFields = give(AdminSettingFields::class);
360
361 $actionList = sprintf(
362 '<ol><li>%1$s</li><li>%2$s</li><li>%3$s</li></ol>',
363 esc_html__(
364 'Make sure to complete the entire PayPal process. Do not close the window until you have finished the process.',
365 'give'
366 ),
367 esc_html__(
368 'The last screen of the PayPal connect process includes a button to be sent back to your site. It is important you click this and do not close the window yourself.',
369 'give'
370 ),
371 esc_html__(
372 'If you’re still having problems connecting: ',
373 'give'
374 ) . $adminSettingFields->getAdminGuidanceNotice(false)
375 );
376
377 $standardError = sprintf(
378 '<div id="give-paypal-onboarding-trouble-notice" class="give-hidden"><p class="error-message">%1$s</p><p>%2$s</p></div>',
379 esc_html__('Having trouble connecting to PayPal?', 'give'),
380 $actionList
381 );
382
383 wp_send_json_success($standardError);
384 }
385
386 /**
387 * Validate admin ajax request.
388 *
389 * @since 2.9.0
390 */
391 private function validateAdminRequest()
392 {
393 if (! current_user_can('manage_give_settings')) {
394 wp_die();
395 }
396 }
397
398 /**
399 * Validate frontend ajax request.
400 *
401 * @since 2.9.0
402 */
403 private function validateFrontendRequest()
404 {
405 $formId = absint($_POST['give-form-id']);
406
407 if (! $formId || ! give_verify_donation_form_nonce(give_clean($_POST['give-form-hash']), $formId)) {
408 wp_die();
409 }
410 }
411
412 /**
413 * This function should return address array in PayPal rest api accepted format.
414 *
415 * @since 3.1.0 Return address only if setting enabled and has valida country in PayPal accepted formatted.
416 * @since 2.11.1
417 */
418 private function getDonorAddressFromPostedDataForPaypalOrder(array $postedData): array
419 {
420 if (empty($postedData['billing_country'])) {
421 return [];
422 }
423
424 $address['address_line_1'] = ! empty($postedData['card_address']) ? $postedData['card_address'] : '';
425 $address['address_line_2'] = ! empty($postedData['card_address_2']) ? $postedData['card_address_2'] : '';
426 $address['admin_area_2'] = ! empty($postedData['card_city']) ? $postedData['card_city'] : '';
427 $address['admin_area_1'] = ! empty($postedData['card_state']) ? $postedData['card_state'] : '';
428 $address['postal_code'] = ! empty($postedData['card_zip']) ? $postedData['card_zip'] : '';
429 $address['country_code'] = ! empty($postedData['billing_country']) ? $postedData['billing_country'] : '';
430
431 return $address;
432 }
433
434 /**
435 * This function should validate PayPal ApproveOrder response and respond to ajax request on error.
436 *
437 * @since 3.2.0
438 */
439 private function returnErrorOnFailedApproveOrderResponse(\stdClass $response)
440 {
441 // Get capture.
442 // ref - https://developer.paypal.com/docs/api/orders/v2/#orders_capture
443 $capture = $response->purchase_units[0]->payments->captures[0];
444
445 // Check if capture status is failed or declined.
446 if (
447 in_array($capture->status, ['FAILED', 'DECLINED'])
448 && property_exists($capture, 'processor_response')
449 ) {
450 $error = ProcessorResponseError::getError($capture->processor_response);
451
452 if ($error) {
453 wp_send_json_error(['error' => $error]);
454 }
455 }
456 }
457 }
458