# 回調安全性

本文件概述了我們支付閘道解決方案中可用於保護回調端點的安全機制。當支付事件發生時，我們的系統透過 HTTP 請求傳送回調至客戶端的後端。為了確保這些回調的完整性、真實性和安全性，我們提供了三種互補的安全方法：

1. 標頭中的簽名驗證
2. IP 白名單
3. 透過 API 進行交易驗證

用戶端可根據其安全需求，實施其中一種或多種方法。

***

## <mark style="color:blue;">1. 標頭簽名驗證</mark>

### <mark style="color:blue;">概述</mark>

\
每個回調請求的簽章標頭中都包含一個加密簽章。此簽章是使用 HMAC-SHA256 將要求正文與秘密密鑰串列後產生的。客戶可以驗證此簽章，以確保回調來自我們的付款閘道，且未被竄改。

> 注意：您可[在此](https://docs.akashicpay.com/traditional-chinese/yi-biao-ban/kai-fa-zhe#api-mi-yue)找到 API 驗證碼。

簽章產生

\
簽章的計算方式如下：

* **演算法**： HMAC-SHA256
* **輸入**： JSON stringified 請求正文
* **鑰匙**： 客戶端特定的 apiSecret
* **輸出**： 十六進位字串

#### 回調請求格式

* **方法**： POST (預設，可設定)
* **標頭 (Headers)**：
  * 內容類型：application/json
  * 簽章： \<hmac-sha256-signature>
* **本體 (Body)**： 包含付款事件詳細資訊的 JSON 有效負載 (JSON payload containing payment event details)
* **逾時**： 5000ms（預設，可設定）

#### 驗證範例

以下是以各種程式語言驗證簽章的實作範例。

{% tabs %}
{% tab title="TypeScript" %}

```typescript
using System.Security.Cryptography;
using System.Text;

public class SignatureVerifier
{
    public static bool VerifySignature(string body, string signature, string apiSecret)
    {
        using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(apiSecret)))
        {
            byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
            string computedSignature = BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
            return computedSignature == signature;
        }
    }
}
```

{% endtab %}

{% tab title="PHP" %}

```php
function verifySignature($body, $signature, $apiSecret) {
    $computedSignature = hash_hmac('sha256', json_encode($body), $apiSecret);
    return hash_equals($computedSignature, $signature);
}
```

{% endtab %}

{% tab title="Java" %}

```java
// with apiSecret
String apiSecret = "your apiSecret";
IAPSdk akashicPay = APSdkFactory.createSDK(APEnvironment.Development, otk, apiSecret);

String requestBody = "request body";
String headerSignature = "signature from requestHeader('key: Signature')";

boolean isVerify = akashicPay.verifySignature(
  requestBody, headerSignature
);

```

{% endtab %}

{% tab title="C#" %}

```csharp
// with apiSecret
string apiSecret = "your apiSecret";
IApSdk akashicPay = APSdkFactory.createSDK(APEnvironment.Development, otk, apiSecret);

string signature = response.Headers.GetValues("Signature").FirstOrDefault();
string responseBody = await response.Content.ReadAsStringAsync();

return akashicPay.VerifySignature(responseBody, signature);
```

{% endtab %}

{% tab title="Go" %}

```go
ap, err := akashicpay.NewAkashicPay(apKey, apL2Address, "Development", "ApiSecretGoesHere")

if err != nil {
    // Handle error
}

// Returns true if valid, indicating the callback has not been altered
verified, err := ap.VerifySignature(body, signature)
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
require 'openssl'
require 'json'
require 'date'

class SignatureVerifier
  def initialize(api_secret)
    @api_secret = api_secret
  end

  def verify_signature(body, signature)
    raise 'API secret not set' if @api_secret.nil?

    begin
      sorted_body = sort_keys(body)
      serialized = JSON.generate(sorted_body, quirks_mode: true, ascii_only: true)
      normalized_signature = signature.strip.gsub(/\A\*?"|"\*?\z/, '')
      computed = OpenSSL::HMAC.hexdigest('SHA256', @api_secret, serialized)
      computed == normalized_signature
    rescue => e
      puts "Verification error: #{e.message}"
      false
    end
  end

  private

  def sort_keys(obj)
    case obj
    when Array
      obj.map { |item| sort_keys(item) }
    when Hash
      obj.keys.sort.each_with_object({}) do |key, result|
        result[key] = sort_keys(obj[key])
      end
    when Time, Date, DateTime
      # Preserve as-is (like JS Date object passed to JSON.stringify)
      obj
    else
      obj
    end
  end
end
```

{% endtab %}
{% endtabs %}

### <mark style="color:blue;">**最佳實作**</mark>

* 安全地儲存 apiSecret (例如，在環境變數中)。
* 使用安全的比較函數 (例如 PHP 中的 hash\_equals)，以防止定時攻擊。
* 以 401 Unauthorized 回應拒絕缺失或無效簽章的要求。

***

## <mark style="color:blue;">2. IP 白名單</mark>

### <mark style="color:blue;">概述</mark>

為了將回呼要求限制為可信來源，我們提供了一組固定的 IP 位址，回呼將從這些 IP 位址發出。用戶端可在防火牆或應用程式邏輯中將這些 IP 列為白名單。請聯繫 [AkashicPay 客服中心](https://t.me/akashicpay_support_bot)以獲取需要白名單的 IP 列表。

### <mark style="color:blue;">**最佳做法**</mark>

* 設定您的環境以使用適當的 IP 清單 (測試網路或主網路)。
* 定期檢查我們文件中 IP 清單的更新。
* 記錄並監控來自非白名單 IP 的要求，以進行安全稽核。

***

## <mark style="color:blue;">3. 透過 API 進行交易驗證</mark>

### <mark style="color:blue;">概述</mark>

客戶端可以使用 L1 或 L2 交易哈希值查詢我們的 Transaction Detail API，從而獨立驗證提款事件。此方法確認回調中引用的付款事件的有效性。

#### API 端點

**基本領域**

* 正式環境：<https://api.akashicscan.com>
* 測試環境: <https://api.testnet.akashicscan.com>

**端點**

* **L2 哈希值驗證**：\
  GET /api/v0/transactions/transfer?l2Hash=\<L2\_HASH>\
  範例: <https://api.akashicscan.com/api/v0/transactions/transferl2Hash=AS123>...
* **L1 哈希值驗證**：\
  GET /api/v0/transactions/hash?txHash=\<L1\_HASH>\
  範例: <https://api.akashicscan.com/api/v0/transactions/hash?txHash=0x123>...

**回應格式**

* **狀態**： 成功時為 200 OK
* **本體**： 包含交易詳細資訊的 JSON 物件（結構依實作而定）
* **錯誤**： 4xx/5xx 狀態代碼，失敗時會顯示錯誤訊息

### <mark style="color:blue;">**最佳做法**</mark>

* 根據您的環境 (測試環境或正式環境) 使用適當的網域。
* 針對瞬間網路故障實施重試邏輯。
* 快取成功的驗證，以減少 API 呼叫（如適用）。

***

## <mark style="color:blue;">安全考慮</mark>

* 對所有回調端點使用 HTTPS，以加密傳輸中的資料。
* 實施速率限制以防止濫用。
* 記錄所有驗證失敗，以便監控和審計。
* 確保 apiSecret 和其他敏感憑證的安全。
