前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >虎皮椒V3 & 个人支付平台

虎皮椒V3 & 个人支付平台

作者头像
收心
发布2022-01-19 14:05:12
3.6K2
发布2022-01-19 14:05:12
举报
文章被收录于专栏:Java实战博客

本文章已被虎皮椒V3官方收录 链接地址:http://www.xunhupay.com/268.html 虎皮椒官方Java Demo已将我代码修改更加像业务逻辑的代码,会更加适配直接配置的兄弟!

下文 代码过长,手机看起来,可能会换行,建议PC 查看

介绍 虎皮椒V3 平台 :我们网站涉及到支付宝、微信的商家服务的时候,都需要各种工商认证。但我们个人开发者,是没有这些证件的,所以就没办法直接对接到支付宝、微信。我们就需要选择一款个人支付平台:虎皮椒支付,虎皮椒支付能做什么呢?就是调用相关API 生成支付宝、或者微信的支付二维码,扫码直接到你个人账户。这里面废话 有点多,请往下看。

先说一下,我个人场景:我搭建一个网站,中间涉及到支付服务,然后就去虎皮椒V3 支付宝进行签约 。我签约花了 88 元 成为虎皮椒V3商家。即一旦那交易成功,虎皮椒平台会抽取2%,支付宝 大约 抽取0.38% 的交易金额,即 总共被抽成大约2.38% 利用虎皮椒支付宝获取,你获取100 元,你将获得97.62 元。当然 ,个人测试一下,或者 个人网站 需要,可以尝试一下。

虎皮椒V3 官网:https://www.xunhupay.com/

切记:被抽成的金额需要你提前充值到你的账户中。用了就会扣除,当存储抽成金额不足时,无法跳转支付页面!

注意:微信、支付宝代码一样 唯一不一样的就是 appid 和 appsecret 不一样 即 微信、支付宝代码通用!

API调用

调用API 之前呢,我们需要获取到自己 appidappsecret(复制的时候 切记不能有空格)。

获取 方式 如图 商家登录 后 点击 支付渠道管理 再点击 我的支付渠道

我们 获取到我们的 appidappsecret 后 就可以 模拟Http 请求,调用api 了

官方 API 解释:https://www.xunhupay.com/doc/api/pay.html

不想看人家网站,直接看我提取内容就够了

客户端请求时 参数含义:

#

参数名

含义

类型

说明

1

version

API 版本号

string(24)

必填。目前为1.1

2

appid

APP ID

string(32)

必填。支付渠道ID

3

trade_order_id

商户订单号

string(32)

必填。请确保在当前网站内是唯一订单号

4

total_fee

订单金额(元)

decimal(18,2)

必填。单位为人民币,精确到分

5

title

订单标题

string(128)

必填。商户订单标题

6

time

当前时间戳

int(11)

必填。PHP示例:time()

7

notify_url

通知回调网址

string(128)

必填。用户支付成功后,我们服务器会主动发送一个post消息到这个网址(注意:当前接口内,SESSION内容无效)

8

return_url

跳转网址

string(128)

可选。用户支付成功后,我们会让用户浏览器自动跳转到这个网址

9

callback_url

商品网址

string(128)

可选。用户取消支付后,我们可能引导用户跳转到这个网址上重新进行支付

10

plugins

备注

string(128)

可选。备注字段,可以传入一些备注数据,回调时原样返回

11

nonce_str

随机值

string(32)

必填。作用:1.避免服务器页面缓存,2.防止安全密钥被猜测出来

12

hash

签名

string(32)

必填。

13

redirect=Y

get请求

string(32)

GET请求必填

服务端响应 参数含义:

#

参数名

含义

类型

说明

1

oderid

订单id

int

订单id

2

url_qrcode

二维码地址

string(156)

可将该参数生成二维码展示出来进行扫码支付

3

url

请求url

string(155)

4

errcode

错误码

int

5

errmsg

错误信息

string(8)

错误信息具体值

6

hash

签名

string(32)

数据签名,参考下面签名算法

正式开始

我们 从官方获取到 API 请求的地址

代码语言:javascript
复制
https://api.xunhupay.com/payment/do.html

