Shopifyエージェンシー向け
見積の時点と変換の時点の間で壊れるもの:Shopify B2Bの見積を変換するときの17の失敗パターン
著者 Jahangir Alam · 2026年9月22日 · 約16分で読めます
- 最終確認日
- Shopify API
- 2026-07
- 対象読者
- Shopifyエージェンシー、開発者、ソリューションアーキテクト
- 範囲
- API 2026-07におけるdraftOrderCalculate、draftOrderCreate、DraftOrderInput、orders/create Webhook。承諾済みの見積とその下書き注文の間で何が変わるか、そして変換をそれに耐えるようどう作るか
バイヤーが見積を承諾した瞬間から、それに対応するShopifyの下書き注文が存在する瞬間までの間に、承諾された数字の下で17のものが変わりえます。カタログ価格、在庫、バリアントそのもの、税、配送料金、通貨、割引、所在地の支払い条件、バイヤーの身元、見積自体の有効期限、そしてそれらすべてを運ぶAPI呼び出しです。
どれもShopifyのバグではありません。それぞれは、下書き注文が作られる瞬間にShopifyが自分の所有するものを再計算している姿であり、見積レイヤーは合意された内容をそのまま持っています。この隔たりを想定していない変換は、請求書が届くまで誰も気づかないまま、間違った注文を出荷します。
このページはその隔たりをひとつずつ挙げます。各ケースでShopifyが何をするか、見積レイヤーがそれに対して何をしなければならないか、何を確認すべきか。そして、驚きを状態に変える2つの仕組みを説明します。封印されたバージョンと照合する事前計算と、2つ目の下書きを作らずに再試行できる変換です。変換ステップを構築または評価するエージェンシーに向けて書いています。見積レイヤーがShopifyに最も深く触れる地点であり、何がどこにあるかと下書き注文とは何かがアーキテクチャの話であることをやめ、火曜の朝のサポートチケットになる地点です。
以下のShopifyに関する記述はすべて、2026年9月22日にAPIバージョン2026-07でShopify自身のページと照合しました。出典は末尾にあります。QuotWayがあるケースをどう扱うかを説明している箇所は、このパターンの実装例のひとつであり、そのように明記しています。
そもそもなぜ隔たりがあるのか
見積は、二者が合意した価格を記録します。下書き注文は、Shopifyが請求する金額を記録します。前者はスナップショットで、後者は計算であり、Shopifyは毎回計算をやり直します。draftOrderCalculate は「DraftOrderを作成せずにその属性を計算し」、今渡した入力に対して、今のカタログ、在庫、税設定、配送料金に基づく明細の合計、配送料金、割引、税を返します。下書き注文は同じ入力から作られ、バイヤーが支払うときにチェックアウトがもう一度再計算します。
つまり、提案、変換、チェックアウトという3つの時点があり、後の2つの状態はShopifyのものです。見積レイヤーが3つの時点を通じて所有するのはただひとつ、承諾された内容の封印された記録です。各明細の価格、数量、通貨、支払い条件、そしてバイヤーが見た合計。変換とは、Shopifyにその記録を履行するよう求め、その間に何が動いたかを知る行為です。
失敗パターン
この表がページの本体です。各行は、提案の時点では真でなかったが変換の時点で真になりうる条件で、Shopifyがそれをどう扱うか、見積レイヤーが何をしなければならないか、どう検出するかを示します。「計算」とは draftOrderCalculate のことで、コストがかからず何も作成しません。
| 条件 | Shopifyの挙動 | 見積レイヤーがすべきこと | 検出方法 |
|---|---|---|---|
| 提案後にカタログ価格が変わった | 上書きのない明細は作成時のカタログ価格を取り、priceOverride のある明細は上書き価格を保つ。価格ロックされたB2B下書きはロックされた価格を保つ |
交渉済みのバリアント明細はすべて、ライブのカタログではなく封印されたバージョンから、表示通貨の priceOverride を付けて送る。書き込む価格の隣に、交渉の起点となった基準価格をマーチャントに見せる |
計算:各明細の originalUnitPriceSet を提案時に保存した基準価格と比較する。差はマーチャントへの情報であって、注文への変更ではない |
| 在庫がなくなった | reserveInventoryUntil を設定しない限り下書きは何も確保しない。下書きは作成されるが、バリアントが超過販売を許可していなければ、バイヤーのチェックアウトは在庫切れエラーで失敗する。確保した数量は設定した期限まで「コミット済み」になる |
確保するかどうか、どれだけの期間かを案件ごとに、提案時ではなく変換時に決める(提案時には何も押さえるべきでない)。どちらなのかをバイヤーに伝える | 計算:呼び出しの在庫関連のユーザーエラーがシグナル。変換の失敗ではなく、マーチャントに見せるずれとして扱う |
| バリアントの削除、または商品の非公開 | 明細に価格をつけることも在庫を確認することもできず、上書きを紐づけるバリアントがない | 計算の前にすべてのバリアントIDを解決する。解決できなくなった明細は、マーチャントが置き換える(カスタム明細または後継バリアント)か、バイヤーの同意を得て削除する。これは編集ではなく新しいバージョンになる | まず解決する。解決の失敗は計算の前のハードストップ |
| 税の再計算 | 税はストアの税設定と顧客の配送先住所に従い、所在地の免税は purchasingEntity を通じて適用され、taxExempt を下書きに設定でき、チェックアウトで再び再計算される |
税は見積もりとして示し、そう明言する。所在地の免税を下書きに引き継ぐ。計算された税を見積もりと比較し、差が大きければ変換を保留する | 計算:totalTaxSet を承諾済みバージョンの税の見積もりとパーセンテージで比較する。人が見るべきかはしきい値が決める |
| 配送 | 料金は availableShippingRates から来るが、有効な配送先住所と明細が必要。またはカスタムの shippingLine。請求書送信後に追加した商品は料金を更新しない |
確定した見積配送料はカスタムの配送明細として引き継ぐ。料金が未定のままなら変換時にShopifyの料金から選ぶ。明細が変わったのに提案時の料金のまま請求書を出さない | 計算:totalShippingPriceSet と料金一覧を見積額と比較する。差を見せ、マーチャントに選ばせる |
| 通貨 | 下書きごとに presentmentCurrencyCode はひとつ。priceOverride はその通貨で、currencyCode は計算を実行したストア通貨。支払い条件つきの多通貨下書きはカードまたは「支払い済みにする」でしか回収できない |
見積の通貨は作成時にバイヤーのマーケットから確定し、以後換算しない。下書きも同じ通貨で作る。為替レートはレポート用のスナップショットとしてのみ保持する | 作成前に presentmentCurrencyCode が見積の通貨と一致することを確認し、一致しなければ拒否する |
| 割引の重複 | acceptAutomaticDiscounts は計算時に自動割引を適用し、allowDiscountCodesInCheckout はチェックアウトでのコード入力を許し、appliedDiscount は注文または明細単位のカスタム割引を加える |
交渉済み価格はすでに割引である。自動割引とチェックアウトのコードをその上に重ねるかを明示的に決め(既定は「重ねない」)、見積単位の割引はひとつの appliedDiscount として、見積が分割されるなら下書きごとに配分して引き継ぐ |
計算:platformDiscounts と totalDiscountsSet はバージョンの内容と一致すべきで、それ以上は重複 |
| カスタム明細 | バリアント、在庫、商品ページがない。自動フルフィルメントや配送アプリでは扱えない。価格は originalUnitPriceWithCurrency、taxable と requiresShipping は入力どおり |
合意した価格とフラグを送る。これらの明細は手動フルフィルメントを前提にする。確保しようとしない | レビュー:カスタム明細のある下書きは、フルフィルメントで人が必要 |
| 支払い条件 | 条件は下書きにテンプレートとスケジュールとして設定される(Net条件は issuedAt、固定日は dueAt)。下書きは自身の条件を持ち、下書きに条件がなければ所在地の既定が適用される |
所在地の現在の既定ではなく、取引で決まった条件を書く。取引に条件がなければ所在地に委ねる | 承諾済みバージョンのテンプレートGIDがまだ存在することを確認する。削除されたテンプレートはハードストップ |
| 前受金 | 下書きのパーセンテージ(deposit)で、Shopify Plusで利用可能。チェックアウトがそれを回収し、残額をスケジュールする |
承諾済みバージョンから交渉済みのパーセンテージを引き継ぐ。Plus以外のストアでは前受金つきの下書きの作成を拒否する | 計算:amountDueNowSet は totalPriceSet の前受金分と一致すべき |
| 会社所在地の欠落または変更 | purchasingEntity は顧客または購買会社のどちらかで、両方にはならない。所在地のカタログ、条件、免税、チェックアウト設定は所在地が下書きにあるときだけ適用される。DraftOrderInput.customerId は2026-07で非推奨 |
会社、所在地、担当者のIDを見積に保存し、作成前に解決する。所在地を移った担当者や削除された所在地は停止であり、D2C下書きへのフォールバックではない | 3つのIDを解決し、解決した所在地を価格の導出元の所在地と比較する |
| 数量ルール | ルールはバリアントごとで、チェックアウトで再検証される。最小未満、最大超過、増分に合わない数量はそこで失敗する | 提案時に所在地のルールに対して数量を検証し、ルールは変わりうるので変換時にも再検証する | 所在地の contextualPricing から quantityRule を読む。違反は作成前の停止 |
| 見積の期限切れ、または下書きの削除 | 2025年4月1日以降に作成された下書きは、編集がないまま1年経つと削除される。見積の期限切れは見積レイヤーの状態 | 承諾時に一度だけ変換する。提案時に先回りして下書きを作らない。未払いのまま古びるだけになる | N日たっても注文のない下書きは突合の対象で、待つものではない |
| 一部承諾と分割 | 各下書き注文は独立しており、合計、税、配送明細、条件をそれぞれ持つ | 承諾された部分集合ごとに1つの下書き。配送と税は明細小計の比率で各下書きに配分するか、マーチャントが下書きごとに上書きする。未決の明細は見積に残る | 各下書きの小計を承諾済み部分集合の小計に足し戻す。差は端数処理かバグ |
| 作成呼び出しが途中で失敗 | draftOrderCreate は冪等キーを受け取らない。2回目の呼び出しは2つ目の下書きになる |
呼び出す前に変換を確保し、返ってきた瞬間に下書きIDを記録し、結果が不明な作成を決して自動再試行しない。表面化させる | 突合:計算はあるが下書きIDがなく数分間動きのない変換は、人が扱うケース |
| 注文が遅れて、二重に、または来ない | 注文はバイヤーが支払ったときに作られる。orders/create は接続1秒・全体5秒のタイムアウトで配信され、4時間で8回再試行され、保証はなく、重複もありうる |
注文IDは、下書きに自分で付けた属性で照合したWebhookから書く。ハンドラーを冪等にする。下書きに order があるかShopifyに尋ねる突合を実行する |
突合:請求書送信済みで1週間注文がなければ、下書きを問い合わせる |
| 請求書送信後に下書きが編集された | draftOrderUpdate は入力を丸ごと置き換え、進行中のチェックアウトとの紐付けを外す。下書きが未処理のまま注文が作られることがある |
送信後の下書きは決して編集しない。承諾後の変更は新しいバージョン、新しい承諾、新しい下書きになる | 見積レイヤー自身のルール。守られていれば検出するものはない |
以下の3行はより長い説明に値します。構築においてそこに設計がまったくないことが最も多いからです。税のずれ、作成呼び出し、Webhookです。
2つの数字と、ひとつのしきい値
表で「計算」とある行はすべて同じ仕組みです。これから作成するものとまったく同じ入力で draftOrderCalculate を実行し、その結果を封印されたバージョンと比較する。これで各項目に2つの数字、承諾された値とShopifyが請求する値が揃い、設計上の問いはそれらが違うときにどうするかになります。
うまくいく答えは、マーチャントが設定するしきい値を税に適用することです。税はShopifyが黙って再計算し、バイヤーが一度も交渉していない唯一の項目だからです。しきい値未満なら差を見積に記録して変換は進みます。しきい値を超えると変換は「計算済み」の状態で止まり、人が判断します。再計算された数字を受け入れる、調整する、バイヤーに戻す。配送のずれは表示されますが変換は止めません。どのみちマーチャントが料金を選ぶからです。在庫のずれは表示され、確保するか、管理画面で支払いを受けるか、待つかをマーチャントが決めます。
しきい値は間違いへの許容範囲ではありません。「Shopifyの税エンジンが提案より新しい情報を持っていた」が「バイヤーが気づく」に変わる地点です。提案から税が2パーセントずれたB2B請求書は端数処理の話ですが、20パーセントずれたなら、適用されなかった免税か、課税管轄を変えた配送先住所であり、送る前に誰かが見るべきです。
これを誠実にする細部が2つあります。比較は、見積オブジェクトが今持っている値ではなく、承諾時に封印された承諾済みバージョンの見積もりに対して行わなければなりません。そして見積が複数の下書きに分割されるときは、バージョンの税と配送を各下書きに(承諾済み小計に占める割合で)配分し、それぞれの比較が同じもの同士になるようにしなければなりません。
なぜ変換は冪等でなければならないか
draftOrderCreate には冪等キーがありません。同じ入力で2回呼べば、下書き注文が2つ、請求書が2通、片方を払ってもう片方に異議を唱えるバイヤーが1人できます。2回呼びうるものは、いずれ必ず2回呼びます。タイムアウトで再試行するジョブキュー、ダブルクリックするマーチャント、変換を起動した後に再配信されるWebhookハンドラー。
パターンは3つの部分からなります。第一に、変換ごとのキー(見積、承諾済み部分集合、バージョン)を呼び出しの前に一意制約つきで保存し、2回目の試行が1回目を見つけられるようにする。第二に、確保。変換の行は、リクエストが出る前に作成が進行中であることを記録し、応答が届いた瞬間に下書きIDを記録する。第三に、この2つの書き込みの間の隙間に対するルール。Shopifyが下書きを作成した後、IDが保存される前にプロセスが死んだ場合、変換を自動で再試行してはなりません。その再試行こそが重複だからです。オペレーターに表面化させ、オペレーターは属性から下書きを探して紐づけられます。
この最後のルールは独自開発が飛ばしがちなものです。人を必要とする状態があると認めることになるからです。同時に、表の中で最悪の結果を防ぐルールでもあります。
注文は戻り値ではなく、Webhookで戻ってくる
draftOrderCreate は下書きを返します。注文は返しませんし、返せません。注文はバイヤーが支払ったとき、またはマーチャントが管理画面で下書きを完了したときに初めて存在するからです。注文を運ぶイベントは orders/create で、Shopifyの条件で届きます。接続タイムアウト1秒、リクエスト全体で5秒、4時間で8回の再試行、保証なし、同じ配信が2回来る可能性。
作成呼び出しの成功時に自分を「注文完了」にする変換は、いつか放棄されたチェックアウトに注文を表示します。Webhookだけを信頼する変換は、いつか注文を見逃します。パターンは両方です。注文IDはWebhookからのみ書き、見積レイヤーが作成時に下書きへ書いた属性で下書きに照合する。ハンドラーは2回目の配信を何もしない処理として扱う。そして突合ジョブが、請求書を送ったのに1週間以内に注文が届いていない変換について、Shopifyに直接(draftOrder(id) { order { id } })尋ね、下書きに注文があれば見逃したイベントを再生する。キャンセルと返金も同じ道、orders/updated で戻り、同じ冪等性で処理します。
QuotWayは各行をどう扱うか
QuotWayの変換は、上のパターンに数字を入れたものです。すべての変換グループは draftOrderCreate の前に draftOrderCalculate で計算され、結果は承諾済みバージョンの数字をそのグループに明細小計の比率で配分したものと比較されます。税のずれはストアごとのしきい値(既定は2パーセント)に対するパーセンテージとして測り、これを超えると自動変換は計算済みの状態で止まり、マーチャントが引き継ぎます。在庫のずれは計算呼び出しの在庫関連ユーザーエラーから読み取り、変換を失敗させずにプレビューに表示します。それ以外のユーザーエラーは変換を失敗させます。見積の配送料はShopifyが計算した配送料と比較し、提案で料金が未定のままなら、Shopifyの availableShippingRates を提示してマーチャントがグループごとに選びます。
価格は、バリアント明細には priceOverride、カスタム明細には originalUnitPriceWithCurrency として、見積の通貨で下書きに届きます。見積の通貨は作成時にバイヤーのマーケットから確定し、換算されることはなく、presentmentCurrencyCode にも明示的に設定されます。見積単位の割引はひとつの appliedDiscount として書かれ、見積が分割されるときは各下書きに配分されます。支払い条件は、取引で決まったテンプレートと、そのテンプレートの種類が必要とするスケジュール(Net条件なら発行日、固定日なら期日)として設定され、交渉済みの前受金パーセンテージはPlusストアで下書きに載ります。会社を認識する見積では、所在地の税設定から免税を下書きに設定し、所在地に見えない明細は変換時に再度警告します。
各変換は見積、グループ、バージョンから作ったデータベース上で一意のキーを持ち、呼び出しの前に作成を確保します。途中で死んだ作成をシステムが再試行することはなく、オペレーター向けに一覧化されます。注文IDは orders/create からのみ書き込まれ、下書き上のひとつの属性(quotway_conversion_group_id)で照合されます。2回目の配信はすでに紐付けが済んでいることを見つけて何もしません。キャンセルと返金は orders/updated で届き、一度だけ記録されます。スイープが3つの止まった状態を閉じます。下書きIDがなく動きのない計算、7日経っても注文のない請求書送信済み(Shopifyに下書きに注文があるか尋ね、イベントを再生して解決)、そして1日誰も動かしていない計算済みグループです。マーチャント向けの説明は見積を下書き注文に変換すると一部承諾と分割変換にあり、変換機能のページは管理画面からの見え方を示しています。
あらゆる構築に使える事前チェックリスト
評価中のアプリでも独自開発でも、見積レイヤーの変換ステップに対して次のテストを実行してください。承諾と変換の間にカタログ、在庫、税、配送を変更できるテスト見積を使います。フィクスチャは50のテストからなる公開チェックリストにあります。
- 承諾後にカタログ価格を変えて変換する:下書きは承諾価格を持ち、マーチャントは基準価格が動いたことを見られる。
- 承諾後に在庫をゼロにして変換する:請求書を出す前にマーチャントに知らされ、確保または保留できる。
- 承諾済みバージョンのバリアントを削除して変換する:Shopifyを呼ぶ前に変換が止まり、該当明細を名指しする。
- 配送先住所を別の課税管轄に変えて変換する:税のずれが測られ、しきい値を超えれば人に確認が求められる。
- 提案で配送を未定のままにして変換する:マーチャントがShopifyの料金から選び、提案時の仮の値のまま請求書は送られない。
- 表示通貨で見積を出して変換する:下書きはその通貨で、各明細の上書きもその通貨になる。
- 見積対象の商品を含む自動割引を有効にして変換する:下書きの合計は承諾済みの合計と一致し、それより少なくならない。
- 承諾後に所在地にNet条件を設定して変換する:下書きは取引の条件を持つ。
- 真部分集合を承諾して変換し、さらに承諾して再度変換する:下書きが2つでき、未決の明細はそのまま、合計は承諾済み部分集合と一致する。
- 作成とデータベース書き込みの間でプロセスを止め、再試行する:2つ目の下書きはできず、ケースがオペレーター向けに一覧化される。
orders/createを2回配信する:注文IDはひとつ、状態変化もひとつ。orders/createを完全に遮断し、請求書を支払う:突合が注文を見つける。
12のうち12を通る構築は、隔たりを設計しています。最初の6つだけ通る構築は、正常系だけを設計しています。
よくある質問
Shopifyの注文の税が見積の税と違うのはなぜですか?
Shopifyは下書き注文の作成時とチェックアウト時に、その時点のストアの税設定と配送先住所から税を再計算するのに対し、見積は提案時の見積もりを持っているからです。住所の変更、購買主体を通じて適用されなかった免税、その間の税設定の変更のどれもが数字を動かします。対処は見積もりを正確にすることではなく(それは不可能です)、変換時に両者を比較し、しきい値を超えて違うときに変換を保留することです。
見積が間違った価格で変換されたのはなぜですか?
たいてい3つのうちのどれかです。明細が priceOverride なしで送られ、Shopifyがライブのカタログから価格をつけた。上書きが間違った通貨で送られた。自動割引やチェックアウトのコードが下書きで有効なままで、交渉済み価格の上に割引が重なった。計算結果の originalUnitPriceSet、priceOverride フラグ、明細ごとの platformDiscounts を承諾済みバージョンと照合してください。
承諾と変換の間に在庫がなくなったらどうなりますか?
Shopifyはそれでも下書きを作成し(reserveInventoryUntil を設定しない限り下書きは在庫を押さえません)、バリアントが超過販売を許可していなければ、バイヤーのチェックアウトは在庫切れエラーで失敗します。draftOrderCalculate は不足をユーザーエラーとして表面化させるため、それを読む変換なら先にマーチャントに警告できます。マーチャントは在庫を追加して確保するか、管理画面で支払いを受けるか、請求書を保留できます。
失敗した draftOrderCreate を再試行できますか?
Shopifyが下書きを作成する前に失敗したと分かっている場合だけです。このミューテーションには冪等キーがないため、結果が不明なまま再試行すると2つ目の下書きができることがあります。呼び出しの前に冪等キーと確保を保存し、返ってきた瞬間に下書きIDを記録し、不明な結果は再試行ループではなく人に回してください。
価格を固定するために、提案の時点で下書き注文を作るべきですか?
いいえ。下書きは指示しない限り価格を固定せず、確保しない限り在庫を押さえず、1年放置されると削除され、編集するとチェックアウトとの紐付けが外れます。提案時に作ると、変更可能で古びていくオブジェクトを交渉の真ん中に置くことになります。承諾時に一度だけ、封印されたバージョンから作ってください。
届いた注文がこの見積のものだとどう分かりますか?
見積レイヤーが作成時に下書きへ書いた属性を、orders/create のペイロードのノート属性から読み戻すことによってです。合計や顧客の一致で判断してはいけません。Webhookは二重にも遅れても届くため、ハンドラーは冪等でなければならず、突合ジョブは下書きに注文があるかをShopifyに尋ねられなければなりません。
分割変換では配送料が二重に請求されますか?
その可能性があります。各下書き注文は独立しており、それぞれ配送明細を持つからです。見積レイヤーは見積の配送料を下書きに配分し(承諾済み小計に占める各下書きの割合、またはマーチャントが設定する下書きごとの明示的な金額で)、各請求書にそのことを明記しなければなりません。
出典
Shopifyのページ。特に記載がなければ、いずれも2026年9月22日、APIバージョン2026-07で確認:
- draftOrderCalculate、CalculatedDraftOrder、DraftOrderWarning
- DraftOrderInput、DraftOrderLineItemInput、PurchasingEntityInput
- draftOrderUpdate、draftOrderComplete(2026年9月21日に確認)
- 下書き注文を作成する、下書き注文の請求書を送信する
- 下書き注文でB2B注文を作成する、B2Bの支払い条件を設定する(2026年9月21日に確認)
- 数量ルールと数量別価格(2026年9月21日に確認)
- Webhook、Webhook配信の検証(2026年9月20日に確認)
QuotWayの変換、ずれ、冪等性、突合の挙動はソースコードに基づいて記述しています。マーチャント向けの説明は上記リンクのドキュメントにあります。プランごとの違いは料金ページを、下書き注文と支払い条件に関する事実の最新状態はShopify B2Bリファレンスをご覧ください。
関連記事
QuotWayがこれをあなたのストアでどう扱うかをご覧ください。