Introduction
I integrated PayPal’s Invoicing API’s into a FileMaker solution for a client of mine a couple of years ago. We use the API to implement push-button invoicing from FileMaker, and then we use PayPal IPN to log all transaction data in the database related to the original order by the merchant’s Invoice ID.
It’s a pretty slick system, and it works great, but the client called me up saying that on 3/26/2014 the value for the invoice ID that was coming back via IPN was incorrect and breaking the relationship between invoices and transactions in their system. As such, stuff wasn’t getting marked paid like it should.
Problem Details
A quick review of the code and some testing revealed the problem, which comes down to slightly bad logic (assumptions) on my end as the developer in combination with surprising (and frustrating) updates on the PayPal end.
During the initial development of this solution, heavy testing revealed that the Invoicing API’s provided IPN’s that were slightly different than IPN’s from other transaction types. One main difference was in the way invoice ID’s were handled.
With most transactions, if you provide a custom invoice ID of your own it will come back in the IPN in the “invoice” parameter. With Invoicing, though, the “invoice” parameter was never getting included. Instead, Invoicing IPN’s contained the following parameters with regards to invoice ID’s.
- invoice_id – This parameter contained PayPal’s Invoice ID.
- invoice_number – This parameter contained the custom invoice ID passed in to the CreateAndSendInvoice API request.
The invoice_number parameter is where things differed from other transaction types. With Invoicing, IPN wasn’t including the “invoice” parameter at all, but instead it was passing that value in “invoice_number”.
A Bad Assumption
Having done this testing and discovered the difference in the IPN’s, I needed to come up with some logic to handle this correctly. You see, we use IPN heavily and we process all sorts of different transaction types, so I still need to handle the “invoice” parameter most of time, and handle “invoice_number” for Invoicing only. The logic that I came up with was this…
$invoice = isset($_POST["invoice"]) ? $_POST["invoice"] : ''; $invoice_number = isset($_POST["invoice_number"]) ? $_POST["invoice_number"] : ''; $merchant_custom_invoice_id = $invoice != '' ? $invoice : $invoice_number;
The thought process here was that if it sees the “invoice” parameter then this must be a typical transaction where we would need to pull from that field. If no “invoice” value exists, it would simply fall to “invoice_number”.
This worked great for a long time, and all the different transaction types were getting handled nicely within my client’s FileMaker system based on the invoice ID relationship. On 3/26/2014, though, PayPal made a change that screwed up my logic.
The Change
Upon studying the IPN’s that came back from all the paid invoices since 3/26/2014, I noticed that we actually had an additional parameter in the IPN. It was now including the regular “invoice” parameter, however, it was sending the PayPal Invoice ID as the value for this field instead of the merchant’s custom ID. So now we were getting the following.
- invoice – This paramter contained PayPal’s Invoice ID.
- invoice_id – This parameter also contained PayPal’s Invoice ID.
- invoice_number – This parameter contained the custom invoice ID passed in to the CreateAndSendInvoice API request.
This caused my logic above to see a value for $invoice and just use that rather than fall to $invoice_number. The value, though, was not the correct value, so the relationship in the database was broken and you could not see the transaction history from an invoice in the system.
The Fix
A simple adjustment to the logic resolved the issue, and I probably should have just done it this way to begin with.
$txn_type = isset($_POST['txn_type']) ? $_POST['txn_type'] : '' ; $invoice = isset($_POST['invoice']) ? $_POST['invoice'] : ''; $invoice_number = isset($_POST['invoice_number']) ? $_POST['invoice_number'] : ''; $merchant_custom_invoice_id = strtolower($txn_type) == 'invoice_payment' ? $invoice_number : $invoice;
With this, it specifically checks to see if this IPN is for an invoice payment. If so, it uses the “invoice_number” parameter. If not, it uses “invoice”. Now all transaction payments are getting handled correctly again within my client’s system.
Conclusion
I can’t say it’s not a little bit annoying that PayPal made this change with no announcement, however, I must concede that had I used the transaction type in my logic from the start this change would not have caused any issues.
Just another interesting circumstance that came up when fine-tuning a PayPal integration with my client’s FileMaker solution. Look out for it!