Claude Media
Claude CodeでShopify Scriptを書き換える — 廃止後のFunctions移行手順

Claude CodeでShopify Scriptを書き換える — 廃止後のFunctions移行手順

2026年6月30日に廃止されたShopify ScriptsをFunctionsで作り直す手順です。Rubyの3種類のScriptをAPIに対応づける表と、Claude Codeで棚卸しして入力JSONで挙動を確かめる流れをまとめます。

Claude CodeでShopify Scriptを書き換える — 廃止後のFunctions移行手順

Shopify Scriptsは2026年6月30日に廃止されました。その時点で公開されたままだったScriptは無効化され、動かなくなっています。Script Editorで作ったRubyの割引・配送・支払いのロジックは、Shopify Functionsで作り直すしかありません。

書き換えの難所は、Rubyの1行ごとの意味を失わずにFunctionsのAPIへ振り分けることです。Claude Codeには、Scriptの棚卸しと入力JSONの作成を任せられます。この記事では、対応表、棚卸しの自動化、入力JSONでの挙動の突き合わせを順に扱います。

廃止後のScriptは止まっている — まず何を見るか

Script Editorのヘルプには、廃止日以降は公開中だったScriptが無効化されたと明記されています。移行の入口は、管理画面の「Shopify Scriptsカスタマイズレポート」です。廃止前に動いていたカスタマイズが、Payment gateways、Shipping、Product discountsの3区分で並びます。

レポートにはScriptの名前、説明、ソースファイルへのリンクが載ります。推奨アプリと、Functionsのチュートリアルへのリンクも付きます。CSVでの書き出しにも対応しています。不要になったカスタマイズは、作り直さずに外してかまいません。

作り直す方法は2通りです。Functionsで作られた既存アプリを入れるか、自前でFunctionsを書くか。後者で押さえておく条件が2つあります。

  • Functionsを使うアプリはApp Bridgeが前提で、管理画面で作るカスタムアプリは対応しません。自作する場合はDev Dashboardで作ってストアにつなぎます
  • Plusプラン以外のストアでは、App Storeで配布される公開アプリに含まれるFunctionsだけが使えます。カスタムアプリのFunctions APIはPlusプランが条件です

自前で書くと決めたところから、Claude Codeの出番です。

Rubyの3種類のScriptはFunctionsのどのAPIに当たるか

Scriptの種類は3つです。行の価格を変えるラインアイテムScript、配送料金を扱うシッピングScript、支払い方法を扱うペイメントScript。FunctionsはこれをAPI単位に分け直しており、1対1ではなく、操作ごとに行き先が違います。

やりたいことScriptのメソッドFunctions APIターゲット / 操作
商品・注文の値引きScriptのメソッドchange_line_priceFunctions APIDiscountターゲット / 操作cart.lines.discounts.generate.run の productDiscountsAdd / orderDiscountsAdd
送料の値引きScriptのメソッドapply_discountFunctions APIDiscountターゲット / 操作cart.delivery-options.discounts.generate.run の deliveryDiscountsAdd
配送方法の名称変更・非表示・並べ替えScriptのメソッドchange_name / delete_if / sort!Functions APIDelivery Customizationターゲット / 操作rename / hide / move
支払い方法の名称変更・非表示・並べ替えScriptのメソッド同上(payment_gateways)Functions APIPayment Customizationターゲット / 操作rename / hide / move
行の価格の上書き・バンドル化Scriptのメソッド(Scriptでは不可)Functions APICart Transformターゲット / 操作lineUpdate など

この表のうち、メソッドとAPIの組み合わせは、各APIの用途説明から対応づけたものです。Shopifyが「このメソッドはこのAPI」と1対1で定めた表ではありません。

注意点が3つあります。

送料の値引きは、配送のカスタマイズではなく割引APIの担当です。Delivery Customizationが扱うのは名称・順序・表示の3つで、金額には触れません。apply_discount をそのままDelivery Customizationに移そうとすると、行き先がありません。

Cart Transformの価格上書きは、移行の逃げ道にしにくい操作です。 lineUpdate を使うアプリは、開発ストアかPlusプランのストアでしか使えません。Scriptは行の値下げしかできなかったので、同じ挙動の再現なら割引APIで足ります。

各APIとも、ストアで有効にできる関数は25個までです。割引、配送のカスタマイズ、支払いのカスタマイズで、それぞれ上限があります。Cart Transformはアプリごとに1つです。

書き換えで挙動が変わる4点

同じ割引を作ったつもりでも、動く仕組みが変わると結果がずれます。移行前に洗い出しておく差は次のとおりです。

