Call Snippet from Paypal IPN

I am currently developing an online shop based on @samnabi’s shopkit plugin. Since german laws are quite strict about the online shopping process, I’ve decided to include paypal express differently than the shopkit plugin does.

The payments work fine, it’s just that I can’t seem to update the order files, empty the cart and send e-Mails with my order.callback snippet if I include it in the file called by the Paypal IPN. The IPN itself works fine as shown by my logfiles.

It just seems to impossible to execute any kirby function.

Any hints?

Here is my IPN callback file:

// Set this to true to use the sandbox endpoint during testing:
$enable_sandbox = true;

// Use this to specify all of the email addresses that you have attached to paypal:
$my_email_addresses = array("sebastian-buyer@completeorganics.de", "sebastian@completeorganics.de");

// Set this to true to send a confirmation email:
$send_confirmation_email = true;
$confirmation_email_address = "Robin Scholz <rbnschlz@gmail.com>";
$from_email_address = "Sebastian Koch <sebastian@completeorganics.de>";

// Set this to true to save a log file:
$save_log_file = true;
$log_file_dir = __DIR__ . "/logs";

// Here is some information on how to configure sendmail:
// http://php.net/manual/en/function.mail.php#118210

// use PaypalIPN;
$ipn = new PaypalIPN();
if ($enable_sandbox) {
    $ipn->useSandbox();
}
$verified = $ipn->verifyIPN();

$data_text = "";
foreach ($_POST as $key => $value) {
    $data_text .= $key . " = " . $value . "\r\n";
}

$test_text = "";
if ($_POST["test_ipn"] == 1) {
    $test_text = "Test ";
}

// Check the receiver email to see if it matches your list of paypal email addresses
$receiver_email_found = false;
foreach ($my_email_addresses as $a) {
    if (strtolower($_POST["receiver_email"]) == strtolower($a)) {
        $receiver_email_found = true;
        break;
    }
}

date_default_timezone_set("Europe/Berlin");
list($year, $month, $day, $hour, $minute, $second, $timezone) = explode(":", date("Y:m:d:H:i:s:T"));
$date = $year . "-" . $month . "-" . $day;
$timestamp = $date . " " . $hour . ":" . $minute . ":" . $second . " " . $timezone;
$dated_log_file_dir = $log_file_dir . "/" . $year . "/" . $month;

$paypal_ipn_status = "VERIFICATION FAILED";
if ($verified) {
    $paypal_ipn_status = "RECEIVER EMAIL MISMATCH";
    if ($receiver_email_found) {
        $paypal_ipn_status = "Completed Successfully";

        // Process IPN
        // A list of variables are available here:
        // https://developer.paypal.com/webapps/developer/docs/classic/ipn/integration-guide/IPNandPDTVariables/

        // This is an example for sending an automated email to the customer when they purchases an item for a specific amount:
        // if ($_POST["item_name"] == "Example Item" && $_POST["mc_gross"] == 49.99 && $_POST["mc_currency"] == "USD" && $_POST["payment_status"] == "Completed") {
        //     $email_to = $_POST["first_name"] . " " . $_POST["last_name"] . " <" . $_POST["payer_email"] . ">";
        //     $email_subject = $test_text . "Completed order for: " . $_POST["item_name"];
        //     $email_body = "Thank you for purchasing " . $_POST["item_name"] . "." . "\r\n" . "\r\n" . "This is an example email only." . "\r\n" . "\r\n" . "Thank you.";
        //     mail($email_to, $email_subject, $email_body, "From: " . $from_email_address);
        // }

        $custom = $_POST['custom'];
        $shipping = $_POST['mc_shipping'];
        $gross = $_POST['mc_gross'];
    }
} elseif ($enable_sandbox) {
    if ($_POST["test_ipn"] != 1) {
        $paypal_ipn_status = "RECEIVED FROM LIVE WHILE SANDBOXED";
    }
} elseif ($_POST["test_ipn"] == 1) {
    $paypal_ipn_status = "RECEIVED FROM SANDBOX WHILE LIVE";
}

