🐱

FileMakerのcURLを利用しECモールへ在庫反映

2021/11/24に公開約17,300字

はじめに

各ECモールには在庫更新に関するAPIが用意されていますが、そのリファレンスは慣れていないと手強いと感じるものもあります(記載不備なリファレンスも割とあります)。

サンプルコードも交えて、丁寧にリファレンスが書かれているECモールもありますが、いかんせんソースがPHPなのと、またFileMakerでの具体的な運用サンプルがネット上でヒットしないが為に、結果的に諦めてしまった方もいるかもしれません。

そこで、
実際に私が携わっているモールでの具体的なcURLオプションの指定例と、また、転送するリクエストボディの記述をしていきながら、注意点やハマりやすい点を中心に伝えていけたらと思います。

※2022/03/23 加筆
Qoo10は、OpenAPIサービス から QAPI へ変更となり仕様が大きく変わりました。

※2023/04/11 加筆
Amazon の MWS API は、2024年3月31日以降は完全に利用できなくなりますので、SP-APIへの移行が必要です。SP-APIでの在庫更新は、コチラを参考にして下さい。
https://zenn.dev/ontherocks_plz/articles/1df1da9288c6ba


注意点

  • リクエストボディは、見やすさを考えて整形しています。実際には余計な改行などを含んだ状態だとエラーになるケースがあります。
  • リクエストボディは、販売商品によって項目や値が変わる場合があります(SKUのバリエーションが有る無し、の違いなど)
  • 各ECモールには、それぞれリクエスト制限があります(1リクエスト/1秒まで、など)

ZenPlus

リファレンスもしっかりしていて、非常に分かり易いですね。

HTTPメソッドがPUTな点に注意です。

cURL option
"-X \"PUT\" "
&
"-H \"Content-Type: application/json; charset=UTF-8\" "
&
"-H \"zenplus-api-key: " & $auth & "\" "
&
"-d @$data"

-Xというオプションで指定をしない場合、デフォルトではPOSTで処理されますので、-XのオプションでPUTをしっかり指定する必要があります。

また、リクエストボディの変数$data"-d @$data"と指定していますが、これは内包という変数の独特な参照方法です。以後、全てこの方法で指定していきます。

$authには、ストア設定で確認できるAPIキーを設定します。

$data
[
    {
        "item_id": "A000010101",
        "item_quantity": 15
    },
    {
        "item_id": "B000010101",
        "item_quantity": 3
    }
]

リクエストボディがJSONなので組みやすくていいですね、また複数のSKUを同時に更新できるので、Loopが要らないのもいい。

注意点としては[ ](大括弧)で囲む点です。ですので、

計算式
JSONSetElement ( "" ;

[ "[]" ; 

	JSONSetElement ( "" ; 
		[ "item_id" ; "A000010101" ; 1 ];
		[ "item_quantity" ; 3 ; 2 ]
	 )

 ; 3 ]

 )

と、入れ子にしてJSONを組み上げるかまたは、

計算式
JSONSetElement( "[]";
	[ "[0].item_id" ; "A000010101"; 1 ];
	[ "[0].item_quantity" ; 3 ; 2 ]
)

として組み上げるか、または単純に、

計算式
JSONSetElement ( "" ; 
	[ "item_id" ; "A000010101" ; 1 ];
	[ "item_quantity" ; 3 ; 2 ]
 )

として、最後にこの計算結果に[]で挟んで結合させるかです。
どれも同じ結果が得られます。


shopserve

HTTPメソッドがPUTな点に注意です。

cURL option
"-X \"PUT\" "
&
"-H \"Authorization: " & $auth & "\" "
&
"-H \"Content-Type: application/json; charset=UTF-8\" "
&
"-d @$data"
$auth
"Basic " & Base64EncodeRFC ( 4648 ; "##ショップID##" & ":" & "##マネジャー認証キー##" )

##ショップID##は管理画面にログインする際のID、##マネジャー認証キー##はモール上で店舗自ら発行できます。

$data
{
	"alert_threshold" : 0,
	"quantity" : 6,
	"unlimited" : "No",
	"variation_name1" : "F",
	"variation_name2" : "レッド"
}

