Shopify App
App administration
Installation instructions Use this page to configure the Shopify app and the external upload integration. Back to app home Shopify Partner setup
{
    "app_url": "https://shopify.grtrading.in/index.php",
    "redirect_url": "https://shopify.grtrading.in/callback.php",
    "uninstall_webhook": "https://shopify.grtrading.in/webhooks/app_uninstalled.php",
    "orders_create_webhook": "https://shopify.grtrading.in/webhooks/orders_create.php"
}
Upload API The upload API accepts browser-based multipart uploads and sends CORS headers. It only works for stores with an active installation.
{
    "method": "POST",
    "url": "https://shopify.grtrading.in/api_upload.php",
    "content_type": "multipart/form-data",
    "fields": {
        "shop": "your-store.myshopify.com",
        "file[]": [
            "<binary file 1>",
            "<binary file 2>"
        ]
    },
    "cors_allowed_origins": "*",
    "store_install_status": "supply ?shop=installed-store.myshopify.com for store-specific details",
    "store_install_id": null,
    "response": {
        "success": true,
        "msg": "Uploaded 2 file(s)",
        "files": [
            {
                "success": true,
                "msg": "Image Uploaded successfully",
                "original_name": "image-1.jpg",
                "file_name": "https://app.example.com/file.php?..."
            },
            {
                "success": true,
                "msg": "Image Uploaded successfully",
                "original_name": "image-2.png",
                "file_name": "https://app.example.com/file.php?..."
            }
        ]
    }
}
Order mapping model You do not need to send `order_id` while uploading. Order tracking only checks line item property values that start with https://shopify.grtrading.in.
{
    "tracking_source": "orders/create webhook",
    "where_url_should_exist": "line item properties",
    "matching_rule": "if a line item property value starts with https://shopify.grtrading.in, that value is recorded for the order"
}
JavaScript example
<script src="https://code.jquery.com/jquery-4.0.0.js" defer="defer"></script>
<script>
(function () {
  function initDesignSubmissionUpload($) {
    if (window.__designSubmissionUploadInitialized) return;
    window.__designSubmissionUploadInitialized = true;

    $(function () {
      UploadFile.init();
      $(document).on("change", "#upload_prod_image", UploadFile.handleFileUpload);
      $(document).on("click", ".upl_remove", function (e) {
        e.preventDefault();
        var url = $(this).attr("data-url");
        UploadFile.removeOne(url);
      });
    });

    window.UploadFile = {
      base_url: "https://shopify.grtrading.in",
      uploadedFiles: [],
      uploadLimit: 1,

      dom: {
        input: "#upload_prod_image",
        wrap: ".upload_resume_progress",
        imgBox: ".upload_resume_progress .imgcontainer"
      },

      init: function () {
        this.getLimit();
        this.applyInputMode();
        this.renderPreview();
        this.ensurePropsContainer();
      },

      getLimit: function () {
        var lim = parseInt($(this.dom.input).attr("data-upload-limit"), 10);
        if (isNaN(lim) || lim <= 0) lim = 1;
        this.uploadLimit = lim;
        return lim;
      },

      applyInputMode: function () {
        if (this.uploadLimit > 1) $(this.dom.input).attr("multiple", "multiple");
        else $(this.dom.input).removeAttr("multiple");
      },

      getUploadRoute: function () {
        return "/api_upload.php";
      },

      getShopDomain: function () {
        if (window.GRTUploadShopUrl) return window.GRTUploadShopUrl;
        if (window.GRTUploadAppConfig && window.GRTUploadAppConfig.shop) return window.GRTUploadAppConfig.shop;
        return "your-store.myshopify.com";
      },

      ensurePropsContainer: function () {
        var $form = $(this.dom.input).closest("form");
        if (!$form.length) return;
        if (!$form.find("._upl_props_wrap").length) {
          $form.append('<div class="_upl_props_wrap" style="display:none!important;"></div>');
        }
      },

      addPropertyInput: function (imageUrl) {
        var $form = $(this.dom.input).closest("form");
        if (!$form.length) return;
        this.ensurePropsContainer();

        var idx = $form.find("._upl_props_wrap input._upl_prop").length + 1;
        var $hid = $("<input>", {
          type: "hidden",
          class: "_upl_prop",
          name: "properties[Uploaded Image " + idx + "]",
          value: imageUrl
        }).attr("data-url", imageUrl);

        $form.find("._upl_props_wrap").append($hid);
      },

      removeOne: function (imageUrl) {
        this.uploadedFiles = $.grep(this.uploadedFiles, function (u) { return u !== imageUrl; });
        $("._upl_props_wrap input._upl_prop[data-url='" + imageUrl.replace(/\\/g, "\\\\").replace(/'/g, "\\'") + "']").remove();
        this.renderPreview();
      },

      handleFileUpload: function (e) {
        var self = UploadFile;
        self.getLimit();
        self.processFiles(e.target.files, function () {
          $(self.dom.input).val("");
        });
      },

      processFiles: function (files, done) {
        var self = this;
        if (!files || !files.length) {
          if (typeof done === "function") done();
          return;
        }

        var remaining = Math.max(0, self.uploadLimit - self.uploadedFiles.length);
        var selected = [];
        for (var i = 0; i < files.length && i < remaining; i++) selected.push(files[i]);

        self.uploadSequential(selected, function () {
          self.renderPreview();
          if (typeof done === "function") done();
        });
      },

      uploadSequential: function (files, done) {
        var self = this;
        var i = 0;

        function next() {
          if (i >= files.length) {
            if (typeof done === "function") done();
            return;
          }
          self.uploadSingle(files[i], function () {
            i++;
            next();
          });
        }

        next();
      },

      uploadSingle: function (file, cb) {
        var self = this;
        var fd = new FormData();
        fd.append("shop", self.getShopDomain());

        if (self.uploadLimit > 1) fd.append("file[]", file);
        else fd.append("file", file);

        $.ajax({
          url: self.base_url.replace(/\/+$/, "") + self.getUploadRoute(),
          type: "POST",
          data: fd,
          contentType: false,
          processData: false,
          dataType: "json",
          success: function (res) {
            if (!res || res.success !== true || !res.files || !res.files.length) {
              if (typeof cb === "function") cb();
              return;
            }

            var paths = [];
            for (var k = 0; k < res.files.length; k++) {
              if (res.files[k] && res.files[k].success && res.files[k].file_name) {
                paths.push(res.files[k].file_name);
              }
            }

            for (var p = 0; p < paths.length; p++) {
              var full = paths[p];
              if (!full) continue;
              if (self.uploadedFiles.indexOf(full) === -1) {
                self.uploadedFiles.push(full);
                self.addPropertyInput(full);
              }
            }

            if (typeof cb === "function") cb();
          },
          error: function () {
            if (typeof cb === "function") cb();
          }
        });
      },

      renderPreview: function () {
        var html = "";
        for (var i = 0; i < this.uploadedFiles.length; i++) {
          var u = this.uploadedFiles[i];
          html += ""
            + "<span class='upl_item'>"
            +   "<img src='" + u + "' style='width:60px;height:60px;object-fit:cover;border:1px solid #ddd;border-radius:6px;margin:4px;'>"
            +   "<a class='upl_remove' href='javascript:void(0)' data-url='" + u + "' style='position:absolute;top:4px;right:4px;width:18px;height:18px;line-height:16px;text-align:center;font-size:16px;color:#fff;background:rgba(0,0,0,0.6);text-decoration:none;border-radius:3px;'>&times;</a>"
            + "</span>";
        }
        $(this.dom.imgBox).html(html);
        $(this.dom.wrap).toggleClass("active", this.uploadedFiles.length > 0);
      }
    };
  }

  function bootWhenJqueryReady() {
    if (window.jQuery) {
      initDesignSubmissionUpload(window.jQuery);
      return;
    }
    window.setTimeout(bootWhenJqueryReady, 50);
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", bootWhenJqueryReady, { once: true });
  } else {
    bootWhenJqueryReady();
  }
})();
</script>
cURL example
curl -X POST 'https://shopify.grtrading.in/api_upload.php' \
  -F 'shop=your-store.myshopify.com' \
  -F 'file[]=@/full/path/image-1.jpg' \
  -F 'file[]=@/full/path/image-2.png'