ソリューション
資金の動きをすべて説明できる金融システム
私たちは、正確性を第一の要件とする決済、台帳、ウォレット、レンディングのプラットフォームを設計・構築します。取引が失われず、二重に適用されず、すべての残高をその裏にある仕訳から説明できることが前提です。
課題の全体像
金融ソフトウェアには、ほとんどのソフトウェアにはない性質があります。エラーが金額として表れ、顧客、経理部門、規制当局の目に留まることです。タイムアウトしてリトライされたリクエストは、二重支払いになりえます。書き換え可能な1つの数値として保存された残高は、それを説明するはずの取引から乖離していきます。プロバイダーの精算ファイルが、誰にも追跡できない金額だけ社内の記録と食い違うこともあります。これらはエンジニアリング上の失敗であり、対処法はよく知られています。その対処法は、土台の段階で組み込んでおく必要があります。
その土台が台帳です。複式簿記の台帳は、すべての資金移動を勘定間で貸借が一致する借方と貸方として記録し、追記専用であり、残高は仕訳から導出します。決済、ウォレット、レンディング、手数料は、いずれもその上に構築されるプロダクトです。台帳の周囲には、分散システムの中で台帳を信頼できるものにする仕組みが並びます。すべての書き込みに対する冪等性、決済ごとの明示的なステートマシン、確実なイベント発行、そしてすべての外部関係者との照合です。
Staroneが提供するのはエンジニアリングです。当社は金融関連のライセンスを保有しておらず、規制当局の承認を得ているとの主張も一切行いません。システムは、それを運用するライセンス保有事業者の義務を支えるように設計します。記録の保存、監査、顧客デューデリジェンス、報告がこれに含まれます。どの義務が適用されるかの判断は、お客様とそのコンプライアンスおよび法務のアドバイザーが行う事項です。
なぜ難しいのか
エンジニアリング上の課題
取引の重複と消失
リクエストとレスポンスの間でネットワークは障害を起こすため、呼び出し側は決済が実行されたかどうかを知ることができません。保護なしにリトライすれば顧客に二重請求し、リトライしなければ決済は宙に浮いたままになります。資金を動かすすべての操作には、同じリクエストが繰り返されても最初の結果を返すようにする冪等キーが必要です。
説明のつかない残高
残高の列を直接更新すると履歴が失われ、2つのトランザクションが同じ口座に触れたときに競合状態を招きます。数値が誤っていても、監査する対象が存在しません。残高は不変の台帳エントリから導出しなければならず、キャッシュされた残高は検証可能なプロジェクションとして扱います。
結果が不確定な外部プロバイダー
カード決済処理事業者、銀行、決済ネットワークは、非同期で応答し、タイムアウトし、Webhookを順不同で送り、ときには数日後に結果を覆します。プロバイダー呼び出しを単純な成功か失敗かで扱うと、滞留した決済や不一致の決済が生まれます。各決済には、保留、不明、取消済みといった状態を明示的にモデル化したステートマシンが必要です。
月末まで放置される照合
社内の記録、プロバイダーのレポート、銀行取引明細は、同じ資金を異なる形式と異なるタイミングで表しています。定期的に手作業でしか比較しなければ、不一致は積み上がり、その原因は分からなくなります。照合は自動化して継続的に実行し、突合できない項目はすべて追跡対象の例外として起票する必要があります。
ホットアカウントでの同時実行
加盟店の精算口座や手数料口座には、全取引のうち大きな割合が触れることがあります。単純な行ロックではそこがボトルネックになり、配慮を欠いた楽観的な手法では残高を超える引き落としを許してしまいます。台帳の設計では、残高が限度額を下回ることを防ぎつつ、競合を意図的に扱わなければなりません。
クリティカルパス上のコンプライアンスチェック
スクリーニング、本人確認、取引モニタリングは、固有のレイテンシーと障害モードを持つ外部サービスに依存します。これらを後付けにすると、障害時には全顧客をブロックするか、あるいはチェックされないまま取引を黙って通してしまいます。フローでは、チェックが遅い場合、利用できない場合、一致の可能性ありと返した場合に何が起きるかを定義しておく必要があります。
構築の進め方
アーキテクチャ
プラットフォームの中心には追記専用の複式簿記の台帳を据え、決済オーケストレーション、プロバイダー連携、コンプライアンス関連のサービスをその周囲に構築し、確実に発行されるイベントで接続します。
- 01
複式簿記の台帳サービス
すべての金融イベントは、借方と貸方のエントリの合計がゼロになるトランザクションとして記録し、PostgreSQLなどのリレーショナルデータベースに、Serializable分離レベルまたは慎重にロックを設計したトランザクションのもとでアトミックに書き込みます。エントリを更新・削除することはなく、訂正は新たな反対仕訳として行います。金額は、明示的な通貨とともに最小通貨単位の整数で保存し、浮動小数点値は決して使いません。
- 02
冪等なAPI層
状態を変更するエンドポイントはすべて、呼び出し側が指定する冪等キーを必須とします。キー、リクエストのフィンガープリント、最終的なレスポンスは、業務上の効果と同じトランザクションで保存します。そのため、リトライには保存済みの結果を返し、同じキーを異なるペイロードで再利用するリクエストは拒否します。同じ原則を、受信するWebhookとメッセージコンシューマーにも適用します。
- 03
決済オーケストレーション
各決済は、作成済み、オーソリ済み、売上確定済み、精算済み、失敗、取消済みといった遷移を定義した、永続化されたステートマシンです。オーケストレーターが遷移を進め、アダプターを通じてプロバイダーを呼び出し、レスポンスが得られない場合にはステータス照会をスケジュールします。資金は外部呼び出しの前に台帳上で保留し、結果に応じて解放または確定します。
- 04
プロバイダーと決済レールのアダプター
銀行、カード決済処理事業者、決済ネットワーク、ウォレットスキームは、それぞれのAPI、エラーコード、Webhook形式を正規化するアダプターで包みます。アダプターは、タイムアウト、バックオフ付きの上限のあるリトライ、サーキットブレーカーを適用します。商業上の取り決めが許す範囲で、あるプロバイダーが劣化した際にはルーティングルールによってトラフィックを代替プロバイダーへ振り向けることができます。
- 05
オンボーディングとコンプライアンスの連携
顧客オンボーディングは、データを収集し、本人確認と制裁リストスクリーニングのプロバイダーを呼び出し、判断のつかない結果を人によるレビューのためのケースキューに回すワークフローです。取引モニタリングはイベントストリームを取り込み、設定可能なルールに基づいてアラートを上げます。ワークフローにはプロバイダーのタイムアウト時の挙動を明示的に定義するため、保留するか続行するかの判断は、記録されたポリシーになります。
- 06
照合(リコンサイル)エンジン
プロバイダーの精算ファイル、銀行取引明細、内部台帳のエントリを取り込み、正規化したうえで、設定可能なルールを用いて参照番号、金額、日付で突合します。一致した項目は自動的にクローズします。不一致や部分一致の項目は、担当者と経過日数の計測を伴う例外となります。エンジンは、元データが許す限りの頻度で実行します。
- 07
監査、レポーティング、イベントストリーム
状態変更は、トランザクショナルOutboxを通じてKafkaなどの耐久性のあるログに発行します。このログが、台帳データベースに負荷をかけることなく、レポーティング用ストア、モニタリング、下流システムにデータを供給します。不変の監査記録には、オペレーターの操作も含め、誰がいつ何を行ったかを残します。規制当局向けレポートと経営管理レポートはこれらのストアから生成し、元のエントリまで遡ることができます。
チャネル
- Web
- モバイル
- パートナーAPI
- バッチファイル
APIゲートウェイ
- 認証
- レート制限
- 冪等性キー
- リクエスト署名
トランザクションサービス
JavaとSpring Boot、ステートレス、水平スケール
- 支払い開始
- オーソリゼーション
- 台帳への記帳
- 限度額とリスク
Kafkaイベント基盤
- トランザクションイベント
- アウトボックスリレー
- パーティション分割トピック
- デッドレタートピック
台帳DB・Redis
正となるのは台帳であり、キャッシュではない
- 複式簿記台帳
- リードレプリカ
- 残高キャッシュ
- 分散ロック
照合(リコンサイル)・監査
- 決済照合
- 不一致の検出
- 不変の監査ログ
- レポート出力
モニタリング
- レイテンシ・エラーのSLO
- コンシューマーラグ
- トレーシング
- ビジネスアラート
プロジェクトの成果物
- 勘定科目体系と記帳ルールを含む台帳設計
- コントラクトを公開した冪等な決済APIとウォレットAPI
- 決済のステートマシンとプロバイダーアダプター
- KYCおよびスクリーニングのプロバイダーと連携したオンボーディングワークフロー
- 例外管理を備えた自動照合(リコンサイル)
- 監査証跡とレポーティング用データストア
- タイムアウト、重複、取消を網羅した障害モードのテストスイート
着手する前に
早い段階で決めておくべきこと
- 台帳を自作するか、既存のものを使うか
- 台帳は、概念としては小さく、細部では容赦のないものです。勘定モデルが自社のプロダクトに合うのであれば、実績のあるオープンソースまたは商用の台帳を採用することで時間を節約できます。既存の選択肢では得られない記帳ルール、マルチカレンシーの挙動、性能特性が必要な場合は自作し、正確性の確保に必要なテストの工数を見込んでおきます。
- 強整合性か、結果整合性か
- 台帳への書き込みと、それを保護する残高チェックは、強整合でなければなりません。ここは結果整合性を用いる場所ではありません。取引明細、通知、分析、検索など、台帳から導出されるものはすべて結果整合でよく、イベントによって供給できます。この線引きを明確にすることで、クリティカルパスを小さく高速に保てます。
- 決済プロバイダーは1社か、複数か
- プロバイダーが1社であれば、連携、照合、挙動の把握が容易であり、出発点として妥当です。可用性の目標、対応地域、コストの面で必要になったときに2社目を追加します。プロバイダーが複数になると、照合用のフィードも複数になり、異議申し立て(ディスピュート)の手続きも異なり、ルーティングロジック自体にもテストが必要になります。
- トランザクションのスループットをどうスケールさせるか
- まず競合を取り除きます。ホットアカウントへの記帳をバッチ化し、補助口座を使い、トランザクションを短く保ちます。次に、レプリカとプロジェクションによって読み取りをスケールさせます。口座やテナントによる台帳のパーティショニングは、十分にチューニングした単一のデータベースが限界であると実証された場合に限ります。パーティションをまたぐ振替は、現実に大きな複雑さを持ち込むためです。
FAQ
よくあるご質問
Staroneはライセンスを受けた金融機関ですか。
いいえ。Staroneはエンジニアリング企業です。当社は、関連するライセンスを保有している、または取得を進めている組織のために、技術を設計・構築します。規制当局の承認を得ているとの主張は一切行わず、ライセンスの取得と規制の解釈は、お客様とそのアドバイザーの責任において行われます。
二重請求をどのように防ぎますか。
すべての層で冪等性を確保することで防ぎます。クライアントのリクエストには冪等キーを持たせ、台帳への記帳とアトミックに記録します。プロバイダー呼び出しでは、プロバイダー側に冪等性の仕組みがあればそれを利用し、メッセージコンシューマーは処理済みメッセージの識別子を記録します。繰り返されたリクエストは、新たな資金移動を発生させるのではなく、最初の結果を返します。
プラットフォームはカードデータとセキュリティ基準に対応できますか。
アーキテクチャは、カードデータに触れる範囲を最小化するように設計します。一般的には、プロバイダーのホスト型入力フィールドとトークン化を利用し、生のカードデータがお客様のシステムに入らないようにします。ネットワークセグメンテーション、暗号化、鍵管理、アクセスログは、PCI DSSなどのフレームワークに沿って設計します。正式な評価と認証の取得は、運用主体となる事業者が認定評価機関とともに行います。
本番稼働前に、障害はどのようにテストしますか。
障害を意図的に注入してテストします。テストスイートは、プロバイダーのタイムアウト、重複したWebhookや順不同のWebhook、記帳中の部分的な失敗、トランザクション途中でのプロセスのクラッシュをシミュレートし、そのうえで台帳の貸借が一致していること、決済の消失や重複がないことを検証します。照合は、意図的に不一致を仕込んだファイルを使って検証します。
