こんにちは!サーバーサイドエンジニアのkurisuです。 入社して3ヶ月で初めて担当したPAN照会システムがリリースされました。 今回はPAN照会システムを開発する際、検討したことをサーバーサイド観点でお伝えします! 相乗りで、既存システムに新規にシステムを導入する時の参考になればと思います。
PAN照会とは
カード発行会社の業務として、警察捜査関係、その他様々なケースで、PAN (Primary Account Number) いわゆるカード番号を扱う業務が発生します。PAN照会はそれらの業務一連のことを指します。 業務が発生する度に、セキュアな情報を扱うため、限られたエンジニアが手動オペレーションで対応していました。
この業務を効率化し、よりセキュアに担当者が業務を実施できるようなシステムを構築する必要がありました。
また、このシステムはPCI DSSに準拠する必要があります。
PCI DSSとは
PCI DSSとは、クレジットカード会員データを安全に取り扱う事を目的として策定された、クレジットカード業界のセキュリティ基準です。 カード発行業者のスマートバンクは必ずこの基準に準拠する必要があります。 スマートバンクのインフラに関しては以下をご覧ください!
PAN照会システム構築の最大の課題は、PCI DSSに準拠することでした。準拠するために一番不確実性が高かったのがインフラです。苦戦した内容が詳細に書かれているので、詳しくは、こちらの記事をご覧ください!
PAN照会API相乗り計画
PAN照会はセンシティブかつ、既存のB/43の決済システム全体にも影響を与えます。 サーバーサイドでは、安全性を確保しながらリリースすることを一番最優先に取り組む必要がありました。
システムアーキテクチャ
引用: エンジニアに興味がある方へ
概要はこちらです。PAN照会システムはPANを扱うためPCI DSS準拠環境に設置する事になりました。
システムアーキテクチャ図から、メインとなるシステムを2つご説明します。
- issuing-api
- カード情報を取り扱っていて、オーソリや3Dセキュア決済機能が存在する
- PCI DSS 準拠環境
- core-api
- B/43の機能全般を司っているシステム
- PCI DSS 非準拠環境
今回のPAN照会システムの導入の導入の際、検討した案は以下2点です。
pros | cons | |
---|---|---|
既存のissuing-apiに相乗り | 管理画面用のAPI機能の追加だけ済む。既存のPAN取得の処理を流用できる | 決済処理と相乗りするので既存の決済に影響を与える可能性がある |
新規のRailsアプリケーションを作成 | 新規で作るので、既存の決済システムに影響がない | 新規アプリケーションが誕生するので、全体の構成が複雑になる。PANの処理を新たに実装する必要がある |
結論として、以下2点の理由から、「既存のissuing-apiに相乗り」を選択しました。
- issuing-apiにPAN照会業務に関する機能を入れても違和感がない
- 既存の複雑なロジックを再利用でき、シンプルに作れる
また、issuing-apiと同じレポジトリでPAN照会を実装しますが、インフラとしては、別インスタンス・ネットワークを構築するようにして、既存の決済システムには影響を与えずらくなるように作ることになりました。
I/F管理
新規システムを導入に関して今後の保守運用を考えて、スマートバンクのメインのcore-apiと同じく、OpenAPIドキュメントでのI/F管理を検討しました。 I/F管理の方法として、大きく分けて3つの案を検討しました。できれば①/②を選択したいという気持ちで調査を始めました。
なるべく記述量を減らせるか | ドキュメントとコードの差分をなくせるか | 既存への影響が少ないか | |
---|---|---|---|
① OpenAPIドキュメント手動→コード自動作成 | o | o(OpenAPIを先に書いているので、差分が少ない) | △(パッケージの構成が変わる可能性あり) |
② コード手動→OpenAPIドキュメントを自動作成 | △(コメントの記述が必要) | △(コメントの記載ミスや更新漏れが発生する) | o |
③手動で管理 | x | x | o |
① OpenAPIドキュメント手動→コード自動作成
まずは、なるべくドキュメントを正にしてフロントとの繋ぎ目の整合性をとることを検討しました。以下がその時の検討ライブラリです。
package | 既存のコードに影響を与えないか | 既存の設計との差分が少ない | 自動生成対象 |
---|---|---|---|
oapi-codegen | o | △(infrastructure/api 配下のrequest/response/apiのハンドリング(+ routing)が、1つのパッケージに集約される) | o(ルーティングとリクエスト・レスポンス) |
ogen | x(リクエスト・レスポンスのみなら△) | △ | △(issuing-apiで利用している echoが使えないのでリクエスト・レスポンスのみ) |
openapi-generator | △(同じく) | △ | x |
調査の結果、oapi-codegenが一番適していたのですが、既存のissuing-apiのアーキテクチャ構造と異なってしまい、今後の開発がしづらくなると考え今回は採用しませんでした。
アーキテクチャを全て変更することも検討したのですが、既存の決済システムの影響の大きさとPJのスケジュールを鑑みて、見送りました。
② コード手動→OpenAPIドキュメントを自動作成
コードからOpenAPIドキュメント生成も検討しました。
package | pros | cons |
---|---|---|
swag | 結構使われている。コード上にコメント残せば、OpenAPIドキュメントが自動生成される生成される | openAPI2.0のみ対応、3はまだ開発中で、3.0のブランチを試したがまだ使えない(2024/5月現在) |
kin-openapi | 結構使われている(oapi-codegenなどの裏側で利用されている)。OpenAPI3.0が利用可能 | 生成するためのコマンドを独自に作って、そのコマンド処理に、都度生成したreuqest/responseを記載する必要あり(コメントに比べると運用が面倒くさい) |
go-swagger3 | OpenAPI3.0に対応している。コード上にコメント残せば、OpenAPIドキュメントが自動生成される | Anonymous structsに対応していない |
以下理由により、満たせるライブラリがありませんでした。 - メインAPIのcore-apiがOpenAPI3.0を扱っているので、3.0に合わせたい - issuing-apiは、Anonymous structsを利用していたので、これにも対応できるようにしたい - サクッと管理できるようにしたい
最終的にはマッチするものがなく「③手動で管理」を選択しました。 現状新規のシステムがまだシンプルなので、今後運用していく中で機能拡張が活発になれば再度検討しようと思います
実装
issuing-apiはGo言語で書かれており、クリーンアーキテクチャ構造をとっています。
プロジェクト構成
既存の決済システムと相乗りする上でどのように処理を区切るかを検討しました。
既存の決済システムとのコンテキストの境界がわかりづらくなってしまうことより、フォルダ構成が異なってしまうことの方が、運用していく中で辛い部分が多いと判断したため、
「① 既存のパッケージ構成に admin_yyy_xx.go
というプレフィックスで配置する」を採用しました。
pros | cons | |
---|---|---|
① 既存のパッケージ構成に admin_yyy_xx.go というプレフィックスで配置する | 既存の設計に沿ったファイル・コードが書けるので、見通しが良い。共通化できるロジックを使いまわしやすい | 既存のissuing-apiとコンテキストが違うことが判別しづらい。 |
② 各配置パッケージにadminフォルダを切る | 既存のAPIのコンテキストと違うことが判別しやすい | 既存の設計と異なるフォルダ構成と可読性が下がってしまう。パッケージインポート時に、エイリアスを貼らないと、admin.xxxxになって可読性が下がる |
usecaseより内側の層は共通化しています。
複雑なロジックを持つPAN取得周りの処理は、既存のコードを再利用できるので、PAN照会でのみ必要なAPIを安全に作ることができます。
├── adapter (外部との連携を行う入り口・出口) │ ├── controller(ユーザからのリクエスト) │ │ └── card_controller.go │ │ └── admin_card_controller.go: ⭐️追加 │ │ │ ├── gateway(DB接続) │ │ └── rdb │ │ └── card_repository.go │ │ │ └── presenter(ユーザへのレスポンス) │ └── card_presenter.go │ └── admin_card_presenter.go : ⭐️追加 │ ├── domain(ビジネスロジックで発生するドメインモデル) │ └── entity │ └── card.go │ ├── infrastructure(フレームワークや外部ライブラリなど一番外側) │ ├── api │ │ ├── request │ │ │ └── card.go │ │ │ └── admin_card.go: ⭐️追加 │ │ │ │ │ ├── response │ │ │ └── card.go │ │ │ └── admin_card.go: ⭐️追加 │ │ │ │ │ ├── card.go │ │ ├── admin_card.go: ⭐️追加 │ │ └── server.go : ⭐️改修 │ │ │ ├── middleware │ │ └── logger.go │ │ │ └── database │ └── mysql │ └── mysql.go │ ├── testutil │ ├── usecase(ビジネスロジック) │ ├── interactor(ユースケースを達成する処理) │ │ └── card_interactor.go │ │ └── admin_card_interactor.go: ⭐️追加 │ │ │ └── port( │ ├── repository │ │ └── card_repository.go │ │ │ └── server │ └── card_server.go │ └── admin_card_server.go: ⭐️追加 │ ├── config │ └── config.go │ └── main.go(サーバー起動)
なるべく影響を少なくしてリリース
機能単位でこまめに変更をマージし、本番にリリースしていきます。
これによって影響範囲の特定が容易になり、早い段階で切り戻しが可能になります。
一方、本PJにおいては、インフラ構築のスケジュール上、認証の実装は最後に残っていました。
認証を実装する前にリリースしてしまうと、相乗りしているため既存の決済システムで、対象APIが認証なしで叩けてしまいます。
そのため、まずはSREチームにインフラ側で対象APIを叩けないようにネットワークを閉じてもらってからリリースするようにしました。
ペアプロを活用
ほとんどのエンジニアはメインのcore-apiを扱っており、issuing-apiは普段実装をほとんど行いません。 そのため、レビューのクオリティを上げるために、積極的にペアプログラミングを取り入れながら開発を進めました。
ドライバーとナビゲーターを交代しながら進めたため、PAN照会を担当した開発エンジニアは全員、サーバーサイドとフロントエンドの両方を開発しました。 この取り組みのおかげで、プロジェクトに関わるエンジニア全員がそれぞれの実装の意図を把握でき、レビューもスムーズになったと考えています。
最後に
PAN照会システムを構築するにあたって安全にリリースするために考えたことについてまとめました。 組織やPJごとに、制約が異なってくるので、それぞれベストプラクティスは異なってきます。少しでもシステムの新規導入時の判断のお役に立てればと思います。 また、このブログをきっかけにB/43の開発に興味を持ってもらえたら嬉しいです。
SmartBankでは、サーバーサイドエンジニアを募集しています! smartbank.co.jp
B/43の開発についてもっと話を聞いてみたい方は、カジュアル面談もご検討ください! smartbank.co.jp