对接银行支付API,自己的demo可以调通,放到项目里,却总提示验签失败。原来竟是因为...

这篇具有很好参考价值的文章主要介绍了对接银行支付API,自己的demo可以调通,放到项目里,却总提示验签失败。原来竟是因为...。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

原因是 字符集(charset)不一致

对接一个银行支付通道的支付API,自己java写的demo可以调通,放到项目工程里,部署到环境上,总是收到验签失败的响应。这个问题,困扰我们的开发大兄弟长达一个星期。

对接通道接口联调不通,常见的场景有许多,如:

  • 签名原串需要对key进行排序。不同的排序算法会导致联调不通。
  • json序列化,不同json序列化对数字的支持不同。可能会导致联调不通。
  • 参数大小写拼写错误,会导致联调不通。
  • 等等。

而这次呢,却是字符编码导致的。

各个加密算法,都是基于字节数据进行加密的。例如,下面的md5加密工具方法,在使用MD5算法加密时,首先要把程序中的字符串转换为byte[],见下方代码中的 text.getBytes()。

public static String md5(String text)
        throws NoSuchAlgorithmException, UnsupportedEncodingException {
    MessageDigest md = MessageDigest.getInstance("MD5");
    byte[] digest = md.digest(text.getBytes());
    StringBuilder sb = new StringBuilder();
    for (byte b : digest) {
        sb.append(String.format("%02x", b & 0xff));
    }
    return sb.toString();
}

上面代码调用 String#getBytes将字符串转换为byte数组。而在这个String与byte[]的转换中,涉及到一个很重要的东西————字符编码。对于相同的字符串,不同的编码格式,得到的结果可能不同,对于中文汉字来说正是如此。


其实,技术点就可以简化为如何来理解 String#getBytes()、String#getBytes(charsetName)。与之对应的是 String的构造器String(byte[])、String(byte[], charsetName) 。这就是 String 与 byte数组 的数据互转。我们看一下它们的源码:

// - - - - - 构造器的重载  - - - - -
/**
 * Constructs a new String by decoding the specified array of bytes using the platform's default charset. The length of the new {@code String} is a function of the charset, and hence may not be equal to the length of the byte array.
 *
 * @since JDK1.1
 */
public String(byte bytes[]) {
    this(bytes, 0, bytes.length);
}
 
/**
 * Constructs a new {@code String} by decoding the specified array of bytes using the specified charset. The length of the new {@code String} is a function of the charset, and hence may not be equal to the length of the byte array.
 *
 * @param  bytes - The bytes to be decoded into characters
 * @param  charsetName - The name of a supported {@linkplain java.nio.charset.Charset charset}
 * @throws  UnsupportedEncodingException - If the named charset is not supported
 *
 * @since  JDK1.1
 */
public String(byte bytes[], String charsetName)
        throws UnsupportedEncodingException {
    this(bytes, 0, bytes.length, charsetName);
}
 
// - - - - - getBytes方法的重载  - - - - -
 
/**
 * Encodes this String into a sequence of bytes using the platform's default charset, storing the result into a new byte array.
 *
 * @since JDK1.1
 */
public byte[] getBytes() {
    return StringCoding.encode(value, 0, value.length);
}
 
/**
 * Encodes this {@code String} into a sequence of bytes using the named charset, storing the result into a new byte array.
 * (使用指定的字符集将此字符串编码为字节序列,并将结果存储到一个新的字节数组中。)
 *
 * @param  charsetName - The name of a supported {@linkplain java.nio.charset.Charset charset}
 * @return  The resultant byte array
 * @throws  UnsupportedEncodingException - If the named charset is not supported
 *
 * @since  JDK1.1
 */
