支付宝spi接口设计验签和返回结果加签注意点,支付宝使用JSONObject对象
支付宝spi接口设计验签和返回结果加签注意点,支付宝使用JSONObject对象
SPI 三方服务接入指南
https://opendocs.alipay.com/isv/spiforisv
服务端实现 Demo
以下 Demo 是通过 Java 实现的 SPI 服务样例,包括验签 支付宝请求报文、业务逻辑处理、商家加签 以及 响应报文构造 的逻辑。
该 Demo 仅供参考,不同语言环境可根据该 Demo 的处理思路自行实现。
@RequestMapping(value = "/isv/spi/service")
@ResponseBody
public String spiService(@RequestParam Map < String , String > params ) {
// http响应结果载体
JSONObject result = new JSONObject();
// 业务处理结果载体
JSONObject response = new JSONObject();
// 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();
}
扩展:return result.toJSONString(); // resultJson
支付宝拿到这个返回的字符串。如何来验签呢?
//模拟接收端的方式
JSONObject jsonObject = JSONObject.parseObject(resultJson);
String responseJson = jsonObject.getString("response");
String signJson = jsonObject.getString("sign");
System.out.println("responseJson=" + responseJson);
System.out.println("signJson=" + signJson); boolean isPass = AlipaySignature.rsaCheck(responseJson, signJson,pub_key, "UTF-8", "RSA2");
System.out.println("isPass=" + isPass);
需要注意点:
1. 接口使用Map来接收 @RequestParam Map < String , String > params
// 1、验签支付宝请求报文
boolean isPass = AlipaySignature.rsaCheckV1( params , alipayPublicKey , "UTF-8" , "RSA2" );
你先验签,然后再解析,验证不用管map是啥,我们传什么你们正常验证就行。而不是使用对象或单个字段来接收,无法确保接收的数据是全部的。不然影响签名。
支付宝调用过来的:是支付宝使用支付宝私钥签名,商户端使用支付宝公钥验签。
同理:如果是商户对结果加签,是使用商户的私钥签名,支付宝接收到后使用商户的公钥来验签。
使用map获取签名的字符串方法:String content = AlipaySignature.getSignCheckContentV1(params);
//map的加签字符是可以固定顺序的。同理:json字符串固定顺序只能通过使用相同的对象(JSONObject)来转换达到字符相同。
public static String getSignCheckContentV1(Map<String, String> params) {
if (params == null) {
return null;
}
params.remove("sign");
params.remove("sign_type");
StringBuilder content = new StringBuilder();
List<String> keys = new ArrayList<String>(params.keySet());
Collections.sort(keys);
int index = 0;
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (StringUtils.areNotEmpty(key, value)) {
content.append((index == 0 ? "" : "&") + key + "=" + value);
index++;
}
}
return content.toString();
}
2. 验签报错,是因为从钉钉里面复制的sign是url编码后的。 而实际接口是未url编码的字符串。
spiServiceCreate occur exception : com.alipay.api.AlipayApiException: RSA2验签遭遇异常,请检查公钥格式或签名是否正确。Signature length not correct: got 267 but was expecting 256
对比看了下,这个日志里面的sign是url编码过的,而在接口请求过来的sign是没有url编码。实际在验证签名的时候,不能使用编码后的字符,如果已经编码过,需要解码。
3.验签用公钥。加签名用私钥。需要注意两端的公私钥,公钥是一致的,如果不一致,则验签不通过。
4.返回结果key参数去掉。无需返回,接口文档中是有这个。 实际会影响对方验证签名。
错误格式,不需要key,影响验签:
{
"response" :{
"code" : "10000" ,
"msg" : "Success" ,
"key" : "value"
},
"sign" : "xxx"
}
正确格式:
{
"response" :{
"code" : "10000" ,
"msg" : "Success" ,
"name" : "lisi"
},
"sign" : "TqnBnkILs86FJWRqWWZptqIpSKLIp2vnwod177h7GLyWuLhzgRHpXgXd8GoD4flyHrHBTycQdiUjWw6VqCE5rYHrJU3iYqI1e0MLlhCb"
数据格式:
response JSONObject
code String
msg String
name String 业务字段
sign String
5.机构合约编号最好用个规范字符串
最好是日期开头20240613+业务标识+业务号+随机+这种有点业务语义的
6.Gson == 转义成了 \u003d\u003d, 需要使用 fastJson,涉及到url编码的字符串尽量不使用Gson。
7.需要对比一下,json字符串里面的字段的顺序。 顺序也会影响签名。关键,比如:123,132,321是3个不同的签名字符。
JSON.toJSONString
String contentToSign = jsonObject2.toJSONString();
测试发现:上面两个转换成json字符串,实际上json字符串中的字段顺序不一致,导致签名和验签的contentToSign不一致,验签错误。
结论:支付宝验签是使用的:JSONObject方式来转的json,所以商户端也需要使用JSONObject来转json字符串。
8. response对象是JSONObject对象,而不是String json字符串。
JSONObject jsonObject = (JSONObject) JSON.toJSON(realResponse); //对象转JSONObject
//spiBaseResponseVo.setResponse(JSON.toJSONString(realResponse)); //错误
//测试代码(解决方案,使用JSONObject,同理:项目中可以使用业务VO对象,在签名获取contentToSign字符串的时候,将业务对象转换为JSONObject也可以解决。)
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.internal.util.AlipaySignature; public class AlipaySignature5Test {
//商户私钥
private static String prv_key = "商户私钥";
//商户公钥
private static String pub_key = "商户公钥"; public static void main(String[] args) throws Exception{ // http响应结果载体
JSONObject result = new JSONObject();
// 业务处理结果载体
JSONObject response = new JSONObject(); response.put("code" , "10000");
response.put("msg" , "Success");
response.put("inst_ser_contract_no" , "20240614Test123456789");
// 3、业务处理结果加签 (可选,查看 服务基础配置 章节)
// contentToSign为 {"code":"10000","msg":"Success","biz":"value","person":{"age":"18","height":"180"}}
String contentToSign = response.toJSONString();
System.out.println("content=" + contentToSign);
String sign2 = AlipaySignature.rsaSign(contentToSign,prv_key ,"UTF-8","RSA2");
System.out.println("sign2=" + sign2); // 4、构造http响应结果
result.put("sign" , sign2);
//可选,查看 服务基础配置
result.put("response" , response);
// 返回json格式响应报文
String resultJson = result.toJSONString();
System.out.println("接口返回的结果=" + resultJson);
//直接拿resultJson验签会返回false
//boolean isPass = AlipaySignature.rsaCheck(resultJson, sign2,pub_key, "UTF-8", "RSA2"); //接收端的方式
JSONObject jsonObject = JSONObject.parseObject(resultJson);
String responseJson = jsonObject.getString("response");
String signJson = jsonObject.getString("sign");
System.out.println("responseJson=" + responseJson);
System.out.println("signJson=" + signJson); boolean isPass = AlipaySignature.rsaCheck(responseJson, signJson,pub_key, "UTF-8", "RSA2");
System.out.println("isPass=" + isPass);
}
}
//测试代码(解决方案,使用JSONObject,同理:项目中可以使用业务VO对象,在签名获取contentToSign字符串的时候,将业务对象转换为JSONObject也可以解决。)
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.internal.util.AlipaySignature; public class AlipaySignature5Test {
//商户私钥
private static String prv_key = "商户私钥";
//商户公钥
private static String pub_key = "商户公钥"; public static void main(String[] args) throws Exception{ // http响应结果载体
JSONObject result = new JSONObject();
// 业务处理结果载体
JSONObject response = new JSONObject(); response.put("code" , "10000");
response.put("msg" , "Success");
response.put("inst_ser_contract_no" , "20240614Test123456789");
// 3、业务处理结果加签 (可选,查看 服务基础配置 章节)
// contentToSign为 {"code":"10000","msg":"Success","biz":"value","person":{"age":"18","height":"180"}}
String contentToSign = response.toJSONString();
System.out.println("content=" + contentToSign);
String sign2 = AlipaySignature.rsaSign(contentToSign,prv_key ,"UTF-8","RSA2");
System.out.println("sign2=" + sign2); // 4、构造http响应结果
result.put("sign" , sign2);
//可选,查看 服务基础配置
result.put("response" , response);
// 返回json格式响应报文
String resultJson = result.toJSONString();
System.out.println("接口返回的结果=" + resultJson);
//直接拿resultJson验签会返回false
//boolean isPass = AlipaySignature.rsaCheck(resultJson, sign2,pub_key, "UTF-8", "RSA2"); //接收端的方式
JSONObject jsonObject = JSONObject.parseObject(resultJson);
String responseJson = jsonObject.getString("response");
String signJson = jsonObject.getString("sign");
System.out.println("responseJson=" + responseJson);
System.out.println("signJson=" + signJson); boolean isPass = AlipaySignature.rsaCheck(responseJson, signJson,pub_key, "UTF-8", "RSA2");
System.out.println("isPass=" + isPass);
}
}
支付宝spi接口设计验签和返回结果加签注意点,支付宝使用JSONObject对象的更多相关文章
- Django 支付宝付款接口的使用
我们在开发的过程中经常会碰到调用微信或者支付宝接口进行付款,付款完成之后,如果用户绑定了我的账号,我只要有活动了,就要给这个关注我的用户推动消息,让用户知道,比如说,我们经常会关注一些公众号,然后这些 ...
- Http请求加签、验证操作
加签.验签的作用 常见的http请求交互过程中,请求参数通过url或者request body等形式传输.但是由于http请求的开放性,使得请求参数很容易被拦截篡改.因此,需要对请求参数进行加签,然后 ...
- Slickflow.NET 开源工作流引擎基础介绍(五) -- 会签加签高级特性介绍
前言:会签和加签是常见审批流程模式,在引擎中,对这两种流程模式做了分别定义和实现,其中也用到了Workflow Pattern的Multiple Instance(多实例) . 1. 会签和加签的定义 ...
- 工作流一期上线原创小故事——【加签】OR【不准】
亲!您有过选择[加签]还是审核[不准]的烦恼吗? 加签分为:向前加签和向后加签,这个相信大家都很熟悉了吧. 审核分为:准和不准,就是√和×,这个相信大家也很熟悉了. 提示①:相邻的2个人审核时,如果意 ...
- Java Http接口加签、验签操作方法
1.业务背景 最近接触了一些电商业务,发现在处理电商业务接口时,比如淘宝.支付类接口,接口双方为了确保数据参数在传输过程中未经过篡改,都需要对接口数据进行加签,然后在接口服务器端对接口参数进行验签,确 ...
- 25-javaweb接入支付宝支付接口
想熟悉支付宝接口支付,后面可能会用,不如在课设中试试手.好吧听说支付宝不微信支付要简单些,就拿支付宝的先练下手吧. 基本学习流程,百度一下,找篇博客看下. 推荐下面这个篇博客,讲的挺好的,复制过来. ...
- Android开发---支付宝功能接口(支付功能)(转载!)
最近在做一个关于购物商城的项目,项目里面付款这块我选的是调用支付宝的接口,因为用的人比较多. 在网上搜索了以下,有很多这方面的教程,但大部分教程过于陈旧,而且描述的过于简单.而且支付宝提供的接口一直在 ...
- RSA加密解密及RSA加签验签
RSA安全性应用场景说明 在刚接触RSA的时候,会混淆RSA加密解密和RSA加签验签的概念.简单来说加密解密是公钥加密私钥解密,持有公钥(多人持有)可以对数据加密,但是只有持有私钥(一人持有)才可以解 ...
- python调用支付宝支付接口
python调用支付宝支付接口详细示例—附带Django demo代码 项目演示: 一.输入金额 二.跳转到支付宝付款 三.支付成功 四.跳转回自己网站 在使用支付宝接口的前期准备: 1.支付宝公 ...
- python调用支付宝支付接口详细示例—附带Django demo代码
项目演示: 一.输入金额 二.跳转到支付宝付款 三.支付成功 四.跳转回自己网站 在使用支付宝接口的前期准备: 1.支付宝公钥 2.应用公钥 3.应用私钥 4.APPID 5.Django 1.11. ...
随机推荐
- 大数据时代下的App数据隐私安全
简介:随着信息技术快速发展,大数据为我们带来信息共享.便捷生活的同时,还存在着数据安全问题,主流商业模式下APP面临新的挑战.工信部持续开展APP侵权整治活动,进行了了六批次集中抽检,检查了76万款 ...
- 压测场景下的 TIME_WAIT 处理
简介: 压测场景下的 TIME_WAIT 处理 1. 序 某专有云项目具备压测场景,在Windows的压测机上用 LoadRunner 进行业务的压力测试,压测运行一段时间后出现大量端口无法分配的报错 ...
- VisualStudio 禁用移动文件到文件夹自动修改命名空间功能
在 VisualStudio 2022 里的某个版本开始,将会在移动文件到其他文件夹时,自动修改命名空间,使用匹配文件夹路径的命名空间.如果这个功能能顺手将其他引用此类型的全部符号同时变更,那自然是很 ...
- pnpm的基本原理及快速使用
基本原理 前置知识:软件链接与硬链接 软链接(符号链接Symbolic link):是一类特殊的文件, 其包含有一条以绝对路径或者相对路径的形式指向其它文件或者目录的引用.在window快捷方式上和其 ...
- Servlet注解的使用,简化配置 以及,使用模板方法设计模式优化oa项目
Servlet注解的使用,简化配置 以及,使用模板方法设计模式优化oa项目 每博一文案 有句谚语说:"一怒之下踢石头,只有痛着脚趾头." 比一件糟糕的事情更可拍的,是你用糟糕的态度 ...
- 【Oracle】Oracle数据库多实例安装
需求:因为需要从RAC的多实例迁移至单虚拟机的多实例.因此,简要概述一下,如何安装数据库的多实例. 不管是Oracle 11g还是10g的多实例,其基本思路都是一致的. 1.调用dbca 在root账 ...
- GitLab 管理 NuGet 包
1 概览 在服务器上构建项目时,需要引用 nuget.org 之外的包,如公司内部开发的.第三方未发布到 nuget.org 上的.怎么办? GitLab 提供了 Package Registry 来 ...
- 手机自适应的单位rem,与自适应网页的区别
一个网站有的会分为pc站和移动站,有的网站只有pc站,而现在更多的是自适应的站点.现在针对自适应的网页设计有很多模板,如bootstrap,它会让你轻松定制一个只适应网站,而现在大多数的网站并不是靠程 ...
- kettle使用3-增量同步(插入的时候判断数据是否存在,存在就更新,不存在就插入)
1.新建转换 2.在DB连接中,新建2个数据库连接 3.在输入中,新建:表输入 4.在输入中,新建:表输入 5.在输出中,新建:插入/更新 说明:更新字段: 是说更新目的表时候,哪些列更新,哪些不更新 ...
- 一个网格合并(weld)小工具
在日常开发中会有需求合并多个Mesh网格,并且它们重合处的顶点也要合并,而并非合并成两个subMesh. 而近期刚好在学习Geomipmap的细分,需要把多个mesh块进行合并,于是写了这个脚本 (简 ...