Can´t save paypal ipn values into a database - php

I have implemented a paypal ipn process. I want to retrieve some data from the ipn and save it in a database table. The problem is it doesn´t work. I used the part of the procedure from a tutorial http://tutorialzine.com/2010/05/donation-center-php-mysql-paypal-api/
Nelow is the code I have used.
ipn.php
<?php
require "paypal_integration_class/paypal.class.php";
require "config.php";
require "connect.php";
$p = new paypal_class;
$p->paypal_url = $payPalURL;
if ($p->validate_ipn()) {
if($p->ipn_data['payment_status']=='Completed')
{
$amount = $p->ipn_data['mc_gross'] - $p->ipn_data['mc_fee'];
mysql_query(" INSERT INTO dc_donations (transaction_id,donor_email,amount,original_request)
VALUES (
'".esc($p->ipn_data['txn_id'])."',
'".esc($p->ipn_data['payer_email'])."',
".(float)$amount.",
'".esc(http_build_query($_POST))."'
)");
}
}
function esc($str)
{
global $link;
return mysql_real_escape_string($str,$link);
}
?>
connect.php
/* Database config */
$db_host = "localhost";
$db_user = "egesachi_ipn";
$db_pass = "lollipop";
$db_database = "egesachi_ipn";
/* End config */
$link = #mysql_connect($db_host,$db_user,$db_pass) or die('Unable to establish a DB connection');
mysql_set_charset('utf8');
mysql_select_db($db_database,$link);
?>
config.php
<?php
// Fill your PayPal email below.
// This is where you will receive the donations.
$myPayPalEmail = 'ben#center.org';
// The paypal URL:
$payPalURL = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
// Your goal in USD:
$goal = 100;
// Demo mode is set - set it to false to enable donations.
// When enabled PayPal is bypassed.
$demoMode = false;
if($demoMode)
{
$payPalURL = 'demo_mode.php';
}
?>
paypal_integration_class/paypal.class.php
Below is the URL where the script above is from.
http://www.phpclasses.org/package/2249-PHP-Process-Paypal-payment-interactions.html
class paypal_class {
var $last_error; // holds the last error encountered
var $ipn_log; // bool: log IPN results to text file?
var $ipn_log_file; // filename of the IPN log
var $ipn_response; // holds the IPN response from paypal
var $ipn_data = array(); // array contains the POST values for IPN
var $fields = array(); // array holds the fields to submit to paypal
function paypal_class() {
// initialization constructor. Called when class is created.
$this->paypal_url = 'https://www.paypal.com/cgi-bin/webscr';
$this->last_error = '';
$this->ipn_log_file = 'ipn_log.txt';
$this->ipn_log = true;
$this->ipn_response = '';
// populate $fields array with a few default values. See the paypal
// documentation for a list of fields and their data types. These defaul
// values can be overwritten by the calling script.
$this->add_field('rm','2'); // Return method = POST
$this->add_field('cmd','_xclick');
}
function add_field($field, $value) {
// adds a key=>value pair to the fields array, which is what will be
// sent to paypal as POST variables. If the value is already in the
// array, it will be overwritten.
$this->fields["$field"] = $value;
}
function submit_paypal_post() {
// this function actually generates an entire HTML page consisting of
// a form with hidden elements which is submitted to paypal via the
// BODY element's onLoad attribute. We do this so that you can validate
// any POST vars from you custom form before submitting to paypal. So
// basically, you'll have your own form which is submitted to your script
// to validate the data, which in turn calls this function to create
// another hidden form and submit to paypal.
// The user will briefly see a message on the screen that reads:
// "Please wait, your order is being processed..." and then immediately
// is redirected to paypal.
echo "<html>\n";
echo "<head><title>Processing Payment...</title></head>\n";
echo "<body onLoad=\"document.form.submit();\">\n";
echo "<center><h3>Please wait, your order is being processed...</h3></center>\n";
echo "<form method=\"post\" name=\"form\" action=\"".$this->paypal_url."\">\n";
foreach ($this->fields as $name => $value) {
echo "<input type=\"hidden\" name=\"$name\" value=\"$value\">";
}
echo "</form>\n";
echo "</body></html>\n";
}
function validate_ipn() {
// parse the paypal URL
$url_parsed=parse_url($this->paypal_url);
// generate the post string from the _POST vars aswell as load the
// _POST vars into an arry so we can play with them from the calling
// script.
$post_string = '';
foreach ($_POST as $field=>$value) {
$this->ipn_data["$field"] = $value;
$post_string .= $field.'='.urlencode($value).'&';
}
$post_string.="cmd=_notify-validate"; // append ipn command
// open the connection to paypal
$fp = fsockopen($url_parsed[host],"80",$err_num,$err_str,30);
if(!$fp) {
// could not open the connection. If loggin is on, the error message
// will be in the log.
$this->last_error = "fsockopen error no. $errnum: $errstr";
$this->log_ipn_results(false);
return false;
} else {
// Post the data back to paypal
fputs($fp, "POST $url_parsed[path] HTTP/1.1\r\n");
fputs($fp, "Host: $url_parsed[host]\r\n");
fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-length: ".strlen($post_string)."\r\n");
fputs($fp, "Connection: close\r\n\r\n");
fputs($fp, $post_string . "\r\n\r\n");
// loop through the response from the server and append to variable
while(!feof($fp)) {
$this->ipn_response .= fgets($fp, 1024);
}
fclose($fp); // close connection
}
if (eregi("VERIFIED",$this->ipn_response)) {
// Valid IPN transaction.
$this->log_ipn_results(true);
return true;
} else {
// Invalid IPN transaction. Check the log for details.
$this->last_error = 'IPN Validation Failed.';
$this->log_ipn_results(false);
return false;
}
}
function log_ipn_results($success) {
if (!$this->ipn_log) return; // is logging turned off?
// Timestamp
$text = '['.date('m/d/Y g:i A').'] - ';
// Success or failure being logged?
if ($success) $text .= "SUCCESS!\n";
else $text .= 'FAIL: '.$this->last_error."\n";
// Log the POST variables
$text .= "IPN POST Vars from Paypal:\n";
foreach ($this->ipn_data as $key=>$value) {
$text .= "$key=$value, ";
}
// Log the response from the paypal server
$text .= "\nIPN Response from Paypal Server:\n ".$this->ipn_response;
// Write to log
$fp=fopen($this->ipn_log_file,'a');
fwrite($fp, $text . "\n\n");
fclose($fp); // close file
}
function dump_fields() {
// Used for debugging, this function will output all the field/value pairs
// that are currently defined in the instance of the class using the
// add_field() function.
echo "<h3>paypal_class->dump_fields() Output:</h3>";
echo "<table width=\"95%\" border=\"1\" cellpadding=\"2\" cellspacing=\"0\">
<tr>
<td bgcolor=\"black\"><b><font color=\"white\">Field Name</font></b></td>
<td bgcolor=\"black\"><b><font color=\"white\">Value</font></b></td>
</tr>";
ksort($this->fields);
foreach ($this->fields as $key => $value) {
echo "<tr><td>$key</td><td>".urldecode($value)." </td></tr>";
}
echo "</table><br>";
}
}
paypal html checkout form
<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="post">
<!-- Identify your business so that you can collect the payments. -->
<input type="hidden" name="business"
value="ben#egesachildrencenter.org">
<!-- Specify a Donate button. -->
<input type="hidden" name="cmd" value="_donations">
<!-- Specify details about the contribution -->
<input type="hidden" name="item_name" value="<?php echo $sponsored; ?>">
<input type="hidden" name="item_number" value="<?php echo $pupilid; ?>">
<input type="hidden" name="amount" value="25.00">
<input type="hidden" name="currency_code" value="USD">
<input type="hidden" name="return" value="http://www.center.org/pap/needy.php">
<input type="hidden" name="notify_url" value="http://www.center.org/pap/ipn.php">
<!-- Display the payment button. -->
<input type="image" name="submit" border="0"
src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif"
alt="PayPal - The safer, easier way to pay online">
<img alt="" border="0" width="1" height="1"
src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" >
</form>
Please help me to find where I am going wrong or suggest another way of achieving th same result. Thanks.

Related

Stripe Payment Gateway - Variable Amounts

I have found lots of questions for this topic, but none seems to answer my question...
NB. Using Larry Ullman's Blog at http://www.larryullman.com/2012/11/28/creating-a-form-for-handling-payments-with-stripe/
Trying to use
$amount = $_REQUEST['amount'];
instead of
$amount = 23400; //EXAMPLE ONLY
But the value of $_REQUEST['amount']; is being ignored?
<?php
// Check for a form submission:
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Stores errors:
$errors = array();
// Need a payment token:
if (isset($_POST['stripeToken'])) {
$token = $_POST['stripeToken'];
$StripeAmount = $_REQUEST['amount']; // $20, in cents
// Check for a duplicate submission, just in case:
// Uses sessions, you could use a cookie instead.
if (isset($_SESSION['token']) && ($_SESSION['token'] == $token)) {
$errors['token'] = 'You have apparently resubmitted the form. Please do not do that.';
} else { // New submission.
$_SESSION['token'] = $token;
}
} else {
$errors['token'] = 'The order cannot be processed. Please make sure you have JavaScript enabled and try again.';
}
// Set the order amount somehow:
// $20, in cents
$amount = $_REQUEST['amount']; // in cents
// Validate other form data!
// If no errors, process the order:
if (empty($errors)) {
// create the charge on Stripe's servers - this will charge the user's card
try {
// Include the Stripe library:
// Assumes you've installed the Stripe PHP library using Composer!
require_once('stripe/init.php');
// set your secret key: remember to change this to your live secret key in production
// see your keys here https://manage.stripe.com/account
\Stripe\Stripe::setApiKey(STRIPE_PRIVATE_KEY);
// Charge the order:
$charge = \Stripe\Charge::create(array(
"amount" => $amount, // amount in cents, again
"currency" => "aud",
"source" => $token,
"description" => $email
)
);
// Check that it was paid:
if ($charge->paid == true) {
$OrderID = $_GET['order_id']; //reference from shop OrderId
$txn_id = $_GET['id']; //transaction number form payment Gateway
$objBooking->updateVisitorOrderRecord($OrderID, $txn_id);
header('location:visitor_payment_thanks.php');
} else { // Charge was not paid!
header('location:visitor_payment_error.php');
}
} catch (\Stripe\Error\Card $e) {
// Card was declined.
$e_json = $e->getJsonBody();
$err = $e_json['error'];
$errors['stripe'] = $err['message'];
} catch (\Stripe\Error\ApiConnection $e) {
// Network problem, perhaps try again.
} catch (\Stripe\Error\InvalidRequest $e) {
// You screwed up in your programming. Shouldn't happen!
} catch (\Stripe\Error\Api $e) {
// Stripe's servers are down!
} catch (\Stripe\Error\Base $e) {
// Something else that's not the customer's fault.
}
} // A user form submission error occurred, handled below.
} // Form submission.
// Set the Stripe key:
// Uses STRIPE_PUBLIC_KEY from the config file.
echo '<script type="text/javascript">Stripe.setPublishableKey("' . STRIPE_PUBLIC_KEY . '");</script>';
?>
<h1>Buy This Thing</h1>
<form action="buy.php" method="POST" id="payment-form">
<?php // Show PHP errors, if they exist:
if (isset($errors) && !empty($errors) && is_array($errors)) {
echo '<div class="alert alert-error"><h4>Error!</h4>The following error(s) occurred:<ul>';
foreach ($errors as $e) {
echo "<li>$e</li>";
}
echo '</ul></div>';
}?>
<div id="payment-errors"></div>
<span class="help-block">You can pay using: Mastercard, Visa, American Express, JCB, Discover, and Diners Club.</span>
<div class="alert alert-info"><h4>JavaScript Required!</h4>For security purposes, JavaScript is required in order to complete an order.</div>
<label>Card Number</label>
<input type="text" size="20" autocomplete="off" class="card-number input-medium">
<span class="help-block">Enter the number without spaces or hyphens.</span>
<label>CVC</label>
<input type="text" size="4" autocomplete="off" class="card-cvc input-mini">
<label>Expiration (MM/YYYY)</label>
<input type="text" size="2" class="card-expiry-month input-mini">
<span> / </span>
<input type="text" size="4" class="card-expiry-year input-mini">
<button type="submit" class="btn" id="submitBtn">Submit Payment</button>
</form>
<script src="buy.js"></script>
Thx Guys
I emailed STRIPE and based on feedback, the issue was that I should have been using SESSIONS....
PHP Manual Page on Session Handling

