1.1.支付宝定义两种类型接口:
1.2 接口列表:
英文名称 | 中文名称 | 使用场景 | 文档 |
spi.alipay.commerce.educate.certification.campuscard.query |
认证学生信息查询 |
支付宝调用认证机构接口验证学生身份 |
|
alipay.commerce.educate.authenticate.campuscard.create | 高校ISV认证信息同步 | 合作ISV同步认证的学生信息到支付宝。 | 查看 |
采用https的方式向服务商发送数据请求。
SPI 的接入流程如下图所示:
支付宝:
第三方系统服务商:
根据支付宝 SPI 的相关规范完成 SPI 的开发。支付宝调用第三方系统服务商SPI服务前需要完成 SPI 功能包的添加并成功发布服务,
步骤 |
事项 |
入口 |
第一步 |
第三方服务商创建一个应用(创建应用),第三方应用授权、小程序,网页应用都可以 |
|
第二步 |
配置应用。登录开放平台,配置应用环境,包括添加应用功能,上传RSA2的公钥、授权回调地址等。 |
获取创建应用并获取 APPID,详见配置密钥 |
第三步 |
添加 SPI 功能包。 实现 SPI 服务时,第三方系统服务商(ISV)需先关联相关产品,关联产品的入口有两个:a.从应用详情页挂载功能包的列表页;b.从能力中心的列表页获取 |
参考添加SPI功能包 |
第四步 |
1.接口管理:查看场景的基本信息以及详细文档 2.接口接入:使用开放平台上提供的测试工具在线测试。 3.在线测试:填入对应的参数。(业务入参【主要包括三部分:header参数、query参数、body参数、】) 4.接口审核:SPI 接口联调通过以后可以发起接口审核. 5.能力发布上线审核 |
参考文档第四步 |
第五步 |
服务端接口开发 1.下载服务端SDK 2.支付宝SPI通信规范说明 3.请求、响应报文格式说明 4.加验签格式说明 5.参考DEMO获取 |
参考文档第五步 |
1.接口名称
spi.alipay.commerce.educate.certification.campuscard.query(认证学生信息查询)
2.2.通信规范
2.2.1 协议规则
外部商户接入支付宝开放平台出口网关,提供的服务必须满足以下规范:
网络传输协议 |
支持 http、https |
数据提交方法 |
支持 GET、POST |
Content-Type |
application/x-www-form-urlencoded |
响应报文格式 |
JSON |
签名算法 |
支持 RSA、RSA2 |
字符集 |
支持 GBK、UTF-8 |
2.2.2 请求报文格式
Header 参数
由 SPI 接口文档中的 Header 参数定义,Header中的key具有固定前缀"x_"。
Query 参数(支付宝开放平台接口统一规范,仅供参考,测试工具中已固定传入,无需在工具中填写Query参数)
字段类型 |
字段 |
类型 |
是否必填 |
描述 |
示例值 |
系统字段(固定) |
method |
String |
是 |
接口 |
alipay.xxx |
charset |
String |
是 |
字符集 |
UTF-8 |
|
version |
String |
是 |
版本号,默认1.0 |
1.0 |
|
biz_app_id |
String |
否 |
商户app_id |
2018XXX123 |
|
invoke_app_id |
String |
否 |
调用方app_id |
2018XXX321 |
|
utc_timestamp |
String |
是 |
时间戳(秒) |
1546077067 |
|
sign_type |
String |
是 |
签名算法,支持RSA、RSA2 |
RSA2 |
|
sign |
String |
是 |
签名值 |
*** |
|
业务字段(自定义) |
无 |
Body 参数
由 SPI 接口文档中的 Body 参数定义,Content-Type为:application/x-www-form-urlencoded
。
名称 | 具体类型 | 是否必填 | 最大长度 | 示例值 | 描述 |
---|---|---|---|---|---|
school_stdcode | String | 必选 | 32 |
4136013438
|
学校国标码
|
name | String | 必选 | 10 |
王小二
|
姓名
|
cert_type | String | 可选 | 10 |
1(身份证),A(护照)
|
证件类型
|
cert_no | String | 可选 | 64 |
21010319940617344X
|
证件号
|
card_number | String | 可选 | 64 |
1334900
|
学号
|
password | String | 可选 | 64 |
123456
|
一卡通密码
|
2.2.3 响应报文格式
响应参数定义
字段 |
类型 |
是否必填 |
描述 |
示例值 |
response |
String |
是 |
JSON格式字符串 |
{"code":"10000","msg":"success","key_1":"Value1"} |
sign |
String |
是 |
签名值 |
TqnBnkILs86FJWRqWWZptqIpSKLIp2vnwod177h7GLyWuLhzgRHpXgXd8GoD 4flyHrHBTycQdiUjWw6VqCE5rYHrJU3iYqI1e0MLlhCb |
app_cert_sn |
String |
否 |
应用证书编号。如果应用在开放门户升级了证书模式,则商户返回报文加签需要使用证书进行加签,同时响应报文需要返回证书编号字段:app_cert_sn |
6cd4ee7e4f31c1adba2380cc65da4a3a |
response 定义
字段 |
类型 |
是否必填 |
描述 |
示例值 |
code |
String |
是 |
错误码只有两种:成功-10000;失败-40004 |
40004 |
msg |
String |
是 |
错误描述:成功-Success;失败-Business Failed |
Business Failed |
sub_code |
String |
否 |
业务错误码,在业务失败的情况下返回,与 SPI 接口文档里的“业务错误码”保持一致 |
INVALID_PARAMS |
sub_msg |
String |
否 |
业务错误描述,在业务失败的情况下返回,与 SPI 接口文档里的“业务错误码”保持一致 |
无效参数 |
业务字段(自定义) |
由 SPI 接口文档中的响应参数见下表 |
业务字段:
名称 | 具体类型 | 是否必填 | 是否脱敏 | 最大长度 | 示例值 | 描述 |
---|---|---|---|---|---|---|
name | String | 必选 | 否 | 10 |
王小二
|
姓名
|
school_stdcode | String | 必选 | 否 | 32 |
4136013438
|
学校国标码
|
school_name | String | 可选 | 否 | 10 |
同济大学
|
学校名称
|
status | String | 可选 | 否 | 10 |
0(在校)
|
在校状态
|
short_code | String | 必选 | 否 | 32 |
1234567890
|
自然人在学校范围内唯一码标识,用于生成校园码
|
expire_at | Date | 必选 | 否 | 10 |
2019-10-01
|
预期毕业(离校)时间,精确到日
|
campus_no | String | 可选 | 否 | 32 |
1234567890
|
学号或教工号
|
ext_info | String | 可选 | 否 | 1024 |
{"email":"092806@tongji.edu.cn"}
|
扩展字段
|
统一对外错误码 | 错误描述 | 解决方案 |
---|---|---|
SYSTEM_ERROR |
系统繁忙
|
通用异常,需要联系ISV
|
SCHOOL_NOT_MAPPING |
学校不匹配
|
学校不匹配
|
STUDENT_NOT_EXIST |
学生不存在
|
学生不存在
|
STUDENT_EXPIRED |
学籍已过期
|
学籍已过期
|
{ "response":{ "code":"10000", "msg":"Success", "name":"王小二", "school_stdcode":"4136013438", "school_name":"同济大学", "status":"0(在校)", "short_code":"1234567890", "expire_at":"2019-10-01", "campus_no":"1234567890", "ext_info":"{\"email\":\"092806@tongji.edu.cn\"}" } ,"sign":"ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE" }
{ "response":{ "code":"40004", "msg":"Business Failed", "sub_code":"INVALID_PARAMS", "sub_msg":"无效参数" }, "sign":"TqnBnkILs86FJWRqWWZptqIpSKLIp2vnwod177h7GLyWuLhzgRHpXgXd8GoD4flyHrHBTycQdiUjWw6VqCE5rYHrJU3iYqI1e0MLlhCb" }
说明:目前支付宝开放平台有两种加密方式,一种是RSA2公私钥方式,一种是证书方式。上例为
RSA2公私钥方式。证书方式可参考
SPI 三方服务接入指南
2.2.5商户验签规则
支付宝出口网关会对http请求加签,签名值放在sign参数中。签名参数包括两部分:业务参数(包括SPI接口定义的header、query、body参数)+系统参数(除去 sign、sign_type 以外的所有系统字段)。所有签名参数组装成待签名的map,然后对此map按照key的ASCII码从小到大排序并生成 k=v 字符串对,k=v对之间以"&"连接,然后待签名字符串按charset设定的编码类型、私钥及加签类型生成签名值。验签流程如下:
a)设置待验签参数
系统参数:
业务参数:
b)根据待验签参数key按ASCII顺序排序
c)生成待验签字符串
biz_app_id=2018XXX123&body_key=body_value&charset=UTF-8&header_key=header_value&invoke_app_id=2018XXX321&method=spi.xxx&query_key=query_value&utc_timestamp=1546077067&version=1.0
d)验签
使用非对称验签算法 RSA(SHA1withRSA)或者 RSA2(SHA256withRSA)对待验签字符串进行验签,具体验签算法由sign_type指定。
验签建议使用 支付宝开放平台SDK 封装的验签工具类进行验签,调用方法如下:
/** * 明文公钥模式:RSA/RSA2验签,sign和sign_type不参与验签 * * @param params 签名参数:业务参数(包括SPI接口定义的header、query、body参数)+系统参数(除去sign、sign_type以外的所有系统字段) * @param publicKey 支付宝公钥明文 * @param charset 验签字符集 * @param charset 验签算法(RSA/RSA2) */ boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String signType) /** * 证书模式:RSA/RSA2验签,sign和sign_type不参与验签 * * @param params 签名参数:业务参数(包括SPI接口定义的header、query、body参数)+系统参数(除去sign、sign_type以外的所有系统字段) * @param alipayPublicCertPath 支付宝公钥证书 * @param charset 验签字符集 * @param charset 验签算法(RSA/RSA2) */ boolean AlipaySignature.rsaCertCheckV1(Map<String, String> params, String alipayPublicCertPath, String charset, String signType)
2.2.6 商户签名规则
商户响应报文必须为 JSON 格式,含 sign 和 response 两个字段,如下:
{ "response":{ "code":"10000", "msg":"Success", "key":"value" }, "sign":"xxx" }
a)生成签名字符串
对 response 节点的值进行加签,待签名字符串为:
{ "code":"10000", "msg":"Success", "key":"value" }
b)签名
签名算法与签名算法一致,有sign_type指定。建议使用 支付宝开放平台SDK 封装的签名工具类进行签名,调用方法如下:
/** * RSA/RSA2签名,sign和sign_type不参与签名 * * @param content * @param privateKey * @param charset * @return * @throws AlipayApiException */ public static String rsaSign(String content, String privateKey, String charset, String signType)
2.3. 服务端实现 DEMO
以下 demo 是通过 Java 实现的 SPI 服务样例,包括验签 支付宝请求报文、业务逻辑处理、商户加签 以及 响应报文构造的逻辑。(该demo仅供参考,不同语言环境可根据该demo的处理思路自行实现)
@RequestMapping(value = "/isv/spi/service") @ResponseBody public String spiService(@RequestHeader HttpHeaders headers, @RequestParam Map<String, String> params) { // http响应结果载体 JSONObject result = new JSONObject(); // 业务处理结果载体 JSONObject response = new JSONObject(); //header中的业务参数也参与签名(可选,根据SPI接口定义而定) params.put("header_biz1", headers.getFirst("header_biz1")); params.put("header_biz2", headers.getFirst("header_biz2")); // 1、验签支付宝请求报文 boolean isPass = AlipaySignature.rsaCheckV1(params, alipayPublicKey, "UTF-8", "RSA2"); if (isPass) { // 2、验签成功:处理业务逻辑,并构造业务处理结果 response.put("code", "10000"); response.put("msg", "Success"); response.put("biz", "value"); JSONObject person = new JSONObject(); person.put("age", "18"); person.put("height", "180"); response.put("person", person); // response中嵌套复杂类型数据结构场景 } else { // 验签失败:构造错误码 response.put("code", "40004"); response.put("msg", "Business Failed"); response.put("sub_code", "ISV-VERIFICATION-FAILED"); response.put("sub_msg", "验签失败"); } // 3、业务处理结果加签 // contentToSign 为 {"code":"10000","msg":"Success","biz":"value","person":{"age":"18","height":"180"}} String contentToSign = response.toJSONString(); String sign = AlipaySignature.rsaSign(contentToSign, isvPrivateKey, "UTF-8", "RSA2"); // 4、构造http响应结果 result.put("sign", sign); result.put("response", response); // 返回json格式响应报文 return result.toJSONString(); }
合作ISV同步认证的学生信息到支付宝,需要调用openapi同步认证数据,默认用户是已认证的状态。
3.1.接口名称:
alipay.commerce.educate.authenticate.campuscard.create(高校ISV认证信息同步)
3.2.入参:
名称 | 具体类型 | 是否必填 | 最大长度 | 示例值 | 描述 |
---|---|---|---|---|---|
cert_no | String | 必选 | 32 |
21020119980615433X
|
证件号
|
cert_type | String | 必选 | 10 |
学生证件类型,默认为1: 1 居民身份证
|
证件类型
|
user_name | String | 必选 | 10 |
王小二
|
用户姓名
|
campus_no | String | 必选 | 32 |
1234567890
|
自然人在学校唯一编号
|
gender | String | 可选 | 10 |
学生性别,0未知,1男,2女,9未说明的性别
|
性别
|
school_stdcode | String | 必选 | 32 |
1234567890
|
学校国标码
|
school_name | String | 必选 | 32 |
同济大学
|
学校名称
|
campus | String | 可选 | 32 |
嘉定校区
|
校区
|
organization | String | 可选 | 128 |
年级/学院/班级
|
组织信息,多个分组以;隔开:AA/BB/CC;A/B/C
|
expire_at | Date | 必选 | 10 |
2019-01-01
|
毕业时间
|
isv_short_code | String | 必选 | 32 |
123456789
|
学生/教职工在学校唯一短号,由isv分配
|
ext_info | String | 可选 | 1024 |
{"name":"123"}
|
扩展字段,json格式
|
card_type | String | 可选 | 10 |
1
|
1学生卡,2教工卡,3临时卡,4其他 不传默认是学生卡
|
名称 | 具体类型 | 是否必填 | 是否脱敏 | 最大长度 | 示例值 | 描述 |
---|---|---|---|---|---|---|
result | String | 必选 | 否 | 10 |
SUCCESS
|
如果学生学籍插入成功,则返回SUCCESS , 失败返回FAIL
|
统一对外错误码 | 内部业务错误码 | 错误描述 | 解决方案 |
---|---|---|---|
SYSTEM_EXCEPTION | SYSTEM_EXCEPTION |
系统繁忙
|
联系支付宝小二处理
|
INVALID_PARAMETER | INVALID_PARAMETER |
参数有误
|
请详细阅读接口文档
|
SCHOOL_NOT_EXIST | SCHOOL_NOT_EXIST |
学校信息不存在
|
请联系支付宝小二入驻学校
|
CONTRACT_ERROR | CONTRACT_ERROR |
学校未签约服务
|
请联系支付宝小二完成学校签约
|
3.3以java为例调用代码
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do","app_id","your private_key","json","GBK","alipay_public_key","RSA2"); AlipayCommerceEducateAuthenticateCampuscardCreateRequest request = new AlipayCommerceEducateAuthenticateCampuscardCreateRequest(); request.setBizContent("{" + "\"cert_no\":\"21020119980615433X\"," + "\"cert_type\":\"学生证件类型,默认为1: 1 居民身份证\"," + "\"user_name\":\"王小二\"," + "\"campus_no\":\"1234567890\"," + "\"gender\":\"学生性别,0未知,1男,2女,9未说明的性别\"," + "\"school_stdcode\":\"1234567890\"," + "\"school_name\":\"同济大学\"," + "\"campus\":\"嘉定校区\"," + "\"organization\":\"年级/学院/班级\"," + "\"expire_at\":\"2019-01-01\"," + "\"isv_short_code\":\"123456789\"," + "\"ext_info\":\"{\\\"name\\\":\\\"123\\\"}\"," + "\"card_type\":\"1\"" + " }"); AlipayCommerceEducateAuthenticateCampuscardCreateResponse response = alipayClient.execute(request); if(response.isSuccess()){ System.out.println("调用成功"); } else { System.out.println("调用失败"); }
响应示例:
{ "alipay_commerce_educate_authenticate_campuscard_create_response":{ "code":"10000", "msg":"Success", "result":"SUCCESS" } ,"sign":"ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE" }