PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 2.29.0
GiveWP – Donation Plugin and Fundraising Platform v2.29.0
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 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 / onBoardingRedirectHandler.php
give / src / PaymentGateways / PayPalCommerce Last commit date
DataTransferObjects 4 years ago Exceptions 3 years ago Migrations 4 years ago Models 3 years ago PayPalCheckoutSdk 3 years ago Repositories 3 years ago Webhooks 4 years ago AccountAdminNotices.php 4 years ago AdminSettingFields.php 3 years ago AdvancedCardFields.php 4 years ago AjaxRequestHandler.php 3 years ago DonationDetailsPage.php 4 years ago DonationFormPaymentMethod.php 4 years ago PayPalClient.php 3 years ago PayPalCommerce.php 3 years ago RefreshToken.php 3 years ago RefundPaymentHandler.php 4 years ago ScriptLoader.php 3 years ago Utils.php 4 years ago onBoardingRedirectHandler.php 3 years ago
onBoardingRedirectHandler.php
466 lines
1 <?php
2
3 namespace Give\PaymentGateways\PayPalCommerce;
4
5 use Exception;
6 use Give\Framework\Exceptions\Primitives\Exception as GiveException;
7 use Give\Log\Log;
8 use Give\PaymentGateways\PayPalCommerce\Models\MerchantDetail;
9 use Give\PaymentGateways\PayPalCommerce\Repositories\MerchantDetails;
10 use Give\PaymentGateways\PayPalCommerce\Repositories\PayPalAuth;
11 use Give\PaymentGateways\PayPalCommerce\Repositories\Settings;
12 use Give\PaymentGateways\PayPalCommerce\Repositories\Webhooks;
13 use Give_Admin_Settings;
14
15 /**
16 * Class PayPalOnBoardingRedirectHandler
17 * @package Give\PaymentGateways\PayPalCommerce
18 *
19 * @since 2.9.0
20 */
21 class onBoardingRedirectHandler
22 {
23 /**
24 * @since 2.9.0
25 *
26 * @var PayPalAuth
27 */
28 private $payPalAuth;
29
30 /**
31 * @since 2.9.0
32 *
33 * @var Webhooks
34 */
35 private $webhooksRepository;
36
37 /**
38 * @since 2.9.0
39 *
40 * @var MerchantDetails
41 */
42 private $merchantRepository;
43
44 /**
45 * @since 2.9.0
46 *
47 * @var Settings
48 */
49 private $settings;
50
51 /**
52 * onBoardingRedirectHandler constructor.
53 *
54 * @since 2.9.0
55 *
56 * @param Webhooks $webhooks
57 * @param MerchantDetails $merchantRepository
58 * @param Settings $settings
59 * @param PayPalAuth $payPalAuth
60 */
61 public function __construct(
62 Webhooks $webhooks,
63 MerchantDetails $merchantRepository,
64 Settings $settings,
65 PayPalAuth $payPalAuth
66 ) {
67 $this->webhooksRepository = $webhooks;
68 $this->merchantRepository = $merchantRepository;
69 $this->settings = $settings;
70 $this->payPalAuth = $payPalAuth;
71 }
72
73 /**
74 * Bootstrap class
75 *
76 * @since 2.9.0
77 */
78 public function boot()
79 {
80 if ($this->isPayPalUserRedirected()) {
81 $details = $this->savePayPalMerchantDetails();
82 $this->setUpWebhook($details);
83 $this->redirectAccountConnected();
84 }
85
86 if ($this->isPayPalAccountDetailsSaved()) {
87 $this->registerPayPalSSLNotice();
88 $this->registerPayPalAccountConnectedNotice();
89 }
90
91 if ($this->isStatusRefresh()) {
92 $this->refreshAccountStatus();
93 }
94 }
95
96 /**
97 * Save PayPal merchant details
98 *
99 * @since 2.25.0 Handle exception.
100 * @since 2.9.0
101 *
102 * @return MerchantDetail
103 */
104 private function savePayPalMerchantDetails()
105 {
106 $paypalGetData = wp_parse_args($_SERVER['QUERY_STRING']);
107 $partnerLinkInfo = $this->settings->getPartnerLinkDetails();
108 $tokenInfo = $this->settings->getAccessToken();
109
110 $allowedPayPalData = [
111 'merchantId',
112 'merchantIdInPayPal',
113 ];
114
115 $payPalAccount = array_intersect_key($paypalGetData, array_flip($allowedPayPalData));
116
117 if (! array_key_exists('merchantIdInPayPal', $payPalAccount) || empty($payPalAccount['merchantIdInPayPal'])) {
118 $errors[] = [
119 'type' => 'url',
120 'message' => esc_html__(
121 'The Merchant ID for PayPal was not found. Try connecting to PayPal again. The PayPal return URL is:',
122 'give'
123 ) . "\n",
124 'value' => urlencode($_SERVER['QUERY_STRING']),
125 ];
126
127 $this->merchantRepository->saveAccountErrors($errors);
128 $this->redirectWhenOnBoardingFail();
129 }
130
131 $restApiCredentials = (array)$this->payPalAuth->getSellerRestAPICredentials(
132 $tokenInfo ? $tokenInfo['accessToken'] : ''
133 );
134 $this->didWeGetValidSellerRestApiCredentials($restApiCredentials);
135
136 try {
137 $tokenInfo = $this->payPalAuth->getTokenFromClientCredentials(
138 $restApiCredentials['client_id'],
139 $restApiCredentials['client_secret']
140 );
141 } catch (GiveException $e) {
142 give(Log::class)->warning(
143 'PayPal Commerce: Error retrieving access token on boarding redirect.',
144 [
145 'category' => 'Payment Gateway',
146 'source' => 'Paypal Commerce',
147 'exception' => $e,
148 ]
149 );
150
151 $errors[] = esc_html__(
152 'There was a problem with retrieving PayPal access token. Please try again or contact support.',
153 'give'
154 );
155
156 $this->merchantRepository->saveAccountErrors($errors);
157 $this->redirectWhenOnBoardingFail();
158 }
159
160 $payPalAccount['clientId'] = $restApiCredentials['client_id'];
161 $payPalAccount['clientSecret'] = $restApiCredentials['client_secret'];
162 $payPalAccount['token'] = $tokenInfo;
163 $payPalAccount['supportsCustomPayments'] = 'PPCP' === $partnerLinkInfo['product'];
164 $payPalAccount['accountIsReady'] = true;
165 $payPalAccount['accountCountry'] = $this->settings->getAccountCountry();
166
167 $merchantDetails = MerchantDetail::fromArray($payPalAccount);
168 $this->merchantRepository->save($merchantDetails);
169
170 // Preserve the seller access token.
171 // This is required to get the merchant rest api credentials.
172 $this->settings->updateSellerAccessToken($this->settings->getAccessToken());
173
174 $this->deleteTempOptions();
175
176 return $merchantDetails;
177 }
178
179 /**
180 * Redirects the user to the account connected url
181 *
182 * @since 2.9.0
183 */
184 private function redirectAccountConnected()
185 {
186 $this->refreshAccountStatus();
187
188 wp_redirect(
189 admin_url(
190 'edit.php?post_type=give_forms&page=give-settings&tab=gateways&section=paypal&group=paypal-commerce&paypal-commerce-account-connected=1'
191 )
192 );
193
194 exit();
195 }
196
197 /**
198 * Sets up the webhook for the connected account
199 *
200 * @since 2.9.0
201 *
202 * @param MerchantDetail $merchant_details
203 */
204 private function setUpWebhook(MerchantDetail $merchant_details)
205 {
206 if (! is_ssl()) {
207 return;
208 }
209
210 try {
211 $webhookConfig = $this->webhooksRepository->createWebhook($merchant_details->accessToken);
212 $this->webhooksRepository->saveWebhookConfig($webhookConfig);
213 } catch (Exception $ex) {
214 $errors[] = esc_html__(
215 'There was a problem with creating webhook on PayPal. A gateway error log also added to get details information about PayPal response.',
216 'give'
217 );
218
219 $this->merchantRepository->saveAccountErrors($errors);
220 $this->redirectWhenOnBoardingFail();
221 }
222 }
223
224 /**
225 * Delete temp data
226 *
227 * @since 2.9.0
228 * @return void
229 */
230 private function deleteTempOptions()
231 {
232 $this->settings->deletePartnerLinkDetails();
233 $this->settings->deleteAccessToken();
234 }
235
236 /**
237 * Register notice if account connect success fully.
238 *
239 * @since 2.9.0
240 */
241 private function registerPayPalAccountConnectedNotice()
242 {
243 Give_Admin_Settings::add_message(
244 'paypal-commerce-account-connected',
245 esc_html__('PayPal account connected successfully.', 'give')
246 );
247 }
248
249 /**
250 * Returns whether or not the current request is for refreshing the account status
251 *
252 * @since 2.9.0
253 *
254 * @return bool
255 */
256 private function isStatusRefresh()
257 {
258 return isset($_GET['paypalStatusCheck']) && Give_Admin_Settings::is_setting_page('gateways', 'paypal');
259 }
260
261 /**
262 * Return whether or not PayPal user redirect to GiveWP setting page after successful onboarding.
263 *
264 * @since 2.9.0
265 *
266 * @return bool
267 */
268 private function isPayPalUserRedirected()
269 {
270 return isset($_GET['merchantIdInPayPal']) && Give_Admin_Settings::is_setting_page('gateways', 'paypal');
271 }
272
273 /**
274 * Return whether or not PayPal account details saved.
275 *
276 * @since 2.9.0
277 *
278 * @return bool
279 */
280 private function isPayPalAccountDetailsSaved()
281 {
282 return isset($_GET['paypal-commerce-account-connected']) && Give_Admin_Settings::is_setting_page(
283 'gateways',
284 'paypal'
285 );
286 }
287
288 /**
289 * validate rest api credential.
290 *
291 * @since 2.9.0
292 *
293 * @param array $array
294 *
295 */
296 private function didWeGetValidSellerRestApiCredentials($array)
297 {
298 $required = ['client_id', 'client_secret'];
299 $array = array_filter($array); // Remove empty values.
300
301 if (array_diff($required, array_keys($array))) {
302 $errors[] = [
303 'type' => 'json',
304 'message' => esc_html__('PayPal client access token API request response is:', 'give'),
305 'value' => wp_json_encode($this->settings->getAccessToken()),
306 ];
307
308 $errors[] = [
309 'type' => 'json',
310 'message' => esc_html__('PayPal client rest api credentials API request response is:', 'give'),
311 'value' => wp_json_encode($array),
312 ];
313
314 $errors[] = esc_html__(
315 'There was a problem with PayPal client rest API request and we could not find valid client id and secret.',
316 'give'
317 );
318
319 $this->merchantRepository->saveAccountErrors($errors);
320 $this->redirectWhenOnBoardingFail();
321 }
322 }
323
324 /**
325 * Handles the request for refreshing the account status
326 *
327 * @since 2.9.0
328 */
329 private function refreshAccountStatus()
330 {
331 $merchantDetails = $this->merchantRepository->getDetails();
332
333 $statusErrors = $this->isAdminSuccessfullyOnBoarded(
334 $merchantDetails->merchantIdInPayPal,
335 $merchantDetails->accessToken,
336 $merchantDetails->supportsCustomPayments
337 );
338 if ($statusErrors !== true) {
339 $merchantDetails->accountIsReady = false;
340 $this->merchantRepository->saveAccountErrors($statusErrors);
341 } else {
342 $merchantDetails->accountIsReady = true;
343 $this->merchantRepository->deleteAccountErrors();
344 }
345
346 $this->merchantRepository->save($merchantDetails);
347 }
348
349 /**
350 * Validate seller on Boarding status
351 *
352 * @since 2.29.0 Validate only primary capabilities during PayPal donations on-boarding.
353 * @since 2.9.0
354 *
355 * @param string $merchantId
356 * @param string $accessToken
357 * @param bool $usesCustomPayments
358 *
359 * @return true|string[]
360 */
361 private function isAdminSuccessfullyOnBoarded($merchantId, $accessToken, $usesCustomPayments)
362 {
363 $onBoardedData = (array)$this->payPalAuth->getSellerOnBoardingDetailsFromPayPal($merchantId, $accessToken);
364 $onBoardedData = array_filter($onBoardedData); // Remove empty values.
365 $errorMessages[] = [
366 'type' => 'json',
367 'message' => esc_html__('PayPal merchant status check API request response is:', 'give'),
368 'value' => wp_json_encode($onBoardedData),
369 ];
370
371 if (! is_ssl()) {
372 $errorMessages[] = esc_html__(
373 'A valid SSL certificate is required to accept donations and set up your PayPal account. Once a
374 certificate is installed and the site is using https, please disconnect and reconnect your account.',
375 'give'
376 );
377 }
378
379 if (array_diff(['payments_receivable', 'primary_email_confirmed'], array_keys($onBoardedData))) {
380 $errorMessages[] = __(
381 'Your account is not fully set up and ready to receive payments. Please log into your PayPal account at <a href="https://paypal.com">paypal.com</a> and address the following issues. Reach out to PayPal support if you need help setting up your account.',
382 'give'
383 );
384
385 // Return here since the rest of the validations will definitely fail
386 return $errorMessages;
387 }
388
389 if (! $onBoardedData['payments_receivable']) {
390 $errorMessages[] = esc_html__('An banking account needs to be connected to your PayPal account and verified', 'give');
391 }
392
393 if (! $onBoardedData['primary_email_confirmed']) {
394 $errorMessages[] = esc_html__('Your primary email address needs to be confirmed', 'give');
395 }
396
397 // This error message is only for the case when the user is using custom payments.
398 // Host card fields are supported only for specific countries and PayPal seller account of PPCP type.
399 if ($usesCustomPayments) {
400 $sellerCapabilities = wp_list_pluck($onBoardedData['capabilities'], 'name');
401 $requiredCapability = 'CUSTOM_CARD_PROCESSING';
402 $customCardProcessingCapabilityIndex = array_search($requiredCapability, $sellerCapabilities, true);
403 $hasCustomCardProcessingCapability = false !== $customCardProcessingCapabilityIndex;
404
405 // If the capability is found then check if it is active.
406 if (false !== $customCardProcessingCapabilityIndex) {
407 $customCardProcessingCapability = $onBoardedData['capabilities'][$customCardProcessingCapabilityIndex];
408 $hasCustomCardProcessingCapability = 'ACTIVE' === $customCardProcessingCapability['status'];
409 }
410
411 if (! $hasCustomCardProcessingCapability) {
412 $errorMessages[] = __(
413 'Advance card processing is not active on your PayPal account. That capability is required in order to display card fields directly on your website. To accept donations with card fields directly on your site, called <a href="https://developer.paypal.com/docs/checkout/advanced/#enable-your-account" title="Link to PayPal Docs">hosted fields</a>, you\'ll need to enable custom card processing. This is something PayPal support can help with, and depends on factors outside of GiveWP\'s control. You can still accept donations with <a href="https://developer.paypal.com/docs/checkout/" title="Link to PayPal Docs">PayPal smart buttons</a>, which allow donors to log into PayPal and complete the donation in a modal window, in the meantime.',
414 'give'
415 );
416 }
417 }
418
419 // If there were errors then redirect the user with notices
420 return count($errorMessages) > 1 ? $errorMessages : true;
421 }
422
423 /**
424 * Redirect admin to setting section with error.
425 *
426 * @since 2.9.0
427 */
428 private function redirectWhenOnBoardingFail()
429 {
430 wp_redirect(
431 admin_url(
432 'edit.php?post_type=give_forms&page=give-settings&tab=gateways&section=paypal&group=paypal-commerce&paypal-error=1'
433 )
434 );
435
436 exit();
437 }
438
439 /**
440 * Displays a notice of the site is not using SSL
441 *
442 * @since 2.9.0
443 */
444 private function registerPayPalSSLNotice()
445 {
446 if (is_ssl() && empty($this->webhooksRepository->getWebhookConfig())) {
447 $logLink = sprintf(
448 '<a href="%1$s">%2$s</a>',
449 admin_url('/edit.php?post_type=give_forms&page=give-tools&tab=logs'),
450 esc_html__('logs data', 'give')
451 );
452
453 Give_Admin_Settings::add_error(
454 'paypal-webhook-error',
455 sprintf(
456 esc_html__(
457 'There was a problem setting up the webhooks for your PayPal account. Please try disconnecting and reconnecting your PayPal account. If the problem persists, please contact support and provide them with the latest %1$s',
458 'give'
459 ),
460 $logLink
461 )
462 );
463 }
464 }
465 }
466