Nordlet

← ブログ

SaaSプラットフォームへのクラウド会計APIの統合方法

元帳を破損させることなく、OAuth、Webhook、税務ルール、照合(reconciliation)に対応できるマルチテナント型会計統合の構築実践ガイド。

Nordlet Team · · 1 分で読める

SaaSプロダクトに「会計機能を追加する」と意気込むチームのほとんどは、まずプロバイダのエンドポイントにHTTPクライアントを接続し、請求書をPOSTすることから始めます。それはデモではうまく動きます。しかし、顧客が2つの会計組織を接続したとき、Webhookが重複して届いたとき、そしてリフレッシュトークンがローテーションしてリクエストの途中で失われたとき、初めて破綻します。この作業の本質は、単なるクライアントの作成ではありません。統合レイヤーの構築です。

このレイヤーは、テナント接続、トークンの保存とローテーション、プロバイダ固有の会計ルール、レコードのマッピング、初回および差分同期、リトライと重複排除を備えたWebhookの取り込み、レートリミット、バージョニング、そしてサポートチームが夜中の2時にも読める監査証跡(audit trail)を管理しなければなりません。この記事では、そのアーキテクチャと、最も重要となる意思決定について解説します。

有用なメンタルモデル:

SaaSプロダクト → 会計統合サービス → プロバイダアダプタ(または統合API) → 非同期の同期および照合(reconciliation)

会計システムは、勘定科目、税コード、計上された取引、仕訳エントリといった元帳オブジェクトの信頼できる情報源(system of record)としての役割を維持します。あなたのSaaSプラットフォーム側は、システム間の安定した識別子、同期状態、マッピングメタデータ、および運用上の監査ログを保持します。

ここで言う「クラウド会計API」が実際に指すもの

このフレーズは通常、QuickBooks Online、Xero、Sage、Zoho Books、あるいは地域特化型システムといった、ホスト型簿記プラットフォームのAPIを指します。また、複数のプロバイダを1つのインターフェースの背後で正規化する、統合会計API(unified accounting API)を指す場合もあります。

実装の意図によってアプローチが異なるため、正確な定義をしておく価値があります。レポート作成のために請求書を読み取る作業は、SaaSのアクティビティから請求書を作成したり、請求書(bills)を起票したり、顧客や商品を同期したり、仕訳エントリを作成したり、真の双方向同期を実行したりする作業とは異なります。エンドポイントに触れる前に、何を構築しているのかを決定してください。

このセクションでは、もう1つの重要な区別についても触れておきます。決済APIは資金を移動します。バンキングAPIは口座データを取得します。請求書作成APIは、総勘定元帳(general ledger)を公開することなくドキュメントを作成できます。一方、組み込み型会計元帳(embedded accounting ledger)はこれらとは異なります。SaaS企業自身が会計エンジンを所有するというアプローチです。この最後の方法に位置するのが、Nordlet です。Nordletは、顧客の外部会計システムにプロダクトを接続するのではなく、型付きSDKとWebhookを備えたAPIファーストの設計を通じて、プラットフォームやマーケットプレイスが自社プロダクト内に、本格的な複式簿記の帳簿、改ざん防止機能を備えた監査対応の元帳、EU VATの処理、およびペイアウト機能を持つことを可能にします。マーケットプレイスにおいて、顧客の会計システムを統合すべきか、それとも自社で帳簿を運営すべきか迷っている場合、それは重大な分かれ道であり、この点については後ほど改めて触れます。

マルチテナンシー: すべての接続を二重にスコープする

SaaSプロダクトでは、1つのアプリケーションが多数の顧客組織に接続します。すべての接続は、SaaSテナントと、XeroのテナントIDやQuickBooksのレルムID(realm ID)などのプロバイダ側の組織識別子の両方によってスコープされなければなりません。

