MDL-69166 pg_paypal: Process after payment
authorShamim Rezaie <shamim@moodle.com>
Wed, 8 Jan 2020 17:06:44 +0000 (04:06 +1100)
committerShamim Rezaie <shamim@moodle.com>
Tue, 27 Oct 2020 03:37:28 +0000 (14:37 +1100)
payment/gateway/paypal/classes/external/transaction_complete.php [new file with mode: 0644]
payment/gateway/paypal/db/install.xml [new file with mode: 0755]
payment/gateway/paypal/db/services.php
payment/gateway/paypal/lang/en/pg_paypal.php
payment/gateway/paypal/settings.php

diff --git a/payment/gateway/paypal/classes/external/transaction_complete.php b/payment/gateway/paypal/classes/external/transaction_complete.php
new file mode 100644 (file)
index 0000000..2569df6
--- /dev/null
@@ -0,0 +1,144 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This class contains a list of webservice functions related to the PayPal payment gateway.
+ *
+ * @package    pg_paypal
+ * @copyright  2020 Shamim Rezaie <shamim@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+declare(strict_types=1);
+
+namespace pg_paypal\external;
+
+use external_api;
+use external_function_parameters;
+use external_value;
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir . '/externallib.php');
+
+class transaction_complete extends external_api {
+
+    /**
+     * Returns description of method parameters.
+     *
+     * @return external_function_parameters
+     */
+    public static function execute_parameters() {
+        return new external_function_parameters([
+            'component' => new external_value(PARAM_COMPONENT, 'The component name'),
+            'componentid' => new external_value(PARAM_INT, 'The item id in the context of the component'),
+            'orderid' => new external_value(PARAM_TEXT, 'The order id coming back from PayPal'),
+            'authorizationid' => new external_value(PARAM_TEXT, 'The authorization id coming back from PayPal'),
+        ]);
+    }
+
+    /**
+     * Perform what needs to be done when a transaction is reported to be complete.
+     * This function does not take cost as a parameter as we cannot rely on any provided value.
+     *
+     * @param string $component Name of the component that the componentid belongs to
+     * @param int $componentid An internal identifier that is used by the component
+     * @param string $orderid PayPal order ID
+     * @param string $authorizationid The PayPal-generated ID for the authorized payment
+     * @return array
+     */
+    public static function execute(string $component, int $componentid, string $orderid,
+            string $authorizationid): array {
+        global $USER, $DB;
+
+        self::validate_parameters(self::execute_parameters(), [
+            'component' => $component,
+            'componentid' => $componentid,
+            'orderid' => $orderid,
+            'authorizationid' => $authorizationid,
+        ]);
+
+        $config = get_config('pg_paypal');
+
+        [
+            'amount' => $amount,
+            'currency' => $currency
+        ] = \core_payment\helper::get_cost($component, $componentid);
+
+        $paypalhelper = new paypal_helper($config->clientid, $config->secret, false);
+        $authorization = $paypalhelper->capture_authorization($authorizationid, $amount, $currency);
+
+        $success = false;
+        $message = '';
+
+        if ($authorization) {
+            switch ($authorization['status']) {
+                case 'COMPLETED':
+                    $success = true;
+                    // Everything is correct. Let's give them what they paid for.
+                    try {
+                        \core_payment\helper::deliver_order($component, $componentid);
+
+                        $paymentid = \core_payment\helper::save_payment($component, $componentid, (int)$USER->id, $amount, $currency,
+                                'paypal');
+
+                        // Store PayPal extra information.
+                        $record = new \stdClass();
+                        $record->paymentid = $paymentid;
+                        $record->pp_orderid = $orderid;
+                        $record->pp_authorizationid = $authorizationid;
+                        $record->pp_paymentid = $authorization->id; // The PayPal-generated ID for the captured payment.
+                        $record->pp_status = 'COMPLETED';
+
+                        $DB->insert_record('pg_paypal', $record);
+                    } catch (\Exception $e) {
+                        debugging('Exception while trying to process payment: ' . $e->getMessage(), DEBUG_DEVELOPER);
+                        $success = false;
+                        $message = get_string('internalerror', 'pg_paypal');
+                    }
+                    break;
+                case 'PENDING':
+                    $success = false;
+                    $message = get_string('echecknotsupported', 'pg_paypal');
+                    break;
+                default:
+                    $success = false;
+                    $message = get_string('paymentnotcleared', 'pg_paypal');
+            }
+        } else {
+            // Could not capture authorization!
+            $success = false;
+            $message = get_string('captureauthorizationfailed', 'pg_paypal');
+        }
+
+        return [
+            'success' => $success,
+            'message' => $message,
+        ];
+    }
+
+    /**
+     * Returns description of method result value.
+     *
+     * @return external_function_parameters
+     */
+    public static function execute_returns() {
+        return new external_function_parameters([
+            'success' => new external_value(PARAM_BOOL, 'Whether everything was successful or not.'),
+            'message' => new external_value(PARAM_TEXT, 'Message (usually the error message).', VALUE_OPTIONAL),
+        ]);
+    }
+}
diff --git a/payment/gateway/paypal/db/install.xml b/payment/gateway/paypal/db/install.xml
new file mode 100755 (executable)
index 0000000..e936131
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<XMLDB PATH="payment/gateway/paypal/db" VERSION="20200110" COMMENT="XMLDB file for PayPal payment gateway plugin"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
+>
+  <TABLES>
+    <TABLE NAME="pg_paypal" COMMENT="Stores PayPal related information">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+        <FIELD NAME="paymentid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="pp_orderid" TYPE="char" LENGTH="255" NOTNULL="true" DEFAULT="The ID of the order in PayPal" SEQUENCE="false"/>
+        <FIELD NAME="pp_authorizationid" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="pp_paymentid" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="The PayPal-generated ID for the captured payment."/>
+        <FIELD NAME="pp_status" TYPE="char" LENGTH="32" NOTNULL="false" SEQUENCE="false"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+        <KEY NAME="paymentid" TYPE="foreign-unique" FIELDS="paymentid" REFTABLE="payment" REFFIELDS="id"/>
+      </KEYS>
+    </TABLE>
+  </TABLES>
+</XMLDB>
index dd66832..38fd89d 100644 (file)
@@ -33,4 +33,21 @@ $functions = [
         'type'        => 'read',
         'ajax'        => true,
     ],