public byte[] getBytes(String charsetName)
        throws UnsupportedEncodingException {
    if (charsetName == nullthrow new NullPointerException();
    return StringCoding.encode(charsetName, value, 0, value.length);
}

其中,在两个没有charset参数的String(byte bytes[])、getBytes()方法里,均会获取JVM默认字符集。String csn = Charset.defaultCharset().name();。我们来看一下java.nio.charset.Charset#defaultCharset源码:

/**
 * Returns the default charset of this Java virtual machine.
 * The default charset is determined during virtual-machine startup and typically depends upon the locale and charset of the underlying operating system.
 *
 * @return  A charset object for the default charset
 *
 * @since 1.5
 */
public static Charset defaultCharset() {
    if (defaultCharset == null) {
        synchronized (Charset.class) {
            String csn = AccessController.doPrivileged(
                new GetPropertyAction("file.encoding"));
            Charset cs = lookup(csn);
            if (cs != null)
                defaultCharset = cs;
            else
                defaultCharset = forName("UTF-8");
        }
    }
    return defaultCharset;
}

 

字符集的选择在字符串和字节数组之间的转换中非常重要,特别是当涉及到非ASCII字符时。确保在转换过程中使用一致的字符集,才能正确地保留和还原字符串的内容。我们通过下面的代码来直观地感受一下区别。当 text 里包含 汉字时,不同的字符集在编码时使用不同的编码方式和字节数,编码后的结果就会有所不同; 当我们修改一下 `text = "I like 3 things in this world.";` 时,由于文本中只包含 ASCII 字符,UTF-8、GB2312 和 ISO-8859-1 都使用相同的编码来表示 ASCII 字符,因此最终的字节长度都是相同的。这就是上面String构造器javadoc里的“The length of the new {@code String} is a function of the charset, and hence may not be equal to the length of the byte array.”这句话的含义。

String text = "我是中国人";
System.out.println(text1.getBytes("ASCII").length); // 返回:5
System.out.println(text.getBytes("UTF-8").length); //返回:15
System.out.println(text.getBytes("GB2312").length); //返回:10
System.out.println(text.getBytes("ISO-8859-1").length); //返回:5

 

由上面java.nio.charset.Charset#defaultCharset源码可以看到,Java的默认字符编码通常是平台的默认编码,这个取决于操作系统。例如,在中文Windows系统上,它可能是GBK或GB2312。

Tomcat默认的字符编码是ISO-8859-1。
我们这位开发大兄弟在对接银行通道时,使用java编写的demo,用到的字符集是 GB2312, 而项目是部署到tomcat容器里的, 两者字符集不同。 所以,出现一个行而另一个不行就不难理解了。

 

Base64区分字符集吗?

Base64不区分字符集。 下面两点,可以帮助你理解。
Base64 encode 和 decode 都是基于byte[]进行编码,返回的也是byte[]。
再一点,Base64 编码表使用固定的字符集,包括大小写字母、数字和两个额外的字符作为填充。 我们常见的字符集有 ASCII、Unicode、UTF-8、ISO-8859-1、GBK、GB2312等,其中ASCII是最早的字符集,它定义了 128 个包括字母、数字和一些特殊字符的编码。其他那些字符集均兼容ASCII(重点)。

对接银行支付API,自己的demo可以调通,放到项目里,却总提示验签失败。原来竟是因为...

 

下图进一步帮助你来直观地理解。

对接银行支付API,自己的demo可以调通,放到项目里,却总提示验签失败。原来竟是因为...

 

由图中可以看到, base64本身的编码和解码都是针对ASCII编码的byte[]数据进行操作,因此,不涉及字符集。 可能存在问题的,则是我们程序的原始字符串 text。 text.getByte(charset) 与 最后的 new String(byte[], charset) ,当其中两个的 charset 不一致时, 结果就会不一致。 以下面代码为例,执行后可以发现 afterText 与 text 的内容不同了。 归根结底, 这里的技术点依然是上面的 字节数据 与 字符串 的转换。

String text = "我是中国人i love China";
String afterText = new String(text.getBytes(StandardCharsets.ISO_8859_1),"UTf-8");

 

关于base64解码,rt.jar 里的 java.util.Base64.Decoder类里,有如下两个重载方法。其中第一个重载里, 默认使用了 ISO-8859-1 对字符串进行编码。

public byte[] decode(String src) {
    return decode(src.getBytes(StandardCharsets.ISO_8859_1));
}
public byte[] decode(byte[] src) {
    ...
}

 

字符 / 字节 / 字符集 ,傻傻分不清?

字符和字节是表示数据的不同表现形式。

字符(Character):字符是指文本中的单个字符,例如字母、数字、标点符号、汉字、特殊符号(如拉丁字母)等。在计算机中,字符通常使用Unicode字符集进行表示。在Java中,表示一个字符使用`char`类型。表示一个字符序列,可以使用String、StringBuilder,它们实现了相同的接口 CharSequence。

字节(Byte):字节是计算机中存储数据、传输的最小单位。一个字节由8个二进制位组成,可以表示从0到255之间的整数。在计算机中,所有数据需要以字节的形式进行存储和传输。

上面提到的,我们编码中使用的是文本字符(character)数据,而数据传输和存储使用字节(byte)的形式。那么,就需要在这两种数据形式之间做数据转换,即字符数据的编码和解码(codec),codec中就涉及到了字符集(charset)。

字符集(Character Set):字符集是一套字符的集合,每个字符在字符集中都有一个唯一的编码值。字符集定义了字符与字节之间的映射关系。常见的字符集包括ASCII、UTF-8、UTF-16、ISO-8859-1等。

在字符串编码和解码过程中,字符集起到了关键的作用。正确选择和匹配字符集是确保字符能够正确存储和传输的关键。

编码(Encoding):编码是将字符转换为字节序列的过程。编码方案根据字符集的定义来确定如何将字符映射为字节。

解码(Decoding):解码是将字节序列转换回字符的过程。解码方案根据字符集的定义来确定如何将字节映射回字符。

 

字符集小常识

字符集(charset)是一种规定了字符与二进制数据之间对应关系的编码方案。它定义了如何将字符映射到二进制表示形式,以便计算机能够存储、处理和传输文本数据。

字符集中的字符可以是字母、数字、符号以及其他特定语言或地区的特殊字符。常见的字符集包括 ASCII、Unicode、UTF-8、ISO-8859-1 等。

ASCII(American Standard Code for Information Interchange)是最早的字符集,定义了 128 个字符的编码,包括基本的拉丁字母、数字和一些特殊字符。然而,ASCII 只适用于英语和一些西欧语言。

为了满足全球范围内的多语言需求,Unicode 被引入,它定义了几乎所有语言的字符集。Unicode 使用唯一的编码值来表示每个字符,其编码空间非常大。

UTF-8(Unicode Transformation Format-8)是一种对 Unicode 进行编码的方式,它使用变长编码来表示字符。UTF-8 是目前最常用的字符集编码方式之一,它兼容 ASCII,并支持各种语言的字符表示。

ISO-8859-1(Latin-1)是一种单字节字符集,它是 ASCII 的扩展,包含了西欧语言的字符。它是许多早期计算机系统默认的字符集编码。

GBK(GuoBiao KangXi)和 GB2312(GuoBiao 2312) 是中国国家标准的字符集,用于表示中文字符。它们都是在 ASCII 的基础上进行扩展的,保留了 ASCII 字符的编码,并添加了更多的中文字符。

字符集的选择取决于所处理数据的特定需求。在进行文本处理、编码转换、网络通信等操作时,正确地理解和使用字符集非常重要,以确保数据的正确性和互操作性。

 

 

ref:
本文物料素材

Base64编码详解文章来源地址https://www.toymoban.com/news/detail-861308.html

到了这里,关于对接银行支付API,自己的demo可以调通,放到项目里,却总提示验签失败。原来竟是因为...的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微信公众号对接ChatGPT-实现基于自己知识库的机器人功能-并且可以人工接入进行回复的客服系统...

    现在很多教程有介绍如何把chatGPT对接到自己的公众号上,利用公众号的自动回复接口功能,实现用户在公众号与chatGPT进行交互。 而我实现的功能比上面的要高级很多 首先,用户在公众号端发送咨询消息,可以得到自动回复,并且这个回复是基于我自己的知识库回答的 然后

    2024年02月16日
    浏览(84)
  • 小程序-真机上接口无法调通,开发者工具上可以

    近期在对接小程序,在这里记录一下,我们在对接小程序的时候碰到的一些奇奇怪怪的问题。 其中一个问题如下: 真实效果如下图 开发者工具上可以,访问没有人任何问题。 真机上接口无法调通,也没有报错,也没有返回值。 检查自己的小程序服务器域名配置(这个一般

    2024年02月14日
    浏览(41)
  • 银行传统支付通道与支付平台结合

    大家好,本人李小胖,08年开始工作以来到目前为止一直与银行打交道,从开始08年做电子渠道到13年赶上移动支付的第一波,有幸见证了SWP-SD、NFC-SIM、全卡、全终端的发展,之后就一直从事支付平台相关的工作。 今天我为大家分享的主题是“银行传统支付通道与支付平台结

    2024年02月08日
    浏览(43)
  • python函数可以通过列表索引放到button事件 by chatgpt

    Python函数可以通过列表索引放到按钮事件中。在按钮事件处理函数中,你可以使用`event.widget`属性获取触发事件的按钮,然后使用`grid_info()`方法获取按钮的行和列索引,最后使用索引从列表中获取函数并调用它。   以下是示例代码:   ```python import tkinter as tk   # 创建主窗口

    2023年04月25日
    浏览(58)
  • 中国建设银行-跨境易支付

    案例介绍 一、项目背景 随着“一带一路”国家战略的推进,越来越多的中资企业开始践行走出国门,跨国投资和贸易往来愈加频繁。目前中资企业在境外的投资覆盖了基建、钢铁、铁路、汽车、高科技等各个领域,进出口贸易、对外直接投资、出口大宗商品、进口原材料、

    2024年02月12日
    浏览(46)
  • 关于支付通道,支付接口,支付对接的100个名词解释

    一份简明易懂的支付术语解释清单,帮助你更好地理解支付通道、支付接口和支付对接等相关概念。 100个名词的简要解释: 在线支付:通过互联网实现的支付方式,包括网银支付、第三方支付等。 支付网关:连接商户和支付机构的中间件,实现支付流程的安全处理和支付数

    2024年02月04日
    浏览(46)
  • 【超详细,全流程】java对接支付宝支付

    本文对接使用的是支付宝的电脑网站支付,其它对接方式可做参考 接入分两种 第一种是常规接入,主要是针对 企业用户 ,需要有 营业执照;经过备案能正常访问的网站 第二种是 沙箱接入 ,主要针对 个人用户 ,只需要有能正常使用的支付宝就可以完成接入,真实企业用户

    2024年02月02日
    浏览(48)
  • 对接支付通道如何收费?支付接口收费标准

    支付接口收费标准是怎样的 反映到平台方来说,就是它的盈利模式,是维持企业生存,到发展壮大的根本保障。目前第三方支付平台费用有:手续费、广告费、服务费、沉淀资金的利息收入四种。 1、手续费 手续费是第三方支付平台费用的最传统的盈利模式之一。即第三方支

    2024年02月16日
    浏览(48)
  • SpringBoot对接支付宝完成扫码支付

    需求:系统A对接支付宝,实现支持用户扫码支付 对接的API文档:https://open.alipay.com/api 可选的支付方式有: 扫码付:出示付款码或者用户扫码付款 APP支付:在APP中唤起支付宝 手机网站支付:在移动端网页中唤起支付宝 App 或支付宝网页 电脑网站支付:在PC端唤起支付宝App或

    2024年02月03日
    浏览(50)
  • PHP 支付宝(单笔转账到银行账户接口)

    alipay.fund.trans.tobank.transfer(单笔转账到银行账户接口) 小程序文档 - 支付宝文档中心 一、下载支付宝SDK,现有版本v1、v2、v3 https://github.com/alipay/alipay-sdk-php-all github 慢的话,DNS   直达即可 140.82.112.3 github.com 【host文件路径c:windowssystem32driversetc】 二、引用代码编写 回调的时

    2024年01月18日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包