PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.10
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.10
4.4.10 4.4.9.1 4.4.9 4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 All 142 releases
← All changes | inc/gateways/paypal/class-lp-gateway-paypal.php +124 -40 4.3.9 → 4.4.10 View file →
@@ -209,16 +209,15 @@
209 209 if ( ! empty( $subscription_data ) ) {
210 210 $subscription_res = $this->pay_via_subscription( $order, $subscription_data );
211 211 $paypal_payment_url = $subscription_res['redirect_url'] ?? '';
212 212 } else {
213 - $data_token = $this->get_access_token();
214 - $paypal_payment_url = $this->create_payment_url( $order, $data_token );
213 + $paypal_payment_url = $this->create_payment_url( $order );
215 214 }
216 215
217 - $result['result'] = 'success';
218 - $result['redirect'] = $paypal_payment_url;
219 -
220 - return $result;
216 + return [
217 + 'result' => 'success',
218 + 'redirect' => $paypal_payment_url,
219 + ];
221 220 }
222 221
223 222 /**
224 223 * Prepare args to send to PayPal
@@ -376,24 +375,20 @@
376 375 /**
377 376 * Create Order PayPal and get checkout url
378 377 *
379 378 * @param LP_Order $order
380 - * @param object $data_token { scope, access_token, token_type, app_id, expires_in, nonce }
381 379 *
382 380 * @return string
383 381 * @throws Exception
384 382 * @since 4.2.4
385 - * @version 1.0.0
383 + * @version 1.0.1
386 384 */
387 - public function create_payment_url( LP_Order $order, $data_token ): string {
385 + public function create_payment_url( LP_Order $order ): string {
388 386 $checkout_url = '';
389 387 $params = $this->get_order_args( $order );
390 388
391 - if ( ! isset( $data_token->access_token ) || ! isset( $data_token->token_type ) ) {
392 - throw new Exception( __( 'Invalid Paypal access token', 'learnpress' ) );
393 - }
394 -
395 - $response = wp_remote_post(
389 + $data_token = $this->get_access_token();
390 + $response = wp_remote_post(
396 391 $this->api_url . 'v2/checkout/orders',
397 392 array(
398 393 'body' => json_encode( $params ),
399 394 'headers' => array(
@@ -443,18 +438,14 @@
443 438 * https://developer.paypal.com/docs/api/orders/v2/#orders_capture
444 439 *
445 440 * @return bool True when capture is completed and order status is updated.
446 441 * @throws Exception
447 - * @version 1.0.1
442 + * @version 1.0.2
448 443 * @since 4.2.4
449 444 */
450 445 public function capture_payment_for_order( string $paypal_order_id ): bool {
451 446 $data_token = $this->get_access_token();
452 - if ( ! isset( $data_token->access_token ) || ! isset( $data_token->token_type ) ) {
453 - return false;
454 - }
455 -
456 - $response = wp_remote_post(
447 + $response = wp_remote_post(
457 448 $this->api_url . 'v2/checkout/orders/' . $paypal_order_id . '/capture',
458 449 array(
459 450 'headers' => array(
460 451 'Content-Type' => 'application/json',
@@ -464,30 +455,49 @@
464 455 )
465 456 );
466 457
467 458 if ( is_wp_error( $response ) ) {
468 - return false;
459 + throw new Exception( $response->get_error_message() );
469 460 }
470 461
471 - $response_code = absint( $response['response']['code'] ?? 0 );
472 - if ( 201 !== $response_code ) {
462 + $body = wp_remote_retrieve_body( $response );
463 + $response_data = LP_Helper::json_decode( $body );
464 + if ( isset( $response_data->debug_id ) ) {
465 + throw new Exception( $response_data->details[0]->description );
466 + }
467 +
468 + $capture = $response_data->purchase_units[0]->payments->captures[0] ?? null;
469 + if ( empty( $capture ) ) {
473 470 return false;
474 471 }
475 472
476 - $body = wp_remote_retrieve_body( $response );
477 - $transaction = LP_Helper::json_decode( $body );
478 - if ( empty( $transaction ) || ! is_object( $transaction ) || ( $transaction->status ?? '' ) !== 'COMPLETED' ) {
473 + $order_id = absint( $capture->custom_id ?? 0 );
474 + if ( $order_id <= 0 ) {
479 475 return false;
480 476 }
481 477
482 - $order_id = absint( $transaction->purchase_units[0]->payments->captures[0]->custom_id ?? 0 );
483 - if ( $order_id <= 0 ) {
478 + $capture_id = $capture->id ?? '';
479 + if ( empty( $capture_id ) ) {
484 480 return false;
485 481 }
486 482
487 483 $lp_order = learn_press_get_order( $order_id );
488 - if ( $lp_order instanceof LP_Order ) {
489 - $lp_order->update_status( LP_ORDER_COMPLETED );
484 + if ( $lp_order instanceof LP_Order && $capture->status === 'COMPLETED' ) {
485 + $transaction_id = $lp_order->get_transaction_id();
486 + if ( $transaction_id === $capture_id ) {
487 + return true;
488 + }
489 +
490 + $lp_order->payment_complete( $capture_id );
491 +
492 + $lp_order->add_note(
493 + sprintf(
494 + __( 'PayPal payment %1$s completed at %2$s %3$s', 'learnpress' ),
495 + $capture_id,
496 + wp_date( 'Y-m-d H:i:s' ),
497 + wp_timezone_string()
498 + )
499 + );
490 500 return true;
491 501 }
492 502
493 503 return false;
@@ -622,9 +632,10 @@
622 632 if ( ! $this->is_subscription_enabled() ) {
623 633 throw new Exception( __( 'PayPal subscriptions are disabled.', 'learnpress' ) );
624 634 }
625 635
626 - $data = $this->validate_data_plan_payload( $data );
636 + $data = $this->validate_data_plan_payload( $data );
637 + $data_token = $this->get_access_token();
627 638
628 639 // PayPal-specific optional keys are normalized at gateway level.
629 640 $description = LP_Helper::sanitize_params_submitted( $data['description'] ?? '', 'html' );
630 641 $trial_days = absint( $data['trial_days'] ?? 0 );
@@ -629,10 +640,8 @@
629 640 $description = LP_Helper::sanitize_params_submitted( $data['description'] ?? '', 'html' );
630 641 $trial_days = absint( $data['trial_days'] ?? 0 );
631 642 $setup_fee = (float) ( $data['setup_fee'] ?? 0 );
632 643
633 - $data_token = $this->get_access_token();
634 -
635 644 // Create product before create plan
636 645 $product_id = $data['product_id'] ?? '';
637 646 $product_data = [ 'id' => $product_id ];
638 647 if ( empty( $product_id ) ) {
@@ -949,13 +958,8 @@
949 958 $current_sum = $current['summary'];
950 959
951 960 $data = $this->validate_data_plan_payload( $data );
952 961
953 - $data_token = $this->get_access_token();
954 - if ( empty( $data_token->access_token ) || empty( $data_token->token_type ) ) {
955 - throw new Exception( __( 'Invalid Paypal access token', 'learnpress' ) );
956 - }
957 -
958 962 $patches = array();
959 963 if ( ! empty( $data['name'] ?? '' ) ) {
960 964 $patches[] = array(
961 965 'op' => 'replace',
@@ -996,8 +1000,9 @@
996 1000 );
997 1001 }
998 1002
999 1003 if ( ! empty( $patches ) ) {
1004 + $data_token = $this->get_access_token();
1000 1005 $patch_response = wp_remote_request(
1001 1006 $this->api_url . 'v1/billing/plans/' . rawurlencode( $plan_id ),
1002 1007 array(
1003 1008 'method' => 'PATCH',
@@ -1340,9 +1345,9 @@
1340 1345 )
1341 1346 );
1342 1347
1343 1348 if ( is_wp_error( $response ) ) {
1344 - throw new Exception( $response->get_error_message(), $response->get_error_code() );
1349 + throw new Exception( $response->get_error_message() );
1345 1350 }
1346 1351
1347 1352 $body = wp_remote_retrieve_body( $response );
1348 1353 $response_data = LP_Helper::json_decode( $body, true );
@@ -1706,9 +1711,9 @@
1706 1711 )
1707 1712 );
1708 1713
1709 1714 if ( is_wp_error( $response ) ) {
1710 - throw new Exception( $response->get_error_message(), $response->get_error_code() );
1715 + throw new Exception( $response->get_error_message() );
1711 1716 }
1712 1717
1713 1718 $verify_result = LP_Helper::json_decode( wp_remote_retrieve_body( $response ), true );
1714 1719 $is_verified = ! empty( $verify_result['verification_status'] )
@@ -1972,8 +1977,87 @@
1972 1977
1973 1978 $manage_url = trailingslashit( $this->paypal_url ) . 'myaccount/autopay/';
1974 1979
1975 1980 return (string) apply_filters( 'learn-press/paypal/subscription/manage-url', $manage_url, $order, $subscription_id, $this );
1981 + }
1982 +
1983 + /**
1984 + * Refund PayPal capture via REST API.
1985 + *
1986 + * @param LP_Order $lp_order
1987 + * @param float $amount 0 or empty means full refund.
1988 + * @param string $note
1989 + *
1990 + * @return array
1991 + * @throws Exception
1992 + * @since 4.4.0
1993 + * @version 1.0.0
1994 + */
1995 + public function refund( $lp_order, float $amount = 0, string $note = '' ): array {
1996 + $order_id = $lp_order->get_id();
1997 + $capture_id = $lp_order->get_transaction_id();
1998 + if ( empty( $capture_id ) ) {
1999 + throw new Exception( __( 'Missing PayPal capture id to refund.', 'learnpress' ) );
2000 + }
2001 +
2002 + $refund_args = [];
2003 + if ( $amount > 0 ) {
2004 + $refund_args['amount'] = [
2005 + 'currency_code' => $lp_order->get_currency(),
2006 + 'value' => strval( round( $amount, 2 ) ),
2007 + ];
2008 + }
2009 +
2010 + $note_to_payer = trim( $note );
2011 + if ( ! empty( $note_to_payer ) ) {
2012 + $refund_args['note_to_payer'] = $note_to_payer;
2013 + }
2014 +
2015 + $refund_args = apply_filters( 'learn-press/paypal-refund/args', $refund_args, $lp_order, $this );
2016 + $data_token = $this->get_access_token();
2017 + $response = wp_remote_post(
2018 + $this->api_url . 'v2/payments/captures/' . rawurlencode( $capture_id ) . '/refund',
2019 + [
2020 + 'body' => json_encode( $refund_args ),
2021 + 'headers' => [
2022 + 'Authorization' => $data_token->token_type . ' ' . $data_token->access_token,
2023 + 'Content-Type' => 'application/json',
2024 + ],
2025 + 'timeout' => 60,
2026 + ]
2027 + );
2028 +
2029 + if ( is_wp_error( $response ) ) {
2030 + throw new Exception( $response->get_error_message() );
2031 + }
2032 +
2033 + $body = wp_remote_retrieve_body( $response );
2034 + $response_data = LP_Helper::json_decode( $body );
2035 + if ( isset( $response_data->debug_id ) ) {
2036 + throw new Exception( $response_data->details[0]->description );
2037 + }
2038 +
2039 + if ( ! empty( $response_data->status ) && $response_data->status === 'COMPLETED' ) {
2040 + update_post_meta( $order_id, '_paypal_refund_id', $response_data->id );
2041 +
2042 + $lp_order->add_note(
2043 + sprintf(
2044 + __( 'PayPal refund completed at %1$s %2$s', 'learnpress' ),
2045 + wp_date( 'Y-m-d H:i:s' ),
2046 + wp_timezone_string()
2047 + )
2048 + );
2049 + do_action( 'learn-press/paypal-refund/success', $lp_order, $response_data, $this );
2050 + } else {
2051 + throw new Exception( __( 'PayPal refund something went wrong.', 'learnpress' ) );
2052 + }
2053 +
2054 + return [
2055 + 'result' => 'success',
2056 + 'refund_id' => $response_data->id,
2057 + 'status' => $result->status ?? '',
2058 + 'response' => $response_data,
2059 + ];
1976 2060 }
1977 2061
1978 2062 /**
1979 2063 * Settings form fields for this gateway