バックエンドエンジニアにとって、法務や規約は「コードの外部仕様」そのものです。
。Webアプリ開発において、これらは単なる「ルール」ではなく、「データの持ち方と消し方を決める設計図」 になります。
Webアプリ開発における「法務・規約」の全体マップ
エンジニアがこれらを意識すべき理由は、「後からデータベースの構造を変えるのは、家を建てた後に土台を入れ替えるくらい大変だから」 です。
1. お金のルール(インボイス・返金)
「正しく計算し、正しく記録する」ためのルールです。
-
インボイス(税金の証明):
国の決まりで、「誰が、誰に、いくら払ったか」の証明書(請求書)に書くべき項目が決まりました。 -
設計への影響: DBに「登録番号」を保存する場所を作り、計算ロジックを「合計してから税率をかける」という1点に集中させる必要があります。
-
返金(トラブル防止):
「やっぱりやめた」と言われた時の処理です。 -
設計への影響: ステートマシン(状態遷移図)を使い、「返金中」のデータを二度と「決済完了」に戻さない、ボタン連打で2回返金しない仕組みをガチガチに固めます。
2. ユーザーとの約束(利用規約)
「どこまで責任を持つか」をシステムに反映させるルールです。
-
免責と制限:
システムはいつか壊れるものです。「壊れても、会社が倒産するほどの賠償はしません」という約束を規約でします。 - 設計への影響: APIの「叩きすぎ(レート制限)」を実装します。規約で「攻撃的なアクセスは禁止」と書いてあっても、システムで防いでいなければ実害が出てしまうからです。
3. プライバシーの守護(GDPR・個人情報保護法)
「預かったデータをどう扱うか」という世界で最も厳しいルールです。
-
削除権と透明性:
「私のデータを消して」「どんなデータを持ってるか見せて」という要求に、システムが即座に応えられなければなりません。 - 設計への影響: 「論理削除(削除フラグを立てるだけ)」では不十分なケースがあります。物理的にデータを上書き(匿名化)するか、完全に消去するバッチ処理が必要です。
実装時にハマりやすい「3つの落とし穴」
第三者に説明する際、特に以下の3点は「エンジニアのこだわり」が法律と衝突する部分です。
① ログに「親切心」を出さない
バグ調査のために、ログにメールアドレスやユーザー名を出したくなりますが、GDPRの観点ではアウトです。
- 対策: ログには内部的なUUIDのみを出し、特定の個人を直接識別できる情報を混ぜない「クリーンなログ」を徹底します。
② 「消せないデータ」を作らない
「ユーザーを消すと、その人が買った注文履歴の合計金額が狂ってしまうから消したくない」という設計上の悩み。
- 対策: 注文履歴には「誰が買ったか」を直接持たず、ユーザー削除時には「匿名ユーザーA」に紐付けを切り替えるなど、「統計(数字)」と「個人(名前)」を切り離す設計が求められます。
③ 外部サービス(Stripe等)との同期
自分のDBだけ消しても、決済代行会社(Stripe)やメール配信ツール(SendGrid)にデータが残っていると、削除依頼を完遂したことになりません。
- 対策: 削除処理を「自分のDBの更新」で終わらせず、外部APIへ削除命令を飛ばす「後処理(ジョブキュー)」の仕組みをセットで考えます。
「Stripeなどの外部決済サービスと連携し、法的要件(インボイス・GDPR・返金)をシステムとして完結させるための具体的な設計」 について。
バックエンドエンジニアが最も神経を使うのは、 「自社DBとStripe(外部)の状態をどう同期させ、かつ法的に正しい証跡を残すか」 という点です。
1. Webhook処理の堅牢な設計(返金・決済)
Stripeで決済や返金が発生すると、Stripeから自社サーバーへ「Webhook」という通知が届きます。ここで失敗すると「お金は返したのに、DB上は有料会員のまま」といった法的トラブルに直結します。
冪等性(Idempotency)の確保
同じ通知が2回届いても、2回処理してはいけません。
-
実装: 処理済みの
Stripe-Event-IDをDBに保存し、重複していれば無視するロジックを入れます。
ステートマシンの実装例
返金処理を例にすると、DBのステータスを以下のように遷移させます。
Paid(支払い済み) → Refund_Pending(返金処理中) → Refunded(返金完了)
// 返金Webhook処理のイメージ
func HandleRefundWebhook(event StripeEvent) error {
// 1. 既に処理済みでないかチェック(冪等性)
if isProcessed(event.ID) { return nil }
// 2. DBのステータスを「返金完了」に更新
// ここで、もし既に Refunded ならエラーを投げて二重処理を防ぐ
err := db.Model(&Order{}).
Where("id = ? AND status = ?", event.OrderID, "Paid").
Update("status", "Refunded").Error
return err
}
2. インボイス制度:PDFの動的生成と保存
インボイス制度では「適格請求書」を保存・提供する義務があります。Stripeの機能でも生成可能ですが、自社でカスタマイズしたPDFを出す場合、以下のフローが一般的です。
- 決済完了Webhookを受信。
- DBから**「登録番号」「税抜き合計」「最後に一回計算した消費税」**を取得。
- PDF生成ライブラリで請求書を作成。
- S3等のストレージに保存(ユーザーが後からいつでもダウンロードできるようにするため)。
- ユーザーに「請求書発行完了」のメールを飛ばす。
ハマりポイント: 税率が変わった際(将来の増税など)に備え、計算時の税率もレコードに保存しておく必要があります。
3. GDPR:外部サービスを含めた「一括削除」
「ユーザーを削除してほしい」というリクエストが来た際、自社DBだけ消しても、Stripe側に顧客情報(メールアドレス等)が残っているとGDPR違反のリスクがあります。
削除の連鎖(Cascading Delete)
自社の削除APIの中で、外部サービスのデータも消しにいきます。
func CompleteUserDeletion(userID string) {
// 1. 自社DBから個人情報を匿名化/削除
db.AnonymizeUser(userID)
// 2. StripeのCustomerオブジェクトを削除(API経由)
stripe.Customer.Delete(stripeCustomerID)
// 3. SendGrid等の配信リストから削除
mailingService.Remove(email)
}
4. エンジニアが運用で「絶対に守るべき」3箇条
最後に、開発・運用中に無意識にやってしまいがちな「NG行為」をまとめます。
- 本番データのローカル保存禁止 (GDPR)
- 「バグ調査のために本番DBのダンプを自分のPCにダウンロードする」のは、GDPRや個人情報保護法の観点で非常に危険です。必ず「本番環境内蔵のツール」か「匿名化されたデータ」で調査しましょう。
- ログに機密情報を流さない
-
fmt.Printf("%+v", user)のような書き方で、構造体の中にあるPasswordやCreditCardTokenがログに出てしまう事故は非常に多いです。
- 「キャンセル=データ削除」にしない
- サブスクを解約(Cancel)したからといって、すぐにデータを消してはいけません。インボイス(税務)の観点では、「過去にいくら払ったか」という履歴は数年間保存する義務があるからです。
第三者へ説明する際のポイント
「なぜそんなに複雑な実装が必要なの?」と聞かれたら、こう答えましょう。
「インボイスは『お金の証跡』を正しく残すため、GDPRは『個人の尊厳』を守るため、規約は『予期せぬトラブルから会社とユーザーの双方を守るため』に必要な、システムの安全装置なんです。」