CSRF session token

I'm trying to implement a simple CSRF protection on a form, but I can't get it right. If someone can point out what I'm doing wrong, I would appreciate it.
The error: Every time I submit the form, I get "Invalid Submission2" because the token changes (after the form is submitted), since is being generated on the same page?
Edit -- I forgot to mention, another file (config.php) already has session_start().
<?php
class Module_Post extends Base_Module {
public function start()
{
requireLogin();
if (isset($_GET['act'])) {
switch($_GET['act']) {
case 'text':
$this->text();
break;
case 'image':
$this->image();
break;
default:
$this->text();
break;
}
} else {
$this->text();
}
}
private function text()
{
// Generate random unique token to prevent cross-site request forgery (CSRF).
if(empty($_SESSION['form_token']))
{
$form_token = md5(uniqid(rand(), TRUE));
$_SESSION['form_token'] = $form_token;
}
if(isset($_POST['submit']))
{
// Clean the content from cross-site scripting (xss)
$content = trim($_POST['content']);
$content = Xss::clean($content);
// Validate that the form token
if(!isset($_POST['form_token'], $_SESSION['form_token'])) {
$err = 'Invalid Submission';
} elseif ($_POST['form_token'] != $_SESSION['form_token']) {
$err = 'Invalid Submission2';
} elseif (strlen($content) < 10) {
$err = 'Your content contains too few characters.';
}
if(isset($err)) {
unset( $_SESSION['form_token']);
$this->setMessage($err, 'FAIL');
header('Location: index.php?mod=post');
exit;
}
// Insert database data here, then redirect
$this->setMessage('Your post was published successfully.', 'GOOD');
header('Location: index.php');
exit;
}
$this->tpl->assign('form_token', $form_token);
$this->tpl->display('new/text.tpl');
}
}
?>
The HTML (text.tpl file)
<form method='post' enctype='multipart/form-data' action='#'>
<fieldset>
<textarea rows="8" id="new_post" name="content" class="input-block-level"></textarea>
<input type="hidden" name="form_token" value="{$form_token}" />
<button type="submit" name="submit" class="btn btn-info pull-left">Create Post</button>
</fieldset>
</form>
You need to change this line
$this->tpl->assign('form_token', $form_token);
into:
$this->tpl->assign('form_token', $_SESSION['form_token']);
That's because you generate your token only with this condition:
if(empty($_SESSION['form_token']))
{
$form_token = md5(uniqid(rand(), TRUE));
$_SESSION['form_token'] = $form_token;
}
and unset it using this condition:
if(isset($err)) {
unset( $_SESSION['form_token']);
}
So if you send your form once and then reload page (without submitting form just url), $form_token variable is unknown because there is $_SESSION['form_token'] is not empty and then in your form you have empty token.
If you had displaying errors turned on in PHP you would see in this case in PHP:
Undefined variable: form_token in

