写点什么

支付宝 v3 自签名如何实现

作者:盐焗代码虾
  • 2023-12-21
    浙江
  • 本文字数:3858 字

    阅读完需:约 13 分钟

今天在看文档的时候,发现支付宝新出了一个 v3 版本的接口调用方式,感觉有点意思,花了点时间研究了下这个版本要怎么实现自签名,大家有兴趣可以看看。



什么是支付宝 API v3 版本?

官网上给的解释是基于 OAS3.0 API 规范,支付宝做了相应的 API 升级(v3),在开发体验上进行了重点优化。

与之前支付宝 API 的主要区别有几条:

  • 使用 RESTful 设计风格,并通过 OpenAPI 规范(OAS)描述接口。

  • 使用 JSON 作为数据交互的格式,不再使用 XML 和表单格式。

  • 简化加验签逻辑,对 HTTP 报文整体进行签名。

  • 简化加解密、文件上传等规范。

更多内容及详细介绍可直接参考 v3 协议简介


其实之前也没有 v2 的概念,不过根据对文档整体的理解来看,v2 版本的概念应该就是之前调用的 通用版本 Easy 版本 。

两个版本的差异可以参考下方表格:



顺便提一句,通用版目前是我用的最多的方式,主要是用习惯了不想改(可不是因为偷懒_(:з」∠)_不过新版方式后面也可以试一下看看。

PS:通用版自签名的方法可以参考:[SDK 如何实现签名],SDK 接口调用方式参考:[代码示例]。


如何对接 v3 版本

v3 版本比起 v2 版本来说 改变的地方还是挺多的,最大的改变就是请求的方式,由原本的 RPC 风格切换成了 RESTful

下面我们就来看看 v3 版本自签名是如何调用的

下面以 统一收单交易支付接口 为例。


步骤一:构造 authString

authString 也就是 v3 版本新加的身份鉴权信息。

需要用到的参数有:

  • app_id:开放平台颁发的应用 id。

  • timestamp:Unix 时间戳,精确到毫秒。

  • nonce:自定义参数,自己生成唯一性字符串,每次请求要保证唯一。

组装示例

long Unix=date.getTime();String authString= "app_id="+app_id+",timestamp="+Unix+",nonce=3246658768654544";
复制代码

authString 生成内容

app_id=2014111111111122,timestamp=1702452177941,nonce=3246658768654544
复制代码


步骤二:拼接待签名字符串

需要用到的参数有:

  • authString:上一步返回。

  • httpMethod:本次请求的 http 方法,例如 GET\POST\PUT。

  • httpReuqestUrl:这个从对应接口文档请求 url 中拿,不包含域名值。

  • httpRequestBody:本次请求的 body 内容;GET 方式请求时,入参空字符串。

注意:下方代码示例中的 \n 千万不能丢!!!不然验签通过不了的 QAQ

组装示例

String httpMethod="POST";String httpReuqestUrl=	"/v3/alipay/trade/precreate";String httpRequestBody="{\"out_trade_no\":\"20181128763521373251698\",\"total_amount\":\"1\",\"subject\":\"123\",\"body\":\"body\"}";String content=authString+"\n"+httpMethod+"\n"+httpReuqestUrl+"\n"+httpRequestBody+"\n";System.out.println("content:"+"\n"+content);
复制代码

返回内容

app_id=2021111111111122,timestamp=1702452177941,nonce=3246658768654544POST/v3/alipay/trade/precreate{"out_trade_no":"20181128763521373251698","total_amount":"1","subject":"123","body":"body"}
复制代码

步骤三:生成 sign

需要用到的参数有:

  • content:上一步返回。

  • privateKey:应用私钥,如何获取参考[如何获取商户私钥]。

  • charset:编码格式。

加签方式这边我想吐槽下:文档上加密方式说是支持 SHA256withRSA 和 SM3WithSM2 两种,但是能用国密加签方式为什么不给我国密配置入口哇,不给入口说支持都是耍流氓啊 😓……

签名代码

 private static String doSign(String content,String privateKey,String charset) throws ApiException {        try {            byte[] encodedKey = privateKey.getBytes();            encodedKey = Base64.getDecoder().decode(encodedKey);
PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
Signature signature = Signature.getInstance("SHA256WithRSA"); signature.initSign(priKey); signature.update(content.getBytes(charset)); byte[] signed = signature.sign(); return new String(Base64.getEncoder().encode(signed)); } catch (Exception e) { String errorMessage = "签名遭遇异常,请检查私钥格式是否正确。content=" + content + " privateKeySize=" + privateKey + " reason=" + e.getMessage(); throw new ApiException(errorMessage); } }
复制代码

返回 sign 值

sign:WDF6pS2qK/kEZnsJDMrhNmd/z82ClZ+VMohYxIUs3MZ2j0m+4reQtSBGa6mZyA5ffbIPPvZTRO+1DLEuuCvZRMQGK3okYSA/ASP7GEqfCDeKmkqzKV2kWrmftNfO+EiIiCnsiyJG4SQ9G7s0OtmCT6wVkphW9wgk7mfUoF5a+Wo3kzvEur3U+7ZfSgLa4HXQG2xE+z7BjmHG8j1qVoVa/3TR1lVBAqOwkodZ9cSPKceK2RxaPkk8gsFbofbuARl5xBqDwkS2caTQu27+DLXT/QJOHRHRw5VtH9v8B7nT+nrijFjktm6hD7aIHuPon6TtEgnbtWltRizEZldh+Fo1Eg==
复制代码

步骤四:发起请求

需要注意的参数:

  • url:文档中的「请求 url」。

  • Content-Type:数据类型。

  • alipay-request-id:自定义参数,保证唯一就可以。

  • authorization:${签名算法} ${authString},sign=${signature}。

注意:上方代码 authorization 中的空格不要丢!!

例如

ALIPAY-SHA256withRSA app_id=2014111111111122,timestamp=1702452177941,nonce=3246658768654544,sign=WDF6pS2qK/kEZnsJDMrhNmd/z82ClZ+VMohYxIUs3MZ2j0m+4reQtSBGa6mZyA5ffbIPPvZTRO+1DLEuuCvZRMQGK3okYSA/ASP7GEqfCDeKmkqzKV2kWrmftNfO+EiIiCnsiyJG4SQ9G7s0OtmCT6wVkphW9wgk7mfUoF5a+Wo3kzvEur3U+7ZfSgLa4HXQG2xE+z7BjmHG8j1qVoVa/3TR1lVBAqOwkodZ9cSPKceK2RxaPkk8gsFbofbuARl5xBqDwkS2caTQu27+DLXT/QJOHRHRw5VtH9v8B7nT+nrijFjktm6hD7aIHuPon6TtEgnbtWltRizEZldh+Fo1Eg==
复制代码


参考文档配置 head 和 body;



请求代码

package HttpClientTest;
import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.client.methods.HttpPost;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.util.EntityUtils;
/* * 使用Apache的HttpClient发送GET和POST请求的步骤如下: * 1. 使用帮助类HttpClients创建CloseableHttpClient对象. * 2. 基于要发送的HTTP请求类型创建HttpGet或者HttpPost实例. * 3. 使用addHeader方法添加请求头部,诸如User-Agent, Accept-Encoding等参数. * 4. 对于POST请求,创建NameValuePair列表,并添加所有的表单参数.然后把它填充进HttpPost实体. * 5. 通过执行此HttpGet或者HttpPost请求获取CloseableHttpResponse实例 * 6. 从此CloseableHttpResponse实例中获取状态码,错误信息,以及响应页面等等. * 7. 最后关闭HttpClient资源. * */
public class V3HttpPostTest {
public static void main(String args[]) throws Exception { // 发送请求的url String url = "https://openapi.alipay.com/v3/alipay/trade/precreate";
// 发送请求的内容 String content = "{\"out_trade_no\":\"20181128763521373251698\",\"total_amount\":\"1\",\"subject\":\"123\",\"body\":\"body\"}"; String chearset = "utf-8"; // 创建请求对象:post或者get HttpPost httpPost = new HttpPost(url); // httpClient实例化 CloseableHttpClient httpClient = HttpClients.createDefault(); // 设置类型 // "application/x-www-form-urlencoded","application/json"、multipart/form-data、text/xml httpPost.setHeader("Content-Type", "application/json"); // 调用方的requestId,用于定位一次请求,需要每次请求保持唯一。 httpPost.setHeader("alipay-request-id", "32432432432423421"); httpPost.setHeader("authorization", "ALIPAY-SHA256withRSA app_id=2021111111111122,timestamp=1702452177941,nonce=3246658768654544,sign=WDF6pS2qK/kEZnsJDMrhNmd/z82ClZ+VMohYxIUs3MZ2j0m+4reQtSBGa6mZyA5ffbIPPvZTRO+1DLEuuCvZRMQGK3okYSA/ASP7GEqfCDeKmkqzKV2kWrmftNfO+EiIiCnsiyJG4SQ9G7s0OtmCT6wVkphW9wgk7mfUoF5a+Wo3kzvEur3U+7ZfSgLa4HXQG2xE+z7BjmHG8j1qVoVa/3TR1lVBAqOwkodZ9cSPKceK2RxaPkk8gsFbofbuARl5xBqDwkS2caTQu27+DLXT/QJOHRHRw5VtH9v8B7nT+nrijFjktm6hD7aIHuPon6TtEgnbtWltRizEZldh+Fo1Eg=="); // 支付宝根证书序列号,使用证书模式时,需要传递该值 // httpPost.setHeader("alipay-root-cert-sn", "");
// 组织数据 StringEntity se = null; try { se = new StringEntity(content); // 设置编码格式 se.setContentEncoding(chearset); // 设置数据类型 se.setContentType("application/json"); // post请求,将请求体填充进httpPost httpPost.setEntity(se); // 通过执行httpPost获取实例 HttpResponse response = httpClient.execute(httpPost); HttpEntity entity = response.getEntity(); String resData = EntityUtils.toString(entity); System.out.println("返回消息:" + resData); // 关闭httpClient资源 httpClient.close(); } catch (Exception e) { e.printStackTrace(); }
}}
复制代码

返回信息

{    "out_trade_no":"20181128763521373251698",    "qr_code":"https://qr.alipay.com/bax05640qkuz8quo6tgn2565"}
复制代码


以上就是 支付宝 v3 自签名如何实现 的所有内容了,希望对你有所帮助 ✿✿ヽ(°▽°)ノ✿

用户头像

还未添加个人签名 2023-10-17 加入

还未添加个人简介

评论

发布
暂无评论
支付宝 v3 自签名如何实现_Java_盐焗代码虾_InfoQ写作社区