🏦

銀行振込設計で考えていること

2024/12/12に公開

これは何

これは Kyash Advent Calendar 2024 の12日目の記事です。
はじめまして、KyashでサーバーサイドのEMをしています おもたに (@k_omotani)です。

Kyashでは銀行口座の入出金機能のためにシステム連携を行うことが多いです。
直近では、B2B事業の中で銀行との接続の開発を行ってきました。

銀行と接続する際には銀行に対する振込、銀行口座からのチャージ、残高参照など色々なシステム連携があります。
その中でも今回は 振込に絞って
「どういう観点で、はまりどころがあるのか、設計時に考えていること」
についてまとめます。

複数の決済システムにまたがる整合性の考え方として普遍的な部分もあるので参考になれば幸いです。

前提知識

銀行のシステムと接続し、自社の残高を銀行振込を用いて別の銀行口座に出金する場合、システムの関係としては以下のようになります。
金融の専門用語として

  • 仕向(しむけ):送金する側
  • 被仕向(ひしむけ):送金を受ける側

という言葉を使います。

各銀行間は全銀ネット(全国銀行資金決済ネットワーク)という機関が運営している全銀システム(全国銀行データ通信システム)でつながっています。
Kyashは銀行業ではなく資金移動業者なので全銀ネットワークには参加しておらず、銀行とのシステム連携を行うことで、銀行振込を実現しています。

Ref: 全銀ネットの取組みについて

各システム境界でネットワークの切断によるタイムアウトが起きる可能性があるので、振込のリクエストの状態に応じて不整合が発生しないように設計する必要があります。

設計時の観点

エラーの区分け

銀行振込を行う際に起こりうるエラーとして大きく分けると以下があります

  • 自社システム側でのバリデーションエラー
    • 口座番号の桁数が足りないなど
  • 銀行システム側でのバリデーションエラー
    • 口座名義チェックなど
  • 仕向口座(振込元の自社の法人口座)の状態がおかしい
    • 残高不足など
  • 被仕向の状態がおかしい
    • 口座凍結など
  • 銀行システムのエラー
    • 障害、メンテなど

それぞれのエラーで銀行システム側からはどういった形式でエラー内容が返されるかを把握し、
システムとしてハンドリングし、ユーザーおよびシステム運用側にどう伝えるか考える必要があります。

バリデーションエラー、被仕向の状態がおかしいものに関しては、エラー内容をユーザーにわかりやすいメッセージと共に返すのみで大丈夫でしょう。
仕向口座の状態がおかしいエラーが発生した場合、運用都合での障害になるので速やかにalertを出す必要があります。
銀行システムのエラーの場合は、取引が確実に失敗したことがわかる場合はユーザーに返し、タイムアウト等で取引状況が不明な場合は整合性チェックのための処理を行う必要があります。詳しくはタイムアウトの扱いの方で触れます。

タイムアウトの扱い

ネットワークを跨いだ決済処理の場合、成功失敗以外にネットワーク分断などのタイムアウトについても考慮しないといけません。
クライアントタイムアウト、サーバータイムアウトに限らずタイムアウトが発生した場合、取引が成功したか失敗したかわからない状態になってしまいます。適切にハンドリングを行わないとエンドユーザーの残高は減っているが銀行に振込されていないケースが発生してしまいます。

自社システム側で持っている残高を減算→銀行システムに対して振込依頼というような流れの場合、

  1. リクエストが成功したケース: そのまま終了しエンドユーザーに成功したことを表示する
  2. リクエストが失敗した場合: 残高をロールバックし、失敗したことをユーザーに表示する
  3. タイムアウトが発生した場合=成功したか失敗したかわからない場合: ?

の3パターンを考えます。
タイムアウトが発生した場合のハンドリング方法は銀行側の仕様により異なりますが、大きく分けると以下の3パターンになります。

  1. リクエストに冪等性IDが含まれており同じリクエスト内容でリトライできるパターン
  2. リクエストに取引IDを付与した上で取引状況参照用のエンドポイントから状況を確認できるパターン
  3. 日時指定などで取引一覧を取得でき突合するパターン

銀行システム側がタイムアウト時に使える機能を提供しておらず問い合わせベースや管理画面ベースでのオペレーションになっていることもあるので、事前に確認する項目になります。

一件ずつか一括か

ウォレットアプリからの出金では振込振替による即時出金が基本だと思いますが、
SaaS等ではユースケースによっては振込振替ではなく総合振込を用いたバッチ処理で実装する可能性もあります。

具体的には、

  • 振込依頼は常時受けるが内部的に溜めておき、特定の時間で一括処理
  • パートナーごとに1日1ファイル受け取り、特定の時間に処理

のようなパターンです。

「ユーザーが振込依頼してから出金されるまでの時間」と「バッチ処理することによる出金コスト」のトレードオフとして、一つの設計案として考えられるでしょう。

コアタイム/モアタイムの考慮

全銀システムでは平日8:30~15:30をコアタイム、それ以外をモアタイムといいます。
元々コアタイムのみ対応していたのを2018年から24時間365日対応できるようにモアタイムシステムが導入されました。
詳しくはこちら: https://www.zengin-net.jp/zengin_system/

銀行システムがモアタイムとコアタイムで内部的に異なり、非機能要件レベルでシステム仕様が異なることがあります。
一般的にモアタイムのほうが制約が多いです。事前に銀行側にコアタイム/モアタイムのシステムの差分を確認し、対応が必要か考慮する必要があります。

振込不能について

基本的に、銀行の振込振替のAPIは内部的には振込の受付を完了したことをAPIの成功としています。
他行宛の振込の場合、A銀行→全銀ネット→B銀行というふうに全銀ネットを通ります。そのため、APIリクエストのトランザクション内で金額の移動が行われることはありません。
そうなると被仕向銀行都合で取引エラーになることが稀にあり、その場合、不能通知として「あの振込依頼、やっぱり失敗してたよ」と銀行から連絡が来ることがあります。

つまり非同期的に取引がキャンセルされることになるので、それを考慮したシステム設計をする必要があります。

不能通知が来た時に残高整合が取れるようにするのは前提として、
ユーザーには成功フローはあくまで振込の依頼が完了したということを伝えた上で、依頼は受け取ったが失敗することもあるというコミュニケーションをする必要があります。

最後に

観点を羅列しましたが、設計する上で参考になれば幸いです。

Kyashでは今回話した振込だけでなく、
チャージなどでの銀行からの入金や、決済ネットワークであるVisa、他にも様々な外部システムとの接続を行っており、面白い技術課題がたくさんあります。

是非是非ご興味のある方はこちらからお申し込みを!!

https://herp.careers/v1/kyash

株式会社Kyash

Discussion