PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 3.19.4
GiveWP – Donation Plugin and Fundraising Platform v3.19.4
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 4 years ago Exceptions 3 years ago Migrations 2 years ago Models 2 years ago PayPalCheckoutSdk 2 years ago Repositories 2 years 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 2 years ago RefreshToken.php 2 years ago RefundPaymentHandler.php 4 years ago ScriptLoader.php 2 years ago Utils.php 2 years ago onBoardingRedirectHandler.php 1 year ago
AjaxRequestHandler.php
448 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
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 ConnectClient
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 $redirectUrl = add_query_arg(
160 [
161 'tab' => 'gateways',
162 'section' => 'paypal',
163 'group' => 'paypal-commerce',
164 'mode' => $mode,
165 ],
166 admin_url('edit.php?post_type=give_forms&page=give-settings')
167 );
168
169 // Set PayPal client mode.
170 give(PayPalClient::class)->setMode($mode);
171
172 $data = $this->payPalAuth->getSellerPartnerLink($redirectUrl, $accountType);
173
174 if (! $data) {
175 wp_send_json_error();
176 }
177
178 $this->settings->updateAccountCountry($country);
179 $this->settings->updatePartnerLinkDetails($data);
180
181 wp_send_json_success($data);
182 }
183
184 /**
185 * give_paypal_commerce_disconnect_account ajax request handler.
186 *
187 * @since 3.16.0 added security nonce check
188 * @since 3.13.0 Add new $keepWebhooks option
189 * @since 2.30.0 Add support for mode param.
190 * @since 2.25.0 Remove merchant seller token.
191 * @since 2.9.0
192 */
193 public function removePayPalAccount()
194 {
195 check_ajax_referer( 'give_paypal_commerce_disconnect_account');
196
197 if (! current_user_can('manage_give_settings')) {
198 wp_send_json_error(['error' => esc_html__('You are not allowed to perform this action.', 'give')]);
199 }
200
201 try {
202 $mode = give_clean($_POST['mode']);
203 $keepWebhooks = rest_sanitize_boolean($_POST['keep-webhooks']);
204 $this->webhooksRepository->setMode($mode);
205 $this->merchantRepository->setMode($mode);
206 $this->refreshToken->setMode($mode);
207 $this->settings->setMode($mode);
208
209 $this->validateAdminRequest();
210
211 // Remove the webhook from PayPal if there is one
212 if ( ! $keepWebhooks && $webhookConfig = $this->webhooksRepository->getWebhookConfig()) {
213 $this->webhooksRepository->deleteWebhook($this->merchantDetails->accessToken, $webhookConfig->id);
214 $this->webhooksRepository->deleteWebhookConfig();
215 }
216
217 $this->merchantRepository->delete();
218 $this->merchantRepository->deleteAccountErrors();
219 $this->merchantRepository->deleteClientToken();
220 $this->settings->deleteSellerAccessToken();
221 $this->refreshToken->deleteRefreshTokenCronJob();
222
223 wp_send_json_success();
224 } catch (\Exception $exception) {
225 wp_send_json_error(['error' => $exception->getMessage()]);
226 }
227 }
228
229 /**
230 * Create order.
231 *
232 * @todo: handle payment create error on frontend.
233 *
234 * @since 3.1.0 Remove unused variable from createOrder argument.
235 * @since 2.9.0
236 */
237 public function createOrder()
238 {
239 $this->validateFrontendRequest();
240 $data = $this->getOrderData();
241
242 try {
243 $result = give(PayPalOrder::class)->createOrder($data);
244
245 wp_send_json_success(
246 [
247 'id' => $result,
248 ]
249 );
250 } catch (\Exception $ex) {
251 wp_send_json_error(
252 [
253 'error' => json_decode($ex->getMessage(), true),
254 ]
255 );
256 }
257 }
258
259 /**
260 * @since 3.4.2
261 */
262 private function getOrderData(): array
263 {
264 $postData = give_clean($_POST);
265 $formId = absint($postData['give-form-id']);
266 $donorAddress = $this->getDonorAddressFromPostedDataForPaypalOrder($postData);
267
268 return [
269 'formId' => $formId,
270 'formTitle' => give_payment_gateway_item_title(['post_data' => $postData], 127),
271 'donationAmount' => isset($postData['give-amount']) ?
272 (float)apply_filters(
273 'give_donation_total',
274 give_maybe_sanitize_amount(
275 $postData['give-amount'],
276 ['currency' => give_get_currency($formId)]
277 )
278 ) :
279 '0.00',
280 'payer' => [
281 'firstName' => $postData['give_first'],
282 'lastName' => $postData['give_last'],
283 'email' => $postData['give_email'],
284 'address' => $donorAddress,
285 ],
286 ];
287 }
288
289 /**
290 * Approve order.
291 *
292 * @todo: handle payment capture error on frontend.
293 *
294 * @since 3.2.0 Discover error by checking capture status.
295 * @since 2.9.0
296 */
297 public function approveOrder()
298 {
299 $this->validateFrontendRequest();
300
301 $orderId = give_clean($_GET['order']);
302 $updateAmount = filter_var(give_clean($_GET['update_amount']), FILTER_VALIDATE_BOOLEAN);
303
304 try {
305 if ($updateAmount) {
306 give(PayPalOrder::class)->updateOrderAmount($orderId, $this->getOrderData());
307 }
308
309 $result = give(PayPalOrder::class)->approveOrder($orderId);
310 // PayPal does not return error in case of invalid cvv. So we need to check capture status and return error.
311 // ref - https://feedback.givewp.com/bug-reports/p/paypal-credit-card-donations-can-generate-a-fatal-error
312 $this->returnErrorOnFailedApproveOrderResponse($result);
313 wp_send_json_success(['order' => $result,]);
314 } catch (\Exception $ex) {
315 wp_send_json_error(['error' => json_decode($ex->getMessage(), true),]);
316 }
317 }
318
319 /**
320 * @since 3.4.2
321 */
322 public function updateOrderAmount()
323 {
324 $this->validateFrontendRequest();
325
326 $orderId = give_clean($_GET['order']);
327
328 try {
329 give(PayPalOrder::class)->updateOrderAmount($orderId, $this->getOrderData());
330
331 wp_send_json_success(['order' => $orderId,]);
332 } catch (\Exception $ex) {
333 wp_send_json_error(['error' => json_decode($ex->getMessage(), true),]);
334 }
335 }
336
337 /**
338 * Return on boarding trouble notice.
339 *
340 * @since 2.9.6
341 */
342 public function onBoardingTroubleNotice()
343 {
344 if (! current_user_can('manage_give_settings')) {
345 wp_die();
346 }
347
348 /* @var AdminSettingFields $adminSettingFields */
349 $adminSettingFields = give(AdminSettingFields::class);
350
351 $actionList = sprintf(
352 '<ol><li>%1$s</li><li>%2$s</li><li>%3$s</li></ol>',
353 esc_html__(
354 'Make sure to complete the entire PayPal process. Do not close the window until you have finished the process.',
355 'give'
356 ),
357 esc_html__(
358 '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.',
359 'give'
360 ),
361 esc_html__(
362 'If you’re still having problems connecting: ',
363 'give'
364 ) . $adminSettingFields->getAdminGuidanceNotice(false)
365 );
366
367 $standardError = sprintf(
368 '<div id="give-paypal-onboarding-trouble-notice" class="give-hidden"><p class="error-message">%1$s</p><p>%2$s</p></div>',
369 esc_html__('Having trouble connecting to PayPal?', 'give'),
370 $actionList
371 );
372
373 wp_send_json_success($standardError);
374 }
375
376 /**
377 * Validate admin ajax request.
378 *
379 * @since 2.9.0
380 */
381 private function validateAdminRequest()
382 {
383 if (! current_user_can('manage_give_settings')) {
384 wp_die();
385 }
386 }
387
388 /**
389 * Validate frontend ajax request.
390 *
391 * @since 2.9.0
392 */
393 private function validateFrontendRequest()
394 {
395 $formId = absint($_POST['give-form-id']);
396
397 if (! $formId || ! give_verify_donation_form_nonce(give_clean($_POST['give-form-hash']), $formId)) {
398 wp_die();
399 }
400 }
401
402 /**
403 * This function should return address array in PayPal rest api accepted format.
404 *
405 * @since 3.1.0 Return address only if setting enabled and has valida country in PayPal accepted formatted.
406 * @since 2.11.1
407 */
408 private function getDonorAddressFromPostedDataForPaypalOrder(array $postedData): array
409 {
410 if (empty($postedData['billing_country'])) {
411 return [];
412 }
413
414 $address['address_line_1'] = ! empty($postedData['card_address']) ? $postedData['card_address'] : '';
415 $address['address_line_2'] = ! empty($postedData['card_address_2']) ? $postedData['card_address_2'] : '';
416 $address['admin_area_2'] = ! empty($postedData['card_city']) ? $postedData['card_city'] : '';
417 $address['admin_area_1'] = ! empty($postedData['card_state']) ? $postedData['card_state'] : '';
418 $address['postal_code'] = ! empty($postedData['card_zip']) ? $postedData['card_zip'] : '';
419 $address['country_code'] = ! empty($postedData['billing_country']) ? $postedData['billing_country'] : '';
420
421 return $address;
422 }
423
424 /**
425 * This function should validate PayPal ApproveOrder response and respond to ajax request on error.
426 *
427 * @since 3.2.0
428 */
429 private function returnErrorOnFailedApproveOrderResponse(\stdClass $response)
430 {
431 // Get capture.
432 // ref - https://developer.paypal.com/docs/api/orders/v2/#orders_capture
433 $capture = $response->purchase_units[0]->payments->captures[0];
434
435 // Check if capture status is failed or declined.
436 if (
437 in_array($capture->status, ['FAILED', 'DECLINED'])
438 && property_exists($capture, 'processor_response')
439 ) {
440 $error = ProcessorResponseError::getError($capture->processor_response);
441
442 if ($error) {
443 wp_send_json_error(['error' => $error]);
444 }
445 }
446 }
447 }
448