0

I'm trying to return product weights so they display on the cart for my customers.

In the cart template, I'm calling this external JS file from the cart template in BigCommerce

storefront: https://store-xxx.mybigcommerce.com/cart.php or backend template: cart.html

$(document).ready(function () {

var url = 'http://xxxx.com/cartProduct.php';
var prodWeights;    


 $(".prod_SKU").each(function () {

    $.ajax({
        url: url, 
        method: 'POST',
        dataType: "json",
        data: {sku: $(this).text()},
        contentType: "application/json; charset=utf-8",
        async: false,
        crossDomain: true,

        success: function (result) {
             prodWeights = result;
        },
        error: function (request, textStatus, errorThrown) {
            console.log(request.responseText);
            console.log(textStatus);
            console.log(errorThrown);
        }
    });    
});



 console.log(prodWeights);


 });

So it passes the SKU properly to the PHP file, "cartProduct.php":

<?php

 $sku = $_POST['sku'];

 $api_url = 'https://store-xxxxxx.mybigcommerce.com/api/v2/products?sku=' . sku;
 $ch = curl_init(); curl_setopt( $ch, CURLOPT_URL, $api_url ); 
 curl_setopt( $ch, CURLOPT_HTTPHEADER, array ('Accept: application/json', 'Content-Length: 0') );                                   
 curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, 'GET'); 
 curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 ); 
 curl_setopt( $ch, CURLOPT_USERPWD, "xxxxx:xxxxxxxxxxxx" ); 
 curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );   
 $response = curl_exec( $ch );   
 $result = json_decode($response); 
 print_r($result);


 ?>

I believe there might be an authorization issue, when I try to hit my PHP as just "http://", I get this error: error1

When I try "https://" I get this: error 2

Andrea Rose
  • 123
  • 11
  • Do you have a valid SSL installed on your script? Try accessing the URL directly via the browser over HTTPS and see if it loads, and if it asks you to manually accept the SSL. –  May 26 '16 at 00:23
  • Also, performing synchronous Ajax requests within a loop is very inefficient, and will most likely lock the browser. You should try re-structuring this to be asynchronous. One such way would be to use a counter that you increment in your success callback, so that once the counter = # of skus, you know that all the requests have finished, and you can now safely move to the final callback. –  May 26 '16 at 01:27

1 Answers1

1

I assume that cartProduct.php and the cart template making the XMLHttpRequest reside on the same domain. e.g.

xxx.com/cart.php ->
XmlHttpRequest to xxx.com/cartProduct.php ->
cURL SSL-Request to store-xxx.com ->
output to browser

Otherwise see Same-Origin Policy and configure a JSONP request insead.

Ensure you set the Content-Type of the response header when jQuery uses dataType: "json". JQuery applies an accept application/json, text/javascript request header when it is used.

Important: As of jQuery 1.4, if the JSON file contains a syntax error, the request will usually fail silently. Avoid frequent hand-editing of JSON data for this reason.

You should be sending the encoded value, and not the decoded variable output from print_r as it applies PHP specific formatting to the output.

Example:

$json = '{"a": "b"}';
print_r(json_decode($json));

Will output

stdClass Object
(
    [a] => b
)

Which is not valid JSON syntax.

So in cartProduct.php

 <?php
 ob_start();
 $sku = $_POST['sku'];

 $api_url = 'https://store-xxxxxx.mybigcommerce.com/api/v2/products?sku=' . sku;
 $ch = curl_init();
 //...
 ob_end_clean();
 header('Content-Type: application/json');
 $response = curl_exec($ch); 
 echo $response;
 exit;

Be sure to remove the ending php script tag, as any line endings or include files afterward will render an additional line break in the output, and cause extra data to be submitted.

Your console.log(prodWeights); Needs to be inside your success function calls, as the variable will not become available to the javascript until after the ajax call is made.

eg ajaxRequest -> console.log(prodWeights) -> success -> declare prodWeights

So the script would need to be changed to

 success: function (result) {
    var prodWeights = result;
    console.log(prodWeights);
 },

Your url can be easier translated for the client's current protocol by using a relative URI var url = '/cartProduct.php'; Otherwise if you are not wanting to support both protocols, be sure to redirect the user to the correct protocol if you're not already.


Last but not least, you should invest some researching into jQuery .queue and .deferred methods instead of using async: false. non-asynchronous requests are depreciated and will eventually be removed from browsers (like Java and npapi), which your developer window warns you about at the top of the console output. See: XMLHttpRequest Spec