OAuthのユーザーIDだけで接続のキーを管理してはなりません。1人のユーザーが複数の会計組織を認可することがあり、また1つの組織が時間の経過とともに異なるユーザーによって再接続されることもあるためです。この実装を誤ると、いずれある顧客の請求書を別の顧客の帳簿に誤って起票してしまい、それは静かに復旧できるようなバグではありません。

最初のアーキテクチャの意思決定: 直接コネクタ、統合API、または組み込み型元帳

直接プロバイダ統合(Direct provider integrations)

直接統合を行うことで、プロバイダ固有の機能、カスタムフィールド、エラーハンドリング、同期頻度、データ保持、コスト構造を最も細かく制御できるようになります。その代償は小さくありません。各プロバイダには、独自のOAuthフロー、スキーマ、クセ、レートリミット、Webhookの挙動、認定要件、継続的なメンテナンスが伴います。

統合会計API(Unified accounting APIs)

統合APIは、顧客、請求書、サプライヤー、勘定科目、支払い、商品といったエンティティを正規化し、複数の会計システムにわたる単一のインターフェースを提供します。初期の構築時間を短縮できる場合があります。ただし、統合の複雑さが完全に消え去るわけではなく、それを解消できると思い込むことはよくある間違いです。

決定を下す前に、以下を確認してください: どのプロバイダと操作がサポートされているか?書き込みが可能か、それとも読み取りのみか?書き込みは同期処理か、それともキューイングされるか?プロバイダ固有のフィールドにパススルー経由でアクセスできるか?データはキャッシュされるか、またその鮮度はどの程度か?プロバイダの障害やAPIの変更には誰が対処するか?料金体系はコール数、連携アカウント数、レコード数、あるいはプラットフォーム料金のいずれか?マッピングのエクスポートやクリーンな切断は可能か?「統合」という言葉が「すべてのプロバイダで機能が同等」を意味することはめったにありません。

組み込み型元帳(Embedded ledger)

プラットフォームの根本的な課題が、ユーザーの既存の帳簿にデータを反映させることではなく、ユーザーのために会計処理を自社で実行することにある場合、組み込み型元帳はその計算を根本から変えます。数千のセラーを抱えるマーケットプレイスや、監査対応のEU VATおよび国別コンプライアンスを内蔵する必要があるSaaSプロダクトにとって、N個の外部コネクタを配線するよりも、会計エンジンを自社で所有する方が得策です。それがNordletのポジショニングです。サードパーティのチェックアウトではなく、APIとしてのリアルな帳簿、改ざん防止の複式簿記、VIES検証とi.SAF登録生成を備えた国別VAT、SEPAエクスポート、そしてXLSX、PDF、またはJSONによるレポートエクスポートを、すべてプロダクト内で完結させます。

筆者が用いる意思決定ルール:

状況 最適な選択肢
1つの会計プロバイダが顧客基盤の大部分を占めており、プロバイダ固有の挙動が中心となる場合 直接コネクタ
コントロールよりも、幅広いプロバイダの網羅性と市場投入までのスピードが重要な場合 統合API
プラットフォームまたはマーケットプレイスであり、EU VATや監査証跡を備えた帳簿を自社で運用・所有する必要がある場合 組み込み型元帳(Nordlet)
混在している場合 ハイブリッド: 一般的なフローには統合APIを、戦略的プロバイダには直接コネクタを使用する

推奨される統合アーキテクチャ

統合機能をコアビジネスロジックから分離する

請求、注文、サブスクリプション、レポートの処理全体にプロバイダ呼び出しを散在させないでください。プロバイダアダプタ、OAuth接続管理、標準化モデル(canonical models)、IDマッピング、同期ジョブとキュー、Webhookエンドポイント、リトライおよびデッドレター処理、監査ログ、照合ツールを保持する専用の統合サービスを構築します。これにより、XeroやQuickBooksのフィールド名がドメインモデルに漏れ出すのを防ぎます。

狭く絞り込んだ標準化モデルを定義する