PayPal Payment Verification, How the IPN works

I am searching for how to use the PayPal IPN (Instant Payment Notification) from past 2 weeks and couldn't understand how exactly to use it in my code.
I am using the following HTML code to create a PayPal button
button.html
<form name="paypalForm" action="paypal.php" method="post">
<input type="hidden" name="id" value="123">
<input type="hidden" name="CatDescription" value="20Percents (12 Credits)">
<input type="hidden" name="payment" value="10">
<input type="hidden" name="key" value="<? echo md5(date("Y-m-d:").rand()); ?>">
<input TYPE="image" SRC="http://www.coachsbr.com/images/site/paypal_button.gif" name="paypal" value="Payment via Paypal" >
</form>
And, the following code to process the payment and verify it
paypal.php
<?php
require_once('paypal.class.php'); // include the class file
$p = new paypal_class; // initiate an instance of the class
$p->paypal_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr'; // testing paypal url
//$p->paypal_url = 'https://www.paypal.com/cgi-bin/webscr'; // paypal url
// setup a variable for this script (ie: 'http://www.micahcarrick.com/paypal.php')
$this_script = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'];
// if there is not action variable, set the default action of 'process'
if (empty($_GET['action'])) $_GET['action'] = 'process';
switch ($_GET['action']) {
case 'process': // Process and order...
$CatDescription = $_REQUEST['CatDescription'];
$payment = $_REQUEST['payment'];
$id = $_REQUEST['id'];
$key = $_REQUEST['key'];
$p->add_field('business', 'info#aoptrading.com');
$p->add_field('return', $this_script.'?action=success');
$p->add_field('cancel_return', $this_script.'?action=cancel');
$p->add_field('notify_url', $this_script.'?action=ipn');
$p->add_field('item_name', $CatDescription);
$p->add_field('amount', $payment);
$p->add_field('key', $key);
$p->add_field('item_number', $id);
$p->submit_paypal_post(); // submit the fields to paypal
//$p->dump_fields(); // for debugging, output a table of all the fields
break;
case 'success': // Order was successful...
echo "<br/><p><b>Payment Successful.</b><br /></p>";
foreach ($_POST as $key => $value) { echo "$key: $value<br>"; }
break;
case 'cancel': // Order was canceled...
echo "<br/><p><b>The order was canceled!</b></p><br />";
foreach ($_POST as $key => $value) { echo "$key: $value<br>"; }
break;
case 'ipn': // Paypal is calling page for IPN validation...
if ($p->validate_ipn()) {
// For this example, we'll just email ourselves ALL the data.
$dated = date("D, d M Y H:i:s", time());
$subject = 'Instant Payment Notification - Recieved Payment';
$to = 'info#aoptrading.com'; // your email
$body = "An instant payment notification was successfully recieved\n";
$body .= "from ".$p->ipn_data['payer_email']." on ".date('m/d/Y');
$body .= " at ".date('g:i A')."\n\nDetails:\n";
$headers = "";
$headers .= "From: Test Paypal \r\n";
$headers .= "Date: $dated \r\n";
$PaymentStatus = $p->ipn_data['payment_status'];
$Email = $p->ipn_data['payer_email'];
$id = $p->ipn_data['item_number'];
if($PaymentStatus == 'Completed' or $PaymentStatus == 'Pending'){
$PaymentStatus = '2';
}else{
$PaymentStatus = '1';
}
foreach ($p->ipn_data as $key => $value) { $body .= "\n$key: $value"; }
fopen("http://www.virtualphoneline.com/admins/TestHMS.php?to=".urlencode($to)."&subject=".urlencode($subject)."&message=".urlencode($body)."&headers=".urlencode($headers)."","r");
}
break;
}
?>
An extra class file: (can be found here) http://pastebin.com/auCdYhaR
The code is working fine, but I am having big issue now, I am trying to validate whether the payment is successful or not from the following lines and doing the action (adding 10 credits to the database for the user on session).
case 'success': // Order was successful...
echo "<br/><p><b>Payment Successful.</b><br /></p>";
foreach ($_POST as $key => $value)
{
/*echo "$key: $value<br>";*/
// Do the required action, **add 10 credits in the database for the user on session**
}
break;
Finally, the issue is when I click on the button from button.html page it redirects to the Paypal.php page and when I enter my PayPal login details, payment is successful
THERE COMES A SMALL LITTLE TEXT -> RETURN TO THE SENDER'S WEBSITE I NEED TO CLICK ON IT, WHEN I CLICK ON IT, IT WILL BRING ME BACK TO PAYPAL.PHP PAGE AND THEN THE CASE 'SUCCESS' IS FIRED. IF I MAKE THE PAYMENT AND JUST CLOSE THE PAYPAL PAGE WITHOUT CLICKING ON RETURN TO THE SENDER'S WEBSITE AND DIDN'T WAIT TILL THE PAGE PAYPAL.PHP LOADS WEBSITE COULDN'T VERIFY THE PAYMENT AND COULDN'T ADD THE CREDITS.
HOW CAN I AUTOMATE THIS PROCESS AND ADD CREDITS UPON SUCCESSFUL PAYMENT NOT UPON SUCCESSFUL RETURN TO THE PAGE PAYPAL.PHP.
THANKS
It sounds to me like you're confusing PDT and IPN. PDT works by sending data to your return URL, but that is not the recommended way to handle post-payment processing tasks.
That's where IPN comes into play, which is a silent POST of data to your listener URL regardless of whether or not the user makes it back to your return URL. That way that code will always run no matter what (assuming you've got everything configured correctly.)
Sounds to me like you're sort of mixing the two and getting mixed results because of it.

