cancel
Showing results for 
Search instead for 
Did you mean: 

Accept.js is getting triggered twice (2x)

Hi there,

 

wondering if you can help me understand why Accept.js is getting triggered two times, when I'm only (intentionally) calling it once.This is a screengrab of Firefox's "Network" debugger tab, where it shows both calls. It's the "OPTIONS" method one that seems to be the troublemaker:
https://www.dropbox.com/s/nqh1xcpcz9u13xk/Screenshot%202018-03-02%2014.46.46.png?dl=0

Here's the button that triggers the call:

<button name="sendRequest" type="button" id="processNowBtn" value="Record Payment" onClick="request_payment('666'); return false;">Record Payment</button>

And here's the function(s) that the above button triggers:

function request_payment(myThirdPartyID) {

	var myveryowntransactiontype = tmpCheckHistorical.Xttype.value;

	var active_form = document.BIGPAYMENTFORM;

	with (active_form) {
		wipeout_unused_form_data();

		if (parseFloat(trxnAmt.value) <= 0.00) {
			$("#messageBoard").text('Please specify a valid amount, greater than 0.00.');
			return;
		}

		if (Xttype.value == 'E' || Xttype.value == 'X') {
			if (orderPymtRecID.value == "" || orderPymtRecID.value == 0) {
				$("#messageBoard").text('Missing dependency value for this request.');
				return;
			}
		}

		// The recording of cash or check payments is handled differently, so simply submit the form
		// right away and let the other process handle those
		if (thisPaymentTypeIs.value == 'cash' || thisPaymentTypeIs.value == 'check') {
			letsdo.value = "sendPayment";
			submit();
		} else {
			var allow_closed_order_payments = 'Y';
			var tmpOrderDescription = 'Order: N' + orderID + ', Company: <?php echo $WOW['company']; ?>';
			var form_data = $("#BIGPAYMENTFORM").serialize();

			// "D" signifies that this is a request to "Capture" funds that have already
			// been "Authorized". This means we have a different set of data that needs
			// co be culled and passed. Note that the orderID and orderDescription parameters
			// are not needed for the Auth.net API call (it would perform the same with or
			// without them), but are still being passed, because they ARE needed in
			// down-stream subprocesses that create the payment record within PW
			if (myveryowntransactiontype == 'D') {
				// Prepare some data
				var tmpOrigTrxnID = document.getElementById("orgID").value;
				var tmpTrxnAmt = document.getElementById("trxnAmt").value;
				var tmp_sprID = document.getElementById("orgID2").value;

				capture_priorauth_funds_thru_gateway(tmpOrigTrxnID, tmpTrxnAmt, orderID, tmpOrderDescription, tmp_sprID, true, true);

			// If not a "Capture", then we are recording a NEW payment record in the
			// payment processor transaction system. The main differences between the two
			// are that this one will need a bit more data, but will NOT need a (prior)
			// transaction ID
			} else {
				var clientKey = '<?php echo trim($WOW['extraSpecialPublicKey']); ?>';
				var tmpPmtStorageRecord = document.getElementById("pmtStorageRecord").value;
				var tmpTrxnAmount = document.getElementById("trxnAmt").value;
				var tmpTrxnTypes = document.getElementsByName('Xttype');
				var tmpTrxnType;
				for(var i = 0; i < tmpTrxnTypes.length; i++){
					if(tmpTrxnTypes[i].checked){
						tmpTrxnType = tmpTrxnTypes[i].value;
					}
				}

				var tmpMpwRequest = true;

				// Here we are expecting either 1 element to the data, or 3 elements. Anything
				// else is an indication that something went south (for Auth.net anyway)

				// One element means new payment data, so gather from the HTML form
				var forker = tmpPmtStorageRecord.split("|");
				if (forker.length == 1) {
					var tmp_sprID = '';
					var tmpCcAcct = document.getElementById("cc__ACCT").value;
					var tmpCcExpM = document.getElementById("cc__expM").value;
					var tmpCcExpY = document.getElementById("cc__expY").value;
					var tmpCcCvv = document.getElementById("cc__CVV2").value;
				// Three elements means stored payment data, so don't bother gathering (but
				// still good to prep data for a normalized function call, so default it all
					// to empty strings)
				} else if (forker.length == 3) {
					var tmp_sprID = document.getElementById("orgID2").value;
					var tmpCcAcct = '';
					var tmpCcExpM = '';
					var tmpCcExpY = '';
					var tmpCcCvv = '';
				} else {
					$("#messageBoard").text('Payment data and/or configuration error. Please report this exact error to Customer Support.');
					$("#messageBoard").delay(6400).slideUp();
					return false;
				}

				record_payment_to_gateway(clientKey, myThirdPartyID, tmp_sprID, tmpPmtStorageRecord, orderID, tmpOrderDescription, tmpTrxnAmount, tmpTrxnType, tmpCcAcct, tmpCcExpM, tmpCcExpY, tmpCcCvv, tmpMpwRequest, true, true);
			}
		}
	}
}