shopserveはリクエストURLに商品番号の指定が必要な為、残念ながら異種商品のSKUを同時にPUTする方法はありません。Loopで回しましょう。

その他の注意点としては、PUTが成功してもレスポンスボディ(メッセージ)は空です。エラーメッセージが返却されるのはエラーが発生した場合のみです。

それでも不安な場合は以下のように指定します。

cURL option
"-X \"PUT\" "
&
"-D $resHeader "
&
"-H \"Authorization: " & $auth & "\" "
&
"-H \"Content-Type: application/json; charset=UTF-8\" "
&
"-d @$data"

3行目に-D $resHeaderを追加していますが、これで変数$resHeaderにレスポンスのヘッダ情報が格納され、その情報が取得できます。

$resHeader
HTTP/1.1 200 OK
Date: Mon, 22 Nov 2020 05:33:56 GMT
Server: Apache/2.2.24 (Unix) mod_ssl/2.2.24 OpenSSL/1.0.0-fips
Content-Length: 0
Content-Type: application/json; charset=UTF-8
Content-Language: ja

1行目、HTTP/1.1 200 OKの中の200はHTTPコードと呼ばれるもので、200 は成功を意味します。


YAHOO

YAHOOはリファレンスが丁寧で、POSTサンプル(ただしPHP)もありますね。

cURL option
"-H \"Authorization: " & $auth & "\" "
&
"-H \"Content-Type: application/x-www-form-urlencoded\" "
&
"-d @$data"
$auth
"Bearer " & "##アクセストークン##"

YAHOOの場合、##アクセストークン##の取得がやや面倒かもしれませんね(コールバックURL等が必要ですので、今回は取得手順は省きます)。

$data
seller_id=##セラーID##&item_code=A00001:A000012233&quantity=6

##セラーID##はストアアカウントのことです。

A00001は商品コードの例ですが、個別商品コードもある場合は:に続けてA000012233といったように個別商品コードを記述します。

複数のSKUを同時に更新したい場合は、次のようにitem_code=quantity=の中身を,で繋げていきます。

seller_id=##セラーID##&item_code=A00001:A000012233,B00001:B000012233&quantity=6,99

au PAY(旧Wowma)

au PAYはモール側に接続元IPアドレスの登録が必要です。動的IPアドレスでも大丈夫ですが、接続元IPアドレスが変わった場合はモールに登録し直さないといけません。

cURL option
"-H \"Authorization: " & $auth & "\" "
&
"-H \"Content-Type: application/xml; charset=utf-8\" "
&
"-d @$data"

Content-Typeの指定は必須で、無い場合はエラーが返ってきますので注意です。

$auth
"Bearer " & "##APIキー##"

##APIキー##はモール上で店舗自ら発行できます。

$data
<request>
    <shopId>##ショップID##</shopId>
    <stockUpdateItem>
        <itemCode>A00001</itemCode>
        <stockSegment>2</stockSegment>
        <choicesStocks>
            <choicesStockHorizontalCode>22</choicesStockHorizontalCode>
            <choicesStockVerticalCode>33</choicesStockVerticalCode>
            <choicesStockCount>6</choicesStockCount>
            <choicesStockShippingDayId>1</choicesStockShippingDayId>
        </choicesStocks>
    </stockUpdateItem>
</request>

2行目の##ショップID##は会員番号のことです。

複数のSKUを同時に更新したい場合は、<stockUpdateItem>...</stockUpdateItem>を繰り返し、次のようにします。

$data
<request>
    <shopId>##ショップID##</shopId>
    <stockUpdateItem>
        <itemCode>A00001</itemCode>
        <stockSegment>2</stockSegment>
        <choicesStocks>
            <choicesStockHorizontalCode>22</choicesStockHorizontalCode>
            <choicesStockVerticalCode>33</choicesStockVerticalCode>
            <choicesStockCount>6</choicesStockCount>
            <choicesStockShippingDayId>1</choicesStockShippingDayId>
        </choicesStocks>
    </stockUpdateItem>
    <stockUpdateItem>
        <itemCode>B00001</itemCode>
        <stockSegment>2</stockSegment>
        <choicesStocks>
            <choicesStockHorizontalCode>22</choicesStockHorizontalCode>
            <choicesStockVerticalCode>33</choicesStockVerticalCode>
            <choicesStockCount>99</choicesStockCount>
            <choicesStockShippingDayId>1</choicesStockShippingDayId>
        </choicesStocks>
    </stockUpdateItem>
