Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * This class contains a list of webservice functions related to the PayPal payment gateway.
  19   *
  20   * @package    paygw_paypal
  21   * @copyright  2020 Shamim Rezaie <shamim@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  declare(strict_types=1);
  26  
  27  namespace paygw_paypal\external;
  28  
  29  use core_payment\helper;
  30  use external_api;
  31  use external_function_parameters;
  32  use external_value;
  33  use core_payment\helper as payment_helper;
  34  use paygw_paypal\paypal_helper;
  35  
  36  defined('MOODLE_INTERNAL') || die();
  37  
  38  require_once($CFG->libdir . '/externallib.php');
  39  
  40  class transaction_complete extends external_api {
  41  
  42      /**
  43       * Returns description of method parameters.
  44       *
  45       * @return external_function_parameters
  46       */
  47      public static function execute_parameters() {
  48          return new external_function_parameters([
  49              'component' => new external_value(PARAM_COMPONENT, 'The component name'),
  50              'paymentarea' => new external_value(PARAM_AREA, 'Payment area in the component'),
  51              'itemid' => new external_value(PARAM_INT, 'The item id in the context of the component area'),
  52              'orderid' => new external_value(PARAM_TEXT, 'The order id coming back from PayPal'),
  53          ]);
  54      }
  55  
  56      /**
  57       * Perform what needs to be done when a transaction is reported to be complete.
  58       * This function does not take cost as a parameter as we cannot rely on any provided value.
  59       *
  60       * @param string $component Name of the component that the itemid belongs to
  61       * @param string $paymentarea
  62       * @param int $itemid An internal identifier that is used by the component
  63       * @param string $orderid PayPal order ID
  64       * @return array
  65       */
  66      public static function execute(string $component, string $paymentarea, int $itemid, string $orderid): array {
  67          global $USER, $DB;
  68  
  69          self::validate_parameters(self::execute_parameters(), [
  70              'component' => $component,
  71              'paymentarea' => $paymentarea,
  72              'itemid' => $itemid,
  73              'orderid' => $orderid,
  74          ]);
  75  
  76          $config = (object)helper::get_gateway_configuration($component, $paymentarea, $itemid, 'paypal');
  77          $sandbox = $config->environment == 'sandbox';
  78  
  79          $payable = payment_helper::get_payable($component, $paymentarea, $itemid);
  80          $currency = $payable->get_currency();
  81  
  82          // Add surcharge if there is any.
  83          $surcharge = helper::get_gateway_surcharge('paypal');
  84          $amount = helper::get_rounded_cost($payable->get_amount(), $currency, $surcharge);
  85  
  86          $paypalhelper = new paypal_helper($config->clientid, $config->secret, $sandbox);
  87          $orderdetails = $paypalhelper->get_order_details($orderid);
  88  
  89          $success = false;
  90          $message = '';
  91  
  92          if ($orderdetails) {
  93              if ($orderdetails['status'] == paypal_helper::ORDER_STATUS_APPROVED &&
  94                      $orderdetails['intent'] == paypal_helper::ORDER_INTENT_CAPTURE) {
  95                  $item = $orderdetails['purchase_units'][0];
  96                  if ($item['amount']['value'] == $amount && $item['amount']['currency_code'] == $currency) {
  97                      $capture = $paypalhelper->capture_order($orderid);
  98                      if ($capture && $capture['status'] == paypal_helper::CAPTURE_STATUS_COMPLETED) {
  99                          $success = true;
 100                          // Everything is correct. Let's give them what they paid for.
 101                          try {
 102                              $paymentid = payment_helper::save_payment($payable->get_account_id(), $component, $paymentarea,
 103                                  $itemid, (int) $USER->id, $amount, $currency, 'paypal');
 104  
 105                              // Store PayPal extra information.
 106                              $record = new \stdClass();
 107                              $record->paymentid = $paymentid;
 108                              $record->pp_orderid = $orderid;
 109  
 110                              $DB->insert_record('paygw_paypal', $record);
 111  
 112                              payment_helper::deliver_order($component, $paymentarea, $itemid, $paymentid, (int) $USER->id);
 113                          } catch (\Exception $e) {
 114                              debugging('Exception while trying to process payment: ' . $e->getMessage(), DEBUG_DEVELOPER);
 115                              $success = false;
 116                              $message = get_string('internalerror', 'paygw_paypal');
 117                          }
 118                      } else {
 119                          $success = false;
 120                          $message = get_string('paymentnotcleared', 'paygw_paypal');
 121                      }
 122                  } else {
 123                      $success = false;
 124                      $message = get_string('amountmismatch', 'paygw_paypal');
 125                  }
 126              } else {
 127                  $success = false;
 128                  $message = get_string('paymentnotcleared', 'paygw_paypal');
 129              }
 130          } else {
 131              // Could not capture authorization!
 132              $success = false;
 133              $message = get_string('cannotfetchorderdatails', 'paygw_paypal');
 134          }
 135  
 136          return [
 137              'success' => $success,
 138              'message' => $message,
 139          ];
 140      }
 141  
 142      /**
 143       * Returns description of method result value.
 144       *
 145       * @return external_function_parameters
 146       */
 147      public static function execute_returns() {
 148          return new external_function_parameters([
 149              'success' => new external_value(PARAM_BOOL, 'Whether everything was successful or not.'),
 150              'message' => new external_value(PARAM_RAW, 'Message (usually the error message).'),
 151          ]);
 152      }
 153  }