How We Troubleshot the Contact Us Module on a Secure Shopping Domain

You may be familiar with the Contact Us module that I wrote up in March 2017. Since then, it's become one of the most popular tutorials on this site, probably because it's a very useful and common bit of functionality to have on the site.

What's been interesting about this is that in less than a year's time, it's also been one of the most commented on posts too. Somewhat unfortunately, most of the comments have been reports of bugs or difficulties during development.

We've managed to address each one that's come up, but there was one recently that was particularly interesting: it's related to the use of the functionality over a secure domain. I figure that this experience could be good to share because it'll show how we got to the root of the problem.

The Report

The customer said that they had been using the functionality just fine but when they switched to a secure shopping domain, the functionality stopped working.

I tested this bug. My test site is not fully HTTPS, so I couldn't replicate the exact same situation, but I could try something similar.

In my distro file, I moved the ContactUs module from shopping.js to myaccount.js. What this would mean, after I compile the code, is that the functionality would be included in the account application and not the shopping one. Therefore, if I wanted to access the form, I would need to log into an account, and then navigate to #contact-us while using my_account.ssp.

When I did, I submitted dummy data in the form, I encounted the same problem as the customer. The issue was almost certainly to do with the fact the form was being submitted from a secure site. But how could that be the case?

The Troubleshooting

In some instances of this functionality, a 500 error is returned even though a case has been successfully created in the backend. In other words, the error is erroneous and a foible of some legacy ERP code. However, in this instance, the case was not being created in the backend, an error was being returned below the form, and the following message was being returned by XHR response:

{"status":500,"code":"ERR_FORM","message":"There was an error submitting the form, please try again later"}

This is a fairly generic error message. Where does it come from? Well, code that we've written in the backend model. It's deliberately returned if the error message isn't one we're expecting:

// Unfortunately, even successfully submitting data this way results in an exception, so we need to handle that
catch(e) {
  // The 'successful' exception is a redirect error, so let's intercept that
  if (e instanceof nlobjError && e.getCode().toString() === 'ILLEGAL_URL_REDIRECT')
  {
    return {
      successMessage: 'Thanks for contacting us'
    }
  }

  // Finally, let's catch any other error that may come
  return {
    status: 500
  , code: 'ERR_FORM'
  , message: 'There was an error submitting the form, please try again later'
  }
}

To improve our diagnostics and get the proper error message back, I changed the final return block (which intercepts the error message) to the following:

return {
  e: e
, code: e.getCode().toString()
, string: e.toString()
}

After redeploying the code, and then submitting the form again, the XHR response was far more verbose (and useful!):

  • Code: SSS_INVALID_URL
  • Message: The URL must be a fully qualified HTTP/HTTPS URL.

Now we're getting somewhere: we know that the issue is do with the URL.

As part of the code we build up the URL string, which results in a variable called url, that's used in nlapiRequestURL.

To diagnose the problem, I added the following code just before the try block:

console.log(url)

After redeploying, I checked the SSP execution log and the following was returned (some of the values I've obscured for privacy):

nullapp/site/crm/externalcasepage.nl?compid=TSTDRV1234567&formid=12&h=abCDefGhiJKlMnOpQrSTuVWxyZ&globalsubscriptionstatus=1

Ah yes, nullapp... that is not my website's URL.

If we look at the code, then we know that the bit before the app part of the URL is determined by the following code:

// Get the URL for the secure login domain
var currentDomainMatch = CommerceAPI.session.getSiteSettings(['touchpoints'])
.touchpoints.login
.match((/^https?://([^/?#]+)(?:[/?#]|$)/i));

var currentDomain = currentDomainMatch && currentDomainMatch[0];

What's happening here is that the commerce API is being called and asked to provide all the URLs for the touch points for the site. Then, a regex runs looking for the bit including and immediately after the https protocol.

On a working instance of this code, this normally returns:

https://checkout.netsuite.com/

One of the things about going fully HTTPS is that your site stops using NetSuite domains. For example, if your site uses https://my.domain.com/ for shopping, then it'll probably use that for the secure parts of your site too.

If you're not running a secure shopping domain, you can test this on your site. Go to your site's homepage and in the developer console input:

var currentDomainMatch = SC.SESSION.touchpoints.login
.match((/^https?://([^/?#]+)(?:[/?#]|$)/i));

var currentDomain = currentDomainMatch && currentDomainMatch[0];
currentDomain

This will return the secure checkout domain.

Now, if you log into the account application and run the same code, what do you get?

null

Hmm. A closer look at the SC.SESSION.touchpoints object in each of the contexts revealed something interesting to me: the touch point URLs vary depending on whether a user's current domain matches the one in the touchpoint; if it is, then it's omitted. This is fine if you're moving from a non-secure domain (ie shopping) to request form in a secure domain (ie checkout); but if your entire site is secure domain, or you're already in a secure domain, then you will not get a fully qualified domain returned, and the regex will fail (returning a null value).

The Fix

The most obvious fix would be to remove this code and simply hard code the URL into it. But that's horrible, so let's not do that.

Instead, I looked at the touch point URL on my site in both the secure and non-secure parts of my site. In both cases, the URL for customercenter returns a fully-qualified HTTPS domain. Thus, my recomendation was to change this line of code:

//change this
.touchpoints.login

//to this
.touchpoints.customercenter

If you save and deploy, you should see the changes take effect. If not, try doing a gulp clean and then redeploying — this will wipe all existing distribution files so that they are definitely fresh when you deploy.

Final Thoughts

One of the things that came out of this is how it's important to have proper error messaging. I mean, we added a handler that returns an error message but we prevented the system from returning a meaningful one, which is a mistake.

Furthermore, we didn't add in anything that automatically logged this information to the SSP execution log. That, too, was also a mistake. We should build that into future error handling.

More Information

Have any stories about troubleshooting services or forms? I'd like to hear them.