支付宝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. ...
随机推荐
- 揭秘sealer背后实现整个集群一键交付的奥秘 | 龙蜥技术
简介:解读集群镜像"开箱即用"神器--sealer! 编者按:集群镜像把整个集群看成一台服务器,把 k8s 看成云操作系统,实现整个集群的镜像化打包和交付,为企业级软件提供一种& ...
- 双引擎驱动Quick BI十亿数据0.3秒分析,首屏展示时间缩短30%
简介:在规划中,Quick BI制定了产品竞争力建设的三大方向,包括Quick(快)能力.移动端能力和集成能力.针对其中的产品"报表查看打开慢""报表开发数据同步慢&q ...
- 一文详解 | 开放搜索兼容Elasticsearch做召回引擎
简介:开放搜索发布开源兼容版,支持阿里云Elasticsearch做搜索召回引擎,本文详细介绍阿里云ES用户如何通过接入开放搜索兼容版丰富行业分词库,提升查询语义理解能力,无需开发.算法投入,即可获 ...
- [FAQ] Python list 的值是带有小括号的是什么意思 ?
python 中的 list 即列表,是用中括号 [ ] 表示的数组列表: dict 即字典,是用花括号 { } 表示的 json 对象: tuple 即元祖,是用小括号表示的序列: 见到它们之间的组 ...
- 5.prometheus监控--监控nginx
1.监控程序环境准备 mkdir /data/docker-compose -p cd /data/docker-compose cat > docker-compose.yaml <&l ...
- 八、Doris外部表及数据导入
DorisDB提供了多种导入方式,用户可以根据数据量大小.导入频率等要求选择最适合自己业务需求的导入方式. 数据导入: 1.离线数据导入:如果数据源是Hive/HDFS,推荐采用 Broker Lo ...
- 【python爬虫案例】用python爬取百度的搜索结果!2023.3发布
目录 一.爬取目标 二.展示结果数据 三.编写爬虫代码 3.1 请求头和cookie 3.2 分析请求地址 3.3 分析页面元素 3.4 获取真实地址 3.5 保存结果数据 四.同步讲解视频 五.附完 ...
- mac常用
目录 Mac 删除键(Delete) 这三招你会吗?可大幅加快打字速度 Mac软件打开提示:已损坏,无法打开.您应该将它移到废纸娄 怎么解决? 卸载手动安装的软件 如何在Mac上使用Charles进行 ...
- 一篇文章让你掌握99%的Python运算符。干货很多,建议收藏!!!
Python 中的运算符是编程中的基础概念,用于执行各种操作和数据计算.以下是一些 Python 中的主要运算符的概述: 运算符 1. 算术运算符 算术运算符语法规则 +:加法 -:减法 *:乘法 / ...
- CompletableFuture学习总结
CompletableFuture 简介 在Java8中,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通 ...