# 接口加签 & 验签

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

## API 请求接口加签

在[get-started](https://dev.fatpay.org/zh/get-started "mention")中，<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-signature-demo](https://dev.fatpay.org/zh/appendix/demo/api-signature-demo "mention")；
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 详情](https://dev.fatpay.org/zh/reference/api-reference/webhooks/webhook-for-order-events)
   * 其次，获取接口全部请求参数，并加入数组
   * 去掉数组中 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` 算法验签