</request>

SUPERDELIVERY

cURL option
"-H \"Content-Type: application/json; charset=UTF-8\" "
&
"-d @$data"
$data
{
	"body" : 
	{
		"productSets" : 
		{
			"productSet" : 
			[
				{
					"dealerProductCode" : "A000012233",
					"stock" : 6
				}
			]
		}
	},
	"header" : 
	{
		"apiAuthCode" : "##認証コード##"
	}
}

認証をヘッダーに含めず、リクエストボディのapiAuthCodeの箇所に記述します。##認証コード##は、モールへ申請すれば取得できます。

複数のSKUを同時に更新したい場合は、productSetの配列内を繰り返し次のようにします。

$data
{
	"body" : 
	{
		"productSets" : 
		{
			"productSet" : 
			[
				{
					"dealerProductCode" : "A000012233",
					"stock" : 6
				},
				{
					"dealerProductCode" : "B000012233",
					"stock" : 99
				}
			]
		}
	},
	"header" : 
	{
		"apiAuthCode" : "##認証コード##"
	}
}

Rakuten

cURL option
"-H \"Content-Type:text/xml; charset=utf-8\" "
&
"-d @$data"
$data
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="java:jp.co.rakuten.rms.mall.inventoryapi.v1.model.entity" xmlns:ns2="https://inventoryapi.rms.rakuten.co.jp/rms/mall/inventoryapi">
    <SOAP-ENV:Body>
        <ns2:updateInventoryExternal>
            <ns2:externalUserAuthModel>
                <ns1:authKey>$auth</ns1:authKey>
                <ns1:userName>##ユーザー名##</ns1:userName>
                <ns1:shopUrl>##店舗URL##</ns1:shopUrl>
            </ns2:externalUserAuthModel>
            <ns2:updateRequestExternalModel>
                <ns1:updateRequestExternalItem>
                    <ns1:UpdateRequestExternalItem>
                        <ns1:HChoiceName>F</ns1:HChoiceName>
                        <ns1:VChoiceName>レッド</ns1:VChoiceName>
                        <ns1:inventory>6</ns1:inventory>
                        <ns1:normalDeliveryId>0</ns1:normalDeliveryId>
                        <ns1:inventoryType>3</ns1:inventoryType>
                        <ns1:inventoryUpdateMode>1</ns1:inventoryUpdateMode>
                        <ns1:itemUrl>A00001</ns1:itemUrl>
                    </ns1:UpdateRequestExternalItem>
                </ns1:updateRequestExternalItem>
            </ns2:updateRequestExternalModel>
        </ns2:updateInventoryExternal>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

リクエストボディに各種情報を含めます。
7行目の##ユーザー名##、8行目の##店舗URL##はモール上で確認できます。

$auth
"ESA " & Base64EncodeRFC ( 4648 ; "##serviceSecret##" & ":" & "##licenseKey##" )

##serviceSecret####licenseKey##は、共にモール上で確認・店舗自ら発行できます。

複数のSKUを同時に更新したい場合は、
<ns1:UpdateRequestExternalItem>...</ns1:UpdateRequestExternalItem>
ここを繰り返し、次のようにします。

