# 接口加签 & 验签

为防止中间人攻击和篡改，所有由合作伙伴发起的 API 接口请求都需要在 `header` 中加入签名参数 `X-Fp-Signature`。同样的，由 FaTPay 发起的订单状态变更的回调 webhook，也将加入签名参数 `X-Fp-Signature`。

## API 请求接口加签

在[申请集成](/zh/get-started/onboarding.md)中，<mark style="color:red;">您将生成</mark> <mark style="color:red;"></mark><mark style="color:red;">`APIPrivateKey`</mark>。API 请求接口的签名就是基于这个 `APIPrivateKey` 生成，所以请千万注意不要泄漏。

### 加签步骤

API 接口请求加签主要由以下几个步骤组成。

1. **获取参数**
   * 首先，获取除 `X-Fp-Signature` 外的[公共 header 参数](https://dev.fatpay.org/v/zh/reference/api-reference#common-header-params)，组成一个数组，并<mark style="background-color:orange;">将所有 key 转为小写</mark>
   * 其次，获取接口全部请求参数，并加入数组
   * 去掉数组中 key 或 value 为空的 item
2. **参数排序**
   * 按所有参数名的字典序（ASCII码）升序排序，<mark style="color:green;">**注意只有 key 名进行排序，值不参与排序**</mark>；
3. **生成待签名字符串**
   * 先将排序后的数组转化为`参数名=参数值`形式，并用`&`符连接，得到请求参数字符串；
   * 再拼接得到 待签名字符串，规则为：`请求方法 + 请求域名 + 请求uri + ? + <`请求参数字符串`>`，请求域名不需要带 `http/https`；
4. **摘要签名**
   * 然后对待签名字符串使用 `APIPrivateKey` 做 `RSA-SHA256` 算法进行摘要签名，生成最终的签名。签名 demo 参见 [API 接口加签](/zh/appendix/demo/api-signature-demo.md)；
5. **签名赋值**
   * 最后，将上一步得到的签名赋值到 header 里的 `X-Fp-Signature`，对 FaTPay API 接口进行调用。FaTPay 网关收到请求后，会统一使用 `APIPublicKey` 校验签名，如果通过校验则返回相应的服务端数据。

### 加签示例

假设我们将对 `GET` 方法的 `testSignature` 接口（请注意，<mark style="background-color:blue;">该接口仅为示例说明，实际并不存在</mark>）参数进行加签。该接口 URI 为 `api/testsignature`。

请求参数如下：

{% code lineNumbers="true" %}

```javascript
{
  "page": 1,
  "index": null,
  "size": 10
}
```

{% endcode %}

公共 header 参数如下：

<pre class="language-javascript" data-line-numbers><code class="lang-javascript">{
  "X-Fp-Nonce": 748219,
  "X-Fp-Partner-Id": "mqMBpCIP630LJxLY",
  "X-Fp-Timestamp": 1656600459,
  "X-Fp-Version": "v1.0"
<strong>}
</strong></code></pre>

#### **获取参数**

首先，获取除 `X-Fp-Signature` 外的[公共 header 参数](https://dev.fatpay.org/v/zh/reference/api-reference#common-header-params)，组成一个数组，并<mark style="background-color:orange;">将所有 key 转为小写</mark>

{% code lineNumbers="true" %}

```json
{
  "x-fp-partner-id": "mqMBpCIP630LJxLY",
  "x-fp-timestamp": 1656600459,
  "x-fp-nonce": 748219,
  "x-fp-version": "v1.0"
}
```

{% endcode %}

其次，获取接口全部请求参数，并加入数组

{% code lineNumbers="true" %}

```json
{
  "x-fp-partner-id": "mqMBpCIP630LJxLY",
  "x-fp-timestamp": 1656600459,
  "x-fp-nonce": 748219,
  "x-fp-version": "v1.0"
  "page": 1,
  "index": null,
  "size": 10
}
```

{% endcode %}

去掉数组中 key 或 value 为空的 item，得到：

{% code lineNumbers="true" %}

```json
{
  "x-fp-partner-id": "mqMBpCIP630LJxLY",
  "x-fp-timestamp": 1656600459,
  "x-fp-nonce": 748219,
  "x-fp-version": "v1.0"
  "page": 1,
  "size": 10
}
```

{% endcode %}

**参数排序**

接着按所有参数名的字典序（ASCII码）升序排序，得到：

{% code lineNumbers="true" %}

```javascript
{
  "page": 1,
  "size": 10,
  "x-fp-nonce": 748219,
  "x-fp-partner-id": "mqMBpCIP630LJxLY",
  "x-fp-timestamp": 1656600459,
  "x-fp-version": "v1.0"
}
```

{% endcode %}

**生成待签名字符串**

{% hint style="warning" %}
参数名称及参数值均为大小写敏感
{% endhint %}

先将格式转化为`参数名=参数值`形式，并用`&`符连接，得到请求参数字符串：

{% code overflow="wrap" %}

```
page=1&size=10&x-fp-nonce=748219&x-fp-partner-id=mqMBpCIP630LJxLY&x-fp-timestamp=1656600459&x-fp-version=v1.0
```

{% endcode %}

再拼接得到 待签名字符串，规则为：`请求方法 + 请求域名 + 请求uri + ? + <`请求参数字符串`>`，得到最终待签名的字符串：

{% code overflow="wrap" %}

```html
GETapi.ramp.fatpay.xyz/api/testsignature?page=1&size=10&x-fp-nonce=748219&x-fp-partner-id=mqMBpCIP630LJxLY&x-fp-timestamp=1656600459&x-fp-version=v1.0
```

{% endcode %}

**摘要签名**

然后对待加签字符串使用 `APIPrivateKey` 做 `RSA-SHA256` 算法进行摘要签名，生成最终的签名：

> akZjLiZak0v07CzJoKr7/uKgsAzW2a8DXevy98xg3k6HeOtiU2OyWeEYuQtX/G5EuOs5NeagnIwsIxxiFCQoo6hh2OkgxuEphUQNg1B2HO9cYxpJWRKJfxcf20fJ/OIKFfI75PLMqSGRSmx5tVl+9vP4mBzQwpFtgYok2nrWZU4=

**签名赋值**

最后，将上一步得到的签名赋值到 header 里的 `X-Fp-Signature`，对 FaTPay API 接口进行调&#x7528;**。**

{% code overflow="wrap" %}

```shell
curl -X 'https://api.ramp.fatpay.xyz/api/testsignature?page=1&size=10' \
  -H 'Content-Type: application/json' \
  -H 'X-Fp-Nonce: 748219' \
  -H 'X-Fp-Partner-Id: mqMBpCIP630LJxLY' \
  -H 'X-Fp-Timestamp: 1656600459' \
  -H 'X-Fp-Version: v1.0' \
  -H 'X-Fp-Signature: akZjLiZak0v07CzJoKr7/uKgsAzW2a8DXevy98xg3k6HeOtiU2OyWeEYuQtX/G5EuOs5NeagnIwsIxxiFCQoo6hh2OkgxuEphUQNg1B2HO9cYxpJWRKJfxcf20fJ/OIKFfI75PLMqSGRSmx5tVl+9vP4mBzQwpFtgYok2nrWZU4='
```

{% endcode %}

## Webhook 回调验签

{% hint style="warning" %}
合作伙伴收到 FaTPay webhook 回调后，必须对收到的回调进行签名校验，通过后方可进行后续相应处理。
{% endhint %}

在订单流程中，FaTPay 平台会主动调用回调地址。为提供给合作伙伴校验合法请求的能力，增加 webhook 接口中的签名。商户收到 webhook 回调通知后，可使用 FaTPay 提供 `WebhookPublicKey` 进行验签，校验是否存在参数篡改或伪造。

合作伙伴服务端的具体验签流程和服务端 API 请求的加签流程基本类似。

1. **获取参数**
   * 首先，获取除 `X-Fp-Signature` 外的，以 `X-Fp`开头的 header 参数，组成一个数组，并将所有 key 转为小写。查看 [header 详情](/zh/reference/api-reference/webhooks/webhook-for-order-events.md)
   * 其次，获取接口全部请求参数，并加入数组
   * 去掉数组中 key 或 value 为空的 item
2. **参数排序**
   * 按所有参数名的字典序（ASCII码）升序排序，注意只有 key 名进行排序，值不参与排序；
3. **生成待验签字符串**
   * 先将排序后的数组转化为参数名=参数值形式，并用&符连接，得到请求参数字符串；
   * 再拼接得到 待签名字符串，规则为：请求方法 + 请求域名 + 请求uri + ? + <请求参数字符串>，请求域名不需要带 `http://`和`https://`。<mark style="color:green;">**请注意，此处**</mark><mark style="color:green;">**`请求域名`**</mark><mark style="color:green;">**和**</mark><mark style="color:green;">**`请求 uri`**</mark><mark style="color:green;">**&#x20;**</mark><mark style="color:green;">**为合作伙伴的webhook 回调地址**</mark>；
4. **验证签名**
   * 合作伙伴使用 `X-Fp-Signature` 传过来的值作为待校验的签名，和上述得到的待验签字符串，以及 FaTPay 提供的 `WebhookPublicKey` 进行`RSA-SHA256` 算法验签


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dev.fatpay.org/zh/reference/api-reference/verification.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
