0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

デジタル商品のオンライン取引を安全に設計する:決済・在庫・Webhook・リトライの考え方

0
Posted at

デジタル商品のオンライン取引を安全に設計する:決済・在庫・Webhook・リトライの考え方

# デジタル商品のオンライン取引を安全に設計する:決済・在庫・Webhook・リトライの考え方

オンラインで販売されるデジタル商品には、物理商品とは異なる設計上の難しさがあります。

たとえば、ゲームのデジタルコード、プリペイド、モバイル通信のチャージ、電子マネーなどでは、

- 決済は成功したが商品が発行されない
- ユーザーが決済ボタンを二重に押す
- Webhookが複数回届く
- 通信タイムアウト後に実際には決済されている
- 同じ商品を二重に発行してしまう

といった問題が発生する可能性があります。

この記事では、デジタル商品のオンライン取引をシステムとして考えた場合に、どのようなポイントに注意すべきかを整理します。

特定の決済サービスや販売サイトの紹介ではなく、デジタル商品の取引システムを設計するときの一般的な考え方をまとめます。

---

## 1. デジタル商品と物理商品の違い

オンラインショップというと、一般的には以下のような流れを想像できます。

```text
注文
 ↓
決済
 ↓
倉庫
 ↓
発送
 ↓
購入者

しかし、デジタル商品では物理的な配送がありません。

たとえばデジタルコードの場合、

注文
 ↓
決済
 ↓
コード発行
 ↓
ユーザーへ通知

となります。

この違いによって、システム設計で重要になるポイントも変わります。

物理商品なら、決済後に発送処理が失敗しても商品を倉庫で保持できます。

一方、デジタル商品では「一度発行したコード」を再利用できないケースがあります。

そのため、

決済済みなのか
発行済みなのか
ユーザーへ配送済みなのか

を明確に管理する必要があります。


2. 基本的な注文フロー

まずは最もシンプルな注文フローを考えてみます。

User
 │
 │ 商品を選択
 ▼
Order API
 │
 │ 注文作成
 ▼
Payment System
 │
 │ 決済
 ▼
Payment Webhook
 │
 │ 決済成功
 ▼
Fulfillment
 │
 │ 商品発行
 ▼
User

ここで重要なのは、

「ユーザーがブラウザ上で決済成功画面を見たこと」と「サーバーが決済成功を確認したこと」は別

という点です。

ブラウザは通信障害によって途中で切断される可能性があります。

そのため、注文の最終状態はクライアント側だけで判断しないほうが安全です。


3. 注文ステータスを明確にする

デジタル商品の販売では、注文ステータスを明確に定義しておくと管理しやすくなります。

例えば、

pending
paid
processing
completed
failed
cancelled

のような状態を用意できます。

それぞれの意味を決めておきます。

pending

注文は作成されたが、まだ決済が完了していない。

paid

決済が成功したことをサーバー側で確認した。

processing

商品発行処理を実行中。

completed

商品発行まで正常に完了した。

failed

商品発行または決済処理で問題が発生した。

cancelled

注文がキャンセルされた。


4. なぜWebhookが重要なのか

決済サービスを利用する場合、Webhookという仕組みが利用されることがあります。

Webhookは、外部サービスから自分のサーバーへイベントを通知する仕組みです。

例えば、

{
  "event": "payment.succeeded",
  "order_id": "ORDER-12345",
  "amount": 50000
}

のようなイベントを受け取ったとします。

サーバー側では、

Webhook受信
 ↓
署名検証
 ↓
イベント確認
 ↓
注文確認
 ↓
決済金額確認
 ↓
注文ステータス更新
 ↓
商品発行

という流れにします。

ここで重要なのは、Webhookの内容を無条件に信用しないことです。


5. Webhookの署名を検証する

外部サービスから送られてきたWebhookが、本当に正しい送信元から送られてきたものなのか確認する必要があります。

一般的には、

  • Secret
  • Signature
  • Timestamp
  • HMAC

などを利用して検証します。

概念的には次のようになります。

const expectedSignature = createSignature(
  payload,
  secret
);

if (expectedSignature !== receivedSignature) {
  throw new Error("Invalid webhook signature");
}

実際の署名方式は利用する決済サービスの公式ドキュメントに従う必要があります。


6. Webhookは複数回届く可能性がある

システム設計で非常に重要なのが「同じWebhookが複数回届く可能性」です。

例えば、

payment.succeeded
payment.succeeded
payment.succeeded

と3回届いたとします。

これを単純に処理すると、

商品発行
商品発行
商品発行

となってしまう可能性があります。

デジタル商品では非常に危険です。


7. Idempotencyを考える

この問題を解決するために重要になるのがIdempotencyです。

簡単に言えば、

同じ処理を複数回実行しても、結果が一度だけになるように設計する

という考え方です。

例えば、

event_id = evt_123456

というイベントIDが送られてきた場合、データベースに処理済みイベントとして保存します。

Webhook
   ↓
event_id確認
   ↓
すでに処理済み?
   ├── YES → 処理終了
   └── NO
        ↓
     処理実行
        ↓
     event_id保存

これだけでも重複処理による事故を減らせます。


8. 注文IDも一意にする

注文にも一意なIDを設定します。

例えば、

ORD-20260819-000001

のようなIDです。

データベースでは、

CREATE UNIQUE INDEX idx_orders_order_id
ON orders(order_id);

のように一意性を保証できます。

また、決済サービス側のtransaction_idなども保存しておくと、後から照合しやすくなります。


9. 金額をクライアントから信用しない

これは決済システムでは非常に重要です。

例えばフロントエンドから、

{
  "product": "voucher",
  "price": 100000
}

というデータが送られてきたとします。

このpriceをそのまま信用してはいけません。

クライアント側のJavaScriptはユーザーによって変更できるためです。

サーバー側で、

product_id
     ↓
Database
     ↓
正式な価格を取得
     ↓
決済金額を生成

という処理にする必要があります。


10. 商品情報と価格情報を分離する

例えばデータベースを、

products
----------------
id
name
category
status

と、

product_prices
----------------
id
product_id
price
currency
active

のように分ける方法があります。

これにより、商品情報と価格情報を柔軟に管理できます。


11. 在庫の考え方

デジタル商品でも「在庫」が存在する場合があります。

例えば、

CODE-AAAA
CODE-BBBB
CODE-CCCC

というプリペイドコードを事前に登録している場合です。

この場合、

available
reserved
sold

などの状態を管理すると安全です。

購入処理では、

available
   ↓
reserved
   ↓
sold

という状態変化を行います。


12. Race Conditionに注意する

複数ユーザーが同時に最後の1個を購入する場合を考えてみます。

User A ─┐
        ├── Last Code
User B ─┘

両方の処理が同時に、

stock = available

と判断すると、同じコードを2人に発行してしまう可能性があります。

このような問題を防ぐために、

  • Database transaction
  • Row Lock
  • Atomic update
  • Unique constraint

などを利用します。

例えばSQLでは、

SELECT *
FROM digital_codes
WHERE status = 'available'
LIMIT 1
FOR UPDATE;

のようなロックを利用する方法があります。

実際の実装では利用するDBの仕様を確認してください。


13. リトライ設計

外部APIとの通信では、タイムアウトが発生することがあります。

例えば、

自分のサーバー
     ↓
外部API
     ↓
レスポンス待ち
     ↓
Timeout

となった場合、

「失敗した」と即断するのは危険です。

外部サービスではすでに処理が成功している可能性があります。

そのため、リトライする場合はIdempotency Keyなどを利用し、

Request
 ↓
Timeout
 ↓
Retry
 ↓
同じ処理として認識

できる設計が重要です。


14. 決済成功と商品発行成功は別々に管理する

個人的に重要だと考えているのがこの部分です。

例えば、

Payment = SUCCESS
Fulfillment = FAILED

という状態があります。

これは、

ユーザーから見ると「お金は払ったが商品を受け取れていない」

という状態です。

したがって、注文データには少なくとも、

payment_status
fulfillment_status

を分けて持つ設計が考えられます。

例えば、

payment_status = paid
fulfillment_status = failed

です。

こうしておけば、商品発行処理だけを再実行できます。


15. 監視とログも重要

デジタル商品の販売システムでは、正常系だけを考えてはいけません。

最低限、

order_created
payment_received
webhook_received
fulfillment_started
fulfillment_completed
fulfillment_failed
refund_requested

などのイベントをログとして残しておくと、障害調査がしやすくなります。

ログには、

  • order_id
  • transaction_id
  • event_id
  • timestamp
  • status

などを記録すると便利です。

ただし、決済情報や個人情報などの機密データをログへそのまま保存することは避けるべきです。


16. ユーザー体験もシステム設計の一部

技術的に正しいシステムでも、ユーザーが状態を理解できなければ使いにくいサービスになります。

例えば、

決済処理中

だけではなく、

お支払いを確認しています。
処理が完了するまで画面を閉じないでください。

など、ユーザーが次に何をすればよいか分かる表示が重要です。

さらに、

注文番号
現在のステータス
購入日時
商品
金額

などを確認できるようにすると、問い合わせ対応もしやすくなります。


17. デジタル商品の購入前に確認したいこと

システムを利用する側のユーザーも、いくつか確認しておくとトラブルを減らせます。

特に重要なのは、

  1. 商品名
  2. 商品の種類
  3. 利用対象
  4. 金額
  5. 有効期限
  6. 対応する地域
  7. 利用方法
  8. キャンセル・返金条件

です。

デジタル商品は物理商品と違って、発行後に返品できないケースがあります。

そのため、購入前に条件を確認することが重要です。


18. オンラインでデジタル商品を購入するときのチェックリスト

最後に、購入者側から見たチェックリストをまとめます。

[ ] 商品内容を確認した
[ ] 利用対象を確認した
[ ] 金額を確認した
[ ] 支払い方法を確認した
[ ] キャンセル条件を確認した
[ ] 正しい電話番号・アカウントを入力した
[ ] 決済完了を確認した
[ ] 注文番号を保存した
[ ] 商品の受取を確認した

特に電話番号やゲームIDなどを入力するタイプの商品では、入力ミスに注意が必要です。


19. 日本と海外のデジタル商品事情

デジタル商品は国によって利用されるサービスが異なります。

例えば日本では、

  • モバイル通信
  • 電子マネー
  • ゲーム関連商品
  • プリペイド
  • オンライン決済

などが広く利用されています。

一方、東南アジアでは、モバイル通信やゲーム、電子ウォレットなどのデジタル商品が日常的な決済手段として利用されるケースがあります。

そのため、デジタル商品のECサイトを設計するときは、単純に商品データだけを管理するのではなく、

Country
Currency
Operator
Product
Payment Method
Fulfillment

の関係を整理しておくと、将来的な拡張がしやすくなります。


20. まとめ

デジタル商品のオンライン販売では、

  • 注文状態
  • 決済状態
  • 商品発行状態
  • Webhook
  • Idempotency
  • 在庫
  • Race Condition
  • Retry
  • Logging
  • Security

など、物理商品のECとは異なるポイントを考える必要があります。

特に重要なのは、

「決済が成功したこと」と「商品が正常に発行されたこと」を別々の状態として管理すること

です。

また、外部APIを利用する場合には、Webhookの署名検証、重複イベントへの対応、リトライ時のIdempotencyなども重要になります。

ユーザー側でも、商品内容・利用条件・金額・対象サービスなどを確認してから購入することで、トラブルを減らすことができます。

デジタル商品の種類や購入方法についてさらに詳しく整理したい場合は、以下のガイドも参考になります。

デジタル商品をオンラインで購入するための完全ガイド

この記事では、オンラインで購入できるデジタル商品の種類、購入時に確認すべきポイント、安全な購入方法などをまとめています。


参考

  • 決済サービスを利用する場合は、それぞれの公式APIドキュメントを確認してください。
  • Webhookを実装する場合は、署名検証やリトライ仕様を必ず確認してください。
  • 個人情報や決済情報を扱う場合は、各サービスおよび関連法令の要件を確認してください。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?