Skip to content

Commit

Permalink
MDL-69166 pg_paypal: capture payment directly
Browse files Browse the repository at this point in the history
  • Loading branch information
rezaies committed Oct 27, 2020
1 parent 9e6630a commit 950e69d
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 78 deletions.
2 changes: 1 addition & 1 deletion payment/gateway/paypal/amd/build/gateways_modal.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion payment/gateway/paypal/amd/build/gateways_modal.min.js.map

Large diffs are not rendered by default.

55 changes: 28 additions & 27 deletions payment/gateway/paypal/amd/src/gateways_modal.js
Expand Up @@ -27,15 +27,15 @@ import Truncate from 'core/truncate';
import Ajax from 'core/ajax';
import ModalFactory from 'core/modal_factory';
import ModalEvents from 'core/modal_events';
import {get_string as getString} from 'core/str';

/**
* Creates and shows a modal that contains a placeholder.
*
* @returns {Promise<Modal>}
*/
const showPlaceholder = async() => {
const showModalWithPlaceholder = async() => {
const modal = await ModalFactory.create({
type: ModalFactory.types.CANCEL,
body: await Templates.render('pg_paypal/paypal_button_placeholder', {})
});
modal.show();
Expand All @@ -59,23 +59,28 @@ export const process = async(amount, currency, component, componentid, descripti
modal,
paypalConfig,
] = await Promise.all([
showPlaceholder(),
showModalWithPlaceholder(),
Repository.getConfigForJs(),
]);

modal.getRoot().on(ModalEvents.outsideClick, (e) => {
// Prevent closing the modal when clicking outside of it.
e.preventDefault();
});

modal.getRoot().on(ModalEvents.hidden, () => {
// Destroy when hidden.
modal.destroy();
});

const paypalScript = `https://www.paypal.com/sdk/js?client-id=${paypalConfig.clientid}&currency=${currency}&intent=authorize`;
const paypalScript = `https://www.paypal.com/sdk/js?client-id=${paypalConfig.clientid}&currency=${currency}`;

callExternalFunction(paypalScript, () => {
modal.setBody('<form></form>'); // This is a hack. Instead of emptying the body, we put an empty form there so the modal
// is not closed when user clicks outside of modal.
modal.setBody(''); // We have to clear the body. The render method in paypal.Buttons will render everything.

paypal.Buttons({ // eslint-disable-line
// Set up the transaction.
createOrder: function(data, actions) {
// This function sets up the details of the transaction, including the amount and line item details.
return actions.order.create({
purchase_units: [{ // eslint-disable-line
amount: {
Expand All @@ -90,26 +95,22 @@ export const process = async(amount, currency, component, componentid, descripti
},
});
},
onApprove: function(data, actions) {
// Authorize the transaction.
actions.order.authorize().then(function(authorization) {
// Get the authorization id.
const authorizationID = authorization.purchase_units[0].payments.authorizations[0].id;

// Call your server to validate and capture the transaction.
return Ajax.call([{
methodname: 'pg_paypal_transaction_complete',
args: {
component,
componentid,
orderid: data.orderID,
authorizationid: authorizationID,
},
}])[0]
.then(function(res) {
modal.hide();
return callback(res);
});
// Finalise the transaction.
onApprove: function(data) {
modal.setBody(getString('authorising', 'pg_paypal'));

// Call server to validate and capture payment for order.
return Ajax.call([{
methodname: 'pg_paypal_create_transaction_complete',
args: {
component,
componentid,
orderid: data.orderID,
},
}])[0]
.then(function(res) {
modal.hide();
return callback(res);
});
}
}).render(modal.getBody()[0]);
Expand Down
80 changes: 41 additions & 39 deletions payment/gateway/paypal/classes/external/transaction_complete.php
Expand Up @@ -29,6 +29,8 @@
use external_api;
use external_function_parameters;
use external_value;
use core_payment\helper as payment_helper;
use pg_paypal\paypal_helper;

defined('MOODLE_INTERNAL') || die();

Expand All @@ -46,7 +48,6 @@ public static function execute_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'),
]);
}

Expand All @@ -57,18 +58,15 @@ public static function execute_parameters() {
* @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 {
public static function execute(string $component, int $componentid, string $orderid): array {
global $USER, $DB;

self::validate_parameters(self::execute_parameters(), [
'component' => $component,
'componentid' => $componentid,
'orderid' => $orderid,
'authorizationid' => $authorizationid,
]);

$config = get_config('pg_paypal');
Expand All @@ -77,52 +75,56 @@ public static function execute(string $component, int $componentid, string $orde
[
'amount' => $amount,
'currency' => $currency
] = \core_payment\helper::get_cost($component, $componentid);
] = payment_helper::get_cost($component, $componentid);

$paypalhelper = new \pg_paypal\paypal_helper($config->clientid, $config->secret, $sandbox);
$authorization = $paypalhelper->capture_authorization($authorizationid, $amount, $currency);
$paypalhelper = new paypal_helper($config->clientid, $config->secret, $sandbox);
$orderdetails = $paypalhelper->get_order_details($orderid);

$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);
if ($orderdetails) {
if ($orderdetails['status'] == paypal_helper::ORDER_STATUS_APPROVED &&
$orderdetails['intent'] == paypal_helper::ORDER_INTENT_CAPTURE) {
$item = $orderdetails['purchase_units'][0];
if ($item['amount']['value'] == $amount && $item['amount']['currency_code'] == $currency) {
$capture = $paypalhelper->capture_order($orderid);
if ($capture && $capture['status'] == paypal_helper::CAPTURE_STATUS_COMPLETED) {
$success = true;
// Everything is correct. Let's give them what they paid for.
try {
payment_helper::deliver_order($component, $componentid);

$paymentid = 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;

$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');
}
} else {
$success = false;
$message = get_string('internalerror', 'pg_paypal');
$message = get_string('paymentnotcleared', 'pg_paypal');
}
break;
case 'PENDING':
} else {
$success = false;
$message = get_string('echecknotsupported', 'pg_paypal');
break;
default:
$success = false;
$message = get_string('paymentnotcleared', 'pg_paypal');
$message = get_string('amountmismatch', 'pg_paypal');
}
} else {
$success = false;
$message = get_string('paymentnotcleared', 'pg_paypal');
}
} else {
// Could not capture authorization!
$success = false;
$message = get_string('captureauthorizationfailed', 'pg_paypal');
$message = get_string('cannotfetchorderdatails', 'pg_paypal');
}

return [
Expand Down

0 comments on commit 950e69d

Please sign in to comment.