$data
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="java:jp.co.rakuten.rms.mall.inventoryapi.v1.model.entity" xmlns:ns2="https://inventoryapi.rms.rakuten.co.jp/rms/mall/inventoryapi">
    <SOAP-ENV:Body>
        <ns2:updateInventoryExternal>
            <ns2:externalUserAuthModel>
                <ns1:authKey>$auth</ns1:authKey>
                <ns1:userName>##ユーザー名##</ns1:userName>
                <ns1:shopUrl>##店舗URL##</ns1:shopUrl>
            </ns2:externalUserAuthModel>
            <ns2:updateRequestExternalModel>
                <ns1:updateRequestExternalItem>
                    <ns1:UpdateRequestExternalItem>
                        <ns1:HChoiceName>F</ns1:HChoiceName>
                        <ns1:VChoiceName>レッド</ns1:VChoiceName>
                        <ns1:inventory>6</ns1:inventory>
                        <ns1:normalDeliveryId>0</ns1:normalDeliveryId>
                        <ns1:inventoryType>3</ns1:inventoryType>
                        <ns1:inventoryUpdateMode>1</ns1:inventoryUpdateMode>
                        <ns1:itemUrl>A00001</ns1:itemUrl>
                    </ns1:UpdateRequestExternalItem>
                    <ns1:UpdateRequestExternalItem>
                        <ns1:HChoiceName>S</ns1:HChoiceName>
                        <ns1:VChoiceName>ブラック</ns1:VChoiceName>
                        <ns1:inventory>99</ns1:inventory>
                        <ns1:normalDeliveryId>0</ns1:normalDeliveryId>
                        <ns1:inventoryType>3</ns1:inventoryType>
                        <ns1:inventoryUpdateMode>1</ns1:inventoryUpdateMode>
                        <ns1:itemUrl>B00001</ns1:itemUrl>
                    </ns1:UpdateRequestExternalItem>
                </ns1:updateRequestExternalItem>
            </ns2:updateRequestExternalModel>
        </ns2:updateInventoryExternal>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Qoo10

※2022/03/23 新仕様で加筆

OpenAPIサービスからQAPIに仕様が変わりました。それに伴い、各メソッドのエンドポイントが変更されています。販売者認証キーも新たなエンドポイントで取得し直す必要があります。

販売者認証キー取得(GETの場合)

GET URL
http://Api.qoo10.jp/GMKT.INC.Front.QAPIService/ebayjapan.qapi?v=1.0&returnType=json&method=CertificationAPI.CreateCertificationKey&key=##APIKey##&user_id=##販売者アカウントID##&pwd=##販売者アカウントパスワード##

##APIKey##は、これはモールにお願いして教えて貰って下さい。
##販売者アカウントID####販売者アカウントパスワード## は、管理画面へのログインIDとパスワードです。

さて、
SKUバリエーション(オプションバリエーション)別に在庫がある場合の、在庫数量のみを更新する方法ですが、エンドポイントを記載しておきます。

endpoint
https://api.qoo10.jp/GMKT.INC.Front.QAPIService/ebayjapan.qapi/ItemsOptions.UpdateInventoryQtyUnit

QAPIになって、リクエストボディがなんと、JSONでの記述が可能になりました。ですので、その場合の cURL option を記載します(xmlでもいけるようですが試していません)。$auth の変数には前述で取得した販売者認証キーを設定しておきます。

cURL option
"-H \"Content-Type: application/json; charset=UTF-8\" "
&
"-H \"GiosisCertificationKey: " & $auth & "\" "
&
"-d @$data"
$data
{
  "ItemCode" : "",
  "OptionCode" : "A000010101",
  "OptionName" : "",
  "OptionValue" : "",
  "Qty" : 330,
  "SellerCode" : "A00001"
 }

OptionCode とは販売者オプションコードSellerCode とは販売者商品コードのことになります。その他のJSONキーは空欄値で構いませんが、キー自体は必要ですので注意して下さい。

※以下は旧OpenAPIサービス時の内容です(一応、現在も利用できるようです)
ここから旧仕様での記載--------------------
SOAP方式でも受け付けてくれますが、REST方式を記述します。

cURL option
"-H \"Content-Type: application/x-www-form-urlencoded\" "
&
"-d @$data"
$data
key=##販売者認証キー##&ItemCode=&SellerCode=A00001&OptionName=サイズ||*カラー&OptionValue=F||*レッド&Optioncode=&Qty=6

