PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 4.14.2
GiveWP – Donation Plugin and Fundraising Platform v4.14.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 9 months ago PayPalCheckoutSdk 1 year ago Repositories 1 year ago Webhooks 2 years ago AccountAdminNotices.php 4 years ago AdminSettingFields.php 7 months ago AdvancedCardFields.php 4 years ago AjaxRequestHandler.php 7 months ago DonationDetailsPage.php 4 years ago DonationFormPaymentMethod.php 2 years ago PayPalClient.php 2 years ago PayPalCommerce.php 10 months ago RefreshToken.php 3 years ago RefundPaymentHandler.php 4 years ago ScriptLoader.php 1 year ago Utils.php 2 years ago onBoardingRedirectHandler.php 7 months ago
AjaxRequestHandler.php
463 lines
1 <?php
2
3 namespace Give\PaymentGateways\PayPalCommerce;
4
5 use Give\PaymentGateways\PayPalCommerce\Models\MerchantDetail;
6 use Give\PaymentGateways\PayPalCommerce\PayPalCheckoutSdk\ProcessorResponseError;
7 use Give\PaymentGateways\PayPalCommerce\Repositories\MerchantDetails;
8 use Give\PaymentGateways\PayPalCommerce\Repositories\PayPalAuth;
9 use Give\PaymentGateways\PayPalCommerce\Repositories\PayPalOrder;
10 use Give\PaymentGateways\PayPalCommerce\Repositories\Settings;
11 use Give\PaymentGateways\PayPalCommerce\Repositories\Webhooks;
12 use Give\Helpers\Form\Utils as FormUtils;
13
14 /**
15 * Class AjaxRequestHandler
16 * @package Give\PaymentGateways\PaypalCommerce
17 *
18 * @sicne 2.9.0
19 */
20 class AjaxRequestHandler
21 {
22 /**
23 * @since 2.9.0
24 *
25 * @var Webhooks
26 */
27 private $webhooksRepository;
28
29 /**
30 * @since 2.9.0
31 *
32 * @var MerchantDetail
33 */
34 private $merchantDetails;
35
36 /**
37 * @since 2.9.0
38 *
39 * @var PayPalAuth
40 */
41 private $payPalAuth;
42
43 /**
44 * @since 2.9.0
45 *
46 * @var MerchantDetails
47 */
48 private $merchantRepository;
49
50 /**
51 * @since 2.9.0
52 *
53 * @var RefreshToken
54 */
55 private $refreshToken;
56
57 /**
58 * @since 2.9.0
59 *
60 * @var Settings
61 */
62 private $settings;
63
64 /**
65 * AjaxRequestHandler constructor.
66 *
67 * @since 2.9.0
68 *
69 * @param Webhooks $webhooksRepository
70 * @param MerchantDetail $merchantDetails
71 * @param MerchantDetails $merchantRepository
72 * @param RefreshToken $refreshToken
73 * @param Settings $settings
74 * @param PayPalAuth $payPalAuth
75 */
76 public function __construct(
77 Webhooks $webhooksRepository,
78 MerchantDetail $merchantDetails,
79 MerchantDetails $merchantRepository,
80 RefreshToken $refreshToken,
81 Settings $settings,
82 PayPalAuth $payPalAuth
83 ) {
84 $this->webhooksRepository = $webhooksRepository;
85 $this->merchantDetails = $merchantDetails;
86 $this->merchantRepository = $merchantRepository;
87 $this->refreshToken = $refreshToken;
88 $this->settings = $settings;
89 $this->payPalAuth = $payPalAuth;
90 }
91
92 /**
93 * give_paypal_commerce_user_onboarded ajax action handler
94 *
95 * @since 2.32.0 Return error response on exception when fetch access token from authorization code.
96 * @since 2.9.0
97 */
98 public function onBoardedUserAjaxRequestHandler()
99 {
100 $this->validateAdminRequest();
101
102 if (empty($_GET['mode']) || ! in_array($_GET['mode'], ['sandbox', 'live'])) {
103 wp_send_json_error('Must include valid mode');
104 }
105
106 $mode = sanitize_text_field(wp_unslash($_GET['mode']));
107
108 // Set PayPal client mode.
109 give(PayPalClient::class)->setMode($mode);
110
111 $partnerLinkInfo = $this->settings->getPartnerLinkDetails();
112
113 try {
114 $payPalResponse = $this->payPalAuth->getTokenFromAuthorizationCode(
115 give_clean($_GET['authCode']),
116 give_clean($_GET['sharedId']),
117 $partnerLinkInfo['nonce']
118 );
119 } catch (\Exception $exception) {
120 wp_send_json_error();
121 }
122
123 $this->settings->updateAccessToken($payPalResponse);
124
125 // Set cron job to refresh token.
126 $refreshToken = give(RefreshToken::class);
127 $refreshToken->setMode($mode);
128 $refreshToken->registerCronJobToRefreshToken($payPalResponse['expiresIn']);
129
130 wp_send_json_success();
131 }
132
133 /**
134 * This function handle ajax request with give_paypal_commerce_get_partner_url action.
135 *
136 * @since 3.0.0 Add support for accountType. This param is required to get partner link.
137 * @since 2.30.0 Add support for mode param.
138 * @since 2.9.0
139 */
140 public function onGetPartnerUrlAjaxRequestHandler()
141 {
142 $this->validateAdminRequest();
143
144 if (empty($accountType = $_GET['accountType']) || ! in_array($accountType, ScriptLoader::$accountTypes, true)) {
145 wp_send_json_error('Must include valid account type');
146 }
147
148 if (empty($country = $_GET['countryCode']) || ! isset(give_get_country_list()[$country])) {
149 wp_send_json_error('Must include valid 2-character country code');
150 }
151
152 if (empty($_GET['mode']) || ! in_array($_GET['mode'], ['sandbox', 'live'])) {
153 wp_send_json_error('Must include valid mode');
154 }
155
156 $country = sanitize_text_field(wp_unslash($_GET['countryCode']));
157 $accountType = sanitize_text_field(wp_unslash($_GET['accountType']));
158 $mode = sanitize_text_field(wp_unslash($_GET['mode']));
159
160 // Generate a unique state token for CSRF protection on PayPal callback.
161 $stateToken = wp_generate_password(32, false);
162 set_transient('give_paypal_onboarding_state_' . $mode, $stateToken, HOUR_IN_SECONDS);
163
164 $redirectUrl = add_query_arg(
165 [
166 'tab' => 'gateways',
167 'section' => 'paypal',
168 'group' => 'paypal-commerce',
169 'mode' => $mode,
170 'give_paypal_state' => $stateToken,
171 ],
172 admin_url('edit.php?post_type=give_forms&page=give-settings')
173 );
174
175 // Set PayPal client mode.
176 give(PayPalClient::class)->setMode($mode);
177
178 $data = $this->payPalAuth->getSellerPartnerLink($redirectUrl, $accountType);
179
180 if (! $data) {
181 wp_send_json_error();
182 }
183
184 $this->settings->updateAccountCountry($country);
185 $this->settings->updatePartnerLinkDetails($data);
186
187 wp_send_json_success($data);
188 }
189
190 /**
191 * give_paypal_commerce_disconnect_account ajax request handler.
192 *
193 * @since 3.16.0 added security nonce check
194 * @since 3.13.0 Add new $keepWebhooks option
195 * @since 2.30.0 Add support for mode param.
196 * @since 2.25.0 Remove merchant seller token.
197 * @since 2.9.0
198 */
199 public function removePayPalAccount()
200 {
201 check_ajax_referer( 'give_paypal_commerce_disconnect_account');
202
203 if (! current_user_can('manage_give_settings')) {
204 wp_send_json_error(['error' => esc_html__('You are not allowed to perform this action.', 'give')]);
205 }
206
207 try {
208 $mode = give_clean($_POST['mode']);
209 $keepWebhooks = rest_sanitize_boolean($_POST['keep-webhooks']);
210 $this->webhooksRepository->setMode($mode);
211 $this->merchantRepository->setMode($mode);
212 $this->refreshToken->setMode($mode);
213 $this->settings->setMode($mode);
214
215 $this->validateAdminRequest();
216
217 // Remove the webhook from PayPal if there is one
218 if ( ! $keepWebhooks && $webhookConfig = $this->webhooksRepository->getWebhookConfig()) {
219 $this->webhooksRepository->deleteWebhook($this->merchantDetails->accessToken, $webhookConfig->id);
220 $this->webhooksRepository->deleteWebhookConfig();
221 }
222
223 $this->merchantRepository->delete();
224 $this->merchantRepository->deleteAccountErrors();
225 $this->merchantRepository->deleteClientToken();
226 $this->settings->deleteSellerAccessToken();
227 $this->refreshToken->deleteRefreshTokenCronJob();
228
229 wp_send_json_success();
230 } catch (\Exception $exception) {
231 wp_send_json_error(['error' => $exception->getMessage()]);
232 }
233 }
234
235 /**
236 * Create order.
237 *
238 * @todo: handle payment create error on frontend.
239 *
240 * @since 3.1.0 Remove unused variable from createOrder argument.
241 * @since 2.9.0
242 */
243 public function createOrder()
244 {
245 $this->validateFrontendRequest();
246 $data = $this->getOrderData();
247
248 try {
249 $result = give(PayPalOrder::class)->createOrder($data);
250
251 wp_send_json_success(
252 [
253 'id' => $result,
254 ]
255 );
256 } catch (\Exception $ex) {
257 wp_send_json_error(
258 [
259 'error' => json_decode($ex->getMessage(), true),
260 ]
261 );
262 }
263 }
264
265 /**
266 * @since 4.2.1 only filter amount for v2 forms
267 * @since 3.4.2
268 */
269 private function getOrderData(): array
270 {
271 $postData = give_clean($_POST);
272 $formId = absint($postData['give-form-id']);
273 $donorAddress = $this->getDonorAddressFromPostedDataForPaypalOrder($postData);
274 $isV3Form = FormUtils::isV3Form($formId);
275
276 if ($isV3Form) {
277 // if coming from v3 forms, fee recovery is already included in the amount and should not be filtered.
278 $amount = isset($postData['give-amount']) ? give_clean($postData['give-amount']) : '0.00';
279 } else {
280 $amount = isset($postData['give-amount']) ?
281 (float)apply_filters(
282 'give_donation_total',
283 give_maybe_sanitize_amount(
284 $postData['give-amount'],
285 ['currency' => give_get_currency($formId)]
286 )
287 ) :
288 '0.00';
289 }
290
291 return [
292 'formId' => $formId,
293 'formTitle' => give_payment_gateway_item_title(['post_data' => $postData], 127),
294 'donationAmount' => $amount,
295 'payer' => [
296 'firstName' => $postData['give_first'],
297 'lastName' => $postData['give_last'],
298 'email' => $postData['give_email'],
299 'address' => $donorAddress,
300 ],
301 ];
302 }
303
304 /**
305 * Approve order.
306 *
307 * @todo: handle payment capture error on frontend.
308 *
309 * @since 3.2.0 Discover error by checking capture status.
310 * @since 2.9.0
311 */
312 public function approveOrder()
313 {
314 $this->validateFrontendRequest();
315
316 $orderId = give_clean($_GET['order']);
317 $updateAmount = filter_var(give_clean($_GET['update_amount']), FILTER_VALIDATE_BOOLEAN);
318
319 try {
320 if ($updateAmount) {
321 give(PayPalOrder::class)->updateOrderAmount($orderId, $this->getOrderData());
322 }
323
324 $result = give(PayPalOrder::class)->approveOrder($orderId);
325 // PayPal does not return error in case of invalid cvv. So we need to check capture status and return error.
326 // ref - https://feedback.givewp.com/bug-reports/p/paypal-credit-card-donations-can-generate-a-fatal-error
327 $this->returnErrorOnFailedApproveOrderResponse($result);
328 wp_send_json_success(['order' => $result,]);
329 } catch (\Exception $ex) {
330 wp_send_json_error(['error' => json_decode($ex->getMessage(), true),]);
331 }
332 }
333
334 /**
335 * @since 3.4.2
336 */
337 public function updateOrderAmount()
338 {
339 $this->validateFrontendRequest();
340
341 $orderId = give_clean($_GET['order']);
342
343 try {
344 give(PayPalOrder::class)->updateOrderAmount($orderId, $this->getOrderData());
345
346 wp_send_json_success(['order' => $orderId,]);
347 } catch (\Exception $ex) {
348 wp_send_json_error(['error' => json_decode($ex->getMessage(), true),]);
349 }
350 }
351
352 /**
353 * Return on boarding trouble notice.
354 *
355 * @since 2.9.6
356 */
357 public function onBoardingTroubleNotice()
358 {
359 if (! current_user_can('manage_give_settings')) {
360 wp_die();
361 }
362
363 /* @var AdminSettingFields $adminSettingFields */
364 $adminSettingFields = give(AdminSettingFields::class);
365
366 $actionList = sprintf(
367 '<ol><li>%1$s</li><li>%2$s</li><li>%3$s</li></ol>',
368 esc_html__(
369 'Make sure to complete the entire PayPal process. Do not close the window until you have finished the process.',
370 'give'
371 ),
372 esc_html__(
373 '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.',
374 'give'
375 ),
376 esc_html__(
377 'If you’re still having problems connecting: ',
378 'give'
379 ) . $adminSettingFields->getAdminGuidanceNotice(false)
380 );
381
382 $standardError = sprintf(
383 '<div id="give-paypal-onboarding-trouble-notice" class="give-hidden"><p class="error-message">%1$s</p><p>%2$s</p></div>',
384 esc_html__('Having trouble connecting to PayPal?', 'give'),
385 $actionList
386 );
387
388 wp_send_json_success($standardError);
389 }
390
391 /**
392 * Validate admin ajax request.
393 *
394 * @since 2.9.0
395 */
396 private function validateAdminRequest()
397 {
398 if (! current_user_can('manage_give_settings')) {
399 wp_die();
400 }
401 }
402
403 /**
404 * Validate frontend ajax request.
405 *
406 * @since 2.9.0
407 */
408 private function validateFrontendRequest()
409 {
410 $formId = absint($_POST['give-form-id']);
411
412 if (! $formId || ! give_verify_donation_form_nonce(give_clean($_POST['give-form-hash']), $formId)) {
413 wp_die();
414 }
415 }
416
417 /**
418 * This function should return address array in PayPal rest api accepted format.
419 *
420 * @since 3.1.0 Return address only if setting enabled and has valida country in PayPal accepted formatted.
421 * @since 2.11.1
422 */
423 private function getDonorAddressFromPostedDataForPaypalOrder(array $postedData): array
424 {
425 if (empty($postedData['billing_country'])) {
426 return [];
427 }
428
429 $address['address_line_1'] = ! empty($postedData['card_address']) ? $postedData['card_address'] : '';
430 $address['address_line_2'] = ! empty($postedData['card_address_2']) ? $postedData['card_address_2'] : '';
431 $address['admin_area_2'] = ! empty($postedData['card_city']) ? $postedData['card_city'] : '';
432 $address['admin_area_1'] = ! empty($postedData['card_state']) ? $postedData['card_state'] : '';
433 $address['postal_code'] = ! empty($postedData['card_zip']) ? $postedData['card_zip'] : '';
434 $address['country_code'] = ! empty($postedData['billing_country']) ? $postedData['billing_country'] : '';
435
436 return $address;
437 }
438
439 /**
440 * This function should validate PayPal ApproveOrder response and respond to ajax request on error.
441 *
442 * @since 3.2.0
443 */
444 private function returnErrorOnFailedApproveOrderResponse(\stdClass $response)
445 {
446 // Get capture.
447 // ref - https://developer.paypal.com/docs/api/orders/v2/#orders_capture
448 $capture = $response->purchase_units[0]->payments->captures[0];
449
450 // Check if capture status is failed or declined.
451 if (
452 in_array($capture->status, ['FAILED', 'DECLINED'])
453 && property_exists($capture, 'processor_response')
454 ) {
455 $error = ProcessorResponseError::getError($capture->processor_response);
456
457 if ($error) {
458 wp_send_json_error(['error' => $error]);
459 }
460 }
461 }
462 }
463