Unfortunately your payment has not yet been fully processed,

Unfortunately your payment has not yet been fully processed,

by John Langlois -
Number of replies: 3
The paypal IPN scenario got ugly. Everything worked as expected in the Sandbox, once I got all the variables in place.

However, when I then went live and made a real world, real money PayPal purchase, we got the dreaded "Unfortunately your payment has not yet been fully processed" message, which then routed us back to paying for the course again.

I looked at the underlying tables using phpMyAdmin and discovered that the transaction was "Verified" and the transaction posted to md_enrol_paypal. All the key infoformation, such as transaction id, user id, course and payment_status of "Completed" were there.

That means that the ipn.php script did its job. A second clue is that none of the error emails that would ordinarily be sent via the error trapping ipn.php were sent.
Therefore, the break down is happening elsewhere, probably in enroll.php or return.php.

Has anyone already worked through this?

Thanks.
Average of ratings: -
In reply to John Langlois

Re: Unfortunately your payment has not yet been fully processed,

by Alan Barrett -

Sometimes PayPal seems to be slow in issuing the POST to ipn.php and it happens at a slightly later time than the Moodle code expects. Since the student is not enrolled by the time return.php gets to run, the error message you mention is displayed although the student is actually fully enrolled a few seconds (or less?) later by the delayed post to ipn.php.

What I have done is change the error message in moodle.php to...

"$string['paymentsorry'] = 'Thank you for your payment. However, your payment has not yet been notified to MYSITE.ORG and you are not yet registered to enter the course \"$a->fullname\". It may just have been delayed a few seconds, so please press \"Continue\" in a few seconds and you should be brought to your course. But, if you continue to have trouble, then please inform <a href=\"XXXXXX\">XXXXXX</a> quoting this message.';"

Did the student actually get enrolled? If so, the above would be a solution. Although a more fancy solution would be to include a retry in JavaScript in return.php.

Alan.

In reply to Alan Barrett

Re: Unfortunately your payment has not yet been fully processed,

by John Langlois -
Thanks, Alan.

For the benefit of the next pilgrim down this path, I suggest you do the following;

1). Make sure that your PayPal sandbox account has Payment Review DISABLED.
Failure to do so makes your payment status= pending, which causes moodle code to flag you as unfinished.

2). Make sure the SELLER's account has the PROFILE subject of Language Encoding set to UTF-8 under MORE OPTIONS.

3). While your are in the PROFILE section, make sure the INSTANT PAYMENT NOTIFICATION Settings include the fully expressed URL to the ipn.php file AND that your have turned on IPN. PDT should not be used.

NOTE: You should also have set up your REAL account in the same fashion.

To switch back and forth from real to sandbox, uncomment the line
//add for Sandox Testing 11/06/09
//$CFG->usepaypalsandbox = 'TRUE'; in config.php under the Moodle folder.

4). The fact that Moodle is posting the enrollment without recognizing that a payment has occurred is troubling. The coders should revisit that.

In the meantime, If you do not receive an email from the system other than the "Fake Payment" one, you may want to try some diagnostic work using ipn.php.

I have posted my revisions to the ipn.php page below. Notice several places where you can sequentially force the system to email you what's happening.
For example, if you don't get even the first round from PayPal then obviously everything else fails. However, my case was paypal sent the ipn data, the system failed to send it back to paypal properly, therefore, we got no reply back from PayPal on the second layer of validation.

Each of the commented out sections reveals a step of the process towards VERIFIED or INVALID.

Once you are getting VERIFIED back from IPN, everything else seems to work.
Note that I have also included the writing of a log file, which provides another way to checking on the initial data sent to you by paypal.

When you look in the folder enrol/paypal, using an FTP program such as FileZilla, you should see the moodle_ipn.log and no error.log. Feel free to delete either one and see how they reappear as things process. Ideally, you should not be seeing the error log here, or in the moodle folder.



[CODE]

<?php // $Id: ipn.php,v 1.22.2.1 2009/09/22 01:51:19 moodler Exp $
//Revised JEL 11/18/09
/**
* Listens for Instant Payment Notification from PayPal
*
* This script waits for Payment notification from PayPal,
* then double checks that data by sending it back to PayPal.
* If PayPal verifies this then it sets up the enrolment for that
*
* Set the $user->timeaccess course array
*
* @param user referenced object, must contain $user->id already set
*/
require("../../config.php");
require("enrol.php");

/// Keep out casual intruders
if (empty($_POST) or !empty($_GET)) {
error("Sorry, you can not use the script that way.");
}

