微信一物一码 解密下载后的buffer文件(附所有工具) java

1. 公众号内找到密钥

申请一物一码成功后,打开公众号,点击右上角通知,找到关于一物一码的消息,里面包含解密用的密钥申请一物一码成功后,打开公众号,点击右上角通知,找到关于一物一码的消息,里面包含解密用的密钥
微信公众号一物一码地址: https://developers.weixin.qq.com/doc/offiaccount/Unique_Item_Code/Unique_Item_Code_Op_Guide.html

2.下载二维码代码(文章末尾包含所有相关接口)

下载二维码工具

public static HashMap applycodedownload(String ACCESSTOKEN, String application_id, String code_start, String code_end, String key) {


        // 拼接请求地址
        String requestUrl = "https://api.weixin.qq.com/intp/marketcode/applycodedownload?access_token=ACCESSTOKEN";
        requestUrl = requestUrl.replace("ACCESSTOKEN", ACCESSTOKEN);

        Map<String, Object> param = new HashMap<>();
        if (StringUtils.isNotBlank(application_id)) {
            param.put("application_id", application_id);
        }
        if (StringUtils.isNotBlank(code_start)) {
            param.put("code_start", code_start);
        }
        if (StringUtils.isNotBlank(code_end)) {
            param.put("code_end", code_end);
        }

        String result = HttpClientUtil.sendPost(requestUrl, JSONObject.toJSONString(param));
        JSONObject jsonObject = JSONObject.parseObject(result);

        // 返回信息,如errcode不为0,则errmsg为错误信息
        if (jsonObject.containsKey("errcode") && jsonObject.getString("errcode").equals("0")) {
        
            String buffer = jsonObject.getString("buffer");
            //解密时,先进行base64解码,然后使用密钥及AES/CBC/PKCS7Padding进行解密。
            //密钥在申请实名接口权限时,会提供到申请方。
            String qrDecrypt = WeChatDecryptUtils.qrDecrypt(buffer, key);
            HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
            objectObjectHashMap.put("buffer", buffer);
            objectObjectHashMap.put("newbuffer", qrDecrypt);
            return objectObjectHashMap;

        } else {
            log.error(jsonObject.toJSONString());
            return null;
        }
    }

代码用到的导包

import com.alibaba.fastjson.JSONObject;

代码用到的http发送方法

 /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url 发送请求的 URL
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param) {
        OutputStreamWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setReadTimeout(20000);
            conn.setConnectTimeout(20000);
            // 获取URLConnection对象对应的输出流
            conn.setRequestProperty("Content-type", "application/json");
            conn.setRequestProperty("Content-Length", param.replace("\n", ""));

            OutputStream outputStream = conn.getOutputStream();
            outputStream.write(param.toString().getBytes());

            out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
            // 发送请求参数
            out.write(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常!" + e);
            e.printStackTrace();
        }
        // 使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result;
    }

