3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Shopify] X Amazon連携、商品情報 (1/2)

Last updated at Posted at 2020-01-09

Shopify x Amazon連携に関してまとめていきます。 商品データ連携、注文データ連携、在庫データ連携、配送データ連携をカバーしていきます。

本日はまず、
Shopifyの商品情報をAmazonにプッシュする方法をカバーします。

長くなるので2部に分けました。まずは、前半を公開します。前半ではShopifyで商品情報が更新された最にWebhookで受け取るところから始まります。 下記がイメージです。

image.png

事前に準備するもの

  • Shopifyアカウント
  • Amazon MWS アカウント
  • Azure アカウント
  • Visual Studio

ShopifyのWebhookを受け取る

まずはShopify上で商品情報に変更がある際にWebhookで更新された内容を探知します。

そのためにAzure Functionsを用意します。Azure Functionsの用意に関しては下記を参照ください。
https://qiita.com/syantien/items/ab62db3b5ccec230db4e

Webhookに関してはShopifyにあるこちらのドキュメントをベースに進めていきます
https://help.shopify.com/en/api/getting-started/webhooks

ShopifyでWebhookの購読を設定

メニュー右下の「設定」をクリック、設定メニューの「通知」をクリックしてください。

image.png

通知設定画面の一番下部分に「Webhookを作成」ボタンがあるのでクリック。

image.png

次にWebhookの内容を選択し、それを受信するURLを入力します。
今回は商品の更新がある度にAmazonでも該当する商品の更新を行うので「商品更新」を指定します。

image.png

Webhookが作成されると、下記のように表示されます。ここ(①)に表記されるURLが正しいことを確認し、下部分にある文字列(②)を使い、正しいWebhookであることを確認します。

image.png

こちらのドキュメントによるとヘッダーで下記の値が渡されるとのことです。

X-Shopify-Topic: orders/create
X-Shopify-Hmac-Sha256: XWmrwMey6OsLMeiZKwP4FppHH3cmAiiJJAweH5Jo4bM=
X-Shopify-Shop-Domain: johns-apparel.myshopify.com
X-Shopify-API-Version: 2019-04

X-Shopify-Topicで正しいトピックか確認、X-Shopify-Hmac-Sha256で②にあった文字列を使いSha256で暗号化された文字列が正しいか確認、X-Shopify-Shop-Domainで対象のストアーであることを確認、X-Shopify-API-VersionでAPIのバージョンを確認してください。

Webhookレスポンスクラスの定義

products/createまたはproducts/updateの際のレスポンス型は下記です。


{
  "id": 788032119674292922,
  "title": "Example T-Shirt",
  "body_html": null,
  "vendor": "Acme",
  "product_type": "Shirts",
  "created_at": null,
  "handle": "example-t-shirt",
  "updated_at": null,
  "published_at": "2019-12-18T12:06:06-05:00",
  "template_suffix": null,
  "tags": "example, mens, t-shirt",
  "published_scope": "web",
  "variants": [
    {
      "id": 642667041472713922,
      "product_id": 788032119674292922,
      "title": "",
      "price": "19.99",
      "sku": "example-shirt-s",
      "position": 0,
      "inventory_policy": "deny",
      "compare_at_price": "24.99",
      "fulfillment_service": "manual",
      "inventory_management": "shopify",
      "option1": "Small",
      "option2": null,
      "option3": null,
      "created_at": null,
      "updated_at": null,
      "taxable": true,
      "barcode": null,
      "grams": 200,
      "image_id": null,
      "weight": 200.0,
      "weight_unit": "g",
      "inventory_item_id": null,
      "inventory_quantity": 75,
      "old_inventory_quantity": 75,
      "requires_shipping": true
    },
    {
      "id": 757650484644203962,
      "product_id": 788032119674292922,
      "title": "",
      "price": "19.99",
      "sku": "example-shirt-m",
      "position": 0,
      "inventory_policy": "deny",
      "compare_at_price": "24.99",
      "fulfillment_service": "manual",
      "inventory_management": "shopify",
      "option1": "Medium",
      "option2": null,
      "option3": null,
      "created_at": null,
      "updated_at": null,
      "taxable": true,
      "barcode": null,
      "grams": 200,
      "image_id": null,
      "weight": 200.0,
      "weight_unit": "g",
      "inventory_item_id": null,
      "inventory_quantity": 50,
      "old_inventory_quantity": 50,
      "requires_shipping": true
    }
  ],
  "options": [
    {
      "id": 527050010214937811,
      "product_id": 788032119674292922,
      "name": "Title",
      "position": 1,
      "values": [
        "Small",
        "Medium"
      ]
    }
  ],
  "images": [
    {
      "id": 539438707724640965,
      "product_id": 788032119674292922,
      "position": 0,
      "created_at": null,
      "updated_at": null,
      "alt": null,
      "width": 323,
      "height": 434,
      "src": "\/\/cdn.shopify.com\/s\/assets\/shopify_shirt-39bb555874ecaeed0a1170417d58bbcf792f7ceb56acfe758384f788710ba635.png",
      "variant_ids": [
      ]
    }
  ],
  "image": null
}