拿到地址 我们 就可以模拟请求了,我们采用 Postman 的方式进行测试。如果不了解 Postman 可以访问 XXX,(这里 Postman 教程 没写,写了会补全)

先看下 成功PostMan 案例。这里泄露我 appid 各位老铁,不要干坏事。

好了 上图 各个参数 都能 只有 time 和 hash 需要我们额外生成,别急,下面有 JAVA 代码的教程。

原理

讲代码之前:我们 先说说 客户端 与 服务端 运行的原理

客户端 与 服务端 之间 相互鉴别 通过一个叫 hash 的签名。如果 hash签名一致,就认定请求成功!

hash ,这里不叫哈希了,他叫签名。他的生成原理是:将请求中的所有参数(除本身外),进行 键的Ascll 从小到大进行排序,之后使用 “&” 进行关联。最后直接拼接上 appsecret (注意,appsecret 前 不需要 “&” 关联)。得到一串字符串后,进行MD5 加密。就达到了 hash 值

将来服务端 验证的时候,根据我们请求参数,重复一遍 生成我们客户端生成的Hash 过程。(注意,我们请求参数里面,没有传 appsecret 。服务端是 自己从自己服务器 根据我们传递 appid 取出 appsecret) 进行比对,信息是否正确。

原理说完,我们说一下代码实现

真实代码实现

说明,我使用了Hutool 中的 加密工具,就需要我们在 Maven 添加

代码语言:javascript
复制
        <!-- Hutool工具 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.5.7</version>
        </dependency>

首先 我们模拟postman 发送请求的时候,唯一缺少的就是 hash 签名 和 秒的时间戳 了。我们就去生成一下。

在模拟 hash签名之前,我们需要去写一个方法 去获取 秒的时间戳

代码语言:javascript
复制
/**
     * 获取精确到秒的时间戳   原理 获取毫秒时间戳,因为 1秒 = 100毫秒 去除后三位 就是秒的时间戳
     * @return
     */
    public static int getSecondTimestamp(Date date){
        if (null == date) {
            return 0;
        }
        String timestamp = String.valueOf(date.getTime());
        int length = timestamp.length();
        if (length > 3) {
            return Integer.valueOf(timestamp.substring(0,length-3));
        } else {
            return 0;
        }
    }

有了秒的时间戳了,就可以去生成 hash签名了 下面是伪代码

代码语言:javascript
复制
      String appid = "你的APPID";  
      Map<String,Object> options = new HashMap<>();
        // 设置版本号
        options.put("version","1.1");
        // 设置 appid
        options.put("appid",appid);

        // 密钥不需要参数
        //options.put("appsecret",appsecret);

        // 订单号 具体内容自己控制 长度 32位
        options.put("trade_order_id","1");

        // 价格价格 精确到分
        options.put("total_fee","0.01");

        // 标题
        options.put("title","测试使用的title");

        // 当前时间戳 调用 刚写的方法 getSecondTimestamp
        options.put("time", getSecondTimestamp(new Date()));

        // notify_url 回调地址
        options.put("notify_url","49.72.184.28");

        // nonce_str  随机值 32位内  作用: 1.避免服务器页面缓存,2.防止安全密钥被猜测出来(md5 密钥越复杂,就越难解密出来)
        options.put("nonce_str","740969606");

        // 定义 sb 为了获取 MD5 加密前的字符串
        StringBuilder sb = new StringBuilder();

        // 将HashMap 进行 键 的 Ascll 从小到大排序 并 将每个 hashmap元素 以 & 拼接起来
        options.entrySet().stream().sorted((e1,e2) -> e1.getKey().compareTo(e2.getKey())).forEach(a ->{
            sb.append(a).append("&");});

        // 去除 最后一位的 &
        sb.deleteCharAt(sb.length()-1);

        // 拼接上密钥
        sb.append(appsecret);

        // 调用 Hutool 的 加密工具 进行 MD5 加密
        String s = SecureUtil.md5(sb.toString());
        
        // 输出hash结果 postman 要用
        System.out.println("我们生成的Hash 是:"+s);
        // 输出time结果 postman 要用
        System.out.println("我们生成的time 是:"+options.get("time"));