How do you add results from PayPal IPN to update database

I am looking to be able to take the info from the PayPal IPN post, and use certain items to update my database. This is the current code I have for my ipn.php
<?php
// tell PHP to log errors to ipn_errors.log in this directory
ini_set('log_errors', true);
ini_set('error_log', dirname(__FILE__).'/ipn_errors.log');
// intantiate the IPN listener
include('ipnlistener.php');
$listener = new IpnListener();
// tell the IPN listener to use the PayPal test sandbox
$listener->use_sandbox = true;
// try to process the IPN POST
try {
$listener->requirePostMethod();
$verified = $listener->processIpn();
} catch (Exception $e) {
error_log($e->getMessage());
exit(0);
}
if ($verified) {
$errmsg = ''; // stores errors from fraud checks
// 1. Make sure the payment status is "Completed"
if ($_POST['payment_status'] != 'Completed') {
// simply ignore any IPN that is not completed
exit(0);
}
// 2. Make sure seller email matches your primary account email.
if ($_POST['receiver_email'] != 'PRIMARY EMAIL ADDRESS') {
$errmsg .= "'receiver_email' does not match: ";
$errmsg .= $_POST['receiver_email']."\n";
}
// 3. Make sure the currency code matches
if ($_POST['mc_currency'] != 'USD') {
$errmsg .= "'mc_currency' does not match: ";
$errmsg .= $_POST['mc_currency']."\n";
}
// 4. Ensure the transaction is not a duplicate.
mysql_connect('localhost', '[DB_USER]', '[DB_PW') or exit(0);
mysql_select_db('DB_NAME') or exit(0);
$txn_id = mysql_real_escape_string($_POST['txn_id']);
$sql = "SELECT COUNT(*) FROM orders WHERE txn_id = '$txn_id'";
$r = mysql_query($sql);
if (!$r) {
error_log(mysql_error());
exit(0);
}
$exists = mysql_result($r, 0);
mysql_free_result($r);
if ($exists) {
$errmsg .= "'txn_id' has already been processed: ".$_POST['txn_id']."\n";
}
if (!empty($errmsg)) {
// manually investigate errors from the fraud checking
$body = "IPN failed fraud checks: \n$errmsg\n\n";
$body .= $listener->getTextReport();
mail('NOTIFICATION EMAIL ADDRESS', 'IPN Fraud Warning', $body);
} else {
<?php
$csvData = file_get_contents($_POST['custom']);
$csvNumColumns = 3;
$csvDelim = ";";
$data = array_chunk(str_getcsv($csvData, $csvDelim), $csvNumColumns);
?>
// add this order to a table
$user_id = mysql_real_escape_string($_POST['item_name']);
$credit_amount = mysql_real_escape_string($_POST['item_number']);
$type = mysql_real_escape_string($_POST['custom']);
$sql = "INSERT INTO TABLE_NAME VALUES
(NULL, '$user_id', '$credit_amount', '$type')";
if (!mysql_query($sql)) {
error_log(mysql_error());
exit(0);
}
}
} else {
// manually investigate the invalid IPN
mail('NOTIFICATION EMAIL ADDRESS', 'Invalid IPN', $listener->getTextReport());
}
?>
This seemed to work fine when testing it with PayPal Sandbox's IPN testing service and I could enter in the needed values for item_name, item_number, and custom (or when using the below code)
<form name="_xclick" action="https://www.sandbox.paypal.com/cgi-bin/webscr"
method="post">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="business" value="SANDBOX EMAIL ADDRESS">
<input type="hidden" name="currency_code" value="USD">
<input type="hidden" name="amount" value="9.99">
<input type="hidden" name="custom" value="<?=$this->package['0']['delivered'];?>"
<input type="hidden" name="item_name" value="<?=$_SESSION["user_id"]?>"
<input type="hidden" name="item_number" value="<?=$this->package['0']['number'];?>"
<input type="hidden" name="return" value="WEBSITE_URL/success">
<input type="hidden" name="notify_url" value="WEBSITE_URL/ipn.php">
<input type="image" src="http://www.paypal.com/en_US/i/btn/btn_buynow_LG.gif"
border="0" name="submit" alt="Make payments with PayPal - it's fast, free and secure!">
</form>
However I soon realized that it would be much better for the 'item_name' to be something recognizable by the customer, rather than having it be defined as the 'user_id'. Is it possible to have the 'custom' pass through defined as all 3 of the variables that I need, separating them within the PayPal button with semicolons (as seem below)
<input type="hidden" name="custom" value="<?=$_SESSION["user_id"]?>;<?=$this->package['0']['number'];?>;<?=$this->package['0']['delivered'];?>"
then using something like
<?php
$csvData = file_get_contents($_POST['custom']);
$csvNumColumns = 3;
$csvDelim = ";";
$data = array_chunk(str_getcsv($csvData, $csvDelim), $csvNumColumns);
?>
to give me separate variables that can then be defined as 'user_id', 'credit_amount', and 'type'
once these variables are separated and defined, they are to be posted to the database, however, if there is already a column within the table that has the same 'user_id' as is trying to be posted AS WELL AS the same 'type' that is trying to be posted, then it should only update that row (the row already within the table before the attempted addition) by ADDING the 'credit_amount' to the corresponding 'credit_amount' cell in the old row.
You can pass multiple values, and have them populated into a single variable such as the variable "custom". There would not be an issue with this. I have done this myself as well in the past. When I coded this, I used the | to separate the values, and then had my IPN parse out the values within my IPN script.

