支付宝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对象的更多相关文章

  1. Django 支付宝付款接口的使用

    我们在开发的过程中经常会碰到调用微信或者支付宝接口进行付款,付款完成之后,如果用户绑定了我的账号,我只要有活动了,就要给这个关注我的用户推动消息,让用户知道,比如说,我们经常会关注一些公众号,然后这些 ...

  2. Http请求加签、验证操作

    加签.验签的作用 常见的http请求交互过程中,请求参数通过url或者request body等形式传输.但是由于http请求的开放性,使得请求参数很容易被拦截篡改.因此,需要对请求参数进行加签,然后 ...

  3. Slickflow.NET 开源工作流引擎基础介绍(五) -- 会签加签高级特性介绍

    前言:会签和加签是常见审批流程模式,在引擎中,对这两种流程模式做了分别定义和实现,其中也用到了Workflow Pattern的Multiple Instance(多实例) . 1. 会签和加签的定义 ...

  4. 工作流一期上线原创小故事——【加签】OR【不准】

    亲!您有过选择[加签]还是审核[不准]的烦恼吗? 加签分为:向前加签和向后加签,这个相信大家都很熟悉了吧. 审核分为:准和不准,就是√和×,这个相信大家也很熟悉了. 提示①:相邻的2个人审核时,如果意 ...

  5. Java Http接口加签、验签操作方法

    1.业务背景 最近接触了一些电商业务,发现在处理电商业务接口时,比如淘宝.支付类接口,接口双方为了确保数据参数在传输过程中未经过篡改,都需要对接口数据进行加签,然后在接口服务器端对接口参数进行验签,确 ...

  6. 25-javaweb接入支付宝支付接口

    想熟悉支付宝接口支付,后面可能会用,不如在课设中试试手.好吧听说支付宝不微信支付要简单些,就拿支付宝的先练下手吧. 基本学习流程,百度一下,找篇博客看下. 推荐下面这个篇博客,讲的挺好的,复制过来. ...

  7. Android开发---支付宝功能接口(支付功能)(转载!)

    最近在做一个关于购物商城的项目,项目里面付款这块我选的是调用支付宝的接口,因为用的人比较多. 在网上搜索了以下,有很多这方面的教程,但大部分教程过于陈旧,而且描述的过于简单.而且支付宝提供的接口一直在 ...

  8. RSA加密解密及RSA加签验签

    RSA安全性应用场景说明 在刚接触RSA的时候,会混淆RSA加密解密和RSA加签验签的概念.简单来说加密解密是公钥加密私钥解密,持有公钥(多人持有)可以对数据加密,但是只有持有私钥(一人持有)才可以解 ...

  9. python调用支付宝支付接口

    python调用支付宝支付接口详细示例—附带Django demo代码   项目演示: 一.输入金额 二.跳转到支付宝付款 三.支付成功 四.跳转回自己网站 在使用支付宝接口的前期准备: 1.支付宝公 ...

  10. python调用支付宝支付接口详细示例—附带Django demo代码

    项目演示: 一.输入金额 二.跳转到支付宝付款 三.支付成功 四.跳转回自己网站 在使用支付宝接口的前期准备: 1.支付宝公钥 2.应用公钥 3.应用私钥 4.APPID 5.Django 1.11. ...

随机推荐

  1. 云原生 DevOps,模型化应用交付能力很重要!

    ​简介: DevOps 文化及其支撑其落地实践的自动化工具与平台能力在云原生架构渐为普及的背后,发挥了关键的价值. 撰稿:溪洋 云原生正在成为企业业务创新和解决规模化挑战的加速器. 云原生带来的变革绝 ...

  2. 图像检索在高德地图POI数据生产中的应用

    ​简介: 高德通过自有海量的图像源,来保证现实世界的每一个新增的POI及时制作成数据.在较短时间间隔内(小于月度),同一个地方的POI 的变化量是很低的. ​ 作者 | 灵笼.怀迩 来源 | 阿里技术 ...

  3. [Nova] belongsTo, belongsToMany 当前页动态 dependsOn 其它 fields, nova-belongs-to-dependency, belongs-to-many-field-nova

    nova-belongs-to-dependency 例子: use Manmohanjit\BelongsToDependency\BelongsToDependency; ... return [ ...

  4. 前端关于获取网络时间的方法api

    淘宝 http://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp 苏宁http://quan.suning.com/getSys ...

  5. 习题8 #第8章 Verilog有限状态机设计-1 #Verilog #Quartus #modelsim

    1. 设计一个"111"串行数据检测器.要求是:当检测到连续3个或3个以上的"1"时输出为1,其他输入情况下输出为0. (1)思路分析:参照本章前文的范例,如第 ...

  6. 一文搞懂drag&drop浏览器拖放功能的实现

    拖放功能,即将一个元素从一个区域,通过拖拽,放置到另一个区域.常见的应用是将文件或图片从一个区域,拖放到另一个区域.中文常常把这表述成拖拽,实际上拖拽的描述并不准确,应该叫拖放,因为drag事件和dr ...

  7. vue解决二级路由redirect(默认路由)不传参的问题

    场景: pageA----pageB(pageB包含三个二级路由) 默认进入pageB时进入第一个页面的路由,之后点击左侧按钮,分别进入其他二级路由 原router.js写法: //应用信息      ...

  8. rails byebug

    Gemfile里添加 gem 'byebug' bundle install 在要打断点的地方写 byebug byebug -h #帮助 c 放行,入下走 n 单行调适 q 退出进行 启动异步任务推 ...

  9. 官宣:Splashtop与JumpCloud合作 提供单次登录远程访问解决方案

    号外! 官宣:Splashtop与JumpCloud合作 提供单次登录远程访问解决方案! 打开百度APP,查看更多高清图片 以下是一本正经的官宣新闻,我是没感情的翻译机器人,嘻嘻. Bad Robot ...

  10. 移动通信网络中的 3A 实现

    目录 文章目录 目录 3A RADIUS Diameter 基本概念 基本协议 应用协议 freeDiameter 3A AAA,即认证(Authentication).授权(Authorizatio ...