Shopifyエージェンシー向け
Shopify B2Bの見積アーキテクチャ:Shopifyに置くものと見積レイヤーに置くもの
著者 Jahangir Alam · 2026年9月20日 · 約16分で読めます
- 最終確認日
- Shopify API
- 2026-07
- 対象読者
- Shopifyエージェンシー、開発者、ソリューションアーキテクト
- 範囲
- ネイティブのShopify B2Bオブジェクト、見積レイヤー(アプリ、独自開発、CPQ)、そして両者をまたぐ2つの受け渡し
Shopifyがすでにオブジェクトを持っているもの(商品、バリエーション、在庫、顧客、会社、所在地、カタログ価格、支払い条件、下書き注文、注文)については、Shopifyをそのまま正の記録系にしておきましょう。
見積レイヤーに渡すのは、Shopifyにオブジェクトがないものだけです。バイヤーの依頼、売り手の提案とそのバージョン、カウンターオファー、承認、交渉の監査証跡、そして合意した時点での価格。見積レイヤーはShopifyをリアルタイムで読み取り、コピーではなく参照と日付付きのスナップショットを保存し、価格が確定したらShopifyに下書き注文を1つだけ渡します。アーキテクチャはこれで全部です。このページの残りはその理由と仕組みで、Shopify自身のドキュメントと照合してあります。
これはB2B構築を検討するエージェンシーや開発者向けの文章で、アプリを選ぶマーチャント向けではありません。見積レイヤーがアプリでも、独自開発でも、CPQでも同じように当てはまります。3つのどれを選ぶかは判断ガイドの仕事です。QuotWayでの実装を説明している箇所は、パターンの一例として書いており、パターンそのものではありません。
ルール:2つ目のコマースデータベースを作らない
B2B見積のプロジェクトはどれも、早い段階で同じ誘惑に出会います。見積には商品名、価格、顧客名、会社情報、在庫数が必要なので、見積システムが商品・価格・顧客の独自テーブルを持ち始め、四半期もすればストアには食い違う2つのカタログ、2つの価格表、2つの顧客レコードができあがります。ShopifyのネイティブB2Bモデルはそれらのオブジェクトをすでに所有しており、誰にも知らせずに変更します。マーチャントがカタログ価格を編集する、所在地に2つ目のカタログが割り当てられる、バリエーションが削除される、連絡先が別の所在地に移る、といった具合です。
これを避けるルールは短いものです。Shopifyは自分が持つオブジェクトをすべて所有する。見積レイヤーはShopifyにないオブジェクトだけを所有する。見積レイヤーがShopifyの値(提案PDFに載せる商品名、交渉の起点になった価格)を持たなければならない場合は、ShopifyのIDを添えた日付付きのスナップショットとして持ちます。レコードが語るのは「3月14日時点でカタログはこう言っていた」であり、「これが価格だ」ではありません。
ネイティブのモデルは見た目より狭く、だからこそ分割がきれいになります。ShopifyのB2Bオブジェクトは、会社、会社所在地、会社連絡先、カタログ(商品の選択と価格表、数量ルール、数量別価格)、下書き注文で、実際の販売先は所在地です。カタログ、支払い条件、免税、チェックアウト設定はすべて所在地に紐づきます。これらのどのオブジェクトも、価格の依頼、バージョン番号付きの提案、カウンターオファー、承認、誰が何を変えたかの履歴を保持できません。下書き注文は、支払いや承認を待つ注文の記録であって、それを生み出した交渉の記録ではありません。Shopify B2B技術リファレンスにオブジェクトモデルの全体があります。
どのシステムが何を所有するか
この表がアーキテクチャです。各行に所有者、見積レイヤーが保持してよいもの、その理由を示します。
| データ | 正の記録系 | 見積レイヤーが保持するもの | 理由 |
|---|---|---|---|
| 商品とバリエーション | Shopify | 商品IDとバリエーションID。加えて提案のバージョンごとに更新する商品名のスナップショット | 3月の提案は9月に読んでも正しく読めなければならない。IDが実際の商品との紐づけを保つ |
| 在庫 | Shopify(ERPがあればERPから供給) | 何も持たない。変換時に読み取り、ずれがあれば警告する | 在庫は毎時変わる。コピーは書いた瞬間に古くなる |
| 顧客の身元とログイン | Shopifyの顧客アカウント | 顧客ID。ゲストの場合はメールアドレスと、後で顧客レコードに紐づけるための仕組み | 認証はShopifyが所有する。B2Bバイヤーは現行の顧客アカウントにワンタイムコードでサインインする |
| 会社、所在地、連絡先 | Shopify B2B | 3つのID | Shopifyではカタログ、支払い条件、免税が所在地に紐づく。会社を複製すれば、それら全部を複製することになる |
| 標準価格、数量ルール、数量別価格 | Shopifyのカタログ | 交渉の起点となった価格。提案時にバイヤーの所在地向けに解決し、カタログIDを出所として添える | カタログ価格は変わり、複数カタログでは最安が採用される。保存したコピーはそれを知りようがない |
| 支払い条件 | Shopify B2B | 案件で選んだ条件テンプレートへの参照。下書き注文の作成時に適用する | 条件は注文に属し、Shopifyがそれに基づいて回収する |
| 前受金 | Shopify(Plus) | 案件で交渉したパーセンテージ。提案と一緒にバージョン管理する | 変換された下書き注文のShopifyチェックアウトで回収される |
| 税 | Shopify | 提案上は概算。変換時とチェックアウト時にShopifyが再計算する | 税は所在地の免税設定と、チェックアウト時点の配送先住所で決まる |
| 見積の依頼 | 見積レイヤー | すべて。明細、数量、希望価格、バイヤーのメッセージ、カスタム項目、発注書番号 | Shopifyに依頼オブジェクトはない |
| 提案とバージョン | 見積レイヤー | すべてのバージョン。送信後は変更不可 | 送信済み提案の明細はロックされる。変更は新しいバージョンかカウンターであり、その場での編集はない |
| カウンターオファー | 見積レイヤー | 各カウンターを独立したバージョンとして、提示者とともに | 交渉こそが製品であり、その履歴が証拠になる |
| 承認 | 見積レイヤー | ポリシー、各提案が通った承認の連鎖、判断ごとの承認者 | Shopifyのスタッフ権限は営業担当が見られるアカウントの範囲を決めるもので、値引きを出してよいかを決めるものではない |
| 監査証跡 | 見積レイヤー | すべての状態変化、実行者、タイムスタンプ | エージェンシーは1年後に「この価格を誰が承認したのか」と聞かれる |
| 承諾された注文 | Shopifyの下書き注文、その後は注文 | 下書き注文IDと注文ID。見積に書き戻す | 注文はShopifyのもの。見積はそれを指すだけ |
| フルフィルメント | ShopifyとERP | 何も持たない | 見積レイヤーの仕事は注文で終わる |
2つの行はもう一度見る価値があります。プロジェクトがつまずくのは、価格と身元の行だからです。
見積レイヤーが価格を所有してはいけない理由
Shopify B2Bの見積で最もよくあるアーキテクチャの誤りは、「そこから交渉できるように」とShopifyやERPから価格表をインポートして、見積アプリが独自の価格表を持つことです。Shopifyのカタログには、そのコピーを数日で誤ったものにする3つの性質があります。
第一に、会社所在地は複数のカタログを持てて、同じ商品が複数に含まれるときShopifyは最安の価格を表示します。再現すべき優先順位の設定は存在せず、唯一の正解は、その所在地に対してその時点でShopifyが計算した値です。第二に、数量別価格は商品価格を固定し、その商品に対するカタログ全体のパーセンテージ調整を無効にします。コピーが計算する「定価の20%引き」は、バイヤーが見ている価格ではありません。第三に、カタログは絶えず編集されます。マーチャントが、ERP同期が、マーケットや通貨が変わればShopify自身が編集します。Plus以外ではストアはB2Bマーケット全体で有効3件のカタログに制限され、所在地への直接割り当てもできないため、その3件は頻繁に変わります。カタログのガイドではマーチャント側の視点で解説しています。
APIは見積レイヤーに、コピーではなく正しいプリミティブを提供しています。ProductVariant.contextualPricing は companyLocationId、country、locationId のコンテキストを受け取り、「すべての調整を適用した後の最終価格」を、そのコンテキストで適用される数量ルールと価格段階とともに返します。正しいパターンは次のとおりです。
- 提案を書くとき、各バリエーションの価格をバイヤーの会社所在地に対して
contextualPricingで解決する。見積の通貨で行う。 - その数値を、交渉の起点となる基準価格として提案の明細に保存し、出所としてカタログIDを添える。
- 基準価格から交渉する。希望価格、提示価格、最終価格は見積レイヤー自身のデータである。Shopifyにはそのためのオブジェクトがないからだ。
- 交渉済みの価格をカタログに書き戻さない。カタログは所在地の標準価格であり、案件は1つの注文である。
QuotWayの会社対応の見積はまさにこれを行っています。会社所在地のカタログ価格は提案送信時にサーバー側で解決され、見積の通貨で基準価格として明細に保存され、Shopifyのカタログ IDがバージョンに記録されます。価格表をインポートすることはありません。同じルールが「ERPは見積アプリに価格をプッシュすべきか」という問いにも答えます。答えはノーです。ERPはShopifyのカタログに価格をプッシュし、見積レイヤーは他のあらゆるチャネルと同じようにそこから読み取ります。
交渉済み価格はどうやって注文に届くか
こちらは逆方向の受け渡しで、初めて変換を実装するチームがつまずく2つの細部があります。
1つ目は、下書き注文は人ではなく会社に対して作られる、という点です。draftOrderCreate は { purchasingCompany: { companyId, companyLocationId, companyContactId } } という形の purchasingEntity を受け取り、Shopifyの下書き注文のドキュメントには、B2B顧客と会社所在地が割り当てられた下書き注文は「その会社の設定を自動的に反映する」(価格、支払い条件、チェックアウトの選択肢)とあります。この文は管理画面で作成した下書きについて述べたものです。「APIがカタログから明細の価格を付けてくれる」と読まないでください。variantId を付けて作成した明細はShopifyが価格を付けますが、2023年9月から同じ質問を集め続けているShopifyコミュニティのスレッドが典型的な結果を示しています。会社向けのカタログ価格が8ドルの商品が、10ドルの商品価格で下書きに載るのです。
2つ目の細部は、合意した数値を運ぶフィールドです。DraftOrderLineItemInput.priceOverride は「明細の価格上書き」で、表示通貨で設定するものであり、バリエーション明細で交渉済み価格を通す必要があるのはこのフィールドです。originalUnitPrice は非推奨で、variantId があるときは title、taxable、requiresShipping は無視されます。カスタム明細(バリエーションなし)は逆で、独自の名称、価格、課税フラグ、配送フラグを持ちます。
したがって変換の手順は次のとおりです。
- 承諾された提案バージョンから下書き注文の入力を組み立てる。見積の会社・所在地・連絡先IDから
purchasingEntityを、承諾された各明細にvariantId、数量、最終単価のpriceOverrideを、カスタム明細には独自の価格を、さらに発注書番号、案件で選んだ支払い条件、Plusなら前受金のパーセンテージを。 - まず
draftOrderCalculateを実行し、Shopifyの小計、税、送料を提案の数値と比較する。税と送料が異なるのは正当な理由による。Shopifyは提案を書いた時点ではなく今、所在地の免税設定と配送先住所に対して計算するからだ。目的はずれを記録し、人の判断が必要なケース(すでに存在しないバリエーション、Shopifyが違う価格を付ける明細、なくなった在庫)を捕まえることにある。 - 冪等キーを付けて下書き注文を作成し、再試行されたリクエストや2つ目のワーカーが1つの承諾に対して2つの下書き注文を作れないようにする。
draftOrderInvoiceSendで請求書を送るか、Shopifyが返すチェックアウトURLでバイヤーに支払ってもらう。下書き注文は支払われるかdraftOrderCompleteで完了したときに注文になる。指示しない限り在庫は確保されず、価格もロックされない。- 作成時に下書き注文IDを、
orders/createウェブフックが届いたら注文IDを見積に書き戻す。ここから先、見積はShopifyのレコードを指すだけで、何かの情報源であることをやめる。
QuotWayの変換サービスはこの通りに作られています。draftOrderCalculate によるプレビューで税・送料・在庫のずれを見積の数値に対して記録し、draftOrderCreate の前に冪等キーと並行作成の排他を置き、バリエーション明細には priceOverride、カスタム明細には originalUnitPriceWithCurrency を使い、注文IDは orders/create ウェブフックから取り込みます。見積時点と変換時点の間で何が壊れうるかは別のテーマです。現時点では下書き注文の制限に関する記事がShopify側を、公開前テストプランがそれを捕まえるテストを扱っており、障害モードに特化した記事も予定しています。
身元:顧客、連絡先、ゲスト
バイヤーのログインを所有するのはShopifyです。B2Bバイヤーは会社所在地のメールアドレスとワンタイムコードで現行の顧客アカウントにサインインします。従来の顧客アカウントは2026年2月26日に廃止され、そもそもB2Bに対応していませんでした。したがって見積レイヤーはパスワードの保管も、「従来アカウント」向けの経路も、独自のIDモデルも持つべきではありません。複数の所在地を持つバイヤーは価格を見る前に1つを選び、ストアが表示する価格はすべて所在地の価格です。
見積レイヤーが保持するのは顧客IDと、会社対応の見積なら3つの会社IDです。それ以上を正当に保持する唯一のケースがゲストです。アカウントを持つ前に見積を依頼するバイヤーや、顧客アカウントを有効化していないストアからの依頼です。この場合、見積レイヤーにはメールアドレスと、バイヤーに連絡する手段(署名付きリンク、独自のワンタイムコード)、そしてShopify顧客が存在するようになった時点で見積を紐づける手段が必要です。QuotWayはサインイン済みのバイヤーをShopifyアカウント内の顧客アカウントUI拡張機能へ、ゲストをホスト型ポータルへ誘導し、ゲストが後でサインインしたときにその見積を顧客レコードに引き継ぎます。どちらにするかはマーチャントではなく、Shopifyが報告する内容からストアごとに決まります。
支払い条件、前受金、発注書番号、通貨、税
これらは見積レイヤーのデータに見えて、そうではないフィールドです。
- 支払い条件はShopifyでは所在地に属し(Net 7からNet 90、フルフィルメント時払い、指定日、または条件なし)、注文はそれに基づいて回収します。見積レイヤーは案件で選んだ条件テンプレートへの参照を保存し、下書き注文の作成時に適用します。独自の売掛管理を持つことはありません。
- 前受金はShopify Plusの機能です。チェックアウト時に支払うパーセンテージで、残額は条件に従います。API 2026-07以降は
DraftOrderInput.depositで設定します。見積レイヤーの仕事はパーセンテージを交渉し、提案と一緒にバージョン管理することです。QuotWayはPlusストアでProfessionalプラン以上からこれを行い、Plus以外では決して行いません。 - 発注書番号は依頼時に取得して下書き注文に渡し、Shopifyが注文に表示します。
- 通貨は見積の作成時に、そのマーケットにおけるバイヤーの表示通貨で固定され、すべてのバージョンのすべての価格がその通貨になります。作成時の為替レートのスナップショットはレポート用に保持するもので、価格付けには使いません。Shopifyが見積レイヤーの代わりに換算することはありません。
- 提案上の税は概算です。Shopifyは
draftOrderCalculateの時点で、そしてチェックアウト時に再度、所在地の免税と配送先住所に対して実際の数値を計算します。だからこそ提案には「概算」と書き、変換ステップは差を防ごうとするのではなく記録すべきです。免税のガイドでマーチャント側の設定を扱っています。
ERPはどこに置くか
Shopify自身のB2Bドキュメントは、ERP、会計、PIMとの接続をB2Bアーキテクチャの一部として扱っており、エージェンシーの構築案件の多くにはERPがあります。所有の表が固まれば、配置の問いは見た目より簡単です。ERPはShopifyと統合するのであって、見積レイヤーとではありません。
- 在庫はERPからShopifyへ流れる。見積レイヤーは変換時にShopifyの在庫を読み、ERPを直接読むことはない。
- 標準価格はERPからShopifyのカタログへ、マーチャントが決めたスケジュールで流れる。見積レイヤーは
contextualPricingでカタログから価格を解決するので、ERPでの価格変更はカタログに届き次第、新しい提案に反映される。 - 注文はShopifyからERPへ、マーチャントがD2C注文ですでに使っているコネクタを通って流れる。変換された見積は、発注書番号、会社、支払い条件、場合によっては前受金を持つ普通のShopify注文であり、ERPは他の注文と同じように扱う。ShopifyのSpring ’26 Editionでは、B2B注文、発注書番号、会社情報をQuickBooksへネイティブに同期する機能が追加されたが、これも同じ形だ。
- 交渉そのものは見積レイヤーに残る。ERPやCRMが見積の送信・カウンター・承諾を知る必要があるなら、それは複製すべきレコードではなくイベントであり、Shopify Flowのトリガーがそのためのネイティブな仕組みだ。トリガーを発行する見積レイヤーなら、マーチャントは独自の統合なしに残りを配線できる。QuotWayはProfessionalプランからFlowトリガーを、EnterpriseでFlowアクションを提供する。ERPやCRMへの直接コネクタは持たないが、このアーキテクチャのおかげで、それがなくても収まる。
名前を挙げておく価値のあるERP主導のバリエーションが1つあります。契約価格がERPにあり、顧客ごとに毎週変わるマーチャントです。このマーチャントもやはり価格をShopifyのカタログへ同期します。ストア、チェックアウト、下書き注文がすべてカタログから読むからで、見積レイヤーも同じ場所から読みます。代わりにERPから価格を引く見積レイヤーは、このルールが防ごうとしている2つ目のコマースデータベースそのものです。
見積レイヤーがそれでも保持するもの、そしてそれがコピーではない理由
中央の列にある3つのものは、Shopifyデータの複製に見えます。しかしこれらはスナップショットであり、その区別が重要です。
- 提案明細の商品名とSKU。3月に送った提案PDFは商取引の文書です。バリエーションが6月に改名されても、3月の文書は送った時のまま読めなければなりません。だから商品名は新しいバージョンごとに更新し、そのバージョンと一緒に凍結します。
- 基準価格。交渉の起点となった数値は交渉履歴の一部で、カタログはその後も動きます。日付とカタログIDとともに保存し、そのバージョン以降は価格付けに二度と使いません。
- 最終価格。これらは下書き注文が存在するまではShopifyのデータですらありません。交渉の結果です。
スナップショットかコピーかを見分けるテストはこうです。スナップショットには日付とIDが添えられていて、「今の価格はいくらか」に答えるために読まれることはありません。コピーはあたかも最新であるかのように読まれます。見積レイヤーのすべての値がこのテストに合格すべきです。
アンインストール、消去、データの所在
見積レイヤーはShopifyが持つものを何も所有しないので、アンインストールしてもストアからは何も消えません。商品、顧客、会社、カタログ、下書き注文、注文はそのままです。見積レイヤーが保持するもの(依頼、バージョン、承認、メッセージ、文書)は、App Storeのすべてのアプリが実装しなければならないShopifyの必須コンプライアンスウェブフックに従います。shop/redact はアンインストールの48時間後に届き、アプリはそのストアのデータを消去しなければなりません。customers/redact は削除リクエストの10日後、または顧客の最終注文から6か月経過した時点で届きます。customers/data_request は、アプリがその顧客について保持しているデータを求めます。見積レイヤーを評価するエージェンシーは、これらのレコードがどこに保存され、保持期間はどれだけで、その期間内の再インストールでどう振る舞うかを尋ねるべきです。評価チェックリストには、それぞれの検証方法とともにこれらの質問があります。QuotWayの回答はデータ保持とアンインストールとGDPRのドキュメントにあります。
よくある質問
このアーキテクチャにShopify Plusは必要ですか
いいえ。2026年4月2日以降、会社、所在地、カタログ、数量ルール、数量別価格、支払い条件、保存済みカードはPlusだけでなくBasic、Grow、Advancedでも使えます。Plusが追加するのは無制限のカタログ、会社や所在地へのカタログ直接割り当て、前受金、部分支払いです。contextualPricing でカタログ価格を読み、purchasingEntity 付きで下書き注文を作る見積レイヤーはどのプランでも動作し、変わるのは前受金の行だけです。プランマトリクスにすべての差分があります。
下書き注文を見積オブジェクトとして使うべきですか
本来の用途で使ってください。支払いや承認を待つ合意済み注文の記録です。下書き注文には依頼、提案、バージョン、カウンターオファーの状態がなく、ロックと確保をしない限り価格も在庫も保持されません。マーチャントの案件が1往復で終わるなら(バイヤーが送信し、マーチャントが調整し、バイヤーが支払う)、Shopifyの「承認のために送信」チェックアウト設定がネイティブに下書き注文を作るので、見積レイヤーは不要です(見積アプリを使うべきでないときに他のケースもあります)。交渉、承認、保持すべき履歴が出てきたら、それを見積レイヤーでモデル化し、最後に下書き注文を作ります。
purchasing entityとは何ですか
下書き注文や注文の相手となるB2Bバイヤーのことです。draftOrderCreate では purchasingEntity: { purchasingCompany: { companyId, companyLocationId, companyContactId } } として渡します。会社、販売先の所在地、注文する連絡先です。これがあることで下書き注文はB2Bの下書き注文になります。所在地の支払い条件、免税、チェックアウト設定が適用され、バイヤーは会社アカウントでそれを確認できます。
見積レイヤーはカタログと衝突しますか
価格表を一切保存しなければ衝突しません。衝突は、アプリが独自の価格を持ち、それがストアの表示するカタログから乖離したときに起こります。提案時に contextualPricing で所在地の価格を読み、そこから交渉し、合意した価格を priceOverride として下書き注文の明細に書きます。カタログは手つかずで、注文は見積と一致します。所在地に複数のカタログがあるとき、依頼はどの価格から始めるべきかはカタログのガイドで扱っています。
バイヤーは送信後の見積を編集できますか
送信済みのものは編集できません。提案は番号付きのバージョンであり、送信した時点で変更不可になります。バイヤーが受け取った文書は商取引の記録だからです。バイヤーの一手はカウンターオファーで、新しいバージョンになります。売り手の一手は新しい提案です。送信済みのバージョンをその場で編集すると、監査証跡は双方が何に合意していたかを語れなくなります。
見積は自動で期限切れになりますか。下書き注文は
見積レイヤーは提案を指定日に期限切れにし、承諾リンクを止められます。それは見積レイヤーの状態です。下書き注文には独自の時計があります。指示しない限り在庫を確保せず価格もロックせず、一定期間動きがなければShopifyが下書き注文を削除します(下書き注文の制限に関する記事を参照)。下書き注文は見積を送った時ではなく、価格が合意された時に作ってください。
見積アプリをアンインストールしたら見積はどうなりますか
Shopifyのデータには何も起きません。アプリ自身のデータ(依頼、提案、承認、メッセージ)は、アンインストールの48時間後にShopifyが shop/redact を送った時点で、アプリが文書化している復旧期間を経て消去されます。必要なものは事前にエクスポートしてください。
出典
Shopifyのページ。いずれも2026年9月20日に確認:
- B2Bアプリの構築 - オブジェクトモデルとB2B向けの下書き注文
- B2Bカタログと数量ルールと数量別価格
- 下書き注文でB2B注文を作成する
- ProductVariant.contextualPricing、ProductVariantContextualPricing、ContextualPricingContext
- DraftOrderLineItemInput
- B2Bのサインインと顧客アカウントとアプリ向け顧客アカウント
- 支払い条件と前受金フィールドのchangelog
- プライバシー法への準拠 - 必須ウェブフック
- B2B for all - 発表、2026年4月2日とプラン別のB2B機能
- Shopify Editions Spring ’26
- 実務者の証言(事実の出典ではない):How to fetch prices from a catalog when creating B2B draft orders using APIs?(Shopifyコミュニティ、2023年9月〜2026年9月)
関連記事
QuotWayがこれをあなたのストアでどう扱うかをご覧ください。