Difference between revisions of "Extension:PayPal.php"

From Organic Design wiki
m
m
 
(49 intermediate revisions by the same user not shown)
Line 1: Line 1:
<?php
+
{{legacy}}
/* IpbWiki Paypal WikiMedia extension{{Category:Extensions|PayPal}}{{php}}{{#filesync:/var/www/extensions/PayPal.php}}
+
<php><?php
 +
/* IpbWiki Paypal WikiMedia extension
 
** IpbWiki (c) 2006
 
** IpbWiki (c) 2006
 
** Installation Instructions: http://www.ipbwiki.com/IpbWiki_Paypal_Extension
 
** Installation Instructions: http://www.ipbwiki.com/IpbWiki_Paypal_Extension
Line 8: Line 9:
 
   - Change button definitions to work from LocalSettings rather than code hack
 
   - Change button definitions to work from LocalSettings rather than code hack
 
   - Output warnings into sitenotice in error box
 
   - Output warnings into sitenotice in error box
 +
  - Log IPN post errors to log file
 
*/
 
*/
  
 
if (!defined('MEDIAWIKI')) die('Not an entry point.');
 
if (!defined('MEDIAWIKI')) die('Not an entry point.');
  
define('PAYPAL_VERSION','1.0.3, 2007-12-04');
+
define('PAYPAL_VERSION','1.0.3, 2007-12-13');
  
 
+
$wgPayPalIPN     = false;           # Use IPN notification
$wgPayPalAmount = false; # Add an input field to enter the amount to donate
+
$wgPayPalTest     = false;           # Set to the email address of the test merchant account if testing IPN in the PayPal sandbox site
$wgPayPalIPN   = false; # Use IPN notification
+
$wgPayPalPollms  = 5000;            # Number of milliseconds to wait between each call to AJAX updater
$wgPayPalTest   = false; # Set to the email address of the test merchant account if testing IPN in the PayPal sandbox site
+
$wgPayPalUpdating = 'updating...';  # Message to add to form totals text (in $3) before IPN post arrives
  
 
$wgExtensionCredits['parserhook'][] = array(
 
$wgExtensionCredits['parserhook'][] = array(
Line 30: Line 32:
 
$ipbwiki_paypal        = array();
 
$ipbwiki_paypal        = array();
 
$wgExtensionFunctions[] = "wfPayPalExtension";
 
$wgExtensionFunctions[] = "wfPayPalExtension";
 +
$wgAjaxExportList[]    = 'wfPayPalAjaxUpdater';
 +
 +
# If returning from merchant, get item number (form id)
 +
$wgPayPalAfterPurchase = (isset($_POST['item_number']) && !($_REQUEST['title'] == '__ipn_post')) ? $_POST['item_number'] : false;
  
 
# Set up the extension
 
# Set up the extension
Line 40: Line 46:
 
# Set button 1 to a default value if not defined
 
# Set button 1 to a default value if not defined
 
if (!isset($ipbwiki_paypal[1]))
 
if (!isset($ipbwiki_paypal[1]))
$ipbwiki_paypal[1] = '<form action="https://www.paypal.com/cgi-bin/webscr" method="post"><input type="hidden" name="cmd" value="_xclick"><input type="hidden" name="business" value="ipbwiki@gmail.com"><input type="hidden" name="item_name" value="IpbWiki PayPal"><input type="hidden" name="no_shipping" value="1"><input type="hidden" name="cn" value="Optional Comments"><input type="hidden" name="currency_code" value="EUR"><input type="hidden" name="tax" value="0"><input type="hidden" name="bn" value="PP-DonationsBF"><input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-but04.gif" border="0" name="submit" alt="Make payments with PayPal - it\'s fast, free and secure!"></form>';
+
$ipbwiki_paypal[1] = '<form action="https://www.paypal.com/cgi-bin/webscr" method="post"><input type="hidden" name="cmd" value="_xclick">
 +
<input type="hidden" name="business" value="ipbwiki@gmail.com"><input type="hidden" name="item_name" value="IpbWiki PayPal">
 +
<input type="hidden" name="no_shipping" value="1"><input type="hidden" name="cn" value="Optional Comments">
 +
<input type="hidden" name="currency_code" value="EUR"><input type="hidden" name="tax" value="0">
 +
<input type="hidden" name="bn" value="PP-DonationsBF">
 +
<input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-but04.gif" border="0" name="submit" alt="Make payments with PayPal - it\'s fast, free and secure!">
 +
</form>';
  
 
# IPN
 
# IPN
 
if ($wgPayPalIPN) {
 
if ($wgPayPalIPN) {
  
global $wgHooks, $wgPayPalTest, $wgPayPalIPNTable, $wgUseAjax;
+
global $wgHooks, $wgPayPalTest, $wgPayPalIPNTable, $wgUseAjax, $wgPayPalAfterPurchase;
 
$db = &wfGetDB(DB_MASTER);
 
$db = &wfGetDB(DB_MASTER);
  
# If using AJAX add JS updater after page rendered, and add the updater to the allowed AJAX functions
+
# If using AJAX add JS updater after page rendered
if ($wgUseAjax) {
+
if ($wgUseAjax && $wgPayPalAfterPurchase) $wgHooks['OutputPageBeforeHTML'][] = 'wfPayPalAddAjaxUpdater';
$wgAjaxExportList[] = 'wfPayPalAjaxUpdater';
 
$wgHooks['OutputPageBeforeHTML'][] = 'wfPayPalAddAjaxUpdater';
 
}
 
  
 
# Create the IPN database table if it doesn't exist
 
# Create the IPN database table if it doesn't exist
 
$wgPayPalIPNTable = $db->tableName('PayPalIPN');
 
$wgPayPalIPNTable = $db->tableName('PayPalIPN');
 
if (!$db->tableExists($wgPayPalIPNTable)) {
 
if (!$db->tableExists($wgPayPalIPNTable)) {
$query = "CREATE TABLE $wgPayPalIPNTable (id INTEGER NOT NULL, total_donated NUMERIC, total_donations INTEGER, PRIMARY KEY (id));";
+
$query = "CREATE TABLE $wgPayPalIPNTable (ipn_id VARCHAR(32), ipn_date TINYTEXT, ipn_item INTEGER NOT NULL, ipn_from TINYTEXT, ipn_amount NUMERIC, ipn_status TINYTEXT, PRIMARY KEY (ipn_id));";
 
$result = $db->query($query);
 
$result = $db->query($query);
 +
$db->freeResult($result);
 
}
 
}
  
Line 69: Line 79:
  
 
# If this is an IPN post from paypal, validate and update the DB if verified
 
# If this is an IPN post from paypal, validate and update the DB if verified
if (isset($_POST['txn_id'])) {
+
if ($_REQUEST['title'] == '__ipn_post') {
  
 
# Disable normal wiki rendering and output
 
# Disable normal wiki rendering and output
Line 76: Line 86:
 
wfResetOutputBuffers();
 
wfResetOutputBuffers();
  
# Read the post from PayPal system and add 'cmd'
+
# Read the relavent info from posted data
 +
$id    = $_POST['txn_id'];
 +
$item  = isset($_POST['item_number']) ? $_POST['item_number'] : 1;
 +
$date  = $_POST['payment_date'];
 +
$from  = $_POST['payer_email'];
 +
$cur    = $_POST['mc_currency'];
 +
$amount = $_POST['payment_gross'];
 +
$status = $_POST['payment_status'];
 +
 
 +
# Post variables back to PayPal (with cmd appended) system (or sandbox if testing) to validate
 +
$log = "Transaction $id ($cur$amt) from $from";
 
$req = 'cmd=_notify-validate';
 
$req = 'cmd=_notify-validate';
 
foreach ($_POST as $k => $v) $req .= "&$k=".urlencode(stripslashes($v));
 
foreach ($_POST as $k => $v) $req .= "&$k=".urlencode(stripslashes($v));
 
# Post back to PayPal system (or sandbox if testing) to validate
 
 
$header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
 
$header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
 
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
 
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
Line 86: Line 104:
 
$domain = $wgPayPalTest ? 'www.sandbox.paypal.com' : 'www.paypal.com';
 
$domain = $wgPayPalTest ? 'www.sandbox.paypal.com' : 'www.paypal.com';
 
$fp = fsockopen ($domain, 80, $errno, $errstr, 30);
 
$fp = fsockopen ($domain, 80, $errno, $errstr, 30);
if (!$fp) wfPayPalLog("HTTP Error: Could not open socket to $domain!");
+
if (!$fp) wfPayPalLog("$log: Could not open HTTP socket to $domain!");
 
else {
 
else {
fputs($fp, $header . $req);
+
fputs($fp, $header.$req);
 
while (!feof($fp)) $res = fgets($fp, 1024);
 
while (!feof($fp)) $res = fgets($fp, 1024);
if (strcmp ($res, "VERIFIED") == 0) {
+
if ($res === 'VERIFIED') wfPayPalUpdateItem($id,$date,$item,$from,$amount,$status); # Update DB
wfPayPalLog('payment_status: '.$_POST['payment_status']); // check the payment_status is Completed
+
else wfPayPalLog("$log returned $res, status: $status");
wfPayPalLog('txn_id: '.$_POST['txn_id']); // check that txn_id has not been previously processed
 
wfPayPalLog('receiver_email: '.$_POST['receiver_email']);
 
wfPayPalLog('payment_amount: '.$_POST['payment_amount']);
 
$log = '';
 
foreach($_POST as $k=>$v) $log .= "$k: $v\n";
 
wfPayPalLog($log);
 
 
 
# Store the payment information in the IPN table
 
# - also store buyer info?
 
#$item = isset($_REQUEST['item_number']) ? $_REQUEST['item_number'] : 1;
 
#$result = $db->query("SELECT CREATE TABLE $wgPayPalIPNTable;");
 
#if ($result instanceof ResultWrapper) $result = $result->result;
 
#if ($row = $db->fetchRow($result)) {
 
#$row[0] # todo
 
#}
 
}
 
elseif (strcmp($res, "INVALID") == 0) wfPayPalLog("PayPal returned INVALID response!");
 
 
}
 
}
 
fclose ($fp);
 
fclose ($fp);
Line 161: Line 162:
 
# IPN
 
# IPN
 
if ($wgPayPalIPN) {
 
if ($wgPayPalIPN) {
global $wgPayPalIPNTable, $wgPayPalTest, $wgServer, $wgScript, $wgTitle, $wgPayPalItemsRendered;
+
global $wgParser, $wgServer, $wgScript, $wgTitle, $wgPayPalIPNTable, $wgPayPalTest, $wgPayPalUpdating, $wgPayPalAfterPurchase;
 +
$wgParser->disableCache();
  
# Change the paypal URL's to sandbox site if testing
+
# Set item_number to this forms id
if ($wgPayPalTest) $form = str_replace('www.paypal.com','www.sandbox.paypal.com',$form);
+
$form = str_replace('</form>',"<input type=\"hidden\" name=\"item_number\" value=\"$part1\" /></form>",$form);
  
 
# Add a notify_url value in the form to tell paypal to post account changes to this script
 
# Add a notify_url value in the form to tell paypal to post account changes to this script
 
# todo: append shared secret to notify url
 
# todo: append shared secret to notify url
$form = str_replace('</form>',"<input type=\"hidden\" name=\"notify_url\" value=\"$wgServer$wgScript\" /></form>",$form);
+
$form = str_replace('</form>',"<input type=\"hidden\" name=\"notify_url\" value=\"$wgServer$wgScript/__ipn_post\" /></form>",$form);
  
# Add the receiver_email if testing in the paypal sandbox
+
# Add the receiver_email and change the URL's to sandbox site if testing in the paypal sandbox
 
if ($wgPayPalTest) {
 
if ($wgPayPalTest) {
 +
$form = str_replace('www.paypal.com','www.sandbox.paypal.com',$form);
 
$form = str_replace('business','receiver_email',$form);
 
$form = str_replace('business','receiver_email',$form);
 
$form = str_replace('</form>',"<input type=\"hidden\" name=\"business\" value=\"$wgPayPalTest\" /></form>",$form);
 
$form = str_replace('</form>',"<input type=\"hidden\" name=\"business\" value=\"$wgPayPalTest\" /></form>",$form);
 
}
 
}
  
# Set return URL to this page and method to GET
+
# Set return URL to this page and method to GET (and include the item id of the submitted form)
 
$url = $wgTitle->getFullUrl();
 
$url = $wgTitle->getFullUrl();
 
$form = str_replace('</form>',"<input type=\"hidden\" name=\"return\" value=\"$url\" /></form>",$form);
 
$form = str_replace('</form>',"<input type=\"hidden\" name=\"return\" value=\"$url\" /></form>",$form);
$form = str_replace('</form>',"<input type=\"hidden\" name=\"rm\" value=\"1\" /></form>",$form);
+
#$form = str_replace('</form>',"<input type=\"hidden\" name=\"rm\" value=\"1\" /></form>",$form);
 +
 
 +
# Get the current totals for this form from DB
 +
wfPayPalGetTotals($part1,$total_donated,$total_donations);
 +
$total_donated = number_format($total_donated,2);
  
# Obtain the amount donated and total number of donations for this id form the DB
+
# If returning from paypal purchase which used this form, wrap totals in spans with id's so they can be updated by AJAX
$total_donated = $total_donations = 0;
+
if ($wgPayPalAfterPurchase == $part1) {
$db = &wfGetDB(DB_SLAVE);
+
$total_donated  = "<span id=\"paypal_donated\">$total_donated</span>";
$result = $db->query("SELECT id,total_donated,total_donations FROM $wgPayPalIPNTable WHERE id = '$part1'");
+
$total_donations = "<span id=\"paypal_donations\">$total_donations</span>";
if ($result instanceof ResultWrapper) $result = $result->result;
+
$total_updating  = "<span id=\"paypal_updating\">$wgPayPalUpdating</span>";
if ($row = $db->fetchRow($result)) {
+
$input = wfMsgReplaceArgs($input,array($total_donated,$total_donations,$total_updating));
$total_donated = $row[1];
 
$total_donations = $row[2];
 
 
}
 
}
  
# Replace $1 and $2 in the text with the total amount donated and the total number of donations
+
# Replace $1, $2 and $3 in the text with the total amount donated and the total number of donations
# - these values are surrounded by div's endowed with id's so that the AJAX updater can access them
+
# Don't display any text if no donations
$total_donated = number_format($total_donated,2);
+
$input = $total_donations ? wfMsgReplaceArgs($input,array($total_donated,$total_donations,'')) : '';
$total_donated = "<span id=\"total_donated_$row[0]\">$total_donated</span>";
 
$total_donations = "<span id=\"total_donations_$row[0]\">$total_donations</span>";
 
$input = wfMsgReplaceArgs($input,array($total_donated,$total_donations));
 
 
}
 
}
  
Line 203: Line 205:
 
}
 
}
  
# Adds a JS function to poll for changes to any of the button's totals
+
# Obtain the amount donated and total number of donations for an item (form number) from the DB
 +
# - must sum all completed transactions associated with the passed item number
 +
function wfPayPalGetTotals($item,&$total_donated,&$total_donations) {
 +
global $wgPayPalIPNTable;
 +
$db = &wfGetDB(DB_SLAVE);
 +
$result = $db->query("SELECT ipn_amount FROM $wgPayPalIPNTable WHERE ipn_item = $item AND ipn_status = 'Completed'");
 +
if ($result instanceof ResultWrapper) $result = $result->result;
 +
$total_donated = $total_donations = 0;
 +
while ($row = $db->fetchRow($result)) {
 +
$total_donations++;
 +
$total_donated += $row[0];
 +
}
 +
$db->freeResult($result);
 +
}
 +
 
 +
# Update a transaction (id) with a new status or insert a new transaction
 +
# - if transaction id already exists, only the date and status are updated
 +
function wfPayPalUpdateItem($id,$date,$item,$from,$amount,$status) {
 +
global $wgPayPalIPNTable;
 +
$db = &wfGetDB(DB_MASTER);
 +
$result = $db->query("SELECT ipn_status FROM $wgPayPalIPNTable WHERE ipn_id = '$id'");
 +
if ($result instanceof ResultWrapper) $result = $result->result;
 +
$exists = $db->fetchRow($result);
 +
$db->freeResult($result);
 +
if ($exists) $db->update($wgPayPalIPNTable,array('ipn_date' => $date, 'ipn_status' => $status),array("ipn_id = '$id'"));
 +
else $db->insert($wgPayPalIPNTable,array(
 +
'ipn_id'    => $id,
 +
'ipn_date'  => $date,
 +
'ipn_item'  => $item,
 +
'ipn_from'  => $from,
 +
'ipn_amount' => $amount,
 +
'ipn_status' => $status
 +
));
 +
}
 +
 
 +
# Adds a JS function to poll for changes to the forms totals after returning from purchase
 
function wfPayPalAddAjaxUpdater(&$out) {
 
function wfPayPalAddAjaxUpdater(&$out) {
global $wgJsMimeType;
+
global $wgJsMimeType,$wgPayPalPollms;
 +
$id = $_POST['txn_id'];
 
$out->addScript("<script type='$wgJsMimeType'>
 
$out->addScript("<script type='$wgJsMimeType'>
function workflowUpdateState(name) {
+
var poll = setInterval('paypalPollUpdater()',$wgPayPalPollms);
clearTimeout(workflowUpdate);
+
function paypalResponseHandler(xmlhttp) {
workflowLastState = workflowData[name][0];
+
var totals = new Array();
var state = workflowData[name][workflowLastState];
+
totals = xmlhttp.responseText.split('|');
sajax_do_call('wfPayPalAjaxUpdater',[wgPageName,name,state],document.getElementById('catlinks'));
+
if (totals[2] == 'Completed') clearTimeout(poll);
}
+
document.getElementById('paypal_donated').innerHTML = totals[0];
function workflowSwitchState(name,dir) {
+
document.getElementById('paypal_donations').innerHTML = totals[1];
clearTimeout(workflowUpdate);
+
document.getElementById('paypal_updating').innerHTML = totals[2];
document.getElementById('workflow-'+name+'-'+state).setAttribute('style','');
+
}
if (workflowLastState != state) workflowUpdate = setTimeout('workflowUpdateState(\"'+name+'\")',$wgWorkflowUpdateDelay);
+
function paypalPollUpdater() {
}
+
sajax_do_call('wfPayPalAjaxUpdater',['$id'],paypalResponseHandler);
</script>");
+
}</script>");
 
return true;
 
return true;
 
}
 
}
  
# The function called by the AJAX dispatcher to return the current totals of all buttons
+
# The function called by the AJAX dispatcher to return the current totals of the form used to make the purchase
function wfPayPalAjaxUpdater() {
+
function wfPayPalAjaxUpdater($id) {
# todo: just return all the items in the db
+
global $wgPayPalIPNTable;
return $something;
+
$db = &wfGetDB(DB_SLAVE);
 +
$result = $db->query("SELECT ipn_status,ipn_item FROM $wgPayPalIPNTable WHERE ipn_id = '$id'");
 +
if ($result instanceof ResultWrapper) $result = $result->result;
 +
$total_donated = $total_donations = 0;
 +
$status = "Transaction $id not found!";
 +
if ($row = $db->fetchRow($result)) {
 +
list($status,$item) = $row;
 +
wfPayPalGetTotals($item,$total_donated,$total_donations);
 +
}
 +
$total_donated = number_format($total_donated,2);
 +
return "$total_donated|$total_donations|$status";
 
}
 
}
  
Line 234: Line 282:
 
@file_put_contents($wgPayPalLog,"$ts $text\n",FILE_APPEND);
 
@file_put_contents($wgPayPalLog,"$ts $text\n",FILE_APPEND);
 
}
 
}
 +
</php>
 +
[[Category:Legacy Extensions|PayPal]][[Category:PayPal]]

Latest revision as of 14:59, 22 October 2014

Legacy.svg Legacy: This article describes a concept that has been superseded in the course of ongoing development on the Organic Design wiki. Please do not develop this any further or base work on this concept, this is only useful for a historic record of work done. You may find a link to the currently used concept or function in this article, if not you can contact the author to find out what has taken the place of this legacy item.

<php><?php /* IpbWiki Paypal WikiMedia extension

Changes by User:Nad (1.0.3 - 2007-12-04):

 - Added IPN support (which can also work with AJAX updating if $wgUseAjax set)
 - Change button definitions to work from LocalSettings rather than code hack
 - Output warnings into sitenotice in error box
 - Log IPN post errors to log file
  • /

if (!defined('MEDIAWIKI')) die('Not an entry point.');

define('PAYPAL_VERSION','1.0.3, 2007-12-13');

$wgPayPalIPN = false; # Use IPN notification $wgPayPalTest = false; # Set to the email address of the test merchant account if testing IPN in the PayPal sandbox site $wgPayPalPollms = 5000; # Number of milliseconds to wait between each call to AJAX updater $wgPayPalUpdating = 'updating...'; # Message to add to form totals text (in $3) before IPN post arrives

$wgExtensionCredits['parserhook'][] = array( 'name' => 'IpbWiki PayPal', 'version' => PAYPAL_VERSION, 'author' => 'Peter De Decker, (IPN support by User:Nad)', 'url' => 'http://www.ipbwiki.com/IpbWiki_Paypal_Extension', 'description' => 'Mediawiki PayPal Extension' );

$wgPayPalLog = str_replace('.php','.log',__FILE__); $ipbwiki_paypal = array(); $wgExtensionFunctions[] = "wfPayPalExtension"; $wgAjaxExportList[] = 'wfPayPalAjaxUpdater';

  1. If returning from merchant, get item number (form id)

$wgPayPalAfterPurchase = (isset($_POST['item_number']) && !($_REQUEST['title'] == '__ipn_post')) ? $_POST['item_number'] : false;

  1. Set up the extension

function wfPayPalExtension() { global $wgParser, $ipbwiki_paypal, $wgPayPalIPN;

# Register the extension with the WikiText parser $wgParser->setHook( "paypal", "wfPayPalRenderButton" );

# Set button 1 to a default value if not defined if (!isset($ipbwiki_paypal[1])) $ipbwiki_paypal[1] = '<form action="https://www.paypal.com/cgi-bin/webscr" method="post"><input type="hidden" name="cmd" value="_xclick"> <input type="hidden" name="business" value="ipbwiki@gmail.com"><input type="hidden" name="item_name" value="IpbWiki PayPal"> <input type="hidden" name="no_shipping" value="1"><input type="hidden" name="cn" value="Optional Comments"> <input type="hidden" name="currency_code" value="EUR"><input type="hidden" name="tax" value="0"> <input type="hidden" name="bn" value="PP-DonationsBF"> <input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-but04.gif" border="0" name="submit" alt="Make payments with PayPal - it\'s fast, free and secure!"> </form>';

# IPN if ($wgPayPalIPN) {

global $wgHooks, $wgPayPalTest, $wgPayPalIPNTable, $wgUseAjax, $wgPayPalAfterPurchase; $db = &wfGetDB(DB_MASTER);

# If using AJAX add JS updater after page rendered if ($wgUseAjax && $wgPayPalAfterPurchase) $wgHooks['OutputPageBeforeHTML'][] = 'wfPayPalAddAjaxUpdater';

# Create the IPN database table if it doesn't exist $wgPayPalIPNTable = $db->tableName('PayPalIPN'); if (!$db->tableExists($wgPayPalIPNTable)) { $query = "CREATE TABLE $wgPayPalIPNTable (ipn_id VARCHAR(32), ipn_date TINYTEXT, ipn_item INTEGER NOT NULL, ipn_from TINYTEXT, ipn_amount NUMERIC, ipn_status TINYTEXT, PRIMARY KEY (ipn_id));"; $result = $db->query($query); $db->freeResult($result); }

# If the table couldn't be created, disable IPN and add error to site notice if (!$db->tableExists($wgPayPalIPNTable)) { global $wgSiteNotice; $wgPayPalIPN = false;

$wgSiteNotice = "

PayPal IPN Error! Could not create IPN database table, IPN functionality is disabled.
Please ensure the wiki database user has CREATE permission, or add the table manually with the following query:
$query

";

}

# If this is an IPN post from paypal, validate and update the DB if verified if ($_REQUEST['title'] == '__ipn_post') {

# Disable normal wiki rendering and output global $wgOut; $wgOut->disable(); wfResetOutputBuffers();

# Read the relavent info from posted data $id = $_POST['txn_id']; $item = isset($_POST['item_number']) ? $_POST['item_number'] : 1; $date = $_POST['payment_date']; $from = $_POST['payer_email']; $cur = $_POST['mc_currency']; $amount = $_POST['payment_gross']; $status = $_POST['payment_status'];

# Post variables back to PayPal (with cmd appended) system (or sandbox if testing) to validate $log = "Transaction $id ($cur$amt) from $from"; $req = 'cmd=_notify-validate'; foreach ($_POST as $k => $v) $req .= "&$k=".urlencode(stripslashes($v)); $header .= "POST /cgi-bin/webscr HTTP/1.0\r\n"; $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; $header .= "Content-Length: ".strlen($req)."\r\n\r\n"; $domain = $wgPayPalTest ? 'www.sandbox.paypal.com' : 'www.paypal.com'; $fp = fsockopen ($domain, 80, $errno, $errstr, 30); if (!$fp) wfPayPalLog("$log: Could not open HTTP socket to $domain!"); else { fputs($fp, $header.$req); while (!feof($fp)) $res = fgets($fp, 1024); if ($res === 'VERIFIED') wfPayPalUpdateItem($id,$date,$item,$from,$amount,$status); # Update DB else wfPayPalLog("$log returned $res, status: $status"); } fclose ($fp); } } }

  1. The callback function for converting the input text to HTML output

function wfPayPalRenderButton( $input, $argv ) { global $ipbwiki_paypal, $wgAuth, $wgSiteNotice, $wgPayPalIPN;

$error = '

warning, specified paypal button not found, defaulting to button 1

';

$pos_space = strpos($input,' '); if (!$pos_space) { if (is_numeric($input)) { // format <paypal>number</paypal> $part1 = $input; $part2 = ; if (!$ipbwiki_paypal[$part1]) { $wgSiteNotice .= $error; $part1 = 1; $part2 = $input; } } else { // format <paypal>text</paypal> & format <paypal></paypal> $part1 = 1; $part2 = $input; } } else { // format <paypal>number text</paypal> $part1 = substr($input,0,$pos_space); $part2 = substr($input,$pos_space+1); if (is_numeric($part1)) { if (!$ipbwiki_paypal[$part1]) { $wgSiteNotice .= $error; $part1 = 1; } } else { // format <paypal>text</paypal> $part1 = 1; $part2 = $input; } } $form = $ipbwiki_paypal[$part1]; // if the ipbwiki interface is available, then use the clean function which is defined there, otherwise just clean the necessities... if (class_exists ('ipbwiki')) { $input = $wgAuth->ipbwiki->ipbwiki->clean_value ($part2); } else { $part2 = str_replace( ">", ">", $part2 ); $part2 = str_replace( "<", "<", $part2 ); $part2 = str_replace( "\"", """, $part2 ); $part2 = str_replace( "!", "!", $part2 ); $part2 = str_replace( "'", "'", $part2 ); $input = $part2; }

# IPN if ($wgPayPalIPN) { global $wgParser, $wgServer, $wgScript, $wgTitle, $wgPayPalIPNTable, $wgPayPalTest, $wgPayPalUpdating, $wgPayPalAfterPurchase; $wgParser->disableCache();

# Set item_number to this forms id $form = str_replace('</form>',"<input type=\"hidden\" name=\"item_number\" value=\"$part1\" /></form>",$form);

# Add a notify_url value in the form to tell paypal to post account changes to this script # todo: append shared secret to notify url $form = str_replace('</form>',"<input type=\"hidden\" name=\"notify_url\" value=\"$wgServer$wgScript/__ipn_post\" /></form>",$form);

# Add the receiver_email and change the URL's to sandbox site if testing in the paypal sandbox if ($wgPayPalTest) { $form = str_replace('www.paypal.com','www.sandbox.paypal.com',$form); $form = str_replace('business','receiver_email',$form); $form = str_replace('</form>',"<input type=\"hidden\" name=\"business\" value=\"$wgPayPalTest\" /></form>",$form); }

# Set return URL to this page and method to GET (and include the item id of the submitted form) $url = $wgTitle->getFullUrl(); $form = str_replace('</form>',"<input type=\"hidden\" name=\"return\" value=\"$url\" /></form>",$form); #$form = str_replace('</form>',"<input type=\"hidden\" name=\"rm\" value=\"1\" /></form>",$form);

# Get the current totals for this form from DB wfPayPalGetTotals($part1,$total_donated,$total_donations); $total_donated = number_format($total_donated,2);

# If returning from paypal purchase which used this form, wrap totals in spans with id's so they can be updated by AJAX if ($wgPayPalAfterPurchase == $part1) { $total_donated = "$total_donated"; $total_donations = "$total_donations"; $total_updating = "$wgPayPalUpdating"; $input = wfMsgReplaceArgs($input,array($total_donated,$total_donations,$total_updating)); }

# Replace $1, $2 and $3 in the text with the total amount donated and the total number of donations # Don't display any text if no donations $input = $total_donations ? wfMsgReplaceArgs($input,array($total_donated,$total_donations,)) : ; }

$output = "

$form$input

";

return $output; }

  1. Obtain the amount donated and total number of donations for an item (form number) from the DB
  2. - must sum all completed transactions associated with the passed item number

function wfPayPalGetTotals($item,&$total_donated,&$total_donations) { global $wgPayPalIPNTable; $db = &wfGetDB(DB_SLAVE); $result = $db->query("SELECT ipn_amount FROM $wgPayPalIPNTable WHERE ipn_item = $item AND ipn_status = 'Completed'"); if ($result instanceof ResultWrapper) $result = $result->result; $total_donated = $total_donations = 0; while ($row = $db->fetchRow($result)) { $total_donations++; $total_donated += $row[0]; } $db->freeResult($result); }

  1. Update a transaction (id) with a new status or insert a new transaction
  2. - if transaction id already exists, only the date and status are updated

function wfPayPalUpdateItem($id,$date,$item,$from,$amount,$status) { global $wgPayPalIPNTable; $db = &wfGetDB(DB_MASTER); $result = $db->query("SELECT ipn_status FROM $wgPayPalIPNTable WHERE ipn_id = '$id'"); if ($result instanceof ResultWrapper) $result = $result->result; $exists = $db->fetchRow($result); $db->freeResult($result); if ($exists) $db->update($wgPayPalIPNTable,array('ipn_date' => $date, 'ipn_status' => $status),array("ipn_id = '$id'")); else $db->insert($wgPayPalIPNTable,array( 'ipn_id' => $id, 'ipn_date' => $date, 'ipn_item' => $item, 'ipn_from' => $from, 'ipn_amount' => $amount, 'ipn_status' => $status )); }

  1. Adds a JS function to poll for changes to the forms totals after returning from purchase

function wfPayPalAddAjaxUpdater(&$out) { global $wgJsMimeType,$wgPayPalPollms; $id = $_POST['txn_id']; $out->addScript("<script type='$wgJsMimeType'> var poll = setInterval('paypalPollUpdater()',$wgPayPalPollms); function paypalResponseHandler(xmlhttp) { var totals = new Array(); totals = xmlhttp.responseText.split('|'); if (totals[2] == 'Completed') clearTimeout(poll); document.getElementById('paypal_donated').innerHTML = totals[0]; document.getElementById('paypal_donations').innerHTML = totals[1]; document.getElementById('paypal_updating').innerHTML = totals[2]; } function paypalPollUpdater() { sajax_do_call('wfPayPalAjaxUpdater',['$id'],paypalResponseHandler); }</script>"); return true; }

  1. The function called by the AJAX dispatcher to return the current totals of the form used to make the purchase

function wfPayPalAjaxUpdater($id) { global $wgPayPalIPNTable; $db = &wfGetDB(DB_SLAVE); $result = $db->query("SELECT ipn_status,ipn_item FROM $wgPayPalIPNTable WHERE ipn_id = '$id'"); if ($result instanceof ResultWrapper) $result = $result->result; $total_donated = $total_donations = 0; $status = "Transaction $id not found!"; if ($row = $db->fetchRow($result)) { list($status,$item) = $row; wfPayPalGetTotals($item,$total_donated,$total_donations); } $total_donated = number_format($total_donated,2); return "$total_donated|$total_donations|$status"; }

  1. A log file for recording IPN error conditions

function wfPayPalLog($text) { global $wgPayPalLog; $ts = date("Y-m-d H:i:s"); @file_put_contents($wgPayPalLog,"$ts $text\n",FILE_APPEND); } </php>