For subscr_payment and web_accept you have to check txn_id whether it has been sent to you before.
But how about
subscr_cancel,
subscr_modify,
subscr_eot,
subscr_failed,
subscr_signup
where there is no txn_id?
If I get subscr_modify 3 times from paypal how does my script know if the same IPN was sent 3 times or if the user modified their subscription 3 times?
Does anyone know if ipn_track_id remains the same if the same IPN message is sent multiple times?
You are only going to get a repeated IPN response from PayPal if the first one wasn’t answered with a Http 200 Response Code, due to a Server 500 Error Code for example, or no answer.
The PayPal documentation is really obscure and complicated, but I found a link that might be useful, it shows a list of every variable present in each type of IPN response:
https://www.paypalobjects.com/en_US/ebook/subscriptions/Appx-ipn_subscription_variables.html
subscr_idMIGHT be what you are looking for, but I’m not sure if it changes with every response or not.