例では縦(サイズ)、横(カラー)2軸がある商品の場合を記述していますが、こういった場合、項目名も項目値もそれぞれ ||* で結合します。ただ残念ながら、複数のSKUを同時に更新する方法はないようです。更新は1SKUずつのみですので、複数のSKU更新時はLoop必須です。

ItemCode=Optioncode=は、例えその値が無くても記述しておく必要があります。省くとエラーが返ってきます。

##販売者認証キー##は、別途、店舗側で取得しておく必要がありますが、YAHOOより簡単です。GET/POSTまたはSOAPで取得できますが、GET が楽でしょう。

GET URL
http://Api.qoo10.jp/GMKT.INC.Front.OpenApiService/Certification.api/CreateCertificationKey?key=##APIKey##&user_id=##販売者アカウントID##&pwd=##販売者アカウントパスワード##

##APIKey##は、これはモールにお願いして教えて貰って下さい。
##販売者アカウントID####販売者アカウントパスワード## は、管理画面へのログインIDとパスワードです。
ここまで旧仕様での記載--------------------


Amazon

AmazonはリクエストURLの作成が非常に面倒なので、それは最後に説明します。

cURL option
"-H \"Content-Type:text/xml; charset=utf-8\" "
&
"-H \"Content-MD5:" & $ContentMD5Value & "\" "
&
"-d @$data"

Content-MD5の値、$ContentMD5Valueの求め方ですが、

$ContentMD5Value
Base64EncodeRFC ( 4648 ; CryptDigest ( $data ; "MD5" ) )

このように$dataのハッシュ化からのBase64EncodeRFCを行います。ただし、$data改行なしのデータにする必要があります。改行があると、POSTしても Content-MD5エラーが返却されますので注意です(Char(10)の改行でもエラーになります)。

$data
<?xml version="1.0" encoding="utf-8"?>
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
    <Header>
        <DocumentVersion>1.01</DocumentVersion>
        <MerchantIdentifier>##セラーId##</MerchantIdentifier>
    </Header>
    <MessageType>Inventory</MessageType>
    <Message>
        <MessageID>1</MessageID>
        <OperationType>Update</OperationType>
        <Inventory>
            <SKU>A000012233</SKU>
            <Quantity>6</Quantity>
            <FulfillmentLatency>1</FulfillmentLatency>
        </Inventory>
    </Message>
</AmazonEnvelope>

5行目の##セラーID##は出品者IDとも言います。

複数のSKUを同時に更新したい場合は、<Message>...</Message>を繰り返すのですが、その際その中の<MessageID>...</MessageID>で囲まれたIDをカウントアップさせましょう。

$data
<?xml version="1.0" encoding="utf-8"?>
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
    <Header>
        <DocumentVersion>1.01</DocumentVersion>
        <MerchantIdentifier>##セラーId##</MerchantIdentifier>
    </Header>
    <MessageType>Inventory</MessageType>
    <Message>
        <MessageID>1</MessageID>
        <OperationType>Update</OperationType>
        <Inventory>
            <SKU>A000012233</SKU>
            <Quantity>6</Quantity>
            <FulfillmentLatency>1</FulfillmentLatency>
        </Inventory>
    </Message>
    <Message>
        <MessageID>2</MessageID>
        <OperationType>Update</OperationType>
        <Inventory>
            <SKU>B000012233</SKU>
            <Quantity>99</Quantity>
            <FulfillmentLatency>1</FulfillmentLatency>
        </Inventory>
    </Message>
</AmazonEnvelope>

さて、問題のリクエストURLは、次のような構成になります。

request URL
"https://mws.amazonservices.jp?" & $params & "&Signature=" & $sig

$params$sigの変数の設定ですが、これが大変です。

まず、$paramsはパラメータ結合順も含め、基本形は次のようになります。基本形と言うのには理由があります(後述)。