論点ScriptsFunctions
同時に動く数Scripts種類ごとに1本だけ公開FunctionsAPIごとに最大25個
割引同士の関係Scripts1本のScriptの中で直列に処理Functions各関数は互いを知らず並行実行
割引コードScriptsdiscount_code は1つだけ返すFunctionsenteredDiscountCodes で入力できる
メタフィールドScripts読めないFunctions入力クエリで取得できる

最も壊れやすいのは2行目です。Scriptでは、1本の中で「VIPなら10%引き、さらに500円引き」と順に書けました。Functionsの割引は互いを知らないまま並行に動き、組み合わせはディスカウント側の併用設定で決まります。ロジックを1つの関数に詰め込むか、複数の割引に分けて併用設定を整えるかは、移行前に決めておく論点です。

3行目も見落としやすい差です。ラインアイテムとシッピングのScriptは、複数の割引コードを併用する注文に対応していませんでした。Functions側は入力で複数のコードを受け取れるので、Rubyの条件分岐をそのまま移すと、コードが複数入った注文でだけ結果が変わることがあります。

Scriptの棚卸しをClaude Codeにやらせる

Scriptが10本以上あるなら、まず台帳を作ります。Claude Codeの非対話モード(claude -p)は、--output-format json と --json-schema を組み合わせると、決めた形のJSONで結果を返します。構造化された出力は structured_output に入るので、jq で取り出せます。

次のスクリプトは、scripts/ に置いたRubyファイルを1本ずつ読ませる例です。読むだけなので Read だけを許可し、--bare で手元のフック類を読み込まない状態にしています。

