> ## Documentation Index
> Fetch the complete documentation index at: https://docs.helloclever.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook：リアルタイムの決済通知

> 決済ステータスの変更を POST で受け取るため、Hello Clever の Webhook を設定します。ペイロードのリファレンス、エラーコード、セキュリティ、リトライの挙動を含みます。

Hello Clever の Webhook を使うと、決済のステータスが変わるたびに（たとえば `pending` から `authorised`、または `authorised` から `waiting` へ移ったときに）サーバーがリアルタイムで通知を受け取れます。API をポーリングする代わりにエンドポイントを登録すれば、ステータスが変わるたびに Hello Clever が自動的に POST リクエストを送信します。

## Webhook の仕組み

決済のステータスが変わると、Hello Clever は設定された `endpoint_url` に対して、取引の現在の状態を示す JSON のペイロードとともに `POST` リクエストを送信します。受信を確認するため、エンドポイントは HTTP `200` を返す必要があります。返さない場合、Hello Clever は各試行の間に 15 分の間隔を置いて最大3回リトライします。

<Note>
  リトライによって同じペイロードが複数回配信される可能性があるため、Webhook のエンドポイントでは冪等な処理を実装してください。重複排除には取引の識別子を使ってください。[標準のペイロード](#standard-payload)では `uuid`、[Payment Gateway 3 のペイロード](#payment-gateway-3-payload)では `transaction_info.uuid` です。
</Note>

## Webhook の設定

Webhook のエンドポイントは2つの方法で設定できます。

**SDK 連携**：既定の Webhook の URL を登録するには Hello Clever へご連絡ください。SDK 経由で作成されたすべての決済がその URL へ通知を送信します。

**API 連携**：決済作成のリクエストに `webhook_notification` のオブジェクトを含めます。指定した場合、その決済については既定の Webhook の URL を上書きします。

```json theme={null}
{
  "webhook_notification": {
    "endpoint_url": "https://your-domain.com/webhook",
    "authorization_header": "Bearer your-secret-token"
  }
}
```

<ResponseField name="webhook_notification" type="object">
  決済作成のリクエストに含める Webhook の設定です。

  <Expandable>
    <ResponseField name="endpoint_url" type="string" required>
      Hello Clever が Webhook の通知を POST する URL です。
    </ResponseField>

    <ResponseField name="authorization_header" type="string">
      任意。このエンドポイントへのすべての Webhook リクエストの `Authorization` ヘッダーで Hello Clever が送信する値です。送信元を検証できます。
    </ResponseField>
  </Expandable>
</ResponseField>

<a id="payment-statuses" />

<a id="payment-statuses" />

## 決済のステータス

Hello Clever は、決済が次のいずれかのステータスに移るたびに Webhook の通知を送信します。

| ステータス | 説明 |
| - | - |
| `pending` | 顧客が新しい決済を開始したが、まだ進めていない状態です。 |
| `authorised` | 決済がオーソリされ、キャプチャできる状態です。 |
| `waiting` | 決済が承認され、資金の精算を待っている状態です。 |
| `received` | 資金を受け取った状態です。 |
| `expired` | 完了する前に決済のセッションが失効した状態です。 |
| `return_pending` | 返金のリクエストが開始され、処理を待っている状態です。 |
| `return_expired` | 返金のリクエストが失効した状態です（失効期間は 10 日）。 |
| `partially_refunded` | 顧客へ一部の返金が行われた状態です。 |
| `return_received` | 顧客へ全額が返金された状態です。 |
| `return_rejected` | 返金のリクエストが拒否された状態です。システムはリトライしません。 |
| `failed` | エラーまたは拒否により決済が失敗した状態です。 |
| `in_dispute` | 顧客が異議を申立て、審査中の状態です。 |
| `dispute_lost` | 異議が顧客の主張どおりに解決した状態です。争われた金額は加盟店へ戻りません。 |

<Note>
  決済が `in_dispute` のステータスにある場合、異議解決の手続きに証拠を提出するため、Hello Clever のサポートへご連絡ください。
</Note>

## Webhook のオブジェクト

Hello Clever が送信するペイロードの構造は2種類あります。どちらが届くかは決済を作成した製品によって決まるため、項目をマッピングする前に、ご利用の連携がどちらに該当するかを確認してください。

| 製品 | ペイロードの構造 |
| - | - |
| Payment APIs (v1)、Multi-Currency Payment APIs (v2)、Card APIs | `status` を含む取引の項目が最上位に並びます。[標準のペイロード](#standard-payload)を参照してください。 |
| Payment Gateway 3 (v3) | 決済リンクの項目が最上位に並び、取引の状態は `transaction_info` の下に入れ子になります。[Payment Gateway 3 のペイロード](#payment-gateway-3-payload)を参照してください。 |

<a id="standard-payload" />

### 標準のペイロード

Payment APIs、Multi-Currency Payment APIs、Card APIs はいずれもこの構造で送信します。各 Webhook の POST のボディには次の項目が含まれます。

| 項目 | 型 | 説明 |
| - | - | - |
| `uuid` | string | 決済取引の一意の識別子。 |
| `name` | string | 顧客の名前。 |
| `email` | string | 顧客のメールアドレス。 |
| `external_id` | string | この決済に対する自社の参照 ID。 |
| `status` | string | 決済の現在のステータス。 |
| `pay_code` | object | 3DS が必要な場合は `3ds_url` を含みます。決済が失敗した場合は `error_code` と `error_message` を含みます。 |
| `pay_code.3ds_url` | string | 顧客が 3DS の認証を完了するための URL（必要な場合）。 |
| `pay_code.error_code` | string | エラーまたは拒否のコード（例：`issuer_declined`）。 |
| `pay_code.error_message` | string | 人が読めるエラーまたは拒否のメッセージ。 |
| `currency` | string | 取引の通貨コード (ISO 4217)。 |
| `amount` | string | 元の決済金額。 |
| `total` | string | 手数料を含む合計金額。 |
| `paid_amount` | string | 支払われた金額。 |
| `is_refundable` | boolean | 決済が返金可能かどうか。 |
| `payment_method` | string | 使用された決済手段（例：`card`）。 |
| `expired_at` | string | 決済 URL が失効する日時 (UTC)。 |
| `status_text` | string | 現在のステータスの詳細な説明。 |
| `description` | string | 決済リクエストに付随する説明。決済の目的や関連する請求書の参照など。 |
| `gst` | boolean | 取引に GST（物品サービス税）が適用されるかどうか。`true` は GST が含まれることを示します。 |
| `gst_amount` | string | `total` のうち GST に相当する金額。 |
| `pay_by` | string | 決済を完了する必要がある最終の日時。 |
| `stage` | string | 決済がフローのどの段階にあるか。値は製品ごとに異なります。Multi-Currency Payment APIs については [Create Payin の stage](/ja/api/v2/payin) を参照してください。 |
| `webhook_notification` | object | Webhook の設定情報。 |
| `webhook_notification.endpoint_url` | string | Webhook の送信先 URL。 |
| `webhook_notification.authorization_header` | string | マスクされた authorization ヘッダーの値。 |
| `refund_information` | object | 返金に関する情報。 |
| `refund_information.total_amount` | string | 返金可能な合計金額。 |
| `refund_information.refund_amount` | string | 返金された金額。 |
| `refund_information.description` | string | 返金の理由。 |
| `sender_details` | object | 決済手段の詳細。 |
| `sender_details.card` | object | カード決済の詳細。 |
| `sender_details.card.card_type` | string | 使用されたカードの種類。 |
| `sender_details.card.card_brand` | string | カードのブランド（例：`visa`、`mastercard`）。 |
| `sender_details.card.card_last_4` | string | カード番号の下4桁。 |
| `sender_details.card.card_country` | string | カードに紐づく2文字の国コード。 |
| `created_at` | string | 決済が作成された日時。 |
| `token` | object | カードのトークンの詳細（ステータスが `authorised` または `waiting` の場合のみ含まれます）。 |
| `token.id` | string | カードの再利用可能なトークン（形式：`tok_...`）。 |
| `token.type` | string | トークンの種類（例：`card`）。 |

#### ペイロードの例：カード決済

```json theme={null}
{
  "uuid": "QVABPPC7",
  "name": "Hello Clever",
  "email": "test@example.com",
  "external_id": "123",
  "status": "authorised",
  "pay_code": {
    "3ds_url": "https://3ds-example.com"
  },
  "currency": "USD",
  "amount": "100.0",
  "total": "100.0",
  "paid_amount": "0.0",
  "is_refundable": false,
  "payment_method": "card",
  "expired_at": "",
  "webhook_notification": {
    "endpoint_url": "https://webhook.site/12da7803-c4cf-4f32-812d-aaeaecf20d9d",
    "authorization_header": "****"
  },
  "refund_information": {
    "total_amount": "250.0",
    "refund_amount": "10.0",
    "description": "Testing refund"
  },
  "sender_details": {
    "card": {
      "card_type": "card",
      "card_brand": "visa",
      "card_last_4": "1111",
      "card_country": "US"
    }
  },
  "created_at": "2025-05-30T05:11:17.602+0000",
  "token": {
    "id": "tok_dfe1988a1ffc0d6562d3",
    "type": "card"
  }
}
```

#### ペイロードの例：銀行振込

銀行振込や QR の決済方法では、`pay_code` に 3DS の URL ではなく、顧客が決済を完了するために必要な情報が入ります。

```json theme={null}
{
  "uuid": "44QU0367",
  "name": "Hello Clever",
  "email": "test@example.com",
  "external_id": null,
  "status": "waiting",
  "pay_code": {
    "qr_string": "00020101021238610010A000000727013100069704520117101050696623331910208QRIBFTTA53037045405100005802VN62460113B00OCDRJ8YULO0505PGVQR0816TT BWIGBT0BCTYTZ630423BC",
    "bank_information": {
      "bank_name": "TMCP Kien Long (Kienlongbank)",
      "account_number": "101050696623331",
      "account_name": "AABB",
      "branch_name": "TP.Ho Chi Minh",
      "description": "Test Payin",
      "swift_code": "KLBKVNVX"
    },
    "message": "Approved by financial institution"
  },
  "currency": "VND",
  "amount": "10000.0",
  "total": "10000.0",
  "paid_amount": "10000.0",
  "is_refundable": false,
  "payment_method": "vn_vietqr_vnd",
  "expired_at": "2025-12-23T06:00:00.000+0000",
  "webhook_notification": {
    "endpoint_url": "https://your-domain.com/webhook",
    "authorization_header": "****"
  },
  "status_text": "Payment has been approved, awaiting funds to settle",
  "description": "Test Payin",
  "gst": false,
  "gst_amount": 0,
  "pay_by": "2025-09-29T07:46:31.300+0000",
  "sender_details": null,
  "stage": "normal_stage"
}
```

<a id="payment-gateway-3-payload" />

<a id="payment-gateway-3-payload" />

### Payment Gateway 3 のペイロード

Payment Gateway 3 は、平坦な取引オブジェクトではなく決済リンクのオブジェクトを送信します。ハンドラーを実装する際は次の違いに注意してください。

* 決済のステータスは `transaction_info.status` にあります。最上位には**ありません**。
* 金額は文字列ではなく JSON の数値です。
* 顧客の情報は最上位の `name` と `email` ではなく、`sender_info` として届きます。
* 顧客がリンクに対して決済を開始するまで、`transaction_info` は `null` です。

| 項目 | 型 | 説明 |
| - | - | - |
| `uuid` | string | 決済リンクの一意の識別子。 |
| `expires_at` | string | 決済リンクの有効期限（ISO 8601、UTC）。 |
| `payment_url` | string | ホスト型チェックアウトページの URL。 |
| `payin_method_code` | string | 選択された決済方法のコード。顧客が未選択の場合は `null`。 |
| `amount` | number | 元の決済金額。 |
| `description` | string | 決済取引の説明。 |
| `redirect_url` | object | チェックアウト後に顧客が戻る URL。 |
| `redirect_url.success` | string | 成功時に使用されるリダイレクト先の URL。 |
| `webhook_notification` | object | Webhook の設定の詳細。 |
| `webhook_notification.endpoint_url` | string | Webhook の送信先の URL。 |
| `webhook_notification.authorization_header` | string | マスクされた authorization ヘッダーの値。 |
| `sender_info` | object | 決済を送る顧客の情報。 |
| `sender_info.email` | string | 顧客のメールアドレス。 |
| `sender_info.first_name` | string | 顧客の名。 |
| `sender_info.last_name` | string | 顧客の姓。 |
| `sender_info.gender` | string | 顧客の性別。 |
| `sender_info.contact_type` | string | 連絡先の種別（例：`individual`）。 |
| `sender_info.dob` | string | 生年月日。 |
| `sender_info.reg_no` | string | 登録番号。 |
| `sender_info.state` | string | 州または県。 |
| `sender_info.state_code` | string | 州のコード。 |
| `sender_info.city` | string | 市区町村。 |
| `sender_info.address` | string | 番地を含む住所。 |
| `sender_info.postal_code` | string | 郵便番号。 |
| `sender_info.country_code` | string | ISO 2文字の国コード。 |
| `sender_info.phone` | string | 顧客の電話番号。 |
| `sender_info.user_id` | string | 貴社システムにおける顧客の ID。 |
| `sender_info.user_name` | string | 貴社システムにおける顧客のユーザー名。 |
| `sender_info.account_name` | string | 貴社システムにおける口座名。 |
| `sender_info.account_number` | string | モバイルマネーの口座番号。 |
| `sender_info.bank_code` | string | 請求に使用する銀行コード。 |
| `sender_info.document_type` | string | 本人確認書類の種類（例：`passport`）。 |
| `sender_info.document_number` | string | 本人確認書類の番号。 |
| `sender_info.upi_id` | string | 顧客の UPI ID。 |
| `sender_info.utr` | string | UTR（Unique Transaction Reference）番号。 |
| `beneficiary_info` | object | 受取人の情報。 |
| `beneficiary_info.upi_id` | string | 受取人の UPI ID。 |
| `metadata` | object | 取引に付与したカスタムのキーと値。そのまま返されます。 |
| `transaction_info` | object | このリンクに対して作成された取引の状態。顧客が決済を開始するまでは `null`。 |
| `transaction_info.uuid` | string | 取引の UUID。配信の重複排除にはこの値を使ってください。 |
| `transaction_info.currency` | string | 取引の通貨（3文字のコード。例：`USD`、`EUR`、`VND`）。 |
| `transaction_info.amount` | number | 元の決済金額。 |
| `transaction_info.paid_amount` | number | 顧客が実際に支払った金額。 |
| `transaction_info.refund_amount` | number | 顧客に返金された金額。返金がない場合は `0`。 |
| `transaction_info.status` | string | 決済の現在のステータス。[決済のステータス](#payment-statuses)を参照してください。 |
| `transaction_info.status_text` | string | 現在のステータスの詳細な説明。 |

#### ペイロードの例

```json theme={null}
{
  "uuid": "7AMCEF7H",
  "expires_at": "2025-10-02T19:05:03.971Z",
  "payment_url": "https://paylink.cleverhub.co/N0FNQ0VGNAD",
  "payin_method_code": "vn_vietqr_vnd",
  "amount": 3000,
  "description": "Test payin",
  "redirect_url": {
    "success": "https://example.com/success"
  },
  "webhook_notification": {
    "authorization_header": "****",
    "endpoint_url": "https://example.com/webhook"
  },
  "sender_info": {
    "email": "test@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "gender": "male",
    "contact_type": "individual",
    "dob": "1990-01-01",
    "reg_no": "A1234567",
    "state": "Ho Chi Minh",
    "user_id": "user_12345",
    "upi_id": "johndoe@upi",
    "utr": "756690058333",
    "user_name": "john_doe123",
    "phone": "0123456789",
    "country_code": "VN",
    "address": "123 abc",
    "city": "Ho Chi Minh City",
    "postal_code": "700000",
    "account_name": "John Doe",
    "account_number": "233240000000",
    "bank_code": "VCB",
    "document_type": "passport",
    "document_number": "P123456789",
    "state_code": "SG"
  },
  "beneficiary_info": {
    "upi_id": "1234567890@oksbi"
  },
  "metadata": {
    "custom_note": "Priority customer",
    "external_id": "1HUJMW34"
  },
  "transaction_info": {
    "uuid": "3D8G9WJJ",
    "currency": "VND",
    "amount": 3000.0,
    "paid_amount": 0.0,
    "refund_amount": 0.0,
    "status": "pending",
    "status_text": "Customer started a new payment but hasn't proceeded yet."
  }
}
```

<Note>
  Payment Gateway 3 の配信の重複排除には `transaction_info.uuid` を使ってください。最上位の `uuid` は決済リンクを識別するもので、1つのリンクから複数の取引が発生する場合があります。
</Note>

## オーソリ済みカードのトークン

カード決済が `authorised` または `waiting` のステータスに達すると、Hello Clever は Webhook のペイロードに `token` のオブジェクトを含めます。このトークンは安全に保管してください。同じ顧客に対して、カード情報の再入力を求めずに将来の決済を作成できます。

```json theme={null}
{
  "token": {
    "id": "tok_773085396b86562040f4",
    "type": "card"
  }
}
```

<Warning>
  `token` のオブジェクトが Webhook の通知に含まれるのは、ステータスが `authorised` または `waiting` に変わったときのみです。それ以外のステータスの通知には含まれません。
</Warning>

## Webhook のセキュリティ

Hello Clever は2つのセキュリティの仕組みに対応しており、単独でも併用でも利用できます。

### Authorization ヘッダー

Webhook の設定で `authorization_header` を指定すると、Hello Clever はエンドポイントへのすべての Webhook リクエストの `Authorization` ヘッダーでその値を送信します。

```
Authorization: Bearer your-secret-token
```

サーバー側でこのヘッダーを既知のシークレットと照合し、リクエストが Hello Clever からのものであることを確認してください。

### HTTP Webhook 署名 (HMAC-SHA256)

Hello Clever は各 Webhook のペイロードを HMAC-SHA256 で署名し、その署名を `HTTP-WEBHOOK-SIGNATURE` のヘッダーで送信します。

```
HTTP-WEBHOOK-SIGNATURE: ff456ed178c487a2fd323043315320f8ec0908327a1b76f1ad54d9f81a000f9d
```

署名を検証するには、Webhook Secret Key を使って**生のリクエストボディ**の HMAC-SHA256 を再計算し、ヘッダーの値と比較してください。

<Steps>
  <Step title="Webhook Secret Key を確認する">
    Merchant Portal で **Dashboard → Developer → Authentication → Webhook タブ**へ移動し、**Webhook secret key** の欄の値をコピーします。
  </Step>

  <Step title="想定される署名を計算する">
    生のリクエストボディ（JSON をパースして再度文字列化しないこと）と Webhook Secret Key を使います。

    <CodeGroup>
      ```javascript Node.js theme={null}
      const crypto = require('crypto');

      function verifySignature(rawBody, webhookSecretKey, receivedSignature) {
        const expectedSignature = crypto
          .createHmac('sha256', webhookSecretKey)
          .update(rawBody, 'utf8')
          .digest('hex');
        return receivedSignature === expectedSignature;
      }

      // In your request handler:
      const receivedSignature = req.headers['http-webhook-signature'];
      const isValid = verifySignature(req.rawBody, process.env.WEBHOOK_SECRET, receivedSignature);
      ```

      ```python Python theme={null}
      import hmac
      import hashlib

      def verify_signature(raw_body: str, webhook_secret_key: str, received_signature: str) -> bool:
          expected_signature = hmac.new(
              webhook_secret_key.encode('utf-8'),
              raw_body.encode('utf-8'),
              hashlib.sha256
          ).hexdigest()
          return received_signature == expected_signature

      # In your request handler:
      received_signature = request.headers.get('HTTP-WEBHOOK-SIGNATURE')
      is_valid = verify_signature(request.get_data(as_text=True), WEBHOOK_SECRET, received_signature)
      ```

      ```java Java theme={null}
      import javax.crypto.Mac;
      import javax.crypto.spec.SecretKeySpec;
      import java.nio.charset.StandardCharsets;

      public boolean verifySignature(String rawBody, String secret, String receivedSignature) throws Exception {
          Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
          SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
          sha256_HMAC.init(secretKey);
          byte[] hash = sha256_HMAC.doFinal(rawBody.getBytes(StandardCharsets.UTF_8));
          StringBuilder hexString = new StringBuilder();
          for (byte b : hash) {
              String hex = Integer.toHexString(0xff & b);
              if (hex.length() == 1) hexString.append('0');
              hexString.append(hex);
          }
          return hexString.toString().equals(receivedSignature);
      }
      ```

      ```ruby Ruby theme={null}
      require 'openssl'

      def verify_signature(raw_body, webhook_secret_key, received_signature)
        expected_signature = OpenSSL::HMAC.hexdigest('sha256', webhook_secret_key, raw_body)
        received_signature == expected_signature
      end

      # リクエストのハンドラー内で：
      received_signature = request.headers['HTTP-WEBHOOK-SIGNATURE']
      is_valid = verify_signature(request.raw_post, ENV['WEBHOOK_SECRET'], received_signature)
      ```

      ```csharp C# theme={null}
      using System;
      using System.Security.Cryptography;
      using System.Text;

      public static bool VerifySignature(string rawBody, string secret, string receivedSignature)
      {
          var key = Encoding.UTF8.GetBytes(secret);
          using (var hmac = new HMACSHA256(key))
          {
              var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(rawBody));
              var expectedSignature = BitConverter.ToString(hash).Replace("-", "").ToLower();
              return expectedSignature == receivedSignature;
          }
      }

      // リクエストのハンドラー内で：
      // var receivedSignature = Request.Headers["HTTP-WEBHOOK-SIGNATURE"];
      // var isValid = VerifySignature(rawBody, webhookSecret, receivedSignature);
      ```

      ```go Go theme={null}
      package main

      import (
          "crypto/hmac"
          "crypto/sha256"
          "encoding/hex"
      )

      func verifySignature(rawBody, secret, receivedSignature string) bool {
          h := hmac.New(sha256.New, []byte(secret))
          h.Write([]byte(rawBody))
          expectedSignature := hex.EncodeToString(h.Sum(nil))
          return hmac.Equal([]byte(expectedSignature), []byte(receivedSignature))
      }

      // リクエストのハンドラー内で：
      // receivedSignature := r.Header.Get("HTTP-WEBHOOK-SIGNATURE")
      // isValid := verifySignature(string(rawBody), webhookSecret, receivedSignature)
      ```
    </CodeGroup>
  </Step>

  <Step title="比較して受け入れるか拒否する">
    署名が一致すれば、ペイロードは正当で改変されていません。異なる場合はリクエストを拒否してください。改ざんされている、または信頼できない送信元から送られた可能性があります。
  </Step>
</Steps>

<Tip>
  セキュリティを最大化するには、`Authorization` ヘッダーと `HTTP-WEBHOOK-SIGNATURE` の両方を検証してください。`Authorization` ヘッダーは送信者を確認し、署名はペイロードが改ざんされていないことを確認します。
</Tip>

### 署名の検証をテストする

正当なリクエストが検証で拒否される場合は、コードを変更する前に、配信された署名と自分の計算結果を手作業で照合してください。

<Steps>
  <Step title="生のリクエストボディを取得する">
    リクエストのログを開き、届いたままの生のボディをそのままコピーします。整形はせず、JSON をパースして再度シリアライズしないでください。空白やキーの順序も署名の対象に含まれます。
  </Step>

  <Step title="アプリケーションの外で HMAC を計算する">
    任意の HMAC-SHA256 の計算ツールに生のボディを入力し、鍵には Webhook のシークレットキーを指定します。入力のエンコーディングは UTF-8、出力のエンコーディングは小文字の16進数に設定してください。
  </Step>

  <Step title="配信されたヘッダーと比較する">
    同じリクエストの `http-webhook-signature` ヘッダーを確認し、2つの値を比較します。

    ```
    計算結果： ff456ed178c487a2fd323043315320f8ec0908327a1b76f1ad54d9f81a000f9d
    配信された値： ff456ed178c487a2fd323043315320f8ec0908327a1b76f1ad54d9f81a000f9d
    ```

    一致する場合、署名は正当であり、問題はアプリケーション側の処理にあります。異なる場合は、リクエストが改ざんされたか、別の鍵で署名されています。対象のマーチャントの正しい Webhook のシークレットキーを使っているか確認してください。
  </Step>
</Steps>

<Warning>
  シークレットを預けても問題のないツールを使ってください。Webhook のシークレットキーを第三者のウェブサイトに貼り付けると、鍵が漏洩します。`openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" body.json` のようなローカルのツールを推奨します。
</Warning>

## エラー処理とリトライ

エンドポイントが HTTP `200` を返さない場合、Hello Clever は各試行の間に **15 分の間隔**を置いて、Webhook の呼び出しを最大 **3 回**リトライします。

リトライを正しく扱うには次のようにしてください。

* 重い処理を行う前に、Webhook を受け取った時点で直ちに `200` を返してください。
* タイムアウトを避けるため、イベントは非同期に処理してください（キューを使うなど）。
* 重複した配信を検出して破棄するため、取引の識別子を使ってください。`uuid`、Payment Gateway 3 では `transaction_info.uuid` です。

## エラーコード

決済が失敗すると、`pay_code` のオブジェクトに `error_code` と `error_message` が含まれます。次の表はすべてのエラーコードの一覧です。

| エラーコード | メッセージ |
| - | - |
| `account_closed` | 顧客の銀行口座が閉鎖されています。 |
| `amount_invalid` | 決済金額が無効か、許可された金額を超えています。 |
| `amount_too_large` | 指定された金額が許容される上限を超えています。より低い金額で再試行してください。 |
| `amount_too_small` | 指定された金額が許容される下限を下回っています。より高い金額で再試行してください。 |
| `authentication_expired` | カードのオーソリが失効しました。 |
| `authentication_failed` | 決済をオーソリできません。 |
| `authentication_required` | 3D セキュアなどの認証が必要な取引のため、カードが拒否されました。 |
| `capture_not_authorised` | キャプチャの前に、取引が `authorised` のステータスである必要があります。 |
| `card_decline_rate_limit_exceeded` | このカードは拒否された回数が多すぎます。24 時間後に再度請求を試せます。 |
| `card_expired` | カードの有効期限が切れています。 |
| `card_lost` | カードが紛失として届出されているため、決済が拒否されました。 |
| `card_not_supported` | このカードはこの種類の購入に対応していません。 |
| `card_number_incorrect` | カード番号が正しくありません。 |
| `card_restricted` | 顧客はこのカードでこの決済を行えません。 |
| `card_stolen` | カードが盗難として届出されているため、決済が拒否されました。 |
| `card_velocity_exceeded` | 顧客がカードの残高、利用枠、または取引金額の上限を超えました。 |
| `currency_not_supported` | このカードは指定された通貨に対応していません。 |
| `customer_canceled` | 顧客が銀行を通じて決済を停止しました。 |
| `cvc_incorrect` | CVC の番号が正しくありません。 |
| `debit_not_authorised` | 顧客が銀行に対して、この決済が承認されていないものだと申告しました。 |
| `expiry_month_invalid` | 有効期限の月が無効です。 |
| `expiry_year_invalid` | 有効期限の年が無効です。 |
| `fraudulent` | 不正の疑いがあるため、決済が拒否されました。 |
| `generic_declined` | 理由不明でカードが拒否された、または決済がブロックされました。 |
| `insufficient_funds` | 購入を完了するための残高がカードに不足しています。 |
| `issuer_declined` | 理由不明でカードが拒否されました。 |
| `issuer_not_available` | カード発行会社に到達できなかったため、決済をオーソリできませんでした。 |
| `pin_required` | PIN が必要なため、カードが拒否されました。 |
| `pin_try_exceeded` | PIN の試行回数の上限を超えました。 |
| `processing_error` | カードの処理中にエラーが発生しました。後で再試行するか、別の決済手段をお試しください。 |
| `return_url_invalid` | Return URL が無効です。 |
| `transaction_is_blocked` | 取引がブロックされています。すでに支払われている場合は返金されます。 |
| `transaction_duplicated` | 同一の内容の取引が最近送信されました。 |
| `withdrawal_count_limit_exceeded` | 顧客がカードで利用できる残高または利用枠を超えました。 |

## ベストプラクティス

1. 処理を行う前に、すべての受信リクエストで `Authorization` ヘッダーまたは `HTTP-WEBHOOK-SIGNATURE` を検証してください。
2. リトライによる副作用の重複を防ぐため、取引の識別子（`uuid`、Payment Gateway 3 では `transaction_info.uuid`）を使って冪等性を実装してください。
3. リクエストを受け取ったら直ちに `200 OK` を返し、ペイロードは非同期に処理してください。
4. Webhook のエンドポイントの URL は非公開に保ち、公開の場での共有は避けてください。
5. 通信中のペイロードを保護するため、Webhook のエンドポイントには HTTPS を使ってください。
6. 開発中は [webhook.site](https://webhook.site) や [ngrok](https://ngrok.com) のようなローカルトンネルを使ってエンドポイントをテストしてください。


## Related topics

- [リアルタイム A2A 決済の詳細](/ja/platform-overview/payment-concepts/real-time-a2a.md)
- [リアルタイム決済とカード決済の比較](/ja/platform-overview/payment-concepts/rtp-vs-cards.md)
- [Hello Clever におけるリアルタイム決済のセキュリティ](/ja/security/real-time-payments-security.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.