跳转到内容

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。应答报文中会原样回显。
KeyIDEVO 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)。

  1. HTTP 方法:POST/GET/PUT/DELETE。
  2. 请求 URL:请求路径 + 查询字符串,注意去掉 URL 中的域名部分。
  3. HTTP 头中的 DateTime:报文发送时间。
  4. 签名密钥:EVO Payment 分配的密钥。
  5. HTTP 头中的 MsgID:商户用于追踪每次 API 请求的 ID。
  6. HTTP body:HTTP 报文体中提交的全部数据。

以下为待签名字符串示例。如果把示例从文档复制到源代码中做验证,请注意空格和换行符:

js
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,示例如下:

js
SignType: SHA256
Authorization: 41e4d284fce485523b62a20922ade75f92469c7eed742dfaa0d8e0b4f213f0ae

这样就得到了一条带签名、可以发送给 EVO Payment 的 HTTP 请求报文:

js
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 针对上述请求示例返回的应答报文,后面的步骤演示如何校验它的签名:

js
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[注意]

  1. GET 类请求的应答,待签名字符串中也需要加入应答报文体。
  2. 如果请求 URL 中带有查询参数,校验签名时,待签名字符串的“请求 URL”一行也需要带上这些查询参数。

:::

以下为待签名字符串示例:

js
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 部分,则该行不存在,例如:

:::

js
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 对接时,商户需要注意以下参数规则并做相应实现:

  1. 请求能否成功与查询参数的顺序无关。
  2. 请求能否成功与请求 JSON 报文体中键值对的顺序无关。
  3. 处理应答时,不要假定应答 JSON 报文体中键值对的顺序。
  4. 新版本 API 可能会新增查询参数,或在报文体中新增 JSON 键值对。
  5. 请求或应答报文体中,值为 null 的 JSON 键值对可以省略。
  6. API 应答中的数据可能包含商户传入的信息,这些信息可能由用户输入且尚未校验。为避免 XSS(跨站脚本)攻击,调用方在使用应答数据前,应根据场景做适当的转义或过滤。
  7. 所有字段的长度都按字节数计算,而不是字符数。

幂等性 ​

概述 ​

幂等性是一项 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 小时,也可能保留更久。
  • 执行与校验:
    • 只有 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. 步骤 1:检查 HTTP 状态码。为 200 时继续。
  2. 步骤 2:校验签名(通知报文同样需要校验),详见“报文签名”。
  3. 步骤 3:检查应答报文体中是否有 action 对象。如果有,在前端网页或 App 中执行对应操作。
  4. 步骤 4:如果应答报文体中没有 action 对象,检查 result 对象,判断 API 调用是否成功。成功则继续。
  5. 步骤 5:根据不同的 API 请求,检查 paymentMethod.token、payment、capture、cancel、refund 或 dataSubmission 对象中的 status 字段,获取请求的业务状态。