So I looked through the code, and it appears as if the functionality to display custom message strings via the messagestack depending on the error response code received from PayPal has been programmed into the payment module.
So in the file /includes/modules/payment/paypaldp.php, the original code for the custom messagestacks is as follows:
Code:
case 'DoDirectPayment':
if ($basicError ||
((isset($_SESSION['paypal_ec_token']) && isset($response['TOKEN'])) && $_SESSION['paypal_ec_token'] != urldecode($response['TOKEN'])) ) {
// Error, so send the store owner a complete dump of the transaction.
if ($this->enableDebugging) {
$this->_doDebug('PayPal Error Log - before_process() - DP', "In function: before_process() - Direct Payment \r\nDid first contact attempt return error? " . ($error_occurred ? "Yes" : "No") . " \r\n\r\nValue List:\r\n" . str_replace('&',"\r\n", urldecode($doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList)))) . "\r\n\r\nResponse:\r\n" . urldecode(print_r($response, true)));
}
$errorText = MODULE_PAYMENT_PAYPALDP_INVALID_RESPONSE;
$errorNum = urldecode($response['L_ERRORCODE0'] . ' ' . $response['RESULT'] . ' <!-- ' . $response['RESPMSG'] . ' -->');
if ($response['RESULT'] == 25) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_NOT_WPP_ACCOUNT_ERROR;
if ($response['L_ERRORCODE0'] == 10500 || $response['L_ERRORCODE0'] == 10501) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_NOT_US_WPP_ACCOUNT_ERROR;
if ($response['HOSTCODE'] == 10500 || $response['HOSTCODE'] == 10501) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_NOT_UKWPP_ACCOUNT_ERROR;
if ($response['HOSTCODE'] == 10558) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_CANNOT_USE_THIS_CURRENCY_ERROR;
if ($response['L_ERRORCODE0'] == 10002) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_SANDBOX_VS_LIVE_ERROR;
if ($response['L_ERRORCODE0'] == 10565) {
$errorText = MODULE_PAYMENT_PAYPALDP_TEXT_WPP_BAD_COUNTRY_ERROR;
$_SESSION['payment'] = '';
}
if ($response['L_ERRORCODE0'] == 10566) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_CARD_TYPE_NOT_SUPPORTED;
if ($response['L_ERRORCODE0'] == 10736) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_ADDR_ERROR;
if ($response['L_ERRORCODE0'] == 10752) {
$errorText = MODULE_PAYMENT_PAYPALDP_TEXT_DECLINED;
$errorNum = '10752';
}
if ($response['L_ERRORCODE0'] == 15012) { // Mastercard CE agreement not signed between merchant and PayPal. Thus cannot accept mastercard.
$errorText = MODULE_PAYMENT_PAYPALDP_TEXT_CARD_TYPE_NOT_SUPPORTED;
$errorNum = '15012';
}
if ($response['L_ERRORCODE0'] == 15005) {
$errorText = 'Card rejected by the bank. Your IP address has been recorded.';
$errorNum = '15005';
}
if ($response['RESPMSG'] != '') $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_DECLINED . ' ' . $errorText;
$detailedMessage = ($errorText == MODULE_PAYMENT_PAYPALDP_INVALID_RESPONSE || $errorText == MODULE_PAYMENT_PAYPALDP_TEXT_DECLINED || $this->enableDebugging || $response['CURL_ERRORS'] != '' || $this->emailAlerts) ? (isset($response['RESULT']) && $response['RESULT'] != 0 ? MODULE_PAYMENT_PAYPALDP_CANNOT_BE_COMPLETED . ' (' . $errorNum . ')' : $errorNum) . ' ' . urldecode(' ' . $response['L_SHORTMESSAGE0'] . ' - ' . $response['L_LONGMESSAGE0'] . ' ' . $response['CURL_ERRORS']) : '';
$explain = "\n\nProblem occurred while customer #" . $_SESSION['customer_id'] . ' -- ' . $_SESSION['customer_first_name'] . ' ' . $_SESSION['customer_last_name'] . ' -- was attempting checkout.' . "\n";
$detailedEmailMessage = MODULE_PAYMENT_PAYPALDP_TEXT_EMAIL_ERROR_MESSAGE . urldecode($response['L_ERRORCODE0'] . ' ' . $response['RESPMSG']. "\n" . $response['L_SHORTMESSAGE0'] . "\n" . $response['L_LONGMESSAGE0'] . $response['L_ERRORCODE1'] . "\n" . $response['L_SHORTMESSAGE1'] . "\n" . $response['L_LONGMESSAGE1'] . $response['L_ERRORCODE2'] . "\n" . $response['L_SHORTMESSAGE2'] . "\n" . $response['L_LONGMESSAGE2'] . ($response['CURL_ERRORS'] != '' ? "\n" . $response['CURL_ERRORS'] : '') . "\n\n" . 'Zen Cart message: ' . $detailedMessage . "\n\n" . $errorInfo . "\n\n" . 'Transaction Response Details: ' . print_r($response, true) . "\n\n" . 'Transaction Submission: ' . urldecode($doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList), true)));
$detailedEmailMessage .= $explain;
zen_mail(STORE_NAME, STORE_OWNER_EMAIL_ADDRESS, MODULE_PAYMENT_PAYPALDP_TEXT_EMAIL_ERROR_SUBJECT . ' (' . zen_uncomment($errorNum) . ')', zen_uncomment($detailedMessage . $explain), STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS, array('EMAIL_MESSAGE_HTML'=>nl2br(zen_uncomment($detailedEmailMessage))), 'paymentalert');
if ($response['L_ERRORCODE0'] == 15012) $detailedEmailMessage = '';
$this->terminateEC(($detailedEmailMessage == '' ? $errorText . ' (' . $errorNum . ') ' : $detailedMessage), ($gateway_mode ? true : false), FILENAME_CHECKOUT_PAYMENT);
return true;
}
break;
Specifically, the last like of code:
Code:
$this->terminateEC(($detailedEmailMessage == '' ? $errorText . ' (' . $errorNum . ') ' : $detailedMessage), ($gateway_mode ? true : false), FILENAME_CHECKOUT_PAYMENT);
The problem is that the terminateEC functioncall never passes the $errorText value because in almost all instances, the ternary operator where the condition $detailedEmailMessage == '' (ie., blank) is never met. I'm not sure why that conditional operator needs to require that $detailedEmailMessage has to be blank in order for the errorText and errorNum values to be passed to the terminateEC functioncall, as it seems more logical that the errorText and errorNum values should be passed in any case where a basicError has occurred when PayPal attempts to process the submitted payment information.
This is why the terminateEC functioncall only ever displays PayPal's generic alert messages, which, in my opinion, are ambiguous, and difficult for the customer to decipher as to what exactly went wrong with their payment attempt.
Here is the modification to the code to make the messagestack display customized error messages:
Code:
case 'DoDirectPayment':
if ($basicError ||
((isset($_SESSION['paypal_ec_token']) && isset($response['TOKEN'])) && $_SESSION['paypal_ec_token'] != urldecode($response['TOKEN'])) ) {
// Error, so send the store owner a complete dump of the transaction.
if ($this->enableDebugging) {
$this->_doDebug('PayPal Error Log - before_process() - DP', "In function: before_process() - Direct Payment \r\nDid first contact attempt return error? " . ($error_occurred ? "Yes" : "No") . " \r\n\r\nValue List:\r\n" . str_replace('&',"\r\n", urldecode($doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList)))) . "\r\n\r\nResponse:\r\n" . urldecode(print_r($response, true)));
}
$errorText = MODULE_PAYMENT_PAYPALDP_INVALID_RESPONSE;
$errorNum = urldecode($response['L_ERRORCODE0'] . ' ' . $response['RESULT'] . ' <!-- ' . $response['RESPMSG'] . ' -->');
if ($response['RESULT'] == 25) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_NOT_WPP_ACCOUNT_ERROR;
if ($response['L_ERRORCODE0'] == 10500 || $response['L_ERRORCODE0'] == 10501) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_NOT_US_WPP_ACCOUNT_ERROR;
if ($response['HOSTCODE'] == 10500 || $response['HOSTCODE'] == 10501) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_NOT_UKWPP_ACCOUNT_ERROR;
if ($response['HOSTCODE'] == 10558) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_CANNOT_USE_THIS_CURRENCY_ERROR;
if ($response['L_ERRORCODE0'] == 10002) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_SANDBOX_VS_LIVE_ERROR;
if ($response['L_ERRORCODE0'] == 10565) {
$errorText = MODULE_PAYMENT_PAYPALDP_TEXT_WPP_BAD_COUNTRY_ERROR;
$_SESSION['payment'] = '';
}
if ($response['L_ERRORCODE0'] == 10566) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_CARD_TYPE_NOT_SUPPORTED;
if ($response['L_ERRORCODE0'] == 10736) $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_ADDR_ERROR;
if ($response['L_ERRORCODE0'] == 10752) {
$errorText = MODULE_PAYMENT_PAYPALDP_TEXT_DECLINED;
$errorNum = '10752';
}
if ($response['L_ERRORCODE0'] == 15012) { // Mastercard CE agreement not signed between merchant and PayPal. Thus cannot accept mastercard.
$errorText = MODULE_PAYMENT_PAYPALDP_TEXT_CARD_TYPE_NOT_SUPPORTED;
$errorNum = '15012';
}
if ($response['L_ERRORCODE0'] == 15005) {
$errorText = 'Credit card was declined, please contact your credit card issuer for assistance or use a different credit card. Your IP address has been recorded as a precautionary measure.';
$errorNum = '15005';
}
if ($response['L_ERRORCODE0'] == 11611) {
$errorText = 'We could not validate your credit card details, please verify that your billing address and 3 digit CVC code were inputted correctly and try again, or contact us for assistance. Your IP address has been recorded as a precautionary measure.';
$errorNum = '11611';
}
if ($response['RESPMSG'] != '') $errorText = MODULE_PAYMENT_PAYPALDP_TEXT_DECLINED . ' ' . $errorText;
$detailedMessage = ($errorText == MODULE_PAYMENT_PAYPALDP_INVALID_RESPONSE || $errorText == MODULE_PAYMENT_PAYPALDP_TEXT_DECLINED || $this->enableDebugging || $response['CURL_ERRORS'] != '' || $this->emailAlerts) ? (isset($response['RESULT']) && $response['RESULT'] != 0 ? MODULE_PAYMENT_PAYPALDP_CANNOT_BE_COMPLETED . ' (' . $errorNum . ')' : $errorNum) . ' ' . urldecode(' ' . $response['L_SHORTMESSAGE0'] . ' - ' . $response['L_LONGMESSAGE0'] . ' ' . $response['CURL_ERRORS']) : '';
$explain = "\n\nProblem occurred while customer #" . $_SESSION['customer_id'] . ' -- ' . $_SESSION['customer_first_name'] . ' ' . $_SESSION['customer_last_name'] . ' -- was attempting checkout.' . "\n";
$detailedEmailMessage = MODULE_PAYMENT_PAYPALDP_TEXT_EMAIL_ERROR_MESSAGE . urldecode($response['L_ERRORCODE0'] . ' ' . $response['RESPMSG']. "\n" . $response['L_SHORTMESSAGE0'] . "\n" . $response['L_LONGMESSAGE0'] . $response['L_ERRORCODE1'] . "\n" . $response['L_SHORTMESSAGE1'] . "\n" . $response['L_LONGMESSAGE1'] . $response['L_ERRORCODE2'] . "\n" . $response['L_SHORTMESSAGE2'] . "\n" . $response['L_LONGMESSAGE2'] . ($response['CURL_ERRORS'] != '' ? "\n" . $response['CURL_ERRORS'] : '') . "\n\n" . 'Zen Cart message: ' . $detailedMessage . "\n\n" . $errorInfo . "\n\n" . 'Transaction Response Details: ' . print_r($response, true) . "\n\n" . 'Transaction Submission: ' . urldecode($doPayPal->_sanitizeLog($doPayPal->_parseNameValueList($doPayPal->lastParamList), true)));
$detailedEmailMessage .= $explain;
zen_mail(STORE_NAME, STORE_OWNER_EMAIL_ADDRESS, MODULE_PAYMENT_PAYPALDP_TEXT_EMAIL_ERROR_SUBJECT . ' (' . zen_uncomment($errorNum) . ')', zen_uncomment($detailedMessage . $explain), STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS, array('EMAIL_MESSAGE_HTML'=>nl2br(zen_uncomment($detailedEmailMessage))), 'paymentalert');
if ($response['L_ERRORCODE0'] == 15012) $detailedEmailMessage = '';
$this->terminateEC($errorText . ' (' . $errorNum . ')', ($gateway_mode ? true : false), FILENAME_CHECKOUT_PAYMENT);
return true;
}
break;
In the code above are examples of what I used in my custom messagestack for error 15005 (credit card declined by customer's bank) and error 11611 (payment rejected based on PayPal's fraud management filters). You can change it to however you see fit for your own store.
It is basically up to you to use your own discretion concerning how much info you wish to divulge to customers concerning their payment attempts. For instance, in my specific situation, I employ PayPal Fraud Management Filters, PayPal automatically logs all IP's for failed payment attempts, I use MaxMind Orders to verify all customer's purchases, and just basic common sense when evaluating whether or not I think a customer is high risk for being fraudulent, not to mention Zen-Cart's own built in credit card slamming feature...
So for me, I was having roughly 5-10% of my customers receiving a generic "15005 Processor Decline - This transaction cannot be processed." message and having them email or call me being frustrated because they don't realize the payment attempt was declined by their own bank... I don't think is the optimal method for handling the situation. A custom messageStack informing them that their bank declined the transation and to contact their credit card issuer for more information I think is much more efficient. But again this is just my opinion and my own preference for how I want to deal with this situation.
I just thought I'd pass along the method/solution to customizing the messageStack based on PayPal's errorcodes, and how anyone wishes to use it can based on their own discretion.
Hope you guys find some use out of this!
Bookmarks