サポートするワークフローのみをモデル化します。一般的なオブジェクト: 顧客、商品またはサービスアイテム、請求書、請求書行、支払い、クレジットノート(貸方票)、サプライヤー、勘定科目、税率、通貨、仕訳エントリ。

会計に影響を与える違いは保持してください。下書き(draft)と承認済(approved)と起票済(posted)、税込みと税抜き、クレジットノートとマイナスの請求書、支払いの割り当て(allocation)と支払いの存在、あるいは顧客向けの請求書番号とプロバイダのオブジェクトIDを同一視してはなりません。各レコードには、SaaSのID、プロバイダのID、プロバイダ名とAPIバージョン、信頼できる情報源のインジケータ、ステータス、タイムスタンプ、最終同期時刻、外部参照、エラー・照合ステータスを持たせる必要があります。

明示的なマッピングテーブルを維持する

SaaSテナントと会計組織、SaaS顧客とプロバイダの連絡先(contact)、SaaS商品とプロバイダのアイテムまたは勘定科目、SaaS請求書とプロバイダの請求書、SaaS支払とプロバイダの支払、税カテゴリと税コード、収益カテゴリと元帳勘定をそれぞれマッピングします。

名前やメールアドレスを永続的な識別子として使用してはなりません。名前は変更され、重複が存在し、2人の顧客が同じメールアドレスを共有することもあります。プロバイダの不変のIDを保存し、元のSaaSのIDも保持してください。

ステートマシンを追加する

接続に明示的な状態を持たせます: 未接続、認可保留中、接続済み、初期同期保留中、同期中、正常、レートリミット到達、再認可要、プロバイダ利用不可、切断済み、永続的失敗。また、レコードにも独自のステータスを持たせます: 保留中、送信済み、確認済み、リトライ可能エラー、ユーザー対応が必要なエラー、照合済み、競合検知。二値の「接続済み/未接続」フラグでは、サポートチームにとって有用な情報は何も得られません。

OAuthと接続のオンボーディング

認可コードフロー(authorization code flow)を使用します。顧客が「接続」をクリックすると、サーバー側で有効期間の短い認可トランザクションが作成され、顧客はプロバイダ側で認証を行い権限(スコープ)を付与し、サーバー側でコードをトークンに交換し、認可された組織を検出し、正しいテナントに紐づけて接続を保存し、初期同期をキューに追加します。

コールバックの検証は慎重に行ってください: サーバー側のトランザクションに対する state の照合、正確なリダイレクトURI、プロバイダの発行者(issuer)、テナントコンテキストを確認します。RFC 9700, OAuth 2.0 Security Best Current Practice は、認可コードフローでのPKCEの使用、インプリシットグラントやパスワードグラントの回避、および厳密なリダイレクトURIマッチングの使用という現在のガイダンスを形式化しています。

最小権限のスコープを要求してください。書き込みが必要ない場合は、読み取り専用を使用します。スコープのモデルは変更されるため、古いサンプルをそのままコピーしないでください。XeroのOAuthドキュメントによると、広範な会計スコープは粒度の細かいスコープに置き換えられており、2026年3月以降の新規アプリには粒度の細かいスコープが割り当てられ、広範なスコープは2027年9月まで利用可能であるとされています。

トークンはサーバー側のみに保存します。ブラウザのJavaScript、モバイルクライアント、フロントエンドのログ、アナリティクス、または顧客向けのエラーメッセージに決して露出させないでください。保存時は暗号化し、ログからはマスクし、接続ごとのメタデータを保持します: 暗号化されたアクセストークンおよびリフレッシュトークン、有効期限、プロバイダ組織ID、付与されたスコープ、発行時刻、最後に成功したリフレッシュ、失効ステータス。

リフレッシュトークンのローテーションがプロダクトを壊すポイント

