PayPal Payflow Pro Duplicate Request ID
We have run into s strange issue with PayPal PayFlow Pro (US).
We have run a very successful store for over a decade on Zen Cart. We were recently alerted to the fact that we were getting an Approved response from PayPal on some orders that actually were flagged as duplicates by them in their system, so we were never paid. Luckily there were only a dozen or so in the last 10 years.
The orders that were declined had the same PayPal txn_id as the previous approved order. The orders had a timestamp within the same second (2021-03-22 06:22:19 vs 2021-03-22 06:22:21 for instance). They had completely different INVNUM and AMT fields (the fields PayPal says it compares to identify duplicates).
We sent the transaction logs to PayPal and their engineers came back with this: "ZenCart is producing the same REQUEST_ID sent in both payment requests... This will effectively invoke the exact same response.
Under these circumstances, this is not a PayPal bug and we would advise that your developer contact ZenCart with the information provided. Effectively, each REQUEST_ID (that they and/or your developer generate, not PayPal), need to be unique ".
Aside from the fact that the internal response and the message we got via API (Declined as Duplicate vs Approved) seems to be a bug to me, I looked and there does seem to be an issue of setting a unique REQUEST_ID in Zen Cart.
The is REQUEST_ID set in paypal_curl.php by invoking time().
if ($this->_mode == 'payflow') {
$values['REQUEST_ID'] = time();
}
Could that be modified to
if ($this->_mode == 'payflow') {
$values['REQUEST_ID'] = time()."-".mt_rand(100, 999);
}
to add a random 3 digits to make this more unique?
Any thoughts would be appreciated.
Re: PayPal Duplicate Request ID
Are you saying those duplicates were legitimately actually purchased at the exact same second?
And did Zen Cart store those orders? (I expect it wouldn't have stored the order if PayPal rejected the payment; thus the customer would have had to go through checkout again. Thus, you wouldn't have shipped an order that someone never paid for. And if PayPal rejected it then you weren't paid for an order that you never shipped.)
Re: PayPal Duplicate Request ID
Yes, the same second. The store is busy enough that it has happened several times in the past several years.
As a result, they have the same "Request_ID" parameter sent. The second charge was declined as duplicate because the Request_ID was the same. In this case they return the info from the accepted charge per their new API, and include [ORIGPPREF] => XXXXXXXXS867553L and [DUPLICATE] => 1 entries
Zen then reads this response and approves the sale.
Array
(
[RESULT] => 0
[PNREF] => BQ1XXXXXXCCB
[RESPMSG] => Approved
[AUTHCODE] => 111111
[AVSADDR] => Y
[AVSZIP] => Y
[CVV2MATCH] => Y
[ORIGPPREF] => XXXXXXXXS867553L
[CORRELATIONID] => dfXXXXXXXXX9
[PROCAVS] => Y
[PROCCVV2] => M
[SETTLE_DATE] => 2021-03-22 15:22:16
[IAVS] => N
[DUPLICATE] => 1
[REQUEST_ID] => 1616451736
[CURL_ERRORS] =>
)
Also, because PPREF is not found in the response, it is omitted from the transaction notes in the admin.
Paypal says Zen is using a deprecated version of the API, and sends a REQUEST_ID instead of the new X-VPS-REQUEST-ID.
https://developer.paypal.com/docs/pa...er-parameters/
Where REQUEST_ID as optional, the new X-VPS-REQUEST-ID is mandatory. From their docs:
Important: If you send in a NEW transaction with a previously used X-VPS-REQUEST-ID, the server ignores the new data and returns the response to the original transaction associated with that X-VPS-REQUEST-ID.
It is VERY IMPORTANT that you check for DUPLICATE=1 and if you receive it and the transaction is not a re-attempt of the original request of a failed transaction, you must change the Request ID.
In the end, what should happen is exactly that, trap for DUPLICATE = 1 and resubmit with a new X-VPS-REQUEST-ID.
Re: PayPal Duplicate Request ID
Alternatively, replace the $values['REQUEST_ID'] = time(); with
1. This code: $values['REQUEST_ID'] = microtime();
which still has the same theoretical weakness of there being a possibility, however remote, of a non-unique value.
or
2. Use the same technique as building the invoice number, and concatenate the user account number with the time (or microtime)
$values['REQUEST_ID'] = $INSERT_USER_ID."-".microtime();
Re: PayPal Duplicate Request ID
Quote:
Originally Posted by
semide
Alternatively, replace the $values['REQUEST_ID'] = time(); with
1. This code: $values['REQUEST_ID'] = microtime();
which still has the same theoretical weakness of there being a possibility, however remote, of a non-unique value.
or
2. Use the same technique as building the invoice number, and concatenate the user account number with the time (or microtime)
$values['REQUEST_ID'] = $INSERT_USER_ID."-".microtime();
Note that option 2 may not sufficient for stores that support guest login/accounts. Including a random number may be a next best bet, although there is still/also a remote chance of clashing against an existing value.
Re: PayPal Duplicate Request ID
Good catch. Perhaps adding the session_id would be sufficient protection - the odds of the same user, with the same session id at the same microsecond would seem to be vanishingly small.
$values['REQUEST_ID'] = microtime()."-".$INSERT_USER_ID."-".session_id();''
Note that the session id would almost certainly be truncated, as PayPal only accepts 32 characters in that position.
Anyone have any other ideas for creating a unique value here? Otherwise trapping for [DUPLICATE] => 1 and resubmitting the request may be the best way.