PHP script to remove e-mail addresses from a comma-separated list [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
I run a small website and my users requested that I set up a mailing list. I found a simple free script that adds e-mail addresses to a protected text file, email.txt, in CSV format:
email1#yahoo.com,email2#yahoo.com,blah,blah,blah
The script works flawlessly. However, it is a nuisance to go through the file to manually remove e-mail addresses when users cancel their list subscriptions. I need to create a simple script that removes e-mail addresses.
All I want is a simple PHP script that displays a text box so users can enter their e-mail addresses and click a "Cancel Newsletter" button. The script should search the text file, find the given e-mail address and remove it and its trailing comma.
For example, say the contents of email.txt are
john#yahoo.com,peter#yahoo.com,steve#yahoo.com
If I type "peter#yahoo.com" into the text box displayed by my desired script, I want the file to look like this:
john#yahoo.com,steve#yahoo.com
UPDATE: I tried this code:
<?php
function showForm() {
echo '
<form method="post" action="">
Email Address: <input type="text" name="email"> <br />
<input type="submit" value="Cancel Newsletter" name="submit">
</form>
';
}
$_POST['email']
$to_delete = 'email';
$file = array_flip(explode(",",file_get_contents("email.txt")));
unset($file[$to_delete]);
file_put_contents("email.txt",implode(",",array_flip($file));
if(!$file_put_contents) {
die('Error occured');
} else {
echo 'Your subscription has been cancelled. You will not receive any further emails from us.';
}
}
} else {
showForm();
}
?>
This code doesn't even show the form.
UPDATE 2:
Another attempt at writing this script:
<?php
$email = $_POST["email"];
$text = file_get_contents("email.txt");
$oldWord = "$email";
$newWord = "";
$text = str_replace($oldWord , $newWord , $text);
$fp = fopen('email.txt', 'w');
fwrite($fp, $text);
fclose($file);
?>
This works as far as removing the e-mail address goes, but there is no announcement (echo). I would like for it to say either "that e-mail isn't subscribed" or "you have been removed," based on whether the script sucessfully finds the $email in the list and deletes it.
UPDATE 3 Dec. 31, 2011:
I tried the advanced code and just got a blank page, so I went back to my version. Here is the code I have now:
<html>
<body>
<form method="post" action="">
<p>Email Address: <input type="text" name="email"></p>
<input type="submit" value="Cancel Newsletter" name="submit">
</form>
<?php
$email = $_POST["email"];
$basetext = file_get_contents("email.txt");
$oldWord = "$email";
$newWord = "";
$text = str_replace($oldWord , $newWord , $basetext);
$str1 = $basetext;
// echo strlen($str1);
$str2 = $text;
// echo strlen($str2);
$fp = fopen('email.txt', 'w');
fwrite($fp, $text);
if ($str1 > $str2) { // This part handles the success/fail message
echo ("Success!");
} else {
echo ("Fail!");
}
fclose($file);
?>
</body>
</html>
This works perfectly. However, it displays the "fail" message when the page is loaded, not when triggered to load, after the submit button is pressed.
I would like to keep the original code if possible, just rearranged so that it only shows "Success!" or "Fail!" once it has executed the code.
I would like the echo messages to be the last script executed on the page.
Is there any reason why you don't use a database?
CREATE TABLE `emails` (`address` VARCHAR(255) NOT NULL, PRIMARY KEY (`address`)) ENGINE=InnoDB
INSERT INTO `emails` VALUES ('user1#example.com')
SELECT * FROM `emails`
DELETE FROM `emails` WHERE `address`='user1#example.com'
These are just infinitely easier and more efficient than a text file...
But if you want to use a text file...
$to_delete = 'user1#example.com';
$file = array_flip(explode(",",file_get_contents("email.txt")));
unset($file[$to_delete]);
file_put_contents("email.txt",implode(",",array_flip($file));
Basically what it does it explodes by the comma, then flips the array so that the emails are keys (and their numeric positions are values, but that doesn't matter), then it removes the email you want to delete and finally reassembles the file. The bonus of this is that it will also strip out any duplicates you may have.
You can use a similar method to add email addresses, just changing the unset line to $file['user1#example.com'] = -1; (to ensure the number doesn't conflict with anything, as that would interfere with the array flipping).
This answer was originally appended to the question body by the OP.
First I moved the form to /cancel.html and used <form action="/cancel_submit.html">.
(Where I have written .html, it is just to demonstrate, as my server is configured to use no page extentions and also so that PHP is parsed on .html pages.)
Then I put the PHP code into the page /cancel_submit.html and moved
if ($str1 > $str2) {
echo ("You Have Successfully Unsubscribed From Our Newsletter....<br>You Will Not Receive Any More Emails From Us.");
} else {
echo ("The Email Address You Specified Is Not In Our Mailing List.");
}
to another set of PHP brackets.
This meant that the e-mail adddress was sent via POST to the other page, which then performed the actual removal of the e-mail address from the list and then checked to see if an address been removed to provide the comfirmation message.
I also added two commas to $oldword = "$email"; to make it $oldword = ",$email,"; so that it only finds the text entered into the e-mail box if it has a comma on either side. This addresses the case where someone submits half of an e-mail address.
I also changed $newWord = ""; to $newWord = ","; so that if the script removes an e-mail address with commas at each side, the two e-mail addresses that were next to it will not be separated by a comma.
Here is the code I have for both pages now:
cancel.html
<p>To cancel our Newsletter please enter your email address below....</p>
<p>
<form method="post" action="/cancel_submit.html">
<p>Email Address: <input type="text" name="email"></p>
<input type="submit" value="Cancel Newsletter" name="submit">
</form>
cancel_submit.html
<?php
$email = $_POST["email"];
$basetext = file_get_contents("email.txt");
$oldWord = ",$email,";
$newWord = ",";
$text = str_replace($oldWord , $newWord , $basetext);
$str1 = $basetext;
// echo strlen($str1);
$str2 = $text;
// echo strlen($str2);
$fp = fopen('email.txt', 'w');
fwrite($fp, $text);
fclose($file);
?>
<?php
if ($str1 > $str2) {
echo ("You Have Successfully Unsubscribed From Our Newsletter....<br>You Will Not Receive Any More Emails From Us.");
} else {
echo ("The Email Address You Specified Is Not In Our Mailing List.");
}
?>
<p>
<p>Please wait to be re-directed or <u>CLICK HERE.</u>
</p>
EDIT:
I made a few improvements. I added:
$email = strtolower($email);
to both the e-mail add script and the e-mail remove script. This converted all characters entered into either form to lowercase; previously, it wouldnt remove e-mails typed in a different case than the big list had.
This messed up the confirmation message command, so I changed it to
if (str_replace($oldWord , $newWord , $basetext)) {
echo ("You Have Successfully Unsubscribed From Our Newsletter....<br>You Will Not Receive Any More Emails From Us.");
} else {
echo ("The Email Address You Specified Is Not In Our Mailing List.");
}
Suggested research:
http://us.php.net/manual/en/function.explode.php
http://us3.php.net/manual/en/function.file-put-contents.php
edited to add:
http://us3.php.net/manual/en/function.file-get-contents.php
If you end up with a 3rd party service, don't pay Aweber. Go for MailChimp. They've got a free plan if your mailing list isn't that big.
In your sample you reference a variable $_POST['email'] without assignment or testing the value. Additionally you may want to sanitize this variable.
Another issue I saw was that $to_delete = 'email';, you are only looking for entries of 'email'.
Your $file_put_contents is not being assigned.
} else { showForm(); } wasn't paired up with an if statement.
<?php
function showForm() {
echo '<form method="post" action="">' . PHP_EOL
. 'Email Address: <input type="text" name="email"> <br />' . PHP_EOL
. '<input type="submit" value="Cancel Newsletter" name="submit">' . PHP_EOL
. '</form>';
}
if($_POST['email']) {
$to_delete = $_POST['email'];
$file = array_flip(explode(",",file_get_contents("email.txt")));
unset($file[$to_delete]);
$file_put_contents = file_put_contents("email.txt",implode(",",array_flip($file));
if(!$file_put_contents) {
die('Error occured');
} else {
echo 'Your subscription has been cancelled. You will not receive any further emails from us.';
}
} else {
showForm();
}
If I understand your question correctly, this is what you are attempting to achieve.
Check to see if the user has posted from a form.
Get the email. (You should ensure that it is a sane value in this step)
Retrieve the member data.
Check to see if the user is on the list.
Remove the user and save the data if applicable.
Output the result of the function.
Display a message with form to submit to self.
I know this can be done as a very simple task, but I don't trust that approach. Additionally, I think anything interacting with permanent storage of data should have some mild to moderate form of abstraction.
I would approach that task this way.
class MailingList {
const EMAIL_OK = 1;
const ERR_EMAIL_EXISTS = -1;
const ERR_EMAIL_INVALID = -2;
const ERR_EMAIL_NOTFOUND = -3;
protected $_db_src;
protected $_db_opt;
protected $members = array(); // An array intended to hold members.
public function email_exists($email) {
return array_key_exists($this->members, $email);
}
public function remove_email($email) {
$this->_sanitize_email($email);
if ($email) {
if (array_key_exists($this->members, $email)) {
unset($this->members[$email]);
$this->_update_members();
return self::EMAIL_OK;
} else {
return self::ERR_EMAIL_NOTFOUND;
}
} else {
return self::ERR_EMAIL_INVALID;
}
}
public function add_email($email) {
$this->_sanitize_email($email);
if ($email) {
if (array_key_exists($this->members) {
return self::ERR_EMAIL_EXISTS;
} else {
$this->members[$email] = -1;
$this->_save_members();
$this->_load_members();
return self::EMAIL_OK;
}
} else {
return self::ERR_EMAIL_INVALID;
}
}
// We expect a data source and options for the
// data source upon instantiation.
// This is to prepare this class for abstraction and allow it to be
// extended to databases.
public function __construct($data_source = "flatfile", $data_options = "email.txt") {
$this->_db_src = $data_source;
$this->_db_opt = $data_options;
$this->_load_members();
}
protected function _load_members() {
// Create the function name to ensure it exists.
$data_function = "handle_" . $this->_db_src;
if (!method_exists(&$this, $this->_db_src)) {
throw new Exception('Invalid data source');
}
// Build our array of parameters to be sent to our handler function.
$parameters = array_merge(array('load'), (array) $this->_db_opt);
// This calls our data function with a load action parameter.
// This is written to expect the data function to populate $this->members.
return call_user_func_array(array(&$this, $data_function), $parameters);
}
// Most of this is similar to the constructor as far as data handling goes.
protected function _save_members() {
// Create the function name to ensure it exists.
$data_function = "handle_" . $this->_db_src;
if (!method_exists(&$this, $this->_db_src)) {
throw new Exception('Invalid data source');
}
// Set up our data options with a save action.
$parameters = array_merge(array('save'), (array) $this->_db_opt);
return call_user_func_array(array(&$this, $data_function), $parameters);
}
// The heart of the storage engine, designed for CSV data.
protected function handle_flatfile($action, $filename) {
switch ($action) {
case "load":
// Make sure we can load members.
if (!is_readable($filename)) {
throw new Exception("File: $filename, is not readable");
}
// Open our data file and load the information.
// Populate $this->members as an array just the way we expect it.
$this->members = array_flip(explode(',', file_get_contents($filename)));
break;
case "save":
// Make sure we can write to the file before we move forward.
if (!is_writeable($filename)) {
throw new Exception("File $filename, is now writable");
}
// Convert our array back to a CSV string and write it to the file.
$status = file_put_contents($filename, implode(',', array_flip($this->members)));
// If we failed to write to the file make sure something is done before we continue.
if (!$status) {
throw new Exception("Writing to file failed!");
}
break;
default:
throw new Exception("Unknown action called on data handler.");
}
}
// converts email addresses to lowercase to avoid duplication.
// should add a regex filter here to ensure that we have a valid address
protected function _sanitize_email(&$email) {
$email = strtolower($email);
}
}
function show_form() {
echo '<form method="post" action="">' . PHP_EOL
. 'Email Address: <input type="text" name="email"> <br />' . PHP_EOL
. '<input type="submit" value="Cancel Newsletter" name="submit">' . PHP_EOL
. '</form>';
}
if (isset($_POST) && isset($_POST['email'])) {
$list = new MailingList();
$status = $list->remove_email($_POST['email']);
switch ($status) {
case MalingList::EMAIL_OK:
echo "<p class='success'>Your email was successfully removed.<p>";
break;
case MailingList::ERR_EMAIL_INVALID:
echo "<p class='error'>The email address provided was invalid.</p>";
case MailingList::ERR_EMAIL_NOTFOUND:
echo "<p class='error'>The email address provided was not registered.</p>";
default:
show_form();
}
} else {
show_form();
}

Categories