Files
booking/booking.paypal.inc
2016-07-23 10:16:12 +10:00

309 lines
11 KiB
PHP

<?php
/**
* Helper function to calculate paypal fees
*/
function _booking_add_paypal_fees($amount, $country)
{
//add the 30 cent fixed cost
$result = '0.00';
$result = (float) ($amount + (float) variable_get('booking_paypal_transaction_fee_fixedcost', '0.3'));
//and the 2.6 percent transaction fee for australian transaction with no currency conversion
if ($country == variable_get('booking_default_country', 'Australia')) {
$percentage = (float) variable_get('booking_paypal_transaction_fee_percentage', '2.6');
} else {
$percentage = (float) variable_get('booking_paypal_transaction_fee_percentage_intl', '3.6');
//watchdog('booking', "Calculating paypal fees for a currency conversion transaction which adds $percentage percent.");
}
//apply the percentage
$percentage = (float) $percentage / 100;
//watchdog('booking', "Paypal percentage transaction fee works out to $percentage.");
$result = $result / (1 - $percentage);
//return result
return $result;
}
/**
* Helper function to generate string to post to paypal to verify a successful IPN
*/
function _booking_paypal_post($data = array()) {
$post = '';
foreach ($data as $key => $value) {
$post .= $key. '='. urlencode($value). '&';
}
$post .= 'cmd=_notify-validate';
return $post;
}
/**
* Helper function to verify a successful IPN from Paypal
* @see https://github.com/paypal/ipn-code-samples/blob/master/paypal_ipn.php
*/
function _booking_paypal_ipn_verify($vars = array()) {
if (variable_get('booking_paypal_sandbox', 0)) {
watchdog('booking', 'Setting IPN verify to true, running in sandbox mode');
// PayPal sandbox requires login in order to even access, so for sandbox mode, simply return true.
return TRUE;
}
$ch = curl_init(BOOKING_PAYPAL_SUBMIT_URL);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, _booking_paypal_post($vars));
ob_start();
if (curl_exec($ch)) {
$info = ob_get_contents();
curl_close($ch);
ob_end_clean();
if (preg_match('/VERIFIED/i', $info)) {
watchdog('booking', 'Payment verification completed successfully: @info', array('@info' => $info));
return TRUE;
} else {
watchdog('booking', 'Payment verification not successful: @info', array('@info' => $info));
return FALSE;
}
} else {
watchdog('booking_paypal', 'Call to curl_exec() failed with error @error. vars=@vars', array(
'@vars' => print_r($vars, TRUE), '@error' => curl_error($ch)), WATCHDOG_ERROR);
return FALSE;
}
}
/**
* Handles an incoming PayPal IPN.
*/
function booking_paypal_ipn() {
$ipn = $_POST;
watchdog('booking', 'Payment notification received: @info', array('@info' => var_export($ipn, TRUE)));
//verify the notification with paypal
if(!_booking_paypal_ipn_verify($ipn)) {
watchdog('booking_paypal', 'Payment verification did not succeed. Retrying...');
//retry the attempt to verify the payment 5 times, sleeping in between each attempt
$continue = TRUE;
$i = 1;
while ($continue && $i < 5)
{
if(!_booking_paypal_ipn_verify($ipn)) {
watchdog('booking_paypal', "Payment verification did not succeed on attempt $i.");
$i++;
sleep(3);
} else {
$continue = FALSE;
watchdog('booking_paypal', "Payment verification succeeded on attempt $i.");
}
}
//exited the loop above with an unsuccessful verification, so return from the function now
if ($continue)
{
watchdog('booking_paypal', 'Payment verification was unsuccessful.');
return;
}
}
//verification was successful if we reach this point
/*
if ($ipn['payment_status'] != 'Pending' && variable_get('booking_paypal_sandbox', 0) == 1) {
watchdog('booking', 'Running in sandbox mode but type is not pending');
return;
}
*/
//@todo Handle refund and payment dispute IPNs
if (empty($ipn['payment_status']) || ($ipn['payment_status'] != 'Completed' && variable_get('booking_paypal_sandbox', 0) == 0))
return;
//if (strcasecmp($ipn['receiver_email'], variable_get('booking_paypal_account', '') <> 0))
if ($ipn['receiver_email'] !== variable_get('booking_paypal_account', ''))
{
watchdog('booking_paypal', 'Receiving address "!receiver" for paypal payment doesnt match configured address "!configured". Full POST: !id',
array('!id' => var_export($ipn, TRUE), '!receiver' => $ipn['receiver_email'], '!configured' => variable_get('booking_paypal_account', '')), WATCHDOG_ERROR);
}
//Insert record into database and remove temporary booking id field from user
_booking_store_paypal_payment($ipn);
}
function _booking_store_paypal_payment($data) {
global $event;
$balance_payment = false;
$amount_owing = 0;
//extract the person node id from the invoice
$pos = strpos($data['invoice'], "_");
if (($pos === false) || ($pos == 0)) {
watchdog('booking', 'Unable to process payment with invalid invoice information: !id', array('!id' => $data['invoice']), WATCHDOG_ERROR);
return;
}
//get the part of the invoice up to the first underscore
$nid = substr($data['invoice'], 0, $pos);
//get the data between the first and second underscore
$eid = substr($data['invoice'], $pos + 1, strrpos($data['invoice'], "_") - $pos - 1);
if (substr($eid,0,3) == "bal") {
$balance_payment = true;
watchdog('booking', 'Balance payment for user with node id: !id', array('!id' => $nid));
}
//verify paypal hasn't already sent through a notification for this payment
$duplicate_check = db_query("SELECT payid, booking_person_nid FROM {booking_payment} where booking_ipn_track_id = :ipn_id ",
array(':ipn_id' => $data['ipn_track_id']))->fetchObject();
if ($duplicate_check) {
watchdog('booking', 'Detected duplicate paypal notifications for transaction id !id, registration id !nid', array('!id' => $data['ipn_track_id'], '!nid' => $nid), WATCHDOG_ERROR);
return;
}
//watchdog('booking', 'Adding payment for user with node id: !id; event id: !eid', array('!id' => $nid, '!eid' => $eid));
$result = db_insert('booking_payment')
->fields(array(
'booking_person_nid' => $nid,
'booking_eventid' => $event->eid,
'booking_mc_gross' => $data['mc_gross'],
'booking_mc_currency' => $data['mc_currency'],
'booking_mc_fee' => $data['mc_fee'],
'booking_quantity' => $data['quantity'],
'booking_invoice' => $data['invoice'],
'booking_payer_id' => $data['payer_id'],
'booking_payment_date' => strtotime($data['payment_date']),
'booking_payment_status' => $data['payment_status'],
'booking_first_name' => $data['first_name'],
'booking_last_name' => $data['last_name'],
'booking_buyer_email' => $data['payer_email'],
'booking_payer_status' => $data['payer_status'],
'booking_item_name' => $data['item_name'],
'booking_ipn_track_id' => $data['ipn_track_id'],
))
->execute();
//Get the person's info so we can update their total amount paid and booking status
$person = node_load($nid);
//check if we found a person matching this payment
if ($person) {
watchdog('booking', 'Found matching user with node id: !id; event id: !eid; existing payment !payment',
array('!id' => $nid, '!eid' => $eid, '!payment' => $person->booking_amount_paid));
_booking_process_person_payment($person, $data['mc_gross'], $balance_payment);
}
else {
//couldn't find a matching nid for this invoice
watchdog('booking', "Unable to process payment for user with node id: '!id'", array('!id' => $nid), WATCHDOG_ERROR);
//db_query("UPDATE {booking_person} SET booking_tempid='' WHERE nid = %d", $nid);
}
}
/**
* Landing page after returning from paypal
*/
function booking_payment_completed_page() {
//get some configuration information
global $event;
$output = "";
$return_array = array();
/*
$parameters = drupal_get_query_parameters();
//check if we got a transaction token from paypal
if (isset($parameters['token'])) {
//check to make sure the value is something we're expecting
$paypal_token = $parameters['token'];
if (! preg_match('/^[0-9A-Fa-f\-]+$/', $paypal_token)) {
//parameter from url is not what we were expecting so ignore it and just use the site-wide tokens for this page
$tokens = booking_define_tokens();
}
else {
//query the payments table to find the attendee that this paypal token belongs to
}
}
*/
//set the page title
$bookingTitle = !empty($event->booking_eventname) ? $event->booking_eventname : 'Event';
drupal_set_title($bookingTitle . ' Registration Completed');
$input = variable_get('booking_regn_completed_page');
//watchdog('booking_debug', "<pre>Paypal landing page token:\n@info</pre>", array('@info' => print_r($input['value'] , true)));
$output = token_replace($input['value'], booking_define_tokens());
$return_array[] = array(
'paragraph' => array(
'#type' => 'markup',
'#markup' => $output
)
);
return $return_array;
}
/**
* Helper function to generate paypal form for payments
*/
function _booking_paypal_form($person, $invoiceid, $amount_owing, $button_text) {
$form = drupal_get_form('_booking_paypal_form_builder', $person, $invoiceid, $amount_owing, $button_text);
return drupal_render($form);
}
/**
* Helper function to generate form elements for paypal form
*/
function _booking_paypal_form_builder($node, &$form_state, $person, $invoiceid, $amount_owing, $button_text) {
global $event;
//get our current path so we can send the user back here if they cancel
$path = isset($_GET['q']) ? $_GET['q'] : '<front>';
//paypal specific settings
$vars = array(
'module' => 'Booking System',
'type' => $event->booking_eventname,
//'custom' => $data,
'item_name' => $event->booking_eventname . ' ' . $person->booking_price_descrip,
'invoice' => $invoiceid,
'no_shipping' => TRUE,
'no_note' => TRUE,
'currency_code' => 'AUD',
'return' => url('bookingfinal', array('absolute' => TRUE)),
'cancel_return' => url($path, array('absolute' => TRUE)),
'rm' => '2',
'amount' => $amount_owing,
'last_name' => $person->booking_lastname,
'first_name' => $person->booking_firstname,
'cmd' => '_xclick',
'notify_url' => url(BOOKING_PAYPAL_IPN_PATH, array('absolute' => TRUE)),
'business' => variable_get('booking_paypal_account', '')
);
$form['#action'] = url(variable_get('booking_paypal_sandbox', 0) ? BOOKING_PAYPAL_SUBMIT_URL_SANDBOX : BOOKING_PAYPAL_SUBMIT_URL, array('absolute' => TRUE));
//turn the array into a form
foreach($vars as $name => $value) {
$form[$name] = array(
'#type' => 'hidden',
'#value' => $value,
);
}
$form['submit'] = array(
'#type' => 'button',
'#value' => t($button_text),
);
//watchdog('booking', 'Booking Balance payment: @info', array ('@info' => var_export($form, TRUE)));
return $form;
}