3.编写解密工具类

 /**
     * description: 初始化密钥
     *
     * @return void
     * @throws Exception
     */
    public static void init() throws Exception {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        KeyGenerator.getInstance(AES).init(128);
    }

    /**
     * description: 生成iv
     *
     * @param iv
     * @return AlgorithmParameters
     * @throws Exception
     */
    public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
        // iv 为一个 16 字节的数组,这里采用和 iOS 端一样的构造方法,数据全为0
        // Arrays.fill(iv, (byte) 0x00);
        AlgorithmParameters params = AlgorithmParameters.getInstance(AES);
        params.init(new IvParameterSpec(iv));
        return params;
    }

    /**
     * description: 执行解密操作
     *
     * @param encryptedData 加密后的字符串
     * @param keyBytes      密钥key
     * @param iv            便宜向量iv
     * @return byte[]
     * @throws Exception
     */
    public static byte[] decrypt(byte[] encryptedData, byte[] keyBytes, AlgorithmParameters iv) throws Exception {
        Key key = new SecretKeySpec(keyBytes, AES);
        Cipher cipher = Cipher.getInstance(AES_CBC_PADDING);
        // 设置为解密模式
        cipher.init(Cipher.DECRYPT_MODE, key, iv);
        return cipher.doFinal(encryptedData);
    }

    /**
     * description: 微信一物一码, 二维码解密
     *
     * @param encrypted
     * @return String
     * @throws Exception
     * https://developers.weixin.qq.com/doc/offiaccount/Unique_Item_Code/Unique_Item_Code_API_Documentation.html#10
     */
    public static String qrDecrypt(String encrypted, String key) {
        // 实名数据,先进行base64解码 
        byte[] encryptedData = Base64.decodeBase64(encrypted);
        // key 没有进行编码,直接转换即可
        byte[] keyBytes = key.getBytes();
        // iv使用加解密钥 --- iv 根据密钥生成的
        String result = null;
        try {
            init();
            AlgorithmParameters iv = generateIV(keyBytes);
            result = new String(decrypt(encryptedData, keyBytes, iv));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

这段代码是参考其他博客(原文地址找不到了,找到补上!!!),感谢原博主

附录:所有相关一物一码接口工具

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 微信一物一码
 */
@Slf4j
public class WeChatMarketcodeUtils {


    /**
     * 2.1接口规则
     * <p>
     * <p>
     * 提交方式:采用POST方法提交。API调用凭证 accesstoken,参照说明:获取access_token。
     * <p>
     * 数据格式:提交和返回数据JSON格式,数据格式如:
     * <p>
     * {
     * "field1":"data1",
     * "field2":"data2",
     * "int_field":123
     * }
     * 字符编码:统一采用UTF-8字符编码。
     *
     * @param
     * @return
     */
    public static String getAccessToken(String appid, String secret) {
        String access_token = null;
        //获取缓存token
        access_token = LocalCache.get(appid + "_access_token");
        log.info("缓存的access_token= " + access_token);
        if (StringUtils.isBlank(access_token)) {
            // 拼接请求地址
            String requestUrl = "https://api.weixin.qq.com/cgi-bin/token";
            String param = "grant_type=client_credential&appid=APPID&secret=APPSECRET";

            String replace = param.replace("APPID", appid).replace("APPSECRET", secret);

            String result = HttpClientUtil.sendGet(requestUrl, replace);

            JSONObject jsonObject = JSONObject.parseObject(result);

            Map map = new HashMap<String, String>();

            if (!jsonObject.containsKey("errcode")) {
                map.put("access_token", jsonObject.getString("access_token"));
                map.put("expires_in", jsonObject.getString("expires_in"));

                access_token = jsonObject.getString("access_token");
                log.info("重新获取的access_token=" + access_token);
                //放入缓存
                LocalCache.put(appid + "_access_token", jsonObject.getString("access_token"), 7200);

                map = JSON.parseObject(JSON.toJSONString(jsonObject), Map.class);
            } else {
                log.info("响应的错误信息" + result);
                return null;
            }
        }

        return access_token;
    }

    /**
     * 2.2申请二维码接口
     * <p>
     * <p>
     * API:https://api.weixin.qq.com/intp/marketcode/applycode?access_token=ACCESSTOKEN
     * <p>
     * ACCESSTOKEN参见接口规则说明。
     * <p>
     * 请求参数:
     * <p>
     * 字段名	中文解释	类型	是否必填	备注
     * code_count	申请码的数量	Uint64	Y	≥10000,≤200000000,10000的整数倍
     * isv_application_id	外部单号	String128	Y	相同isv_application_id视为同一申请单
     * 返回参数:
     * <p>
     * 字段名	中文解释	类型	是否必填	备注
     * errcode	返回状态码	Int32	Y	0
     * errmsg	错误提示	String128	N	返回信息,如errcode不为0,则errmsg为错误信息
     * application_id	申请单号	Uint64	Y	无
     * 入参示例:
     * <p>
     * {
     * "code_count":100000,
     * "isv_application_id":"testid124"
     * }
     * 返回示例:
     * <p>
     * {
     * "errcode":0,
     * "errmsg":"ok",
     * "application_id":581865877
     * }
     *
     * @param ACCESSTOKEN
     * @param code_count         申请码的数量   ≥10000,≤200000000,10000的整数倍
     * @param isv_application_id 外部单号 相同isv_application_id视为同一申请单
     * @return
     */
    public static JSONObject applycode(String ACCESSTOKEN, String code_count, String isv_application_id) {

        // 拼接请求地址
        String requestUrl = "https://api.weixin.qq.com/intp/marketcode/applycode?access_token=ACCESSTOKEN";
        requestUrl = requestUrl.replace("ACCESSTOKEN", ACCESSTOKEN);

        Map<String, Object> param = new HashMap<>();
        param.put("code_count", code_count);
        if (StringUtils.isNotBlank(isv_application_id)) {
            param.put("isv_application_id", isv_application_id);
        }
        String s = JSONObject.toJSONString(param);
        String result = HttpClientUtil.sendPost(requestUrl, JSONObject.toJSONString(param));

        JSONObject jsonObject = JSONObject.parseObject(result);

//        // 返回信息,如errcode不为0,则errmsg为错误信息
//        if (jsonObject.containsKey("errcode") && jsonObject.getString("errcode").equals("0")) {
//
//            String application_id = jsonObject.getString("application_id");
//
//            Map map = JSON.parseObject(JSON.toJSONString(jsonObject), Map.class);
//
//            return map;
//        } else {
//
//            Map map = JSON.parseObject(JSON.toJSONString(jsonObject), Map.class);
//            log.error(jsonObject.toJSONString());
//            return null;
//            // 返回信息,如errcode不为0,则errmsg为错误信息
//        }

        return jsonObject;
    }

    /**
     * 2.3 查询二维码申请单接口
     * <p>
     * <p>
     * API:https://api.weixin.qq.com/intp/marketcode/applycodequery?access_token=ACCESSTOKEN
     * ACCESSTOKEN参见接口规则说明。
     * <p>
     * 请求参数:
     * <p>
     * 字段名	中文解释	类型	是否必填	备注
     * application_id	申请单号	Uint64	Y	无
     * isv_application_id	外部单号	String128	Y	相同isv_application_id视为同一申请单
     * 返回参数:
     * <p>
     * 字段名	中文解释	类型	是否必填	备注
     * errcode	返回状态码	Int32	Y	0
     * errmsg	错误提示	String128	N	返回信息,如errcode不为0,则errmsg为错误信息
     * status	申请单状态	String128	Y	INIT PROCESSING FINISH为可下载
     * isv_application_id	外部单号	String128	Y	无
     * application_id	申请单号	Uint64	Y	无
     * create_time	创建时间	Uint32	Y	无
     * update_time	更新时间	Uint32	Y	无
     * code_start	开始位置	Uint64	Y	包含,如0
     * code_end	结束位置	Uint64	Y	包含,如49999,上述0-49999为一个号码段
     * 入参示例:
     * <p>
     * {
     * "application_id":"581865877"
     * }
     * 返回示例:
     * <p>
     * {
     * "errcode": 0,
     * "errmsg": "ok",
     * "status": "FINISH",
     * "code_generate_list": [{
     * "code_start": 0,
     * "code_end": 49999
     * },{
     * "code_start": 50000,
     * "code_end": 99999
     * }]
     * }
     *
     * @param ACCESSTOKEN
     * @param application_id     申请单号	Uint64	Y	无
     * @param isv_application_id 外部单号	String128	Y	相同isv_application_id视为同一申请单
     * @return
     */
    public static Map applycodequery(String ACCESSTOKEN, String application_id, String isv_application_id) {

        // 拼接请求地址
        String requestUrl = "https://api.weixin.qq.com/intp/marketcode/applycodequery?access_token=ACCESSTOKEN";
        requestUrl = requestUrl.replace("ACCESSTOKEN", ACCESSTOKEN);

        Map<String, Object> param = new HashMap<>();
        if (StringUtils.isNotBlank(application_id)) {
            param.put("application_id", application_id);
        }
        if (StringUtils.isNotBlank(isv_application_id)) {
            param.put("isv_application_id", isv_application_id);
        }

        String result = HttpClientUtil.sendPost(requestUrl, JSONObject.toJSONString(param));

        JSONObject jsonObject = JSONObject.parseObject(result);

        // 返回信息,如errcode不为0,则errmsg为错误信息
        if (jsonObject.containsKey("errcode") && jsonObject.getString("errcode").equals("0")) {
            //INIT PROCESSING FINISH为可下载
            String status = jsonObject.getString("status");

            if (StringUtils.isNotBlank(isv_application_id)) {
                //外部单号	String128	Y	无
                String isv_application_ids = jsonObject.getString("isv_application_id");
                //申请单号	Uint64	Y	无
                String application_ids = jsonObject.getString("application_id");
                //创建时间	Uint32	Y	无
                String create_time = jsonObject.getString("create_time");
                //更新时间	Uint32	Y	无
                String update_time = jsonObject.getString("update_time");

                String code_generate_list = String.valueOf(jsonObject.get("code_generate_list"));

                JSONArray objects = JSONArray.parseArray(code_generate_list);
                for (int i = 0; i < objects.size(); i++) {
                    JSONObject jsonObject1 = objects.getJSONObject(i);
                    //开始位置	Uint64	Y	包含,如0
                    String code_start = jsonObject1.getString("code_start");
                    //结束位置	Uint64	Y	包含,如49999,上述0-49999为一个号码段
                    String code_end = jsonObject1.getString("code_end");
                }
            } else {

                String code_generate_list = jsonObject.getString("code_generate_list");
                JSONArray objects = JSONArray.parseArray(code_generate_list);
                for (Object object : objects) {
                    JSONObject jsonObject1 = JSONArray.parseObject((String) object);
                    String code_start = jsonObject1.getString("code_start");
                    String code_end = jsonObject1.getString("code_end");
                }
            }

            Map map = JSON.parseObject(JSON.toJSONString(jsonObject), Map.class);

            return map;

        } else {
            Map map = JSON.parseObject(JSON.toJSONString(jsonObject), Map.class);
            log.error(jsonObject.toJSONString());
            return null;
        }

    }

    /**
     * 2.4 下载二维码包接口
     * <p>
     * API:https://api.weixin.qq.com/intp/marketcode/applycodedownload?access_token=ACCESSTOKE
     * ACCESSTOKEN参见接口规则说明。
     * <p>
     * 请求参数:
     * <p>
     * 字段名	中文解释	类型	是否必填	备注
     * application_id	申请单号	Uint64	Y	无
     * code_start	开始位置	Uint64	Y	来自查询二维码申请接口
     * code_end	结束位置	Uint64	Y	来自查询二维码申请接口
     * 返回参数:
     * <p>
     * 字段名	中文解释	类型	是否必填	备注
     * errcode	返回状态码	Int32	Y	0
     * errmsg	错误提示	String128	N	返回信息,如errcode不为0,则errmsg为错误信息
     * buffer	文件buffer	String128	Y	需要先base64 decode,再做解密操作(解密参见3.1)
     * 解密后的数据格式为:
     * <p>
     * a1 b1 c1 d1
     * a2 b2 c2 d2
     * <p>
     * 即以 /n 换行,每行四个元素以 /t 分隔。
     * <p>
     * a为361字节的01点阵,用于支持生成19*19的微型码,0为白,1为黑,
     * <p>
     * b为原始码数据,最长9位,
     * <p>
     * c为该码在此次申请的索引位置,
     * <p>
     * d为28位字符,用于支持转为普通二维码。可生成码制2,纠错等级Q的二维码。
     * <p>
     * 入参示例:
     * <p>
     * 如下表示获取该申请单的0-49999这一段的码。
     * <p>
     * {
     * "application_id":581865877,
     * "code_start": 0,
     * "code_end": 49999
     * }
     * 返回示例:
     * <p>
     * {
     * "errcode":0,
     * "errmsg":"ok",
     * "buffer": "ajfiwejfoiawjfijweofi"
     * }
     *
     * @param application_id 申请单号	Uint64	Y	无
     * @param code_start     开始位置	Uint64	Y	来自查询二维码申请接口
     * @param code_end       结束位置	Uint64	Y	来自查询二维码申请接口
     * @return
     */

    public static HashMap applycodedownload(String ACCESSTOKEN, String application_id, String code_start, String code_end, String key) {


        // 拼接请求地址
        String requestUrl = "https://api.weixin.qq.com/intp/marketcode/applycodedownload?access_token=ACCESSTOKEN";
        requestUrl = requestUrl.replace("ACCESSTOKEN", ACCESSTOKEN);

        Map<String, Object> param = new HashMap<>();
        if (StringUtils.isNotBlank(application_id)) {
            param.put("application_id", application_id);
        }
        if (StringUtils.isNotBlank(code_start)) {
            param.put("code_start", code_start);
        }
        if (StringUtils.isNotBlank(code_end)) {
            param.put("code_end", code_end);
        }

        String result = HttpClientUtil.sendPost(requestUrl, JSONObject.toJSONString(param));
        JSONObject jsonObject = JSONObject.parseObject(result);

        // 返回信息,如errcode不为0,则errmsg为错误信息
        if (jsonObject.containsKey("errcode") && jsonObject.getString("errcode").equals("0")) {

            String buffer = jsonObject.getString("buffer");

            //解密时,先进行base64解码,然后使用密钥及AES/CBC/PKCS7Padding进行解密。
            //密钥在申请实名接口权限时,会提供到申请方。
            String qrDecrypt = WeChatDecryptUtils.qrDecrypt(buffer, key);
            HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
            objectObjectHashMap.put("buffer", buffer);
            objectObjectHashMap.put("newbuffer", qrDecrypt);
            return objectObjectHashMap;

        } else {
            log.error(jsonObject.toJSONString());
            return null;
        }
    }


    /**
     * 2.5 激活二维码接口
     *
     * @param application_id 申请单号	Uint64	Y	无
     *                       activity_name	活动名称	String128	Y	数据分析活动区分依据,请规范命名
     *                       product_brand	商品品牌	String128	Y	数据分析品牌区分依据,请规范命名
     *                       product_title	商品标题	String128	Y	数据分析商品区分依据,请规范命名
     *                       product_code	商品条码	String128	Y	EAN商品条码,请规范填写
     *                       wxa_appid	小程序的appid	String128	Y	扫码跳转小程序的appid
     *                       wxa_path	小程序的path	String128	Y	扫码跳转小程序的path
     *                       wxa_type	小程序版本	Int32	N	默认为0正式版,开发版为1,体验版为2
     *                       code_start	激活码段的起始位	Uint64	Y	如0(包含该值)
     *                       code_end	激活码段的结束位	Uint64	Y	如9999(包含该值)
     * @return access_token    网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
     * expires_in	access_token接口调用凭证超时时间,单位(秒)
     * refresh_token	用户刷新access_token
     * openid	用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
     * scope	用户授权的作用域,使用逗号(,)分隔
     */

    public static JSONObject codeactive(String ACCESSTOKEN, String application_id, String activity_name, String product_brand,
                                        String product_title, String product_code, String wxa_appid, String wxa_path, Integer wxa_type, String code_start, String code_end) {
        // 拼接请求地址
        String requestUrl = "https://api.weixin.qq.com/intp/marketcode/codeactive?access_token=ACCESSTOKEN";
        requestUrl = requestUrl.replace("ACCESSTOKEN", ACCESSTOKEN);

        Map<String, Object> param = new HashMap<>();
        if (StringUtils.isNotBlank(application_id)) {
            param.put("application_id", application_id);
        }
        if (StringUtils.isNotBlank(activity_name)) {
            param.put("activity_name", activity_name);
        }
        if (StringUtils.isNotBlank(product_brand)) {
            param.put("product_brand", product_brand);
        }
        if (StringUtils.isNotBlank(product_title)) {
            param.put("product_title", product_title);
        }
        if (StringUtils.isNotBlank(product_code)) {
            param.put("product_code", product_code);
        }
        if (StringUtils.isNotBlank(wxa_appid)) {
            param.put("wxa_appid", wxa_appid);
        }
        if (StringUtils.isNotBlank(wxa_path)) {
            param.put("wxa_path", wxa_path);
        }
        if (!Objects.isNull(wxa_type)) {
            param.put("wxa_type", wxa_type);
        }
        if (StringUtils.isNotBlank(code_start)) {
            param.put("code_start", code_start);
        }
        if (StringUtils.isNotBlank(code_end)) {
            param.put("code_end", code_end);
        }

        String result = HttpClientUtil.sendPost(requestUrl, JSONObject.toJSONString(param));
        JSONObject jsonObject = JSONObject.parseObject(result);

        // 返回信息,如errcode不为0,则errmsg为错误信息
//        if (jsonObject.containsKey("errcode") && jsonObject.getString("errcode").equals("0")) {
//
//            String errmsg = jsonObject.getString("errmsg");
//        }
//        Map map = JSON.parseObject(JSON.toJSONString(jsonObject), Map.class);

        return jsonObject;
    }

    /**
     * 2.6 查询二维码激活状态接口
     * <p>
     * application_id	申请单号	Uint64	Y	无
     * code	九位的字符串原始码	String	N	code与code_index二选一
     * code_index	该码在批次中的偏移量	Uint64	N	code与code_index二选一
     */

    public static Map codeactivequery(String ACCESSTOKEN, String application_id, String code, String code_index) {
        // 拼接请求地址
        String requestUrl = "https://api.weixin.qq.com/intp/marketcode/codeactivequery?access_token=ACCESSTOKEN";
        requestUrl = requestUrl.replace("ACCESSTOKEN", ACCESSTOKEN);

        Map<String, Object> param = new HashMap<>();
        param.put("application_id", application_id);
        param.put("code", code);
        param.put("code_index", code_index);

        String result = HttpClientUtil.sendPost(requestUrl, JSONObject.toJSONString(param));
        JSONObject jsonObject = JSONObject.parseObject(result);

        // 返回信息,如errcode不为0,则errmsg为错误信息
        if (jsonObject.containsKey("errcode") && jsonObject.getString("errcode").equals("0")) {
          /*  返回示例:
            {
                "errcode": 0,
                    "errmsg": "ok",
                    "code_start": 0,
                    "code_end": 200,
                    "activity_name": "test_name",
                    "product_brand": "test_brand",
                    "product_title": "test_title",
                    "product_code": "test_code",
                    "wxa_appid": "test_appid",
                    "wxa_path": "test_path"
            }*/
            //活动名称
            String activity_name = jsonObject.getString("activity_name");
            //商品品牌
            String product_brand = jsonObject.getString("product_brand");
            //商品标题
            String product_title = jsonObject.getString("product_title");
            //小程序的appid
            String wxa_appid = jsonObject.getString("wxa_appid");
            //小程序的path
            String wxa_path = jsonObject.getString("wxa_path");
            //小程序版本
            String wxa_type = jsonObject.getString("wxa_type");
            //激活码段的起始位
            String code_start = jsonObject.getString("code_start");
            //激活码段的结束位
            String code_end = jsonObject.getString("code_end");
        }
        Map map = JSON.parseObject(JSON.toJSONString(jsonObject), Map.class);
        return map;
    }

    /**
     * 2.7 code_ticket换code接口
     * <p>
     * 请求参数:
     * openid	用户的openid	String	Y	无
     * code_ticket	跳转时带上的code_ticket参数	String256	Y	无
     * <p>
     * 返回参数:
     * <p>
     * 字段名	中文解释	类型	是否必填	备注
     * errcode	返回状态码	Int32	Y	0
     * errmsg	错误提示	String128	N	返回信息,如errcode不为0,则errmsg为错误信息
     * code	原始码	String16	Y	返回原始码数据,并返回对应的激活信息
     * application_id	申请单号	Uint64	Y	无
     * isv_application_id	外部单号	String128	Y	无
     * activity_name	活动名称	String128	Y	无
     * product_brand	商品品牌	String128	Y	无
     * product_title	商品标题	String128	Y	无
     * wxa_appid	小程序的appid	String128	Y	无
     * wxa_path	小程序的path	String128	Y	无
     * code_start	激活码段的起始位	Uint64	Y	如0(包含该值)
     * code_end	激活码段的结束位	Uint64	Y	如9999(包含该值)
     */

    public static JSONObject codeactivequery(String ACCESSTOKEN, String openid, String code_ticket) {
        // 拼接请求地址
        String requestUrl = "https://api.weixin.qq.com/intp/marketcode/tickettocode?access_token=ACCESSTOKEN";
        requestUrl = requestUrl.replace("ACCESSTOKEN", ACCESSTOKEN);

        Map<String, Object> param = new HashMap<>();
        param.put("openid", openid);
        param.put("code_ticket", code_ticket);

        String result = HttpClientUtil.sendPost(requestUrl, JSONObject.toJSONString(param));
        JSONObject jsonObject = JSONObject.parseObject(result);

        /**
         * 处理结果如下 复制就行


        // 返回信息,如errcode不为0,则errmsg为错误信息
//        if (jsonObject.containsKey("errcode") && jsonObject.getString("errcode").equals("0")) {
//          /*  返回示例:
//            {
//                "errcode": 0,
//                "errmsg": "ok",
//                "code": "8",
//                "code_start": 0,
//                "code_end": 200,
//                "activity_name": "test_name",
//                "product_brand": "test_brand",
//                "product_title": "test_title",
//                "product_code": "test_code",
//                "wxa_appid":"wx3sxjifjwojfsffef",
//                "wxa_path":"pages/index/index"
//                }*/
//            //原始码	String16	Y	返回原始码数据,并返回对应的激活信息
//            String code = jsonObject.getString("code");
//            //申请单号
//            String application_id = jsonObject.getString("application_id");
//            //外部单号
//            String isv_application_id = jsonObject.getString("isv_application_id");
//            //活动名称
//            String activity_name = jsonObject.getString("activity_name");
//            //商品品牌
//            String product_brand = jsonObject.getString("product_brand");
//            //商品标题
//            String product_title = jsonObject.getString("product_title");
//            //小程序的appid
//            String wxa_appid = jsonObject.getString("wxa_appid");
//            //小程序的path
//            String wxa_path = jsonObject.getString("wxa_path");
//            //激活码段的起始位
//            String code_start = jsonObject.getString("code_start");
//            //激活码段的结束位
//            String code_end = jsonObject.getString("code_end");
//        }
//        Map map = JSON.parseObject(JSON.toJSONString(jsonObject), Map.class);
        return jsonObject;
    }
}

上文中用的ACCESSTOKEN 保存工具类

import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
 * create by Nathan on 2020/11/18
 */

/**
 * 本地缓存服务
 * key = 字符串 | value = 字符串 | expire = 秒(有效时间)
 *
 * put 添加缓存
 * get 读取缓存值
 *
 * 失效策略:
 *  1. 定期删除策略:启动1个线程,每2分钟扫描一次,超时数据移除
 *  2. 懒惰淘汰策略:每次访问时校验有效性,如果失效移除
 */
public class LocalCache {


    private final static Logger logger = LoggerFactory.getLogger(LocalCache.class);

    /**
     * 启动开始后延迟5000秒执行时效策略
     */
    private static final int INITIAL_DELAY_TIME = 5000;
    /**
     * 执行时效策略间隔时间
     */
    private static final int PERIOD_TIME = 5000;
    /**
     * 本地缓存map
     */
    private static ConcurrentHashMap<String, Cache> store;
    /**
     * 执行时效策略线程池
     */
    private final static ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

    /*
     * 静态代码块
     *
     * 初始化缓存map
     * 添加时效策略定时线程任务
     */
    static {
        store = new ConcurrentHashMap<>();
        executor.scheduleAtFixedRate(new Task(), INITIAL_DELAY_TIME, PERIOD_TIME, TimeUnit.SECONDS);
    }

    public static void put(String key, String value) {
        put(key, value, 0);
    }

    /**
     * 设置缓存
     * @param key 唯一key
     * @param value 值
     * @param expire 超时时间-单位(s/秒)
     */
    public static void put(String key, String value, long expire) {
        logger.info("添加缓存,key={}, value={}, expire={}秒", key, value, expire);
        if (expire > 0) {
            store.put(key, new Cache(value, expire));
        } else {
            store.put(key, new Cache(value));
        }
    }

    public static String get(String key) {
        Cache cache = store.get(key);
        if (cache == null) {
            return null;
        }

        if (cache.getExpire() > 0 && cache.getExpire() < System.currentTimeMillis()) {
            remove(key);
            return null;
        }
        return store.get(key).getValue();
    }

    private static void remove(String key) {
        Cache cache = store.remove(key);
        logger.info("懒惰淘汰策略: 移除超时失效数据, cache={}", cache);
    }

    private static void removeAll() {
        logger.info("定期删除策略: 开始执行, store={}", store);
        for (String key : store.keySet()) {
            Cache cache = store.get(key);
            if (cache.getExpire() > 0 && cache.getExpire() < System.currentTimeMillis()) {
                store.remove(key);
                logger.info("定期删除策略: 移除超时失效数据, key={}, value={}, time={}", key, cache.getValue(), cache.getExpire());
            }
        }
    }

    /**
     * 定时移除时效数据任务
     */
    private static class Task implements Runnable {
        @Override
        public void run() {
            try {
                LocalCache.removeAll();
            } catch (Exception e) {
                logger.info("定期删除策略异常", e);
            }
        }
    }

    /**
     * 本地缓存对象
     */
    @Data
    private static class Cache {
        private String value;
        private long expire = 0;

        Cache(String value, long expire) {
            this.value = value;
            this.expire = System.currentTimeMillis() + expire * 1000;
        }

        Cache(String value) {
            this.value = value;
        }
    }

    public static void main(String[] args) {
        LocalCache.put("a", "10a", 7);
        String a = LocalCache.get("a");
        System.out.println(a);
    }
}

对于下载后的buffer二维码和解密后的二维码,我采用的方式都是保存到数据库,然后再把解密后的二维码以txt格式保存到了oss中,用户下载和查看都是返回oss的路径!

–保存oss 请移步 oss工具

如有不足,请各位留言评论!!!

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值