# 签名

{% hint style="info" %}
UseePay 使用签名-验签名机制来防止数据传输的过程中被篡改。目前 API 集成 UseePay 支持两种签名方式: MD5, RSA。无论是 MD5 还是 RSA 方式, sign 和 value trim后为空的 key-value 都不参与签名计算过程!
{% endhint %}

## MD5 <a href="#md5" id="md5"></a>

### 生成MD5密钥

在 UseePay 商户后台的**配置中心->密钥管理**中点击获取MD5密钥

![](/files/F1ZVxlN2A4u6iRjH7rfq)

### 生成MD5签名规则

1. 按照ASCII码生序对请求的 payload 进行排序
2. 按第一步获取的结果，进行key-value 遍历, 对 value trim  后不为空的 key-value 使用 URL 键值对的格式进行拼接(key1=value1\&key2=value2...),
3. 第2步取到字符串, 拼接上 \&pkey=商户的MD5密钥, 然后对该字符串进行MD5, 得到的值即为签名值

### MD5签名生成示例

{% tabs %}
{% tab title="Node.js" %}

```
function getPayload(){
    payload['version'] = '1.0'
    payload['autoRedirect'] = false
    payload['transactionId'] = ORDER_ID_IN_YOUR_SYSTEM
    payload['transactionType'] = 'pay'
    payload['transactionExpirationTime'] = 14400
    payload['appId'] = YOUR_APP_ID
    payload['amount'] = 1234
    payload['currency'] = 'USD'
    
    const userInfo = {
        userId: USER_ID_IN_YOUR_SYSTEM,
        phoneNo: USER'S_PHONE_NO,
        email: USER'S_EMAIL,
        IP: USER'S_IP
    }
    payload['userInfo'] = JSON.stringify(userInfo)
    
    const payerInfo = {
        paymentMethod: 'credit_card',
        authorizationMethod: 'cvv'
    }
    const billingAddress = {}
    billingAddress['houseNo'] = CUSTOMER'S_HOUSE_NO
    billingAddress['email'] = CUSTOMER'S_EMAIL
    billingAddress['phoneNo'] = CUSTOMER'S_PHONE_NO
    billingAddress['firstName'] = CUSTOMER'S_FIRST_NAME
    billingAddress['lastName'] = CUSTOMER'S_LAST_NAME
    billingAddress['street'] = CUSTOMER'S_STREET
    billingAddress['postalCode'] = CUSTOMER'S_POSTAL_CODE
    billingAddress['city'] = CUSTOMER'S_CITY
    billingAddress['state'] = CUSTOMER'S_STATE
    billingAddress['country'] = CUSTOMER'S_COUNTRY // ISO 3166-1-alpha-2
    payerInfo['billingAddress'] = billingAddress
    payload['payerInfo'] = JSON.stringify(payerInfo)
    
    const orderInfo = {
        subject: ORDER_SUBJECT_IN_YOUR_SYSTEM,
    }
    const goodsInfo = Array()
    goodsInfo.push(
      {
        id: SKU_ID_IN_YOUR_SYSTEM,
        name: PRODUCT_NAME,
        price: PRICE,
        quantity: QUANTITY,
        url: PRODUCT_LINK,
        image: IMAGE_OF_PRODUCT
      },
      {    
        id: SKU_ID_IN_YOUR_SYSTEM,
        name: PRODUCT_NAME,
        price: PRICE,
        quantity: QUANTITY,
        url: PRODUCT_LINK,
        image: IMAGE_OF_PRODUCT
      },
    )
    const shippingAddress = {}
    shippingAddress['houseNo'] = CUSTOMER'S_HOUSE_NO
    shippingAddress['email'] = CUSTOMER'S_EMAIL
    shippingAddress['phoneNo'] = CUSTOMER'S_PHONE_NO
    shippingAddress['firstName'] = CUSTOMER'S_FIRST_NAME
    shippingAddress['lastName'] = CUSTOMER'S_LAST_NAME
    shippingAddress['street'] = CUSTOMER'S_STREET
    shippingAddress['postalCode'] = CUSTOMER'S_POSTAL_CODE
    shippingAddress['city'] = CUSTOMER'S_CITY
    shippingAddress['state'] = CUSTOMER'S_STATE
    shippingAddress['country'] = CUSTOMER'S_COUNTRY // ISO 3166-1-alpha-2
    orderInfo['goodsInfo'] = goodsInfo
    orderInfo['shippingAddress'] = shippingAddress
    payload['orderInfo'] = JSON.stringify(orderInfo)
    
    payload['signType'] = 'MD5'
    payload['merchantNo'] = YOUR_MERCHANT_NO
    payload['notifyUrl'] = ASYNC_NOTIFY_URL
    payload['echoParam'] = ECHO_PARAM
    payload['sign'] = calcMD5(payload)

}

function calcMD5(payload) {
  const data = Object.keys(payload)
    .sort()
    .reduce((obj, key) => {
      obj[key] = payload[key]
      return obj
    }, {})
  var str = ''
  Object.keys(data).forEach((key) => {
    if (data[key] != '' && key != 'sign') {
      str = str + key + '=' + data[key] + '&'
    }
  })
  str = str + 'pkey=' + YOUR_MD5_SECRET_KEY
  return md5(str)
}
```

{% endtab %}

{% tab title="php" %}

{% endtab %}

{% tab title="Java" %}

{% endtab %}
{% endtabs %}

## RSA

### 生成商户RSA密钥对

{% hint style="info" %}
注意: RSA的格式为 1024 PKCS8
{% endhint %}

访问如 <http://www.metools.info/code/c80.html> 生成密钥对:

![](/files/Ip7HIhVAGtsCAy2hdkug)

### 上传商户RSA公钥

{% hint style="info" %}
注意, 请将公钥字符串中的首行(-----BEGIN PUBLIC KEY-----)与尾行(------END PUBLICK KEY-----)删除, 然后将中间部分格式化成一行粘贴进去
{% endhint %}

![](/files/wMDpiQzB3qx2t4G8iIFm)

### 保存 UseePay RSA 公钥

在上图中, 你需要保存 UseePay RSA 公钥, 请求的响应数据, UseePay 会使用 UseePay 的 RSA 私钥进行签名, 并返回 sign 值, 你需要使用 UseePay RSA 公钥进行验签&#x20;

### 生成RSA签名规则

1. 按照ASCII码生序对请求的 payload 进行排序
2. 按第一步获取的结果，进行key-value 遍历, 对 value trim  后不为空和 key 不为 sign 的 key-value 使用 URL 键值对的格式进行拼接(key1=value1\&key2=value2...),
3. 对第2步取到字符串, 用之前生成的商户 RSA 密钥进行签名做 base64, 获取 sign 值

### RSA签名生成示例

{% tabs %}
{% tab title="Node.js" %}

```
// Node.js 示例倒入了 node-rsa
const NodeRSA = require('node-rsa')

function getPayload(){
    payload['version'] = '1.0'
    payload['autoRedirect'] = false
    payload['transactionId'] = ORDER_ID_IN_YOUR_SYSTEM
    payload['transactionType'] = 'pay'
    payload['transactionExpirationTime'] = 14400
    payload['appId'] = YOUR_APP_ID
    payload['amount'] = 1234
    payload['currency'] = 'USD'
    
    const userInfo = {
        userId: USER_ID_IN_YOUR_SYSTEM,
        phoneNo: USER'S_PHONE_NO,
        email: USER'S_EMAIL,
        IP: USER'S_IP
    }
    const billingAddress = {}
    billingAddress['houseNo'] = CUSTOMER'S_HOUSE_NO
    billingAddress['email'] = CUSTOMER'S_EMAIL
    billingAddress['phoneNo'] = CUSTOMER'S_PHONE_NO
    billingAddress['firstName'] = CUSTOMER'S_FIRST_NAME
    billingAddress['lastName'] = CUSTOMER'S_LAST_NAME
    billingAddress['street'] = CUSTOMER'S_STREET
    billingAddress['postalCode'] = CUSTOMER'S_POSTAL_CODE
    billingAddress['city'] = CUSTOMER'S_CITY
    billingAddress['state'] = CUSTOMER'S_STATE
    billingAddress['country'] = CUSTOMER'S_COUNTRY // ISO 3166-1-alpha-2
    payerInfo['billingAddress'] = billingAddress
    payload['userInfo'] = JSON.stringify(userInfo)
    
    const payerInfo = {
        paymentMethod: 'credit_card',
        authorizationMethod: 'cvv'
    }
    payload['payerInfo'] = JSON.stringify(payerInfo)
    
    const orderInfo = {
        subject: ORDER_SUBJECT_IN_YOUR_SYSTEM,
    }
    const goodsInfo = Array()
    goodsInfo.push(
      {
        id: SKU_ID_IN_YOUR_SYSTEM,
        name: PRODUCT_NAME,
        price: PRICE,
        quantity: QUANTITY,
        url: PRODUCT_LINK,
        image: IMAGE_OF_PRODUCT
      },
      {    
        id: SKU_ID_IN_YOUR_SYSTEM,
        name: PRODUCT_NAME,
        price: PRICE,
        quantity: QUANTITY,
        url: PRODUCT_LINK,
        image: IMAGE_OF_PRODUCT
      },
    )
    const shippingAddress = {}
    shippingAddress['houseNo'] = CUSTOMER'S_HOUSE_NO
    shippingAddress['email'] = CUSTOMER'S_EMAIL
    shippingAddress['phoneNo'] = CUSTOMER'S_PHONE_NO
    shippingAddress['firstName'] = CUSTOMER'S_FIRST_NAME
    shippingAddress['lastName'] = CUSTOMER'S_LAST_NAME
    shippingAddress['street'] = CUSTOMER'S_STREET
    shippingAddress['postalCode'] = CUSTOMER'S_POSTAL_CODE
    shippingAddress['city'] = CUSTOMER'S_CITY
    shippingAddress['state'] = CUSTOMER'S_STATE
    shippingAddress['country'] = CUSTOMER'S_COUNTRY // ISO 3166-1-alpha-2
    orderInfo['goodsInfo'] = goodsInfo
    orderInfo['shippingAddress'] = shippingAddress
    payload['orderInfo'] = JSON.stringify(orderInfo)
    
    payload['signType'] = 'RSA'
    payload['merchantNo'] = YOUR_MERCHANT_NO
    payload['notifyUrl'] = ASYNC_NOTIFY_URL
    payload['echoParam'] = ECHO_PARAM
    payload['sign'] = calcRSA(payload)

}

function calcRSA(payload) {
  const data = Object.keys(payload)
    .sort()
    .reduce((obj, key) => {
      obj[key] = payload[key]
      return obj
    }, {})
  var str = ''
  Object.keys(data).forEach((key) => {
    if (data[key] != '' && key != 'sign') {
      str = str + key + '=' + data[key] + '&'
    }
  })
  str = str.substr(0, str.length - 1)
  return new NodeRSA(YOUR_RSA_PRIVATE_KEY, 'pkcs8-private').sign(
    Buffer.from(str),
    'base64',
  )
}
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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://useepay.gitbook.io/developer/guide/signature-guide/signature.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.