リフレッシュのレスポンスによって、新しいリフレッシュトークンが返される場合があります。最新の値をアトミック(不可分)に永続化しなければ、顧客がロックアウトされます。Xeroの開発者向けFAQによると、リフレッシュのたびに新しいリフレッシュトークンが生成され、リフレッシュトークンの有効期限は最大60日であり、レスポンスが失われた場合に既存のトークンで再試行するための30分間の猶予期間が設けられています。QuickBooks Onlineは、最新のレスポンスから得られた最新のリフレッシュトークンを保存するよう開発者に求めています。リフレッシュトークンの有効期限は最大100日であり、24時間に1回の頻度でローテーションする可能性があります。

実践上のポイント: 接続ごとのリフレッシュロックを使用し、リフレッシュトークンをアトミックに置き換え、トークンエンドポイントの一時的な障害にはリトライで対処し、明確な再認可状態を公開し、有効なアクセストークンが残っている状態ですべてのAPIコール時にリフレッシュを行わないようにします。

初期同期

初期同期はバックグラウンドジョブとして実行します。OAuthのコールバックやUIをブロックしないようにしてください。

堅牢なシーケンス: 組織のメタデータを取得し、勘定科目、税率、通貨、連絡先、アイテムを取得し、プロバイダIDとマッピングを保存し、プロダクトに必要な履歴期間のみをインポートし、依存関係の順序に従って依存レコードをキューに入れ、チェックポイントを記録し、検証が成功した場合にのみ接続を「正常」としてマークします。

依存関係の順序は重要です。請求書を作成するには、事前に連絡先、アイテム、収益勘定、税コード、通貨が存在している必要があります。盲目的に重複を作成してはなりません。保存された外部ID、顧客が提供した参照、定義されたマッチング規則、または曖昧なケースにおける明示的なユーザー選択ステップに基づいて一致させます。

差分同期

Webhookを信頼できるレコードではなく、シグナルとして扱う

Webhookは通常、何らかの変更があったことを通知するものです。署名を検証し、生イベントを記録し、必要な成功レスポンスを速やかに返し、重複排除を行い、後続のフェッチをキューに入れ、プロバイダから現在のオブジェクトを取得し、トランザクションとしてアップデートを適用し、チェックポイントを更新し、失敗したものはリトライまたは照合(reconciliation)へルーティングします。これにより、不完全なペイロードや順序が前後したイベントを処理できます。

プロバイダのルールは異なるため、1つの普遍的な動作を期待してはなりません。XeroのWebhookは x-xero-signature ヘッダーにHMAC-SHA256署名を含み、HTTPSを必須とし、最大24時間の間隔を短縮しながらリトライを行い、度重なる失敗により無効化されることがあり、復旧後のリプレイのために最大31日間イベントを保存できます。QuickBooks OnlineはHMAC-SHA256と検証用トークンで検証を行い、3秒以内にHTTP 200を返すことを期待し、最終的に6時間間隔に落ち着く固定スケジュールでリトライを行います(Intuitは非同期処理を推奨しています)。

重複排除とべき等性(Idempotency)

「少なくとも1回(at-least-once)の配送」は一般的です。Webhookの重複、実際の書き込みは成功したもののタイムアウト後のリトライ、順序が入れ替わったイベント、ユーザーによる二重クリックなどを想定してください。可能な場合はプロバイダのイベントIDを使用し、永続ストレージ内の固有のイベントキー、各書き込みの前に生成されるオペレーションID、データベースのユニーク制約、アップサート(upsert)のセマンティクス、そして曖昧なタイムアウト後の照合クエリを使用します。

すべての会計APIがネイティブのべき等性キーを提供しているとは仮定しないでください。プロバイダが提供していない場合は、内部オペレーションキーを保存し、リトライ前に期待されるリモートオブジェクトがすでに存在するかどうかを確認してください。Stripeのべき等なリクエスト(Idempotent requests)や一般的なWebhookのべき等性ガイドラインのパターンがここにうまく適用できます。

ポーリングと変更検知

