Since I have implemented the Accept.js in our payment terminal we have been getting up to 33% error rate from our clients using it - some have tried a couple times with no success and others have had periodic success. I can't seem to track any pattern but the error response I am getting is: 'The element 'opaqueData' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd' has incomplete content. List of possible elements expected: 'dataDescriptor' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'
I thought maybe if I posted the code someone could point out the obvious. I have tried to trim it down to the minimum necessary to follow the flow:
client side javascript
/* * Once the form fields are confirmed to be correct during the submit event, * this function is called. */ function sendPaymentDataToAnet() { var secureData = {}, authData = {}, cardData = {}; data = formData(Billing.flags.sensitive); cardData.cardNumber = data.card_number; cardData.month = data.exp_month; cardData.year = data.exp_year; cardData.cardCode = data.card_code; cardData.zip = Billing.changes.hasOwnProperty('zip') ? Billing.changes.zip : Billing.fields.zip; var name = [(Billing.changes.hasOwnProperty('first_name') ? Billing.changes.first_name : Billing.fields.first_name), (Billing.changes.hasOwnProperty('last_name') ? Billing.changes.last_name : Billing.fields.last_name)]; var fullname = name.join(' '); cardData.fullname = fullname.trim(); secureData.cardData = cardData; authData.clientKey = BDP.clientKey; authData.apiLoginID = BDP.apiLoginID; secureData.authData = authData; Accept.dispatchData(secureData, 'creditCardHandler'); } function creditCardHandler(response) { var data = formData(); delete data.card_number; delete data.card_code; if (response.messages.resultCode === 'Error') { var errorMsg = ''; for (var i = 0; i < response.messages.message.length; i++) { errorMsg = errorMsg + response.messages.message[i].code + ':' + response.messages.message[i].text + ' '; } data.error = errorMsg; } else if ( response.hasOwnProperty('opaqueData') ) { var opaqueData = response.opaqueData; data.dataDescriptor = opaqueData.hasOwnProperty('dataDescriptor') ? opaqueData.dataDescriptor : 'COMMON.ACCEPT.INAPP.PAYMENT'; data.dataValue = opaqueData.hasOwnProperty('dataValue') ? opaqueData.dataValue : null; if ( data.dataValue === null || data.dataValue.length < 1 ) { data.error = "Payment gateway returned an invalid response: dataValue is empty."; } } $.post("/billing/terminal.php",data) .always(function(data) { $('#confirm').hide(); $('#entry-form').hide(); $('#response .contents').empty().append(data); $('#response').show(); $("body").css("cursor", "default"); }); }
server-side php
$pdo = new PDO("mysql:host=".$DB['host'].";dbname=".$DB['schema'], $DB['user'], $DB['pwd']); if ($_SERVER['REQUEST_METHOD'] === 'POST') { $merchantAuthentication = new AnetAPI\MerchantAuthenticationType(); $merchantAuthentication->setName(\Billing\Constants::MERCHANT_LOGIN_ID); $merchantAuthentication->setTransactionKey(\Billing\Constants::MERCHANT_TRANSACTION_KEY); // $base: information specific to the account being charged along with the dollar amount // $address: billing address // $group := we have several product lines and this holds information about // which line holds the account being charged. $data = array_merge($base, $address,['dataDescriptor'=>$_POST['dataDescriptor'], 'dataValue'=>$_POST['dataValue']],$group); $response = cc_transaction($merchantAuthentication, $data, $pdo); } function cc_transaction($merchantAuthentication, $data, $pdo) { global $anetUrl; $refId = 'ref' . time(); $op = new AnetAPI\OpaqueDataType(); $op->setDataDescriptor($data['dataDescriptor']); $op->setDataValue($data['dataValue']); $paymentOne = new AnetAPI\PaymentType(); $paymentOne->setOpaqueData($op); $billTo = new AnetAPI\CustomerAddressType(); $billTo->setFirstName($data['first_name']) ->setLastName($data['last_name']) ->setAddress($data['address']) ->setCity($data['city']) ->setState($data['state']) ->setZip($data['zip']); $customer = new AnetAPI\CustomerDataType(); $customer->setId($data['customer_id']); $customer->setEmail($data['email']); $orderType = new AnetAPI\OrderType(); $invoiceNumber = substr($data['customer_id'] . '_' . date('ymdHis'),0,20); $orderType->setInvoiceNumber($invoiceNumber); $orderType->setDescription($data['description'] . ' payment terminal'); $transactionRequestType = new AnetAPI\TransactionRequestType(); $transactionRequestType->setAmount($data['amount']); $transactionRequestType->setBillTo($billTo); $transactionRequestType->setCustomer($customer); $transactionRequestType->setOrder($orderType); $transactionRequestType->setPayment($paymentOne); $transactionRequestType->setTransactionType( "authCaptureTransaction"); $request = new AnetAPI\CreateTransactionRequest(); $request->setMerchantAuthentication($merchantAuthentication); $request->setRefId( $refId); $request->setTransactionRequest( $transactionRequestType); $controller = new AnetController\CreateTransactionController($request); $response = $controller->executeWithApiResponse($anetUrl); if (($response != null) && ($response->getMessages()->getResultCode() == "Ok") ) { $tresponse = $response->getTransactionResponse(); if ($tresponse != null && $tresponse->getMessages() != null) { // success $respData = array( 'amount'=>$data['amount'], 'response_description'=>$tresponse->getMessages()[0]->getDescription(), 'auth_code'=>$tresponse->getAuthCode(), 'transaction_id'=>$tresponse->getTransId(), 'payment_method'=>$tresponse->getAccountType() . ' ' . $tresponse->getAccountNumber(), 'memo'=> '(' . $tresponse->getAccountType() . ') ' . $tresponse->getAccountNumber() . ' - AUTH: ' . $tresponse->getAuthCode() ); $avsResultCode = $tresponse->getAvsResultCode(); if ( isset(\Billing\Lists::$AvsResponse[$avsResultCode]) ) $respData['avs_response'] = \Billing\Lists::$AvsResponse[$avsResultCode]; $lead_in = response_page_lead(false); $payment_information = payment_information($respData); $billing_information = billing_information($data); $action_buttons = action_buttons(false); $infoResponse = join('',[$lead_in,$payment_information,$billing_information,$action_buttons,'</div>']); } else { // error if($tresponse->getErrors() != null) { $errorMessage = $tresponse->getErrors()[0]->getErrorCode() . " " . $tresponse->getErrors()[0]->getErrorText(); $error_code = $tresponse->getErrors()[0]->getErrorCode(); $error_text = $tresponse->getErrors()[0]->getErrorText(); $account_type = $tresponse->getAccountType(); $account_number = $tresponse->getAccountNumber(); $codes = join('|',array($tresponse->getErrors()[0]->getErrorCode(), $tresponse->getAvsResultCode(), $tresponse->getCvvResultCode(), $tresponse->getCavvResultCode())); $error = "CC Failure (" . $account_type . ") " . $account_number . ": " . $codes . ' ' . $error_text; $logMessage = date('D, d M Y h:i:s O') . " ERROR : (" . __FILE__ . ' : ' . __LINE__ . ") - " . $errorMessage; if (AUTHORIZENET_LOG_FILE) file_put_contents(AUTHORIZENET_LOG_FILE, $logMessage, FILE_APPEND); $payment_information = billing_information($data); $action_buttons = action_buttons(true); } else { $error = "General payment error."; } $amountStr = 'Amount: $' . $data['amount'] . '<br />'; $lead_in = response_page_lead($amountStr . $error); $billing_information = billing_information($data); $action_buttons = action_buttons(true); $infoResponse = join('',[$lead_in,$billing_information,$action_buttons,'</div>']); } } else { // error $errorMessages = $response->getMessages()->getMessage(); $error_code = $errorMessages[0]->getCode(); $error_text = $errorMessages[0]->getText(); $account_type = $data['card_type']; $account_number = $data['safe_number']; $error = "CC Failure (" . $account_type . ") " . $account_number . ": " . $error_text; $errorMessage = $error_code . " " . $error_text; $logMessage = date('D, d M Y h:i:s O') . " ERROR : (" . __FILE__ . ' : ' . __LINE__ . ") - " . $errorMessage; if (AUTHORIZENET_LOG_FILE) file_put_contents(AUTHORIZENET_LOG_FILE, $logMessage, FILE_APPEND); $amountStr = 'Amount: $' . $data['amount'] . '<br />'; $lead_in = response_page_lead($amountStr . $error); $billing_information = billing_information($data); $action_buttons = action_buttons(true); $infoResponse = join('',[$lead_in,$billing_information,$action_buttons,'</div>']); } return $infoResponse; }
08-03-2017 04:13 PM
Hi @wdbaker54,
Glancing at the code, I see no obvious problems. The error you're getting indicates that the request from the PHP page includes an <opaqueData> object, but doesn't include a <dataDescriptor> object within it.
I'd do a couple of things.
1. Check the log file. By default, our PHP SDK has logging, and that log includes a copy of the complete XML transaction request sent to our site. Check the log for one of the transactions reporting an error, and see if it really doesn't have the opaqueData element built properly. If it's not in fact sending the dataDescriptor or dataValue, you can start working backwards to figure out why.
2. Put in more logging (or put in breakpoints) at each step where the opaqueData is received or sent. In your html page, log the response from Accept.js to the console once it's received. Once you create the opaqueData variable in your JavaScript and load it up, log the variable to the console to make sure it's got the dataDescriptor in it. Once you post to the PHP page, write everything received from the post to a file before you start assembling the transaction request... and so on.
We're assuming from the error that at some point, you don't have the opaqueData anymore, so we're just trying to figure out where it disappears.
08-03-2017 04:34 PM