I’m running UNA 14.0.0 with the latest Payments module. Analysis of a bug I've identified (with some formatting and research help from ChatGPT) is listed below. This bug existed all the way back to the Dolphin days - and still exists in Una today.
PayPal successfully processes a payment and sends back an IPN with:
payment_status=Completed
But UNA sometimes logs:
_bx_payment_pp_err_wrong_transaction
and PayPal returns:
INVALID
during the IPN verification step.
After reviewing the UNA debug logs and the provider code, the cause can be reproduced and confirmed.
🔍 What the UNA logs show
- UNA sends a "custom" value like:
custom = NHw4MQ%3D%3D
- PayPal returns the IPN with the exact same value:
custom = NHw4MQ%3D%3D
- But UNA’s validation request (cmd=_notify-validate) shows:
custom = NHw4MQ%253D%253D
The %3D inside the custom value has been encoded twice.
PayPal requires the validation payload to match the original IPN exactly, so it responds:
INVALID
and UNA rejects an otherwise successful payment.
📘 PayPal IPN Specification Requirement
Direct quote from PayPal’s IPN documentation:
“You must send back the entire message, exactly as PayPal sent it, in your (cmd=_notify-validate) message.
The message must match byte-for-byte, otherwise PayPal will return INVALID .”
UNA’s current PayPal provider violates this requirement by re-encoding POST values.
🎯 Root Cause
The issue is in:
modules/boonex/payment/classes/BxPaymentProviderPayPal.php
Inside:
protected function _validateCheckout(&$aData, &$aPending)
UNA builds the validation request like this:
$sRequest .= '&' . urlencode($sKey) . '=' . urlencode(bx_process_pass($sValue));
This always encodes the value again, even if PayPal already encoded it.
Example:
NHw4MQ%3D%3D → NHw4MQ%253D%253D
As soon as this happens, PayPal will always return INVALID .
This is not environment-related — the behavior is in the provider code.
🛠 Fix / Patch
Modify the loop in _validateCheckout() so that the original POST data
is passed back to PayPal without re-encoding the value.
Replace this:
$sRequest .= '&' . urlencode($sKey) . '=' . urlencode(bx_process_pass($sValue));
With this:
// Do not double-encode values — PayPal requires exact original values
$sRequest .= '&' . $sKey . '=' . $sValue;
This ensures:
- UNA sends PayPal the exact original value
- PayPal returns VERIFIED
- Checkout completes successfully
🧪 Why this works
PayPal sends:
custom = NHw4MQ%3D%3D
If UNA returns:
custom = NHw4MQ%3D%3D
→ PayPal: VERIFIED
If UNA returns:
custom = NHw4MQ%253D%253D
→ PayPal: INVALID
The fix restores the required byte-for-byte match.
📝 Environment context
- UNA 14.0.0
- Payments module fully up to date
- Apache + PHP-FPM 8.2.x
- 57
