支付宝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. 3月2日,阿里云开源 PolarDB 企业级架构将迎来重磅发布

    简介:2022年3月2日,开源 PolarDB 企业级架构将迎来重磅发布!本次发布会将首次公开开源 PolarDB 的总体结构设计和企业级特性,对 PolarDB for PostgreSQL 的存储 ...

  2. 阿里巴巴云原生 etcd 服务集群管控优化实践

    简介: 这些年,阿里云原生 etcd 服务发生了翻天覆地的变化,这篇文章主要分享一下 etcd 服务在面对业务量大规模增长下遇到的问题以及我们是如何解决的,希望对读者了解 etcd 的使用和管控运维提 ...

  3. 从0开始:500行代码实现 LSM 数据库

    简介: LSM-Tree 是很多 NoSQL 数据库引擎的底层实现,例如 LevelDB,Hbase 等.本文基于<数据密集型应用系统设计>中对 LSM-Tree 数据库的设计思路,结合代 ...

  4. 推荐一个使用 HardLink 硬链接减少重复文件占用磁盘空间的工具

    在 NTFS 文件系统里面,咱可以使用 HardLink 硬链接的方式,将多个重复的文件链接到磁盘的同一份记录里面,从而减少在磁盘里面对重复文件存储多份记录,减少磁盘空间的占用.本文将和大家推荐我所做 ...

  5. 记因为 NVIDIA 显驱错误而让 WPF 应用启动闪退问题

    本文记录一个因为 NVIDIA 显卡驱动错误而让 WPF 应用启动闪退问题 表现是 WPF 应用程序,在启动时,立刻闪退.在事件管理器看到的异常代码是 0xC0000005(Access Violat ...

  6. JavaScript数组Array方法介绍,使用示例及ES6拓展

    数组定义 有次序和编号的一组值 类似数组对象 函数agruments对象,字符串,DOM元素集 实例属性 Array.prototype.length length可以赋值,用以改变数组长度 arr. ...

  7. 不只有 Spring,这四款 Java 基础开发框架同样值得关注! 审核中

    Java 开发不只有 Spring ,今天给大家推荐几个同样优秀的 Java 基础开发框架,为日常项目开发提供更多的选择.答应我,请不要再叫我 Spring 小子了,​好吗? 项目概览: Guice: ...

  8. C语言笔记001-用C语言编写程序

    例2-1  在屏幕上显示一个短句"Hello World!". #include <stdio.h> int main(void) { printf("hel ...

  9. 【HarmonyOS】安装DevEco Studio后检查环境出现ohpm not set up

    按照官网的操作方式,下载完ohpm总是检测不到 打开cmd,发现我之前因为其他项目改过编码 输入 regedit 打开注册表编辑页 按路径[计算机\HKEY_LOCAL_MACHINE\SOFTWAR ...

  10. 深入理解 Swift Combine

    Combine 文中写一些 Swift 方法签名时,会带上 label,如 subscribe(_ subscriber:),正常作为 Selector 的写法时会忽略掉 label,只写作 subs ...