PayPal IPN unique identifier

arnaslu picture arnaslu · Feb 11, 2012 · Viewed 19.6k times · Source

I always assumed that txn_id sent with IPN message is unique. PayPal guidelines seem to support this idea - https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_admin_IPNIntro

Avoid duplicate IPN messages. Check that you have not already processed the transaction identified by the transaction ID returned in the IPN message. You may need to store transaction IDs returned by IPN messages in a file or database so that you can check for duplicates. If the transaction ID sent by PayPal is a duplicate, you should not process it again.

However I found that PayPal's eCheck payment IPN is sent twice with the same transaction ID. Once during initial payment with payment_status as "Pending" and again after couple days when eCheck is actually processes with payment_status as "Completed".

I want to store both transactions, but still would like to avoid storing duplicates. There is another field in IPN called ipn_track_id and it's different for both transactions, but I can't find documentation for it, except this vague description:

Internal; only for use by MTS and DTS

Anyone else is using ipn_track_id to uniquely identify IPN messages?

Answer

Robert picture Robert · Feb 11, 2012

ipn_track_id shouldn't be used; mainly because this is for internal use only as stated, and because it's unique for every IPN message.
The txn_id is unique for each transaction, not each IPN message.

What this means is; one transaction can have multiple IPN messages. eCheck, for example, where it will go in a 'Pending' state by default, and 'Complete' once the eCheck has cleared.
But you may also see reversals, canceled reversals, cases opened and refunds against the same txn_id.

Pseudo code:

If not empty txn_id and txn_type = web_accept and payment_status = Completed  
    // New payment received; completed. May have been a transaction which was pending earlier.
    Update database set payment_status = Completed and txn_id = $_POST['txn_id']  

If not empty txn_id and txn_type = web_accept and payment_status = Pending  
    // New payment received; completed  
    Update database set payment_status = Pending and payment_reason = $_POST['pending_reason'] and txn_id = $_POST['txn_id']

You can find a lot more IPN variables listed on https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_IPNandPDTVariables#id08CTB0S055Z

Basically; PayPal will generate a unique seller transaction ID. This tranaction ID may go through various stages before it's 'Completed', so you'll need to be able to handle these exceptions.

As for PayPal's note in the documentation: PayPal may resend individual IPN mesages if it encounters errors during delivery. For example, it's required for your script to return a proper HTTP/1.1 200 OK HTTP status response whenever PayPal POST's the IPN data to it.
If you don't return a HTTP/1.1 200 OK response, PayPal will reattempt sending the same data up to 16 times per indiviudal IPN message.

Note: A seller's transaction ID is different from a buyer's transction ID, since they're two different actions (one debit, one credit).