上面 代码 自己创建一下 run 一下 控制台会输出 hash 和 time,有了这些数据,我们就可以postman 模拟了 ,看到 Hashmap集合中填写的参数,放到 postman unlencoded 格式中

最终,会看到结果 Success

当然,第一次测试,大部分都不会成功,如图

一定要确保 参数正确。就可以了。

简单的 postman 测试成功了,我们 就准备 制作我们的 支付 – 跳转 功能了。

待更新!!!

好了 完成 Java 调用的测试了,上代码。

Java 代码

代码语言:javascript
复制
package com.zlk;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import org.junit.Test;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author : zanglikun
 * @date : 2021/2/20 13:48
 * @Version: 1.0
 * @Desc : 费劲,没啥好说的
 */
public class pay {

    // 请求参数的含义 与 值范围,请访问呢: https://www.xunhupay.com/doc/api/pay.html
    @Test
    public void pay(){
        // appid
        String appid = "201906134645";
        // appsecret
        String appsecret = "你的密钥";
        // 请求路径
        String url = "https://api.xunhupay.com/payment/do.html";

        // 设置 传递参数的集合,方便 传递数据。
        Map<String,Object> options = new HashMap<>();
        // 必填 设置版本号
        options.put("version","1.1");
        // 必填 设置 appid
        options.put("appid",appid);

        // 密钥不需要直接传递
        //options.put("appsecret",appsecret);

        // 必填 订单号 具体内容自己控制 长度 32位 官网说 请确保在当前网站内是唯一订单号,具体含义 我测试了 在描述 此备注
        options.put("trade_order_id","1");

        // 必填 价格 精确到RMB分
        options.put("total_fee","0.01");

        // 必填 标题
        options.put("title","测试使用的title");

        // 必填 当前时间戳 调用 刚写的方法 getSecondTimestamp
        options.put("time", getSecondTimestamp(new Date()));

        // 必填 通知回调地址 url 什么含义 我们后台需要知道 用户支付了。
        options.put("notify_url","https://zanglikun.cn1.utools.club/paycallback"); // 只有这个有用

        // 非必填 使用 响应字段中 url 就直接跳到百度了,如果访问,url_qrcode ,不会直接跳转,只有当支付完成后,再次刷新 url_qrcode中的连接,才会跳转。
        options.put("return_url","https://www.baidu.com");

        // 非必填 用户取消支付,跳转的页面   经过测试,没有触发机制,建议不传递
        options.put("callback_url","https://www.sina.com.cn/");

        //plugins 非必填 备注信息
        options.put("plugins","我是备注信息");

        // nonce_str  必填 随机值 32位内  作用: 1.避免服务器页面缓存,2.防止安全密钥被猜测出来(md5 密钥越复杂,就越难解密出来)
        options.put("nonce_str","740969606");

        // 定义 sb 为了获取 MD5 加密前的字符串
        StringBuilder sb = new StringBuilder();

        // 将HashMap 进行 键 的 Ascll 从小到大排序 并 将每个 hashmap元素 以 & 拼接起来
        options.entrySet().stream().sorted((e1,e2) -> e1.getKey().compareTo(e2.getKey())).forEach(a ->{
            sb.append(a).append("&");});

        // 去除 最后一位的 &
        sb.deleteCharAt(sb.length()-1);
        // 拼接上密钥
        sb.append(appsecret);

        // 调用 Hutool 的 加密工具 进行 MD5 加密
        String s = SecureUtil.md5(sb.toString());

        // 输出hash结果 postman 要用
        System.out.println("我们生成的Hash 是:"+s);
        // 输出time结果 postman 要用
        System.out.println("我们生成的time 是: "+options.get("time"));

        System.out.println();
        // 必填 hash 签名
        options.put("hash", s);


        System.out.println("我们传递的参数有:"+options.toString());
        System.out.println("开始调 虎皮椒支付 接口...");

        // 调用 Hutool 的HttpUtil 发送 post 请求
        String post = HttpUtil.post(url, options);

        System.out.println("结束调 虎皮椒支付 接口...\n");
        System.out.println("虎皮椒支付 接口 响应的结果是:"+post+"\n");

        // 说明:这里 因为虎皮椒支付 响应结果 不统一,正确是Json;不正确 就是一行String 。没办法 判断是否请求是否有效。所以 只能通过 是由能够解析成json 有无异常判断 是否调用成功
        try{
            Map map = (Map)JSON.parse(post);
            map.keySet().stream().forEach(k -> {
                if (k == "url") {
                    System.out.println("url二维码链接是: "+map.get(k));
                }
            });
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("调 虎皮椒支付 时 出现了问题");
        }


    }