/// Read all the data from PayPal and get it ready for later;
/// we expect only valid UTF-8 encoding, it is the responsibility
/// of user to set it up properly in PayPal business acount,
/// it is documented in docs wiki.

$req = 'cmd=_notify-validate';

$data = new object();

foreach ($_POST as $key => $value) {
$value = stripslashes($value);
$req .= "&$key=".urlencode($value);
$data->$key = $value;
}

$custom = explode('-', $data->custom);
$data->userid = (int)$custom[0];
$data->courseid = (int)$custom[1];
$data->payment_gross = $data->mc_gross;
$data->payment_currency = $data->mc_currency;
$data->timeupdated = time();


/// get the user and course records

if (! $user = get_record("user", "id", $data->userid) ) {
email_paypal_error_to_admin("Not a valid user id", $data);
die;
}

if (! $course = get_record("course", "id", $data->courseid) ) {
email_paypal_error_to_admin("Not a valid course id", $data);
die;
}

if (! $context = get_context_instance(CONTEXT_COURSE, $course->id)) {
email_paypal_error_to_admin("Not a valid context id", $data);
die;
}

/// write a log to prove we got this far. Look for it in the enrol/paypal folder.
// While there check for any error logs.

//Uncomment below to see the raw data before processing
//email_paypal_error_to_admin("In ipn.php Before log line 67", $data);

$log = fopen("moodle_ipn.log", "a");
fwrite($log, "\n\nipn - " . gmstrftime ("%b %d %Y %H:%M:%S", time()) . "\n");

//echo "key is $key and value is $value"."<br/>";

//Write the transaction details to the log file.
//The log file is useful for debugging problems. You'll ideally never lose any details, and if the database is down for any //reason, you need a backup..
//If the log is empty then $req did not get built
//---------------------------------------------
fwrite($log,$req."\ n");

// post back to PayPal system to validate
// If you are not getting back a VERIFIED or INVALID immediately from PayPal, the problem is probably in the
// header buildup or the ports used for confirmation
// This worked in both the sandbox and live site.

$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";


If(empty($CFG->usepaypalsandbox)){
$fp = fsockopen ('www.paypal.com', 80, $errno, $errstr, 30); // <- Use this line for real use
}else{
$fp = fsockopen ('www.sandbox.paypal.com', 80, $errno, $errstr, 30); // <- Use this line when testing in SandBox
}

if (!$fp) { /// Could not open a socket to PayPal - FAIL
echo "<p>Error: could not access paypal.com</p>";
email_paypal_error_to_admin("Could not access paypal.com to verify payment", $data);
die;
}


//JEL Foggy Bottom Web Design

if (!$fp) {
echo "$errstr ($errno)";
fwrite($log, "Failed to open HTTP connection!");
$res = "FAILED to open FP";
}
else {
fputs ($fp, $header . $req);

//uncomment the two lines below to receive an email of the information sent TO PayPal for verification
//$admin = get_admin();
//email_to_user($admin, $admin, "We put this to IPN", $req);
}

/// Now read the response and check if everything is OK.

