API Rules
协议与证书
EVO Payment API 是 HTTP API,必须使用 HTTPS,TLS 版本不低于 v1.2。客户端在 TLS 握手时需要校验服务端下发的证书。EVO Payment API 的各个接口地址使用知名证书颁发机构(CA)签发的证书。
时间格式
所有 DateTime 类型的 API 字段(例如 merchantTransInfo.merchantTransTime)都遵循 ISO 8601 定义的格式(YYYY-MM-DDThh:mm:ssTZD)。
HTTP 头
向 EVO Payment 发起 API 请求、接收 EVO Payment 报文时,需要以下 HTTP 头参数:
| Header 参数 | 说明 |
|---|---|
| Content-Type | 填 application/json; charset=utf-8,表示请求和应答的报文体必须是 JSON 格式,字符编码为 UTF-8。应答报文中会原样回显。 |
| DateTime | 报文发送时间,格式遵循 ISO 8601,即 YYYY-MM-DDThh:mm:ssTZD,例如 2021-05-26T10:08:25+08:00。应答报文中会原样回显。 |
| KeyID | EVO Payment 分配的签名密钥唯一标识。可选传入;传入时,请求和应答中的值应一致。 |
| MsgID | 商户用于追踪每次 API 请求的 ID。建议使用 UUID 或 GUID,例如 2d21a5715c034efb7e0aa383b885fc7a,长度建议不超过 32 个字符。EVO Payment 不校验该值,会在应答报文中原样回显。使用说明:字段类型 String(1024)。 |
| SignType | 报文签名方式,可选 SHA256、SHA512、HMAC-SHA256 或 HMAC-SHA512。 |
| Authorization | 报文签名,详见“报文签名”。 |
报文签名
每条 API 请求和应答报文都带有需要校验的报文签名。发送方必须在发送前生成签名;接收方必须先校验签名,再执行业务逻辑。生成和校验报文签名的步骤如下。
获取签名密钥
商户需要向收单机构或 EVO Payment 客户经理申请 EVO Payment 分配的签名密钥。密钥为 32 位字符串,按 sid 分配,例如 64b59e70e15445196b1b5d2935f4e1bc。商户应在服务端妥善保管密钥。如果密钥有泄露风险,请及时联系收单机构或 EVO Payment 客户经理重置。
为待发送报文生成签名
向 EVO Payment 发送请求报文前,商户需要生成报文签名并放入请求报文。
步骤 1:构造待签名字符串
使用具体的签名算法之前,商户需要先构造待签名字符串。
按以下方法构造待签名字符串。待签名字符串共 6 行,每行一个参数,除最后一行外,每行以换行符结尾。某一行为空时,该行不存在(直接省略)。换行符为 \n(ASCII 码 0x0A)。
- HTTP 方法:POST/GET/PUT/DELETE。
- 请求 URL:请求路径 + 查询字符串,注意去掉 URL 中的域名部分。
- HTTP 头中的 DateTime:报文发送时间。
- 签名密钥:EVO Payment 分配的密钥。
- HTTP 头中的 MsgID:商户用于追踪每次 API 请求的 ID。
- HTTP body:HTTP 报文体中提交的全部数据。
以下为待签名字符串示例。如果把示例从文档复制到源代码中做验证,请注意空格和换行符:
POST
/g2/v1/payment/mer/S024116/payment
2021-12-31T08:30:59+08:00
64b59e70e15445196b1b5d2935f4e1bc
2d21a5715c034efb7e0aa383b885fc7a
{
"merchantTransInfo": {
"merchantTransID": "e05b93cc849046a6b570ba144c328c7f",
"merchantTransTime": "2021-12-31T08:30:59+08:00"
},
"storeInfo": {
"mcc": "5411"
},
"transAmount": {
"currency": "USD",
"value": "10.00"
},
"validTime": "120",
"returnURL": "https://YOUR_COMPANY.com/RETURNURL",
"paymentMethod": {
"type": "e-wallet",
"e-wallet": {
"paymentBrand": "Alipay"
}
},
"transInitiator": {
"platform": "WEB"
},
"tradeInfo": {
"tradeType": "Sale of goods",
"goodsName": "iPhone 13",
"goodsDescription": "This is an iPhone 13",
"totalQuantity": "2"
},
"webhook": "https://YOUR_COMPANY.com/WEBHOOK",
"metadata": "This is a metadata"
}步骤 2:计算签名值
使用 SHA256、SHA512、HMAC-SHA256 或 HMAC-SHA512 计算待签名字符串的哈希值,得到的哈希值即为签名值。HMAC 类签名方式使用的密钥就是 EVO Payment 分配的签名密钥。
例如,对上述待签名字符串使用 SHA256,签名值为 41e4d284fce485523b62a20922ade75f92469c7eed742dfaa0d8e0b4f213f0ae。
例如,对上述待签名字符串使用 HMAC-SHA256,签名值为 ef949039abf8ba97f82cb80afb2e595a0edccfea9c330ff39cc40d9cf1ec3e05。
步骤 3:将签名放入 HTTP 头
将签名算法填入 HTTP 头 SignType,将签名值填入 Authorization,示例如下:
SignType: SHA256
Authorization: 41e4d284fce485523b62a20922ade75f92469c7eed742dfaa0d8e0b4f213f0ae这样就得到了一条带签名、可以发送给 EVO Payment 的 HTTP 请求报文:
POST /g2/v1/payment/mer/S024116/payment HTTP/1.1
Host: {EVO_Cloud_DOMAIN_NAME.com}
DateTime: 2021-12-31T08:30:59+08:00
MsgID: 2d21a5715c034efb7e0aa383b885fc7a
SignType: SHA256
Authorization: 41e4d284fce485523b62a20922ade75f92469c7eed742dfaa0d8e0b4f213f0ae
Content-Type: application/json
Content-Length: 815
{
"merchantTransInfo": {
"merchantTransID": "e05b93cc849046a6b570ba144c328c7f",
"merchantTransTime": "2021-12-31T08:30:59+08:00"
},
"storeInfo": {
"mcc": "5411"
},
"transAmount": {
"currency": "USD",
"value": "10.00"
},
"validTime": "120",
"returnURL": "https://YOUR_COMPANY.com/RETURNURL",
"paymentMethod": {
"type": "e-wallet",
"e-wallet": {
"paymentBrand": "Alipay"
}
},
"transInitiator": {
"platform": "WEB"
},
"tradeInfo": {
"tradeType": "Sale of goods",
"goodsName": "iPhone 13",
"goodsDescription": "This is an iPhone 13",
"totalQuantity": "2"
},
"webhook": "https://YOUR_COMPANY.com/WEBHOOK",
"metadata": "This is a metadata"
}校验 EVO Payment 报文的签名
强烈建议商户校验 EVO Payment 发来报文的签名,包括 API 应答报文和通知报文。
以下为 EVO Payment 针对上述请求示例返回的应答报文,后面的步骤演示如何校验它的签名:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
DateTime: 2021-12-31T08:30:59+08:00
MsgID: 2d21a5715c034efb7e0aa383b885fc7a
SignType: SHA256
Authorization: 5ebcac84d8438af64bf9ef7f1fe0b63014ac05e3f2abb4c82c817aa7b9108b49
{
"result": {
"code": "S0000",
"message": "Success",
"pspResponseCode": "PAYMENT_IN_PROCESS",
"pspMessage": "Payment is processing."
},
"action": {
"type": "redirectUser",
"redirectData": {
"url": "https://ALIPAY_REDIRECT_URL.com",
"method": "GET"
}
},
"paymentMethod": {
"e-wallet": {
"paymentBrand": "Alipay"
}
},
"payment": {
"status": "Pending",
"merchantTransInfo": {
"merchantTransID": "e05b93cc849046a6b570ba144c328c7f",
"merchantTransTime": "2021-12-31T08:30:59+08:00"
},
"evoTransInfo": {
"evoTransID": "6a3b2e6b5ab74d6da7202cdf8e97fa6e",
"evoTransTime": "2021-12-31T00:30:59Z"
},
"pspTransInfo": {
"pspTransID": "012650163996361073624683217162626594RAUmxGgaUF202112190006141885",
"pspTransTime": "2021-12-31T08:30:59+08:00"
},
"transAmount": {
"currency": "USD",
"value": "10.00"
}
},
"pspData": {
"name": "Alipay"
},
"metadata": "This is a metadata"
}步骤 1:构造待签名字符串
获取应答报文体,按“报文签名”步骤 1 中的相同规则构造待签名字符串。
:::caution[注意]
- GET 类请求的应答,待签名字符串中也需要加入应答报文体。
- 如果请求 URL 中带有查询参数,校验签名时,待签名字符串的“请求 URL”一行也需要带上这些查询参数。
:::
以下为待签名字符串示例:
POST
/g2/v1/payment/mer/S024116/payment
2021-12-31T08:30:59+08:00
64b59e70e15445196b1b5d2935f4e1bc
2d21a5715c034efb7e0aa383b885fc7a
{
"result": {
"code": "S0000",
"message": "Success",
"pspResponseCode": "PAYMENT_IN_PROCESS",
"pspMessage": "Payment is processing."
},
"action": {
"type": "redirectUser",
"redirectData": {
"url": "https://ALIPAY_REDIRECT_URL.com",
"method": "GET"
}
},
"paymentMethod": {
"e-wallet": {
"paymentBrand": "Alipay"
}
},
"payment": {
"status": "Pending",
"merchantTransInfo": {
"merchantTransID": "e05b93cc849046a6b570ba144c328c7f",
"merchantTransTime": "2021-12-31T08:30:59+08:00"
},
"evoTransInfo": {
"evoTransID": "6a3b2e6b5ab74d6da7202cdf8e97fa6e",
"evoTransTime": "2021-12-31T00:30:59Z"
},
"pspTransInfo": {
"pspTransID": "012650163996361073624683217162626594RAUmxGgaUF202112190006141885",
"pspTransTime": "2021-12-31T08:30:59+08:00"
},
"transAmount": {
"currency": "USD",
"value": "10.00"
}
},
"pspData": {
"name": "Alipay"
},
"metadata": "This is a metadata"
}:::tip[提示]
重要:对于通知报文,待签名字符串中的请求 URL取请求报文中的 webhook,webhook 由商户自行定义。如果 webhook 没有 Path 部分,则该行不存在,例如:
:::
POST
2021-12-31T08:30:59+08:00
64b59e70e15445196b1b5d2935f4e1bc
2d21a5715c034efb7e0aa383b885fc7a
{
"eventCode": "Payment",
"paymentMethod": {
"e-wallet": {
"paymentBrand": "Alipay"
}
},
"payment": {
"status": "Pending",
"merchantTransInfo": {
"merchantTransID": "e05b93cc849046a6b570ba144c328c7f",
"merchantTransTime": "2021-12-31T08:30:59+08:00"
},
"evoTransInfo": {
"evoTransID": "6a3b2e6b5ab74d6da7202cdf8e97fa6e",
"evoTransTime": "2021-12-31T00:30:59Z"
},
"pspTransInfo": {
"pspTransID": "012650163996361073624683217162626594RAUmxGgaUF202112190006141885",
"pspTransTime": "2021-12-31T08:30:59+08:00"
},
"transAmount": {
"currency": "USD",
"value": "10.00"
}
},
"pspData": {
"name": "Alipay"
},
"metadata": "This is a metadata"
}步骤 2:计算签名值
读取 HTTP 头中的 SignType,用 SignType 指定的算法计算待签名字符串的哈希值。EVO Payment 发出的报文会使用与你 POST 请求相同的 SignType。
在上面的示例中,对待签名字符串使用 SHA256 计算,签名值为 5ebcac84d8438af64bf9ef7f1fe0b63014ac05e3f2abb4c82c817aa7b9108b49。
步骤 3:校验签名值
读取 HTTP 头中的 Authorization,与上面计算出的签名值比对。
- 一致:商户继续执行业务逻辑。
- 不一致:商户不要继续执行业务逻辑,先检查签名密钥是否正确;如有需要,联系 EVO Payment 客户经理协助处理。
参数规则
与 EVO Payment 对接时,商户需要注意以下参数规则并做相应实现:
- 请求能否成功与查询参数的顺序无关。
- 请求能否成功与请求 JSON 报文体中键值对的顺序无关。
- 处理应答时,不要假定应答 JSON 报文体中键值对的顺序。
- 新版本 API 可能会新增查询参数,或在报文体中新增 JSON 键值对。
- 请求或应答报文体中,值为 null 的 JSON 键值对可以省略。
- API 应答中的数据可能包含商户传入的信息,这些信息可能由用户输入且尚未校验。为避免 XSS(跨站脚本)攻击,调用方在使用应答数据前,应根据场景做适当的转义或过滤。
- 所有字段的长度都按字节数计算,而不是字符数。
幂等性
概述
幂等性是一项 Web API 设计原则:同一操作执行多次,结果与第一次执行后相同。EVO Payment API 保证 GET、PUT 和 DELETE 请求的幂等性,因此重试这些请求始终是安全的。由于网络请求难免会偶发失败,商户需要一种与服务端对齐失败请求的机制,幂等性正是这样的机制。
PUT 和 DELETE 请求的幂等性
严格来说,幂等性的要点并不是多次相同的请求得到相同的应答,而是商户多次发送同一请求后,服务端状态与只发送一次时相同。为了简化商户的异常处理,EVO Payment 通过 HTTP 头 Idempotency-Key: <key> 对 PUT 和 DELETE 请求做幂等处理。
主要特性
- 幂等键(Idempotency Key):
- 报文的唯一标识,最长 64 个字符(建议使用 UUID)。
- 如果商户没有收到应答(例如超时),可以带上相同的请求头安全地重试。
- 处理逻辑:
- 如果 EVO Payment 已处理过该请求,会直接返回第一次请求的应答,不会重复处理。
- EVO Payment 的幂等层会把新请求的参数与原请求比对;参数不一致时返回错误,防止误用。
- 键存储:
- 幂等键在 EVO Payment 中按门店级别保存,即幂等键在同一
sid下唯一。 - 幂等键自首次提交起至少保留 24 小时,也可能保留更久。
- 幂等键在 EVO Payment 中按门店级别保存,即幂等键在同一
- 执行与校验:
- 只有 API 接口开始执行后,才会保存结果。
- 如果请求参数校验失败,由于 API 接口尚未执行,不会保存幂等结果,可以放心重试。
- 重试策略:
- 重试交易时建议使用指数退避策略,避免请求过于密集而触发限流。了解指数退避。
商户可以通过应答头 Idempotent-Replayed: true 判断该应答是否为 EVO Payment 重放的历史执行结果。
为实现幂等处理,EVO Payment 需要维护一个一致且有状态的数据存储,用来比对请求。幂等处理依赖该数据存储的可用性。
万一该数据存储不可用,API 会返回 HTTP 503 – Service Unavailable。商户可以稍后使用同一幂等键重试。如果多次收到 503,商户可以暂停处理、稍后再重试,也可以不传 Idempotency-Key 头,退回到非幂等处理。
GET 和 POST 请求的幂等性
GET 请求本身就是幂等的,在 GET 请求中传幂等键不起作用,应避免这样做。
EVO Payment API 不保证 POST 请求的幂等性。POST 请求的异常处理方法,见《EVO Payment 接入指南》中的“异常处理”。
返回状态与判断
API 请求成功(HTTP 状态码 200)时,EVO Payment 会在应答报文体中通过 3 类对象向商户描述返回状态:
| 对象名称 | 用途 |
|---|---|
| action | 告知商户需要在前端网页或 App 上执行额外操作才能完成请求。收到含该对象的应答后,商户应使用返回的参数执行 action.type 指定的操作。详见《EVO Payment 接入指南》及第 7 章中各 API 的规范。 |
| result | 表示 API 调用结果。EVO Payment 所有 HTTP 状态码为 200 的应答都包含该对象。商户可以据此判断 API 调用结果,其中也会说明失败原因。详见附录中的结果码。 |
| paymentMethod.token、payment、capture、cancel、refund、customClearance | 这些对象分布在不同的 API 接口中,都包含一个 status 字段,告知商户该对象的状态。商户可以据此判断各对象的业务状态。详见第 7 章中各 API 的规范。 |
收到 EVO Payment 的应答报文后,商户应按以下步骤处理:
- 步骤 1:检查 HTTP 状态码。为 200 时继续。
- 步骤 2:校验签名(通知报文同样需要校验),详见“报文签名”。
- 步骤 3:检查应答报文体中是否有 action 对象。如果有,在前端网页或 App 中执行对应操作。
- 步骤 4:如果应答报文体中没有 action 对象,检查 result 对象,判断 API 调用是否成功。成功则继续。
- 步骤 5:根据不同的 API 请求,检查 paymentMethod.token、payment、capture、cancel、refund 或 dataSubmission 对象中的 status 字段,获取请求的业务状态。