Webhookと定期的なポーリングを組み合わせます。Webhookが無効化されたり、遅延したり、設定ミスで失われたり、特定のオブジェクトに限定されたりすることがあるためです。XeroはUTCタイムスタンプ以降の変更に対して If-Modified-Since ヘッダーをサポートしていますが、すべての変更が UpdatedDateUTC を更新するわけではないため、一部の部分的支払いが行われたトランザクションや連絡先フィールドはそのフィルターには表示されないとドキュメントで警告されています。QuickBooks OnlineのChange Data Captureは過去30日間の変更を追跡し、レスポンスを1,000オブジェクトに制限し、削除されたエンティティを Deleted ステータスで返し、より短いポーリングウィンドウで最も効果を発揮します。

プロバイダごと、テナントごとにチェックポイントを保存し、オーバーラップウィンドウを使用します。最後のチェックポイントの少し前からの変更をリクエストし、プロバイダIDと変更バージョンで重複排除を行います。これにより、クロックのズレや部分的な障害から保護されます。

会計特有のデータ整合性

この部分が、動く統合と信頼できる統合を分ける境界線となります。

税の挙動は互換性がありません。Xeroのドキュメントによると、LineAmountTypes が未設定の場合、請求書、クレジットノート、発注書はデフォルトで税抜き(tax-exclusive)になり、領収書や銀行取引はデフォルトで税込み(tax-inclusive)になり、手動仕訳(manual journals)はデフォルトで税なしになります。税率名ではなく TaxType フィールドを使用してください。「20% VAT」という表現が税コードを一意に特定できると決して仮定しないでください。国、取引の種類、発効日、組織の設定によって異なる場合があります。ここが、EUの多国籍VATを手動で正しく処理しようとするとコストがかかる部分であり、国別のコンプライアンスが組み込まれた組み込み型元帳が存在価値を発揮する場面です。

金額にはバイナリ浮動小数点数ではなく、十進法(decimal)または固定小数点演算を使用してください。通貨コードと精度、単価、数量、割引、税額、純額、総額、為替レートを永続化します。行レベルの金額を送信してプロバイダに合計を計算させるのか、あるいは事前に計算された合計を送信するのかを明確に決定してください。両者を混在させると、会計士が見つけるような丸め誤差が生じます。

元帳勘定は表示名ではなく、プロバイダのIDまたはコードで参照してください。書き込みの前に、勘定科目、税コード、顧客、アイテム、通貨がアクティブであり、サポートされていることを検証してください。請求書の書き込み失敗は、一時的なAPIエラーではなくマッピングや設定の問題であることが多いため、「同期失敗」と表示するのではなく、UIで修正方法を説明すべきです。

オブジェクトの同等性を安易に仮定しないでください。「支払い(payment)」は、入金(receipt)、割り当て(allocation)、預金(deposit)、銀行取引、または仕訳エントリを意味する場合があります。SaaSの「サブスクリプション」が、会計上の「請求書」にきれいにマッピングされるとは限りません。APIオブジェクトを選択する前に、表現したい会計イベントを定義してください。1つのプロバイダがこれをどのようにモデル化しているかについては、Xeroの支払ドキュメントを参照してください。

レートリミットとバージョニング

制限は通常、テナント、組織、アプリケーション、またはそれらの組み合わせ単位でスコープされます。Xeroの制限に関するFAQによると、テナントあたり5件の同時リクエスト、テナントあたり1分間に60件、テナントあたり1日に5,000件、そしてアプリ全体で1分間に10,000件となっており、残り回数を示すヘッダーや429レスポンス時の Retry-After が含まれています。QuickBooks Onlineは、1分あたり500リクエスト、レルムIDあたり1秒あたり10リクエストを規定しており、1リクエストあたり最大30件の操作をサポートするバッチエンドポイントを備えています。

テナントごとのレートリミット、グローバルなスロットリング、ジッター付きの指数バックオフ(exponential backoff with jitter)、Retry-After の尊重、並行数の制限、ページネーション、429エラーや残りクォータに関するメトリクスを構築してください。インポートのために無制限の並行処理を使用しないでください。1人の巨大な顧客が、そのテナント上の他のすべてのジョブを飢餓状態に陥らせる可能性があります。