while (!feof($fp)) {
$result = fgets($fp, 1024);

//uncomment the two lines below to receive an email of the information RECEIVED from PayPal for verification
//$admin = get_admin();
//email_to_user($admin, $admin, "We got this back line 117", $res);

if (strcmp($result, "VERIFIED") == 0) { // VALID PAYMENT!


// check the payment_status and payment_reason

// If status is not completed or pending then unenrol the student if already enrolled
// and notify admin

if ($data->payment_status != "Completed" and $data->payment_status != "Pending") {
role_unassign(0, $data->userid, 0, $context->id);
email_paypal_error_to_admin("Status not completed or pending. User unenrolled from course", $data);
die;
}

// If currency is incorrectly set then someone maybe trying to cheat the system

if ($data->mc_currency != $course->currency) {
email_paypal_error_to_admin("Currency does not match course settings, received: ".addslashes($data->mc_currency), $data);
die;
}

// If status is pending and reason is other than echeck then we are on hold until further notice
// Email user to let them know. Email admin.

if ($data->payment_status == "Pending" and $data->pending_reason != "echeck") {
email_to_user($user, get_admin(), "Moodle: PayPal payment", "Your PayPal payment is pending.");
email_paypal_error_to_admin("Payment pending", $data);
die;
}

// If our status is not completed or not pending on an echeck clearance then ignore and die
// This check is redundant at present but may be useful if paypal extend the return codes in the future

if (! ( $data->payment_status == "Completed" or
($data->payment_status == "Pending" and $data->pending_reason == "echeck") ) ) {
die;
}

// At this point we only proceed with a status of completed or pending with a reason of echeck



if ($existing = get_record("enrol_paypal", "txn_id", addslashes($data->txn_id))) { // Make sure this transaction doesn't exist already
email_paypal_error_to_admin("Transaction $data->txn_id is being repeated!", $data);
die;

}

if ($data->business != $CFG->enrol_paypalbusiness) { // Check that the email is the one we want it to be
email_paypal_error_to_admin("Business email is $data->business (not $CFG->enrol_paypalbusiness)", $data);
die;

}

if (!$user = get_record('user', 'id', $data->userid)) { // Check that user exists
email_paypal_error_to_admin("User $data->userid doesn't exist", $data);
die;
}

if (!$course = get_record('course', 'id', $data->courseid)) { // Check that course exists
email_paypal_error_to_admin("Course $data->courseid doesn't exist", $data);;
die;
}

// Check that amount paid is the correct amount
if ( (float) $course->cost < 0 ) {
$cost = (float) $CFG->enrol_cost;
} else {
$cost = (float) $course->cost;
}

if ($data->payment_gross < $cost) {
$cost = format_float($cost, 2);
email_paypal_error_to_admin("Amount paid is not enough ($data->payment_gross < $cost))", $data);
die;

}
// ALL CLEAR !

if (!insert_record("enrol_paypal", addslashes_object($data))) { // Insert a transaction record
email_paypal_error_to_admin("Error while trying to insert valid transaction", $data);
}

if (!enrol_into_course($course, $user, 'paypal')) {
email_paypal_error_to_admin("Error while trying to enrol ".fullname($user)." in '$course->fullname'", $data);
die;
} else {
$teacher = get_teacher($course->id);

if (!empty($CFG->enrol_mailstudents)) {
$a->coursename = $course->fullname;
$a->profileurl = "$CFG->wwwroot/user/view.php?id=$user->id";
email_to_user($user, $teacher, get_string("enrolmentnew", '', $course->shortname),
get_string('welcometocoursetext', '', $a));
}

if (!empty($CFG->enrol_mailteachers)) {
$a->course = $course->fullname;
$a->user = fullname($user);
email_to_user($teacher, $user, get_string("enrolmentnew", '', $course->shortname),
get_string('enrolmentnewuser', '', $a));
}

if (!empty($CFG->enrol_mailadmins)) {
$a->course = $course->fullname;
$a->user = fullname($user);
$admins = get_admins();
foreach ($admins as $admin) {
email_to_user($admin, $user, get_string("enrolmentnew", '', $course->shortname),
get_string('enrolmentnewuser', '', $a));
}
}

}


} else if (strcmp ($result, "INVALID") == 0) { // ERROR
insert_record("enrol_paypal", addslashes_object($data), false);
email_paypal_error_to_admin("Received an invalid payment notification!! (Fake payment?)", $data);
}
}

fclose($fp);
flcose($log);
exit;

/// FUNCTIONS //////////////////////////////////////////////////////////////////

function email_paypal_error_to_admin($subject, $data) {
$admin = get_admin();
$site = get_site();

$message = "$site->fullname: Transaction failed.\n\n$subject\n\n";

foreach ($data as $key => $value) {
$message .= "$key => $value\n";
}

email_to_user($admin, $admin, "PAYPAL ERROR: ".$subject, $message);

}
?>

[/CODE]


In reply to John Langlois

Re: Unfortunately your payment has not yet been fully processed,

by Radovan Hernandez -

Current as 04/07/2014 path for setting UTF-8 enconding in PayPal profile options has changed.

(Sorry if my translation is not accurate, for I view PayPal site in Spanish. Shurely you can find the path easily.)

 

1. In PayPal, go to My Account > Profile > My Selling tools

2. At the bottom of this page, click PayPal language encoding

3. Click in More options

4. In drop-down list, choose UTF-8. Make shure you check Yes in "Would you like to use the same encoding..."

5. Click Save

6. Restart web services in your server

 

Returning to Moodle, this process did not avoid the annoying "Unfortunately your payment has not yet been fully processed" message. But after receiving this message and clicking in Continue, the student can clic again in the course title and gain access the course.

Maybe a work around could be warning the student what to spect in a tutorial or manual...

Currently using:

• Moodle 2.5 hosted on GoDaddy LInux Shared (which by the way and I am about to change)
• PayPal personnal premium account

Looking forward to try the same arrangement in other hosting, so I can tell you about it.

Cheers