for f in scripts/*.rb; do
  claude --bare -p "$f を読み、種類と操作を分類して" \
    --allowedTools "Read" \
    --output-format json \
    --json-schema '{"type":"object","properties":{
      "file":{"type":"string"},
      "kind":{"enum":["line_item","shipping","payment"]},
      "methods":{"type":"array","items":{"type":"string"}},
      "uses_discount_code":{"type":"boolean"},
      "risk":{"type":"string"}},
      "required":["file","kind","methods"]}' \
    | jq -c '.structured_output'
done > inventory.jsonl

inventory.jsonl ができたら、先ほどの対応表で行き先を振ります。ここでの狙いは、Rubyのロジックを機械的に写すことではありません。どのScriptが、どのAPIの、何個の関数になるかを先に決めることです。risk の欄に「割引コードに依存」「並び順に依存」といった注記が出ていれば、前節の差に当たるScriptです。

台帳を作る段階で、Claudeの判定を鵜呑みにしないでください。分類を誤ると、後の関数がAPIごと違ってしまいます。methods に挙がったメソッド名が実際のRubyに出てくるか、grep で突き合わせるのが確実です。

CLAUDE.mdに移行のルールを書いておく

Functionsの書き方そのものは、Claude CodeでShopify Functionsを書く手順で扱っています。移行の場合は、そこに「Scriptを正とする」ルールを足します。

# Shopify Scripts から Functions への移行
## 前提
- 元の Ruby は scripts/*.rb。動作の正はこのファイルの挙動とする
- Functions の入出力は shopify app function schema が書き出す schema.graphql に従う
## 手順
- 1 つの Script を移すたびに、test/<script名>/ に入力 JSON を 3 件以上置く
  (対象になる / 対象外 / 空のカート)
- 各入力を shopify app function run で実行し、出力を期待値と見比べる
- Ruby にあって schema に対応するフィールドが見つからない条件は、
  推測で実装せず「未対応」と報告する
## 禁止
- shopify app deploy は実行しない

最後から2つ目の「未対応と報告する」が要です。Rubyの product.gift_card? に当たるフィールドは、割引Function APIのページの入力例に出てきません。Claudeに任せると、それらしい名前のフィールドを書いてしまいがちです。schema.graphql に存在するかを確かめさせ、無ければ止まらせます。

古い言語のスクリプトを読み解く作業そのものは、Claude CodeでPerlの古いスクリプトを読解する記事と同じ考え方で進められます。先に仕様を文章にしてから移すと、取りこぼしが減ります。

入力JSONで挙動の差を確かめる

具体例で流れを見ます。移行対象は、VIPタグの顧客だけ、ギフトカード以外の行を10%引きにするラインアイテムScriptです。

customer = Input.cart.customer
if customer && customer.tags.include?('VIP')
  Input.cart.line_items.each do |line_item|
    next if line_item.variant.product.gift_card?
    line_item.change_line_price(line_item.line_price * 0.9, message: 'VIP 10% OFF')
  end
end
Output.cart = Input.cart

Functions側の入力クエリは、顧客のタグ判定とカート行のIDがあれば足ります。割引APIの公式サンプルには、顧客の hasAnyTag(tags: ["VIP"]) を入力で取る例があります。

query Input {
  cart {
    lines { id }
    buyerIdentity { customer { hasAnyTag(tags: ["VIP"]) } }
  }
  discount { discountClasses }
}

ギフトカードの除外はここに入っていません。前節の理由で、フィールドの有無を schema.graphql で確かめてから足します。

入力JSONは、Rubyの分岐を1つずつ踏む形で用意します。

ケース入力の特徴期待する出力
A入力の特徴VIPの顧客、通常商品1行期待する出力10%の商品割引が1件
B入力の特徴VIPではない顧客期待する出力operations が空
C入力の特徴カートが空期待する出力operations が空

ケースAの入力JSONは次のようになります。

{
  "cart": {
    "lines": [{ "id": "gid://shopify/CartLine/1" }],
    "buyerIdentity": { "customer": { "hasAnyTag": true } }
  },
  "discount": { "discountClasses": ["PRODUCT"] }
}

関数をビルドして、ケースごとに実行します。

shopify app function build
shopify app function run \
  --export cart_lines_discounts_generate_run \
  --input test/vip-discount/case-a.json --json

ケースAの出力は、次のような形になっていれば期待どおりです(設計上の期待値で、実行結果ではありません)。

{
  "operations": [{
    "productDiscountsAdd": {
      "candidates": [{
        "message": "VIP 10% OFF",
        "targets": [{ "cartLine": { "id": "gid://shopify/CartLine/1" } }],
        "value": { "percentage": { "value": "10.0" } }
      }],
      "selectionStrategy": "FIRST"
    }
  }]
}

複数行のカートでは、候補を行ごとに分けるか、selectionStrategy をどうするかで結果が変わります。1行のケースで形を固めたあとに、2行以上のケースを足し、スキーマの定義と出力を見比べます。ここはClaudeに推測させず、schema.graphql を読ませたうえで書かせる箇所です。

切り分け

ケースBで割引が出たときの切り分け

  1. Q入力JSONのhasAnyTagがfalseになっているか?
    答えがいいえのとき

    falseなのに割引が出るなら、関数が入力のタグ判定を見ていません。入力クエリと条件分岐の対応を読み直します。

    答えがはい
  2. QdiscountClassesに対象のクラスが含まれているか?
    答えがいいえのとき

    クラスが合わないケースの扱いが、Rubyの挙動と食い違っている可能性があります。含まれないときに空を返す実装かを確かめます。

    答えがはい

Rubyとの分岐が一致した

次はVIP以外の割引と、併用する別の割引を加えたケースへ進みます。

金額と併用の差は、本番に近い入力で確かめる

手書きの入力で確かめられるのは、条件分岐の対応までです。金額の丸めや、他の割引との併用は、手書きの1行カートでは現れません。

Rubyでは Money オブジェクトが金額を計算していました。Functionsの割引は割合や固定額を返す形で、最終的な金額はShopify側が計算します。計算の主体が変わるので、複数行・複数個・端数の出る金額のケースを足して、Scriptで実際に請求していた金額と見比べます。手元に過去の注文があれば、その入力を再現したJSONが最も説得力のある期待値になります。

併用の確認は、関数の外側の話です。割引同士の組み合わせは、ディスカウント側の併用設定で決まります。function run では1つの関数の出力しか見えないため、併用の結果はテスト用のストアで実際にカートを組んで確かめる必要があります。

よくある質問

元のRubyのソースはどこから取り出すのか

カスタマイズレポートの各区分に、廃止前にアクティブだったカスタマイズの名前とソースファイルへのリンクが載ります。そこから元のRubyを取り出して、scripts/ に保存します。

既存アプリで済む場合、Claude Codeは要らないのか

要りません。カスタマイズレポートには、各Scriptに合う推奨アプリが出ます。アプリで満たせるものはアプリに任せ、レポートに該当がないロジックだけを自前の関数にする使い分けが現実的です。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/headless
  2. shopify.dev/docs/apps/build/functions
  3. shopify.dev/docs/api/functions/latest/discount
  4. help.shopify.com/en/manual/checkout-settings/script-editor/transitioning-to…
  5. help.shopify.com/en/manual/checkout-settings/script-editor/shopify-scripts
この記事を共有:XはてブLinkedIn