ここで一つ、Visual Studioでのやばいテクニックを紹介。上記のサンプルJsonをコピーします。このJsonファイルのモデルを定義したいクラスファイルを開き、Visual Studioの「編集」→「形式を選択して貼り付け」→JSONクラスとして貼り付けるをクリック。

image.png

クリック後、下記のようなモデルが自動で生成されます。

public class Rootobject
{
  public long id { get; set; }
  public string title { get; set; }
  public object body_html { get; set; }
  public string vendor { get; set; }
  public string product_type { get; set; }
  public object created_at { get; set; }
  public string handle { get; set; }
  public object updated_at { get; set; }
  public DateTime published_at { get; set; }
  public object template_suffix { get; set; }
  public string tags { get; set; }
  public string published_scope { get; set; }
  public Variant[] variants { get; set; }
  public Option[] options { get; set; }
  public Image[] images { get; set; }
  public object image { get; set; }
}

public class Variant
{
  public long id { get; set; }
  public long product_id { get; set; }
  public string title { get; set; }
  public string price { get; set; }
  public string sku { get; set; }
  public int position { get; set; }
  public string inventory_policy { get; set; }
  public string compare_at_price { get; set; }
  public string fulfillment_service { get; set; }
  public string inventory_management { get; set; }
  public string option1 { get; set; }
  public object option2 { get; set; }
  public object option3 { get; set; }
  public object created_at { get; set; }
  public object updated_at { get; set; }
  public bool taxable { get; set; }
  public object barcode { get; set; }
  public int grams { get; set; }
  public object image_id { get; set; }
  public float weight { get; set; }
  public string weight_unit { get; set; }
  public object inventory_item_id { get; set; }
  public int inventory_quantity { get; set; }
  public int old_inventory_quantity { get; set; }
  public bool requires_shipping { get; set; }
}

public class Option
{
  public long id { get; set; }
  public long product_id { get; set; }
  public string name { get; set; }
  public int position { get; set; }
  public string[] values { get; set; }
}

public class Image
{
  public long id { get; set; }
  public long product_id { get; set; }
  public int position { get; set; }
  public object created_at { get; set; }
  public object updated_at { get; set; }
  public object alt { get; set; }
  public int width { get; set; }
  public int height { get; set; }
  public string src { get; set; }
  public object[] variant_ids { get; set; }
}

ということで、Shopify Webhook (Product/Create Product/Update)のJsonを受けるC#モデルは上記です。

Webhookを受けるFunction

では、さっそくFunctionもこのデータを受け取るようにします。


using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using ShopifyWebhookHandler.Models;
using System.Text;
using System.Security.Cryptography;

namespace ShopifyWebhookHandler
{
    public class ShopifyWebHookHandlerProductUpdateHandler
    {
        private string _sharedSecret = "Your Key Here";

        [FunctionName("ShopifyWebHookHandlerProductUpdateHandler")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("Hello? Did you call me?");

            // Read all
            req.Headers.TryGetValue("X-Shopify-Topic", out var topic);
            req.Headers.TryGetValue("X-Shopify-Hmac-Sha256", out var hMacSha256);
            req.Headers.TryGetValue("X-Shopify-Shop-Domain", out var domain);
            req.Headers.TryGetValue("X-Shopify-API-Version", out var apiVersion);
            string requestBody = await new StreamReader(req.Body,Encoding.UTF8).ReadToEndAsync();

            if (topic != "")
            {
                // do something if it's not the tipc you are looking for
                log.LogInformation("Topic was: " + topic );
            }

            var keyBytes = Encoding.UTF8.GetBytes(_sharedSecret);
            var dataBytes = Encoding.UTF8.GetBytes(requestBody);

            var hmac = new HMACSHA256(keyBytes);
            var hmacBytes = hmac.ComputeHash(dataBytes);
            var createSignature = Convert.ToBase64String(hmacBytes);

            if (hMacSha256 == createSignature)
            {
                // do something if key does not match
                
            }

            log.LogInformation("I compare " + hMacSha256 + " vs " + createSignature);

            if (domain != "")
            {
                // do something if it's not the domain you are looking for
                log.LogInformation("Domain was: " + domain);
            }
            
            // Map data into ProductModel
            var data = JsonConvert.DeserializeObject<ProductModel>(requestBody);

            log.LogInformation("The product " + data.title + " was updated. Api version is " + apiVersion + ".");

            return data != null
                ? (ActionResult)new OkObjectResult("Good news, all good.")
                : new BadRequestObjectResult("Bad news, I got NULL.");
        }

        private string sha256(string randomString)
        {
            var crypt = new System.Security.Cryptography.SHA256Managed();
            var hash = new System.Text.StringBuilder();
            byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(randomString));
            foreach (byte theByte in crypto)
            {
                hash.Append(theByte.ToString("x2"));
            }
            return hash.ToString();
        }
    }
}

ではさっそくテストをしてみます。テストは先ほどの管理画面右側から送ることができます。

image.png

そして下記がFunctionsのLogになります。

image.png

X-Shopify-Hmac-Sha256もちゃんと照合することができました。

次はAmzazon API を使って商品情報をプッシュします。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?