function record_payment_to_gateway(authnetClientKey, myThirdPartyID, pymtStoragePointerRecID, pymtStorageRec, orderID, orderDescription, trxnAmount, trxnType, ccAcct, ccExpM, ccExpY, ccCvv, mpwRequest, reloadPage, reloadParent)
{
	if (pymtStorageRec == 'hi') {
		var pymtRequestType = 'OneX';
	} else if (pymtStorageRec == 'there') {
		var pymtRequestType = 'Stored';
	} else {
		console.log('TRACKING THE ERROR: 34');
		return false;
	}

	// Set some defaults
	if (!reloadPage) {
		reloadPage = false;
	}
	if (!reloadParent) {
		reloadParent = false;
	}

	var secureData = {}; authData = {}; cardData = {};

	if (pymtRequestType == "OneX") {
		if (ccAcct == '' || ccExpM == '' || ccExpY == '' || ccCvv == '') {
			console.log('TRACKING THE ERROR: 35');
			return false;
		}

		cardData.cardNumber = ccAcct;
		cardData.month = ccExpM;
		cardData.year = ccExpY;
		cardData.cardCode = ccCvv;
		secureData.cardData = cardData;

		authData.clientKey = authnetClientKey;
		authData.apiLoginID = myThirdPartyID;
		secureData.authData = authData;

		// Pass the card number, expiration date, and CVV to Accept.js for submission to Authorize.net.
		// Note: Attempting to avoide possible callback conflicts, I've added the acronym-ize name of
		// the parent function, for each of the "*_response)handler" calls
		Accept.dispatchData(secureData, rptg_response_handler);

	} else if (pymtRequestType == "Stored") {
		var cloudResponse;

		create_anet_transaction_for_order_from_saved_profile(pymtRequestType, orderID, orderDescription, trxnAmount, trxnType, pymtStoragePointerRecID, aCustProID, aCustPmtProID, mpwRequest,
			function (callbackData) {
				cloudResponse = callbackData;
				if (cloudResponse.success) {
					console.log('TRACKING THE SUCCESS: 01');
					console.log('Transaction successfully and securely made.<br />Transaction ID: ' + cloudResponse.success.transactionResponse.transId);
					$("#messageBoard").text('Transaction successfully and securely made.<br />Transaction ID: ' + cloudResponse.success.transactionResponse.transId);
					if (reloadPage) {
						$("#messageBoard").delay(6400).slideUp(location.reload());
					} else {
						$("#messageBoard").delay(6400).slideUp();
					}
					if (reloadParent) {
						refreshParent();
					}
				} else if (cloudResponse.error) {
					console.log('TRACKING THE ERROR: 01');
					console.log(cloudResponse.error);
				}
			}
		);
	} else {
		console.log('Error determining the nature of the charge. Please report this exact error to Customer Support.');
		return false;
	}

	// Process the response from Authorize.Net to retrieve the two elements of the payment nonce. If
	// the data looks correct, record the OpaqueData to the console and call the data storage
	// function.
	function rptg_response_handler(response) {
		console.log(response.messages.resultCode);
		if (response.messages.resultCode === 'Error') {
			for (var i = 0; i < response.messages.message.length; i++) {
				console.log('TRACKING THE ERROR: 02');
				console.log(response.messages.message[i].text);
			}
		} else {
			console.log('TRACKING THE SUCCESS: 02');
			use_opaque_payment_data(pymtRequestType, orderID, orderDescription, trxnAmount, trxnType, response.opaqueData, aCustProID, aCustPmtProID, mpwRequest, reloadPage, reloadParent)
		}
		return false;
	}
	function use_opaque_payment_data(pymtRequestType, orderID, orderDescription, trxnAmount, trxnType, responseData, aCustProID, aCustPmtProID, mpwRequest, reloadPage, reloadParent) {
		var cloudResponse;
		create_anet_transaction_for_order(pymtRequestType, orderID, orderDescription, trxnAmount, trxnType, aCustProID, aCustPmtProID, mpwRequest, responseData.dataValue,
			function (callbackData) {
				cloudResponse = callbackData;
			}
		);

		// These variables are for sub-string checks on the result (JS didn't like me using the
		// returned object, nor the literal string in the "indexOf" command, thus the variables)
		var tmpResult = String(cloudResponse.success);
		var tmpErrorCheck = 'ERROR';
		// Successful transaction request the the API
		if (cloudResponse.success && tmpResult.indexOf(tmpErrorCheck) == -1) {
			console.log('TRACKING THE SUCCESS: 03');
			if (reloadPage) {
				$("#messageBoard").delay(6400).slideUp(location.reload());
			} else {
				$("#messageBoard").delay(6400).slideUp();
			}
			if (reloadParent) {
				refreshParent().delay(6400);
			}
		// This indicates that thte API call was a suggess, but that the request was not granted
		} else if (tmpResult.indexOf(tmpErrorCheck) != -1) {
			$("#messageBoard").text(cloudResponse.success);
			console.log('TRACKING THE ERROR: 04');
		// This suggests a problem with the actual API call
		} else if (cloudResponse.error) {
			$("#messageBoard").text(cloudResponse.error);
			console.log('TRACKING THE ERROR: 05');
		}
		return false;
	}
}