if ($save_log_file) {
    // Create log file directory
    if (!is_dir($dated_log_file_dir)) {
        if (!file_exists($dated_log_file_dir)) {
            mkdir($dated_log_file_dir, 0777, true);
            if (!is_dir($dated_log_file_dir)) {
                $save_log_file = false;
            }
        } else {
            $save_log_file = false;
        }
    }
    // Restrict web access to files in the log file directory
    $htaccess_body = "RewriteEngine On" . "\r\n" . "RewriteRule .* - [L,R=404]";
    if ($save_log_file && (!is_file($log_file_dir . "/.htaccess") || file_get_contents($log_file_dir . "/.htaccess") !== $htaccess_body)) {
        if (!is_dir($log_file_dir . "/.htaccess")) {
            file_put_contents($log_file_dir . "/.htaccess", $htaccess_body);
            if (!is_file($log_file_dir . "/.htaccess") || file_get_contents($log_file_dir . "/.htaccess") !== $htaccess_body) {
                $save_log_file = false;
            }
        } else {
            $save_log_file = false;
        }
    }
    if ($save_log_file) {
        // Save data to text file
        file_put_contents($dated_log_file_dir . "/" . $test_text . "paypal_ipn_" . $date . ".txt", "paypal_ipn_status = " . $paypal_ipn_status . "\r\n" . "paypal_ipn_date = " . $timestamp . "\r\n" . $data_text . "\r\n", FILE_APPEND);
    }
}

// Reply with an empty 200 response to indicate to paypal the IPN was received correctly
header("HTTP/1.1 200 OK");

    file_put_contents($dated_log_file_dir . "/" . $test_text . "paypal_ipn_" . $date . ".txt", "paypal_ipn_status = " . $paypal_ipn_status . "\r\n" . "paypal_ipn_date = " . $timestamp . "\r\n" . page('shop/orders/'.$_POST['custom'])->url() . "\r\n", FILE_APPEND);



if($_POST['txn_id'] != '' ) {
    
    // Validate the PayPal transaction against the pending order
    $txn = page('shop/orders/'.$_POST['custom']);

    if (round($txn->subtotal()->value,2) == round($gross-$shipping,2) and
    round($txn->shipping()->value,2) == round($shipping,2)) {

        // Set Shopkit payment status
        $payment_status = in_array($_POST['payment_status'], ['Completed','Processed']) ? 'paid paypal' : 'pending paypal';

        try {
        // Update transaction record
            $txn->update([
                'paypal-txn-id' => $_POST['txn_id'],
                'status'  => $payment_status,
                'payer-id' => $_POST['payer_id'],
            ], 'de');

        // Update stock and notify staff
        snippet('order.callback', ['txn' => $txn]);
        return true;

        } catch(Exception $e) {
            // Updates or notification failed
            snippet('mail.order.update.error', [
                'txn' => $txn,
                'payment_status' => $txn->status(),
                'payer_firstname' => $txn->firstname(),
                'payer_lastname' => $txn->lastname(),
                'payer_street' => $txn->street(),
                'payer_extra' => $txn->extrainfo(),
                'payer_postcode' => $txn->postcode(),
                'payer_city' => $txn->city(),
                'payer_email' => $txn->email(),
            ]);
            return false;
        }

    } else {
        // Integrity check failed - possible tampering
        snippet('mail.order.tamper', ['txn' => $txn]);
        return false;
    }
} else {
    // Data didn't come back properly from PayPal; no txn_id found
    return false;
}

don’t know you snippets code but try calling snippet with third param true unless you want it to echo something. like described at Snippet as variable value.

you could also use a page model function instead of a snippet.

I moved my code to be called at /paypal now and its working correctly.

Thanks though!

Hi Robin,

Could you share your gateway code with me? I’ve been trying to make the PayPall gateway work on my setup but the status of my orders won’t change :confused:

Thanks in advance!

I’m afraid I can‘t just blindy share code here. Where exactly are you stuck?