Example: https://jsfiddle.net/4r2avewo/ (asynchronous ajax chain, with realtime ticker, view XHR status in console to validate requests are being chanined)

var def = $.Deferred();
var promise = def.promise()
$('.prod_SKU').each(function() {
  var sku = $(this).text();
  //this creates the chaining
  promise = promise.then(function() {
    return $.ajax('/path/to/resource.php', {
      method: 'post',
      dataType: 'json',
      data: {'sku': sku},
      success: function(result) {
        console.log(result);
      }
    });
  });
});
promise.done(function(){
    //do something after the last request has completed
});
def.resolve(); //start the ajax request chain
Will B.
  • 17,883
  • 4
  • 67
  • 69
  • 2
    In this case, `cartProduct.php` is an externally hosted file, as BigCommerce is a closed source platform that does not support custom server side code. Additionally though, using `jsonp` is not explicitly required, as simply setting the CORS header on the external script would suffice. Good catch on the invalid php response. –  May 26 '16 at 03:21
  • 1
    Correct, among other methods, which should be explained in the linked Same-Origin Policy reference. Though I have found most users grasp JSONP easier, since it is just working out a simple call_back function name within the Javascripts, as opposed to modifying the cross-origin request headers, and less chance of the developer using `Access-Control-Allow-Origin: *` eg: `echo call_back_name(' . $result . ');';` – Will B. May 26 '16 at 03:32
  • 1
    Thank you, my issue was resolved with the CORS and PHP headers. However, I notice that Mix-Media issue because my PHP is hosted on a non-SSL certified server (and will never be SSL-certified, this is a private development server), the workaround for that was browser-inclusive [see this topic: http://stackoverflow.com/questions/18251128/why-am-i-suddenly-getting-a-blocked-loading-mixed-active-content-issue-in-fire], is there any way to make this work naturally without telling the browser? – Andrea Rose May 26 '16 at 16:55
  • 1
    You can use `stunnel` to facilitate the SSL requirement in your development environment. https://www.stunnel.org/index.html You may need to trust and install the generated certificate to remove the invalid certifcate warnings. – Will B. May 26 '16 at 16:58
  • @AndreaRose each individual browser/client would need to manually accept the certificate in order for the JS request to work, which obviously is not ideal. There is no way to bypass this via JS. If this application is meant to work for publicly for all users, you would absolutely need to purchase an SSL if you wish to use this over HTTPS. –  May 26 '16 at 21:07
  • @love @fyrye So would `stunnel` not work for my desired application. Essentially our company has no place to host this code, as you know BigC has no PHP hosting-- so we're placing our applications/scripts on a webserver whose sole purpose is for code hosting / image hosting / holds a test wordpress blog / test opencart stores etc. We can't get this SSL certified because we only need it for a few things-- and I know SSL certs are a little expensive. Stunnel wouldn't facilitate my needs? – Andrea Rose May 27 '16 at 14:55
  • 1
    `stunnel` is only applicable for your development environments so that you do not need to set up SSL hosting during development. Your production/deployment web server, serving the AJAX request clients from BigC should be able to implement SSL and should if you're dealing with commerce data online anyway. – Will B. May 27 '16 at 16:05
  • @fyrye Definitely, and I've been trying to reach for something like this for our company anyway. I believed this to be the ultimate issue it comes down to anyway-- however, I appreciate your understanding and to-the-point responses. Out of curiosity do you know if there are any SSL-server hosting services? I don't want to pay for a huge server if we can just get a little chunk of hosting. Sounds stupid-- I know we should ultimately just purchase the cert. for our deployment server but just searching for alternatives. – Andrea Rose May 27 '16 at 16:53
  • 1
    Dreamhost Secure Hosting is the cheapest I have found for Unlimited Storage/Traffic PHP+MySQL+SSL. I believe it is $10/mo for PHP shared hosting and $20/yr for SSL certificate from Comodo. To top it off only host I've found that guarantees 100% uptime. https://www.dreamhost.com/hosting/shared/ Looks like SSL is free now.. – Will B. May 27 '16 at 17:07
  • 1
    @AndreaRose could you please update your question with the page location the ajax script is being executed on. eg: `https://store-xxx.mybigcommerce.com/products.php` – Will B. May 27 '16 at 17:14
  • @fyrye Much appreciated, I will look into hosting with them. I've also updated my question as well. – Andrea Rose May 31 '16 at 12:30