TIA for any insight!

jima
Regular Contributor
16 REPLIES 16

Hi ,

 

OPTIONS call is due to the Cross Origin Resource Sharing(CORS). You are calling resource on authorize.net domain from the domain you have hosted your application on, which is different than authorize.net. This is where CORS comes into play.

 

"The Cross-Origin Resource Sharing standard works by adding new HTTP headers that allow servers to describe the set of origins that are permitted to read that information using a web browser. The specification mandates that browsers "preflight" the request, soliciting supported methods from the server with an HTTP OPTIONS request method, and then, upon "approval" from the server, sending the actual request with the actual HTTP request method."

 

As you can see, first an OPTIONS call is made and then the actual HTTP request is made. This is the reason you see two calls being made.

  

Hope this helps!

 

https://en.wikipedia.org/wiki/Cross-origin_resource_sharing#Preflight_example

 

Thanks,

Naman

 

 

nbansal
Authorize.Net Developer Authorize.Net Developer
Authorize.Net Developer

Ok, but then why am I getting a fail response to it, but a success on the actual call? And why is the OPTION call getting returned last (and reported to the user) as an "Accept.js. encryption Failed" message?

jima
Regular Contributor

Hi,

 

In the screenshot you shared both the calls have 200 OK response.

Can you please share details about the request in which you are getting error?

 

Thanks,

Naman

nbansal
Authorize.Net Developer Authorize.Net Developer
Authorize.Net Developer

So, no ideas here from anyone?

jima
Regular Contributor

I had this problem and added this code to the top of the makePayment function

            this.submitted=false;
                    
            this.makePayment = function() {

                if ( this.submitted == 'true') {
                    console.log('called twice');
                } else {
                    this.submitted='true';
                   ...
                   ... normal processing code here
                   ...
                }

I get the concept of what you are doing there, but my guess is that my call is different than yours. I'm not doing this as an onSubmit (form) action. I think that may change the way to implement.

Also, if this does work - and it looks like it may indeed - it is still a hack for something that shouldn't be happening in the first place. I just don't see why my call (to Accept.js) is happening twice! It has to be that either (a) somewhere there's a loose callback that I can't find, or (b) that Accept.js has an issue with it's response structure (or timing). If the former, then I'm stumped, cuz I can't find anything that would cause the 2nd call. If the latter, then that should be something addressed by the Accept.js development team.

What's frustrating is that this started happening out of the blue! No changes made, but now the response is *consistently* throwing hte "encryption" error response.

Any additional thoughts and/or advice would be greatly appreciated!

jima
Regular Contributor

Hi @jima

 

Have you seen our Accept sample app on Github for a reference implementation of Accept.js 

 

https://github.com/AuthorizeNet/accept-sample-app/blob/master/acceptJSCaller.js

 

Hope it helps !!!





Send feedback at developer_feedback@authorize.net
Yes, I agree it's a hack and should be sorted by the Dev team. Like you mine want firing twice all the time but enough to cause problems. I based my initial prototype on the example app and got twice firing occasionally.
I wondered if it was a callback issue but ploughed ahead.
I now have the code sat in a uniquely named class as my customers basket could contain products from multiple vendors, so I wrap each call with connection details in a Js function class. I added this back as I still occasionally get the twice callback but not very often.
For me it works and is now in production.