$params
"AWSAccessKeyId=" & "##AWSアクセスキーID##" &
"&Action=" & "SubmitFeed" &
"&FeedType=" & "_POST_INVENTORY_AVAILABILITY_DATA_" &
"&MarketplaceIdList.Id.1=" & "A1VC38T7YXB528" &
"&Merchant=" & "##セラーID##" &
"&PurgeAndReplace=" & "false" &
"&SignatureMethod=" & "HmacSHA256" &
"&SignatureVersion=" & "2" &
"&Timestamp=" & $timeStamp &
"&Version=" & "2009-01-01"

基本形はこれで問題ないのですが、実は各値はURLエンコード(パーセントエンコード)されている必要があります。が、実際の所URLエンコードが必要な値は恐らく$timeStampでしょう。

$timeStamp は、UTC(協定世界時2020-11-23T15:01:46Zの形式)での記述になりますが、この中の時間の区切り:の部分にURLエンコードが必要です。

$timeStamp
Substitute ( 
	Left ( GetAsTimestamp ( Get ( 現在の時刻 UTC ミリ秒 ) / 1000 ) ; 19 ) ;
	[ " " ; "T" ];
	[ "/" ; "-" ];
	[ ":" ; "%3A" ]
 )
& "Z"

これを実行すると、2020-11-23T15%3A01%3A46ZとURLエンコードされた値が得られます。注意点は、絶対にFileMaker関数のGetAsURLEncodedでエンコードしてはいけません

次に $sig ですが、手順としてまず$paramsの頭に3行追加します(これを$procとします)。

$proc
"POST" & Char(10) & "mws.amazonservices.jp" & Char(10) & "/" & Char(10) & $params

実際の$procの中身は次のようになります。

$procの実際の中身
POST
mws.amazonservices.jp
/
AWSAccessKeyId=##AWSアクセスキーID##&Action=SubmitFeed&FeedType=_POST_INVENTORY_AVAILABILITY_DATA_...

このように頭に3行追加しますが、注意点としては改行コードが違いますのでで改行結合してはいけません。必ずChar(10)で改行結合します。

この$proc##シークレットキー##を使ってハッシュベースの認証コードを求め、Base64EncodeRFCで排出される可能性のある記号、+ / =をURLエンコードします。

$sig
Substitute ( 
	Base64EncodeRFC ( 4648 ; CryptAuthCode ( $proc ; "SHA256" ; "##シークレットキー##" ) );
	[ "+" ; "%2B" ];
	[ "/" ; "%2F" ];
	[ "=" ; "%3D" ]
 )

URLエンコード用のカスタム関数を作っておくのもいいでしょうね。これを実行すると、以下のような値が得られます。

QEAGyZpsitVE2C4GJc09RaCAMLj92OmGdsab%2B2jOadQ%3D

なかなか面倒ですが、$params $sigを求めることができましたので、これでリクエストURLも完成、cURLオプションと共に在庫データ送信できます。

POST に成功すると、以下のようなSUBMITTEDレスポンスがあります。

<?xml version="1.0"?>
<SubmitFeedResponse xmlns="http://mws.amazonaws.com/doc/2009-01-01/">
    <SubmitFeedResult>
        <FeedSubmissionInfo>
            <FeedSubmissionId>321535019999</FeedSubmissionId>
            <FeedType>_POST_INVENTORY_AVAILABILITY_DATA_</FeedType>
            <SubmittedDate>2020-11-23T02:34:32+00:00</SubmittedDate>
            <FeedProcessingStatus>_SUBMITTED_</FeedProcessingStatus>
        </FeedSubmissionInfo>
    </SubmitFeedResult>
    <ResponseMetadata>
        <RequestId>dcaa96be-5199-4f75-b4f4-52fbe99999b6</RequestId>
    </ResponseMetadata>
</SubmitFeedResponse>

おわりに

いかがだったでしょうか。
なかなか手強いECモールもありますが、今までPHPなどを介してでしか出来なかったAPIでの反映が、FileMakerネイティブで出来るようになった意味は非常に大きいです。

ちなみに、urlから挿入スクリプトステップはサーバー側でも実行でますので、サーバー側のスクリプトスケジュールでスケジュール実行が行え、ASPのように定期自動在庫更新が可能です。

それでは
Let's enjoy FileMaker!

Discussion

ログインするとコメントできます