スケーラブルな決済システムの設計
•2 min read•
51 views
paymentsdistributed systemsarchitecturegolang
決済システムの設計には、高い一貫性、信頼性、セキュリティが求められます。この記事では、エンタープライズ規模での決済処理のための高レベルアーキテクチャを設計し、Idempotency(冪等性)、Transactional Integrity(トランザクションの整合性)、**Asynchronous Processing(非同期処理)**に焦点を当てます。
コアエンジニアリング原則
- Idempotency(冪等性): すべての決済リクエストは、一意の
idempotency_keyを持つ必要があります。これにより、ネットワークタイムアウトが発生してクライアントがリトライした場合でも、二重課金が発生しないようにします。 - ACID Transactions(ACIDトランザクション): 財務記録は、アトミックで一貫性がある必要があります。台帳の更新には、厳密なロックを備えたリレーショナルデータベース(PostgreSQL/MySQL)を使用します。
- Scalable State Machine(スケーラブルなステートマシン): 決済は、
PENDING→PROCESSING→SUCCEEDED/FAILEDのいくつかの状態を経ます。
高レベルアーキテクチャ
決済システムの高レベルアーキテクチャを以下に示します。
Live architecture
Compiled: v2.0-ProductionAnalyzing Schema...
Arch Note
Interactive logic enabled. Click components in expanded view for technical service definitions.
Layer.0 / Distributed_System_Viz
データベーススキーマ (ERD)
堅牢な決済システムは、監査可能性のための適切に設計されたスキーマから始まります。
Live architecture
Compiled: v2.0-ProductionAnalyzing Schema...
Arch Note
Interactive logic enabled. Click components in expanded view for technical service definitions.
Layer.0 / Distributed_System_Viz
実装: Golang での Idempotency
リレーショナルデータベースにアクセスする前に、Redis を使用してリクエストキーを迅速に保存および検証します。
func (s *PaymentService) ProcessPayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) {
// 1. Redis で既存の Idempotency Key を確認
exists, err := s.redis.SetNX(ctx, req.IdempotencyKey, "PROCESSING", 30*time.Minute).Result()
if err != nil {
return nil, fmt.Errorf("idempotency check failed: %w", err)
}
if !exists {
return nil, ErrDuplicateRequest
}
// 2. DB トランザクションでラップ
err = s.db.WithTransaction(func(tx *sql.Tx) error {
// ... コア決済ロジック ...
return nil
})
if err != nil {
// ビジネスエラーでリトライ可能な場合は、idempotency をリセット
s.redis.Del(ctx, req.IdempotencyKey)
return nil, err
}
return &PaymentResponse{Status: "SUCCESS"}, nil
}分散障害の処理
外部プロバイダー(Stripe、銀行)を扱う場合、ローカルDBトランザクションでラップすることはできません。Saga Pattern (Choreography-based) を使用します。
- Local Transaction(ローカルトランザクション): DB に
PENDINGレコードを作成します。 - External Call(外部呼び出し): Payment Provider API を呼び出します。
- Async Callback(非同期コールバック): Webhook を受信するか、ステータスをポーリングします。
- Completion(完了): DB を更新し、Kafka 経由でダウンストリームイベントをトリガーします。
このアーキテクチャにより、ステップ2の後にサーバーがクラッシュした場合でも、保留中のレコードを使用して後で状態を調整できます。
決済フロー
- クライアントは、一意の idempotency key を持つ決済リクエストを送信します。
- API Gateway は、それを Payment Service にルーティングします。
- Payment Service は、この idempotency key がすでに処理されているかどうかをデータベースで確認します。
- 処理されていない場合、保留中の決済レコードを作成します。
- 外部決済プロバイダーを呼び出します。
- 決済が成功すると、データベースを更新し、「Payment Succeeded」イベントを Kafka に発行します。
- Notification Service がイベントをピックアップし、ユーザーにメールを送信します。
このアーキテクチャは、信頼性とスケールを保証します。