    /**
     * 获取精确到秒的时间戳   原理 获取毫秒时间戳,因为 1秒 = 100毫秒 去除后三位 就是秒的时间戳
     * @return
     */
    public static int getSecondTimestamp(Date date){
        if (null == date) {
            return 0;
        }
        String timestamp = String.valueOf(date.getTime());
        int length = timestamp.length();
        if (length > 3) {
            return Integer.valueOf(timestamp.substring(0,length-3));
        } else {
            return 0;
        }
    }

}

测试的结果 截图:

再次 强调一下:请求支付成功,返回的数据中,有 url 和 url_qrcode ,建议去使用 url 的链接 支付,因为 它可以直接 跳转 请求成功的页面。当支付成功时,再次访问 这两个url 都会自动跳转 你指定的页面。

上述 代码中 蓝色 标注的代码是 回调地址。会spring框架的兄弟 都能看懂。

代码语言:javascript
复制
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * @author : zanglikun
 * @date : 2021/2/22 12:04
 * @Version: 1.0
 * @Desc : 费劲,没啥好说的
 */
@Controller
public class PayCallback {

    @RequestMapping("/paycallback")
    @ResponseBody
    public String abc(HttpServletRequest request){
        // 记得 map 第二个泛型是数组 要取 第一个元素 即[0]
        Map<String, String[]> parameterMap = request.getParameterMap();
        System.out.println("展示 回调的所有结果:");
        // 处理 回调 结果
        parameterMap.keySet().stream().forEach((k) ->{
            System.out.println(k+":"+parameterMap.get(k)[0]);
        });

        System.out.println("展示 回调的所有结果完成");
        System.out.print("\n最终结果是:");
        
        if("OD".equals(parameterMap.get("status")[0])){
            System.out.println("用户支付成功了");
        }else {
            System.out.println("用户支付不成功");
        }
        return "success";  // 这里一定要返回success,不然虎皮椒还会回调
    }

}

控制台输出 结果展示:

代码语言:javascript
复制
展示 回调的所有结果:
trade_order_id:1
total_fee:0.01
transaction_id:2021022222001486491437925203
open_order_id:20207745197
order_title:测试使用的title
status:OD
plugins:我是备注信息
nonce_str:9375163117
time:1613975317
appid:201906134645
hash:621a90afe443c278f22927a1220f0ee5
展示 回调的所有结果完成

最终结果是:[Ljava.lang.String;@790a1d8e
用户支付成功了

特殊说明: 解决问题的光鲜,藏着磕Bug的痛苦。 万物皆入轮回,谁也躲不掉! 以上文章,均是我实际操作,写出来的笔记资料,不会出现全文盗用别人文章!烦请各位,请勿直接盗用!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 注意:微信、支付宝代码一样 唯一不一样的就是 appid 和 appsecret 不一样 即 微信、支付宝代码通用!
  • API调用
    • 客户端请求时 参数含义:
      • 服务端响应 参数含义:
      • 正式开始
      • 原理
      • 真实代码实现
        • Java 代码
          • 测试的结果 截图:
          • 再次 强调一下:请求支付成功,返回的数据中,有 url 和 url_qrcode ,建议去使用 url 的链接 支付,因为 它可以直接 跳转 请求成功的页面。当支付成功时,再次访问 这两个url 都会自动跳转 你指定的页面。
          • 控制台输出 结果展示:
      相关产品与服务
      云支付
      云支付(Cloud Pay,CPay)为您提供开放、可靠的聚合收款技术服务和商户管理功能。云支付支持刷卡支付、扫码支付、一码多付多种支付方式。服务商也可使用云支付提供的 SDK 和 HTTPS 接口,将云支付集成进自己的系统中,为商户提供的个性化解决方案。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档