Hello,

I am using a script to process subscriptions on our sites - I can sucessfully get the subscription payment to process, I am having issues processing a cancel IPN request.

// assign posted variables to local variables
$item_name = make_safe($_POST['item_name']);
$item_number = make_safe($_POST['item_number']);
$payment_status = make_safe($_POST['payment_status']);
$payment_amount = make_safe($_POST['mc_gross']);
$payment_currency = make_safe($_POST['mc_currency']);
$txn_id = make_safe($_POST['txn_id']);
$txn_type = make_safe($_POST['txn_type']);
$subscr_id = make_safe($_POST['subscr_id']);
$receiver_email = $_POST['receiver_email'];
$payer_email = make_safe($_POST['payer_email']);
$custom = numOnly($_POST['custom']);
$date = time();

mail("lblah"," Payment","ID $custom.  $payment_status, $receiver_email TXN TYPE = $txn_type","From:NB");


if (!$fp) {
// HTTP ERROR
} else {
fputs ($fp, $header . $req);
while (!feof($fp)) {
$res = fgets ($fp, 1024);
if (strcmp ($res, "VERIFIED") == 0) {
}

    //check the payment_status is Completed
    if($payment_status != "Completed"){
        $error .= "This Transaction has not been completed";
    //mail("lcah","$sitename Payment2","ID $custom.  $payment_status, $receiver_email $error FAIL","From: $sitename");
    }else{
        //check if cancelled
    if($txn_type == "subscr_cancel"){
  
    }

    if($txn_type == "recurring_payment_outstanding_payment_failed"){
      //cancel & remove
    }
    }

    // process payment
    if(!$error){
           //works
    }else{
            //works
    }
}
elseif (strcmp ($res, "INVALID") == 0) {
// log for manual investigation

}
}
fclose ($fp);
}

I have tried the if $txn_type == "subscr_cancel" in several places but the easiest understanding I have is that $payment_status is not passed when a the IPN sends a cancel request.

Does anyone have some hints or had similar difficulty with something like this before.

Any help would be appreciated.

Dani AI

Generated

Good call, — subscription cancel messages are a different event type and should be handled before you rely on payment fields. Non-payment subscription notifications (subscr_signup, subscr_cancel, subscr_modify, subscr_eot) do not include the usual payment fields (payment_status, txn_id, mc_gross) the way subscr_payment does, so treating cancel as a payment will fail. (developer.paypal.com)

Suggested listener flow: verify the IPN with PayPal, return HTTP 200 quickly, then branch by txn_type. Handle cancel/profile-cancel events immediately (use subscr_id or the recurring profile id to mark the subscription canceled in your DB) and exit. Only for payment-type txn_type values check payment_status == "Completed" and process amounts/txn_id. Example pattern:

$raw = file_get_contents('php://input');
$verified = verify_with_paypal($raw); // POST back to ipnpb.paypal.com with cmd=_notify-validate
if ($verified) {
  $txn = $_POST['txn_type'] ?? '';
  if ($txn === 'subscr_cancel' || $txn === 'recurring_payment_profile_cancel') {
    // mark subscription canceled by subscr_id/profile_id
    exit;
  }
  if (!empty($_POST['payment_status']) && $_POST['payment_status'] === 'Completed') {
    // process payment
  }
}

Use PayPal's recommended verification endpoint (https://ipnpb.paypal.com/cgi-bin/webscr) and the cmd=_notify-validate handshake; collect the entire response body and look for the single-word VERIFIED or INVALID reply rather than relying on fragile line-by-line strcmp logic. Also log the raw POST payload and IPN verification response while debugging, and make your processing idempotent (store processed txn_id or subscr/profile id + last status) because PayPal will retry IPNs until your listener returns 200 OK. (developer.paypal.com)

Finally, remember there are two subscription models: classic HTML subscriptions (subscr_) and the Recurring Payments API (recurring_paymentprofile), so handle both if you accept either. Use the IPN simulator to test all txn_type cases before going live. (developer.paypal.com)

Realised the logical error of putting the code in the else of the section where payment_status was not sent.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.