+    'pg_paypal_get_sdk_url' => [
+        'classname'   => 'pg_paypal\external\get_sdk_url',
+        'methodname'  => 'execute',
+        'classpath'   => '',
+        'description' => 'Generates and returns the URL of the PayPal JavaScript SDK',
+        'type'        => 'read',
+        'ajax'        => true,
+    ],
+    'pg_paypal_create_transaction_complete' => [
+        'classname'   => 'pg_paypal\external\transaction_complete',
+        'methodname'  => 'execute',
+        'classpath'   => '',
+        'description' => 'Takes care of what needs to be done when a PayPal transaction comes back as complete.',
+        'type'        => 'write',
+        'ajax'        => true,
+        'loginrequired' => false,
+    ],
 ];
index 0739395..9945ae8 100644 (file)
 
 $string['brandname'] = 'Brand name';
 $string['brandname_desc'] = 'The optional label that overrides the business name in the PayPal account on the PayPal site.';
+$string['captureauthorizationfailed'] = 'Could not capture authorization.';
 $string['clientid'] = 'Client ID';
 $string['clientid_desc'] = 'The client ID that PayPal generated for your application.';
+$string['echecknotsupported'] = 'E-check is not supported.';
 $string['gatewaydescription'] = 'PayPal is an authorised payment gateway provider for processing credit card transactions.';
 $string['gatewayname'] = 'PayPal';
+$string['internalerror'] = 'Internal error.';
+$string['paymentauthorizationwarning'] = 'For PayPal payment authorizations, you must enable this feature on your PayPal account.';
+$string['paymentnotcleared'] = 'payment not cleared by PayPal.';
 $string['pluginname'] = 'PayPal';
 $string['pluginname_desc'] = 'The PayPal plugin allows you to receive payments via PayPal.';
+$string['repeatedorder'] = 'This order has already been processed earlier.';
+$string['secret'] = 'Secret';
+$string['secret_desc'] = 'The secret thatPayPal generated for your application.';
index d417def..1ea9157 100644 (file)
 defined('MOODLE_INTERNAL') || die();
 
 if ($ADMIN->fulltree) {
+
     $settings->add(new admin_setting_heading('pg_paypal_settings', '', get_string('pluginname_desc', 'pg_paypal')));
 
+    $warning = $OUTPUT->notification(get_string('paymentauthorizationwarning', 'pg_paypal'), 'warning');
+    $settings->add(new admin_setting_heading('pg_paypal/warning', '', $warning));
+
     $settings->add(new admin_setting_configtext('pg_paypal/brandname', get_string('brandname', 'pg_paypal'),
             get_string('brandname', 'pg_paypal'), '', PARAM_TEXT));
     $settings->add(new admin_setting_configtext('pg_paypal/clientid', get_string('clientid', 'pg_paypal'),
             get_string('clientid_desc', 'pg_paypal'), '', PARAM_TEXT));
+    $settings->add(new admin_setting_configtext('pg_paypal/secret', get_string('secret', 'pg_paypal'),
+            get_string('secret_desc', 'pg_paypal'), '', PARAM_TEXT));
 }