APIのバージョンをピン留めするか、明示的に管理してください。QuickBooks Onlineは、バージョンが指定されていない場合、マイナーバージョン75をデフォルトとします。バージョンをコンフィグで一元化し、サンドボックスに対してコントラクトテストを実行し、リリースノートを監視し、未知のレスポンスフィールドは上位互換性があるものとして扱ってください。サンドボックスは本番環境のすべての条件を再現するわけではないため、ローンチ前に、切断、再接続、同意の取り消し、期限切れトークン、レートリミット、プロバイダ側の編集をテストしてください。

最初に構築すべきもの

明日からこれを始めるとしたら、以下の順序で進めます:

  1. 正確な会計ワークフローを定義する: 読み取る、または書き込むイベントとオブジェクトを決定する。
  2. プロバイダの戦略を選択する: 直接接続、統合、組み込み、またはハイブリッド。
  3. プロバイダの組織ID、スコープ、ステータス、トークンメタデータを持つ、テナントスコープの接続モデルを構築する。
  4. 認可コードフロー、PKCE、ステータス検証、最小権限スコープ、サーバー側トークンストレージを備えたOAuthを実装する。
  5. 安定した内部インターフェースの背後に1つのプロバイダアダプタを構築する。
  6. 不変のプロバイダIDに基づいてIDマッピングを作成する。
  7. まず読み取りを実装し、書き込みを行う前に参照データをインポートする。
  8. ページネーション、レートリミット、チェックポイント、依存関係の順序を備えた初期同期ジョブを追加する。
  9. 顧客、アイテム、税、勘定科目のマッピングが検証された後にのみ、1つの限定的な書き込み(請求書の作成)を実装する。
  10. 内部オペレーションID、ユニーク制約、リモート状態のチェックを使用して、書き込みをリトライ安全にする。
  11. 高速に応答し非同期で処理する、署名付きWebhookを追加する。
  12. Webhookが単なる唯一の信頼できる情報源ではなくアクセラレータとなるよう、ポーリングと照合(reconciliation)を追加する。
  13. 障害モードを意図的にテストする。
  14. 障害の原因を説明可能にし、修復可能にするサポートおよび監査ツールを追加する。
  15. オブジェクトのカバー範囲を段階的に拡大する。

FAQ

統合APIを使用すべきか、それとも直接コネクタを構築すべきか?

顧客の大部分が1つのプロバイダに依存している場合や、プロバイダ固有の挙動が中心となる場合は、直接コネクタを使用してください。コントロールよりも、幅広いカバレッジとスピードが重要な場合は、統合APIを検討してください。真のニーズが、顧客のシステムへの同期ではなく、自社での帳簿の運用である場合、どちらの質問の答えにもなりません。代わりにNordletのような組み込み型元帳を検討してください。

Webhookだけで同期を維持するのに十分か?

いいえ。Webhookが無効化されたり、遅延したり、失われたりすることがあり、ペイロードが不完全であったり順序が前後したりすることがあります。これらを変更シグナルとして扱い、その後に現在のオブジェクトを取得し、定期的なポーリングと照合(reconciliation)でバックアップしてください。

リフレッシュトークンの失敗によって顧客がロックアウトされるのを防ぐにはどうすればよいか?

更新のたびに最新のリフレッシュトークンをアトミックに永続化し、接続ごとのロックを使用し、トークンエンドポイントの一時的な障害をリトライし、明確な再認可状態を公開します。Xeroの30分間の猶予期間も役立ちますが、アトミックな永続化こそがあなたを救うものです。

顧客名やメールアドレスでレコードをマッチングできるか?

永続的な識別子としては使用できません。名前は変更され、重複が発生し、2人の顧客が同じメールアドレスを共有することもあります。プロバイダの不変のIDを保存し、自身のSaaSのIDをその隣に保持してください。

参考文献