今回はShopifyアプリをGoogle Cloud PlatformのGAE(Google App Engine)を使ってGoで書いてみました。
Shopifyとは?
**Shopify**とは、カナダ産のEC(e-Commerce)のSaaSです。ひと昔前風に言うと、GMOメイクショップや、フューチャーショップ、Baseの様なEC向けのASPです。 現在、カナダだけではなく、北米はもとより世界中で利用されており、昨年くらいからアジアオセアニア地域でのマーケティングにお力を入れ始めた、**ECプラットフォーム界の巨人**です。 グローバルに展開しているサービスなので、世界中のローカルの決済等が利用でき、越境EC向けのECプラットフォームです。標準機能も素晴らしいのですが、Shopifyアプリというサードパーティーのアプリでその機能を拡張できる点も魅力的な点です。
すでに世界中で様々なShopifyアプリが開発されており、日本国内でも増えてきています。
Shopifyアプリとは?
じゃあ、具体的にShopifyアプリってどうなってんの?というと、基本的にShopifyのWebAPIを叩くsomethingです。
APIには伝統的なRESTのAPIと最近流行りのGraphQLがあります。
Shopify内の何かしらのイベントに対応したEventかWebhookでキックされる感じです。
APIとアプリにはいくつか種類があって、全体的なAPIと、管理画面上の要素をごにょごにょするAPIとかフロント用のAPIとか実店舗や倉庫向けのPOSアプリ用のAPIとかもありますよくわかりません。とりあえず今回の要件に必要な分だけ調べました。
Shopifyアプリには、大きく分けて公開されてて誰でも利用することができるPublic Appsと、その店舗専用みたいなPrivate Appsがあります。
Private Apps
ShopifyのAppストアに出さないそのショップ専用のアプリ。Public Appと違って、認証周りが楽。トークンとか取得しなくて良い。https://{API_key}:{password}@{shop}.myshopify.com/admin/api/2019-10/shop.json
みたいにBasic認証でリクエストして処理できる。今回使ったのはこれ
Public Apps
ShopifyのAPPストアからインストールできる。APIの接続には認証とトークンが必要。一般的なAppsといったらこっち。
App Storeに公開しなくても使える。その場合はPrivate Appsっぽい扱いができる。Private Appじゃないメリットは後述のEmbedded SDKとかが使えること。管理画面系のアプリはPublicにしないとできない。
自分が作ったアプリを広く売りたいなら通常のShopify App
Public AppsはShopifyの利用者に向け販売できます。だいたいのアプリがサブスクリプション方式なので、定期的な収入が欲しい方は作ってみてはいかがでしょうか?
英語など多言語に対応すれば市場はものすごく大きいので、ポテンシャルは高いです。ただ、現状だと日本語と日本にしか対応していないアプリは厳しそうです。実際、現在日本語で提供されているアプリも、決済系かロジ系が多く、アプリじゃなくて自社サービスと連携させるアプリを出して、自社サービスで利益を出すタイプが多い様です。
Embedded apps
なぜこれがPublic AppsとPrivate Appsと同じレベルで説明されているのかわからないけど、上記のPublic Appsで使える管理画面系のアプリのためのSDKがあり、これらを使う様なアプリはEmbedded appsと呼ばれる。
-
Embedded App SDK
- 管理画面に埋め込む系の何かを作る時用のSDK
-
POS App SDK- iPadとかのタブレットなどで使えるShopify POSアプリがあり、それ用のSDK。今は非推奨。iOS9以降は対応してないっぽい。
-
Shopify App Bridge
- Embedded App SDK、POS SDKに代わる新しいしくみ。JavaScriptのライブラリで、管理画面やShopify POS Appのフロントエンド向け。
どの言語でShopify Appを作るか?
ぶっちゃけどれでも良い。APIとのやり取りがメインなので、何だったらCでもアセンブラでも良い。
RESTかGraphQL叩ければ良い。ただ、いろんな言語でShopify API用のライブラリが出てるのでそれらを使うと楽。
公式に推してるのはRuby on Rails
https://github.com/Shopify/shopify_app
最初、Railsで書いてたけど、Railsが動くApp Serverを立てるのとそのメンテが面倒だったので途中でやめた。たぶんHerokuとか使えば良いんだろうけど、嫌いなのでテンション下がってやめた。今回はクライアントにとってもHerokuとRailsを使うメリットはあまり無かった。
Shopify自体がRubyで書かれていて、UAがRubyでAPPにアクセスしてくる。
Shopifyって中は何で書いてるんだろう?って思ってたんですが、Wenhookに関してはアクセスしてくるUAがRubyとなってるので、中でも結構Rubyが使われてるのかも。 Matzすごい
Ruby以外の言語のライブラリ
Ruby on Railsで書くのを止めた段階で候補はGoかPHPしかなかったんだけど、ざっくり調べて見た。
- Go
- go-shopify(今回使ったやつ)
- PHP
- Shopify公式 (メンテされてない)
- Laravel Shopify App
- Symfony Shopify Bundle (最近コミットされてなくて怪しい)
- node
- Java
- Shopify SDKちゃんとメンテされてるっぽい
- Shopify4J - Caffeinated Shopify BindingsAndroid用(メンテされてない)
- Elixir
世界中で使われてるSaaSなだけあって、とにかくいっぱいある。ただ、日本語対応とか日本向けはまだまだ少ないので、日本人的には辛い。日本人頑張らねば
PHPが公式のリポジトリにあるにも関わらずメンテされていなかったので、何となく避けた。Laravelのライブラリは開発が盛んで盛り上がっているみたいだったので、少し興味を持ったけど今回はLaravel使うほどのものでもなかったのでGoでベタ書きすることにした。結局100行ちょいのmain.go
1ファイルで終わった。
go-shopifyを使ってgolangでShopify Appを書く
goでShopify Appを書く場合、go-shopifyを使うと色々と便利。大体のShopifyのオブジェクトの構造体が準備されてるし、.Create()
で追加したり色々できる。
とりあえずgo get
してインポートする。
$ go get github.com/bold-commerce/go-shopify
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"time"
goshopify "github.com/tao-s/go-shopify"//これ
)
go-shoipfyの基本的な使い方
まずはAPIの認証。 Private AppとPublic Appで少し違う
Public AppのShopify APIの認証
API Key
とAPI Secret
をセットしてトークンを取得する
// Create an app somewhere.
app := goshopify.App{
ApiKey: "abcd",
ApiSecret: "efgh",
RedirectUrl: "https://example.com/shopify/callback",
Scope: "read_products,read_orders",
}
API Key
とAPI Secret
はShopifyのパートナーページのアプリ管理で取得できる。
次にトークン取得
// Create an oauth-authorize url for the app and redirect to it.
// In some request handler, you probably want something like this:
func MyHandler(w http.ResponseWriter, r *http.Request) {
shopName := r.URL.Query().Get("shop")
authUrl := app.AuthorizeURL(shopName)//shopNameはストアの管理画面のドメイン。
http.Redirect(w, r, authUrl, http.StatusFound)//アプリ認証画面にリダイレクトさせる
}
[^3]: go-shopifyのreadmeから引用
// 認証画面からの戻りの画面
func MyCallbackHandler(w http.ResponseWriter, r *http.Request) {
// Check that the callback signature is valid
if ok, _ := app.VerifyAuthorizationURL(r.URL); !ok {//戻りの検証
http.Error(w, "Invalid Signature", http.StatusUnauthorized)
return
}
query := r.URL.Query()
shopName := query.Get("shop")
code := query.Get("code")
token, err := app.GetAccessToken(shopName, code)//トークンを取得。ここで取得したトークンはDBとかに保存しとく
// Do something with the token, like store it in a DB.
}
トークンが取得できたらAPI叩ける。
// Create an app somewhere.
app := goshopify.App{
ApiKey: "abcd",
ApiSecret: "efgh",
RedirectUrl: "https://example.com/shopify/callback",
Scope: "read_products",
}
// Create a new API client
client := goshopify.NewClient(app, "shopname", "token")//取得したshopnameとトークンを渡してクライアントを初期化
// Fetch the number of products.
numProducts, err := client.Product.Count(nil)//APIをCallする
Private AppのShopify APIの認証
Private Appの場合は、APIキー取得するとこが違う。Public Appはパートナーページだったけど、Pricvate Appはショップのアプリ管理から取得する。
取得した** API Keyとパスワード**で初期化。トークンは空文字ですぐクライアント作れる。
このプライベートアプリがどのAPIにアクアセスできるか?とかはストアのプライベートアプリの管理画面で設定する。
// Create an app somewhere.
app := goshopify.App{
ApiKey: "apikey",
Password: "apipassword",//API Secretじゃなくてpassword
}
// Create a new API client (notice the token parameter is the empty string)
client := goshopify.NewClient(app, "shopname", "")
アプリの起動はWebhookで
アプリの起動方法は2種類あって、あらかじめshopifyのEventに登録しておく方法と、Webhookを使う方法。プライベートアプリならWebhookが楽。
Webhookの設定は、ストアの管理画面の通知メニューから設定する。
ここでWebhookの送信先とかリクエストの検証用キーとかが設定できる。
Private Appはトークンいらない代わりにWebhookリクエストの検証が必要。じゃないとショップ以外の悪意ある第三者からのリクエストも処理しちゃう。
func ValidateWebhook(httpRequest *http.Request) (bool) {
shopifyApp := goshopify.App{ApiSecret: "ratz"}
return shopifyApp.VerifyWebhookRequest(httpRequest)
}
Webhookを起点にShopify APIを叩く
Webhookのパース
今回は受注のタイミングで支払いステータスを変更したいって要件だったので、注文作成のWebhookを設定した。その際、作成された注文の情報がWebhookのリクエストに入ってくるので、必要な情報だけを取得する。
go-shopifyでもできるんだけど、諸事情によりここだけ手作業でやった。
b, err := ioutil.ReadAll(r.Body)//リクエストのbodyをパース
defer r.Body.Close()
if err != nil {
panic(err)
}
var jsonData Webhook//jsonの形式に合わせたstructを初期化
if err := json.Unmarshal(b, &jsonData); err != nil {//jsonをパースして構造体に値を入れる。
panic(err)
}
//Webgook Webhookの構造体
type Webhook = struct {
ID json.Number `json:id`//これがID。受注番号とは違う。
Gateway string `json:gateway`
FinancialStatus string `json:financial_status`//支払いステータス 読み取り専用
}
Shopify Admin APIを叩いてTransactionを追加する
Shopifyの受注データの支払いステータスは読み取り専用で直接変更できない。変更するためにはTransactionという支払いデータを追加して、そのステータスをsuccess
に設定する必要がある。具体的にはOrderのIDを使ってtransactionを追加する。
ntr := goshopify.Transaction{
ParentID: &tr[0].ID,//親のトランサクションID、受注時に正成される
Status: "success",
Kind: "sale",//必須項目
Amount: tr[0].Amount,
Gateway: tr[0].Gateway,
Currency: tr[0].Currency,
OrderID: orderID,
SourceName: "external",
Source: "external",//ここが今回のハマりポイント
}
rtr, err := client.Transaction.Create(orderID, ntr) //API叩いて追加
ここで今回最大にハマったのが、この**Source
ってパラメータ。なんと公式ドキュメントに書いてないw**
なのに必須要素で、**'external'**に指定しないとTransactionが追加できない。公式のドキュメントにも載ってないのでgo-shopifyにも定義されてないw
仕方が無いのでgo-shopifyをいじって、Source
を設定できる様にして解決。そのPR
これで無事、受注ステータスをShopify Appから変更できる様になりました。いやー、最後の最後でハマった...
環境はまたGAE使いました
Goの簡単な環境作るならこれがGoogle Cloud PlatformのApp Engineが一番楽ですね。運用も楽だし、速い、安い。
[PR]株式会社クロスキューブではShopifyアプリの開発も請け負っております!
最後に、弊社株式会社クロスキューブでは、Shopifyアプリ開発やShopifyを使ったECサイト構築のお仕事も大募集中です!
何かShopifyアプリでお困りの方がいらっしゃいましたら**ぜひお問合せ願います**。