首页 > 其他分享 >支付宝spi接口设计验签和返回结果加签注意点,支付宝使用JSONObject对象

支付宝spi接口设计验签和返回结果加签注意点,支付宝使用JSONObject对象

时间:2024-06-15 15:33:02浏览次数:6  
标签:支付宝 String JSONObject sign spi key put response

支付宝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);
    }
}

 

标签:支付宝,String,JSONObject,sign,spi,key,put,response
From: https://www.cnblogs.com/oktokeep/p/18249346

相关文章

  • 支付宝签名和验签使用JSONObject是最优解。json字符串顺序和==符号都一致演示代码
    支付宝签名和验签使用JSONObject是最优解。json字符串顺序和==符号都一致演示代码支付宝spi接口设计验签和返回结果加签注意点,支付宝使用JSONObject对象https://www.cnblogs.com/oktokeep/p/18249346packagecom.example.core.mydemo;importcom.alibaba.fastjson.JSON;imp......
  • ncverilog与finesim联合进行混合仿真的详细过程(以spice为顶层)
    第一步:Makefile仿真命令one:ncverilog+access+rwc+nc64bit+loadvpi=finesim.so:finesim_startup-frun.f第二步:环境结构(1)以模拟为顶层,顾名思义是把CDL网表中某一个模块替换为数字的function,其余全是CDL,以上图为例,把其中inv替换为数字的function。(2)需要文件:testben......
  • 核心(Hutool-core)工具类(SPI加载工具-ServiceLoaderUtil)
    介绍SPI(ServiceProviderInterface),是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。更多介绍见:https://www.jianshu.com/p/3a3edbcd8f24使用定义一个接口:packagecn.hutool.test.spi;publicinterfaceSPIService......
  • 【SPIE独立出版、有ISBN号和ISSN号、往届均已实现EI检索】第四届计算机视觉、应用与算
    第四届计算机视觉、应用与算法国际学术会议(CVAA2024)The4thInternationalConferenceonComputerVision, ApplicationandAlgorithm(CVAA2024)一、重要信息会议官网:www.iccvaa.org (点击投稿/参会/了解会议详情)会议时间:2024年8月30日-9月1日会议地点:中国-杭州截......
  • 【2024算力大会分会 | SPIE独立出版 | 往届均已完成EI检索】2024云计算、性能计算与深
    【2024算力大会分会|SPIE出版】2024云计算、性能计算与深度学习国际学术会议(CCPCDL2024)2024 InternationalconferenceonCloudComputing,PerformanceComputingandDeepLearning *CCPCDL往届均已完成EI检索,最快会后4个半月完成!一、重要信息大会官网:www.ccpc......
  • PasteSpider的集群组件PasteCluster(让你的项目快速支持集群模式)的思路及实现(含源码
    PasteSpider是什么?一款使用.net编写的开源的Linux容器部署助手,支持一键发布,平滑升级,自动伸缩,Key-Value配置,项目网关,环境隔离,运行报表,差量升级,私有仓库,集群部署,版本管理等!30分钟上手,让开发也可以很容易的学会在linux上部署你得项目![从需求角度介绍PasteSpider(K8S平替部署......
  • .net8 aspire 启动不用https 报ASPIRE_ALLOW_UNSECURED_TRANSPORT故障
    故障显示System.AggregateException:“Oneormoreerrorsoccurred.(The'applicationUrl'settingmustbeanhttpsaddressunlessthe'ASPIRE_ALLOW_UNSECURED_TRANSPORT'environmentvariableissettotrue.Thisconfigurationiscommonlyset......
  • AXI Quad SPI IP核中命令的使用
    1双通道SPI和混合内存模式下支持的常用命令对于配置中Mode设置为Dual且SlaveDevice设置为Mixed的情况,IP核支持表3-1中列出的命令。这些命令在Winbond、Micron和Spansion内存设备上具有相同的命令、地址和数据行为。某些命令,如fastread、dualI/Ofastread和dualoutputf......
  • Renesas MCU之SCI_SPI接口驱动LCD
    目录概述1软硬件介绍1.1软件版本信息1.2 ST7796-LCD1.3 MCUIO与LCDPIN对应关系2FSP配置项目2.1配置项目参数2.2生成项目框架3代码实现 3.1SPI的库函数3.1.1R_SCI_SPI_Open()3.1.2 R_SCI_SPI_Read()3.1.3  R_SCI_SPI_Write()3.2应用函数接口3.......
  • 关于LTspice如何导入第三方的.lib文件进行仿真
    转载自:https://bbs.eeworld.com.cn/thread-1265324-1-1.html1.在芯片官网找到对应的PSPICE模型下载后,将.lib文件移入到路径下的sub文件夹中。(例如C:\Users\\'username'\Documents\LTspiceXVII\lib\sub)2.将.lib文件拖入LTspice后右键单击.subckt后的芯片名称,选择CreatSymbol,即......