微信小程序开发实战:AccessToken管理与小程序码生成

一、背景与配置

在微信小程序开发中,AccessToken 是调用所有微信API的全局凭证(有效期2小时),而小程序码则是推广业务的重要载体。首先需要在配置文件中设置关键参数:

# application.yml 配置
wx:
  appid: wx64b5b88bd3013081
  secret: 978c53ac1d6d1acaef7d1ef88e15de09
  access-token-key: wx:access_token  # Redis存储键名

二、AccessToken获取与缓存实现

核心流程

三、动态生成小程序码

请求参数说明
参数名必填说明
scene场景值(最长32字符),用于携带业务参数(如:orderId=123
page页面路径(如:pages/order/detail),需去除开头的/
width二维码宽度(默认430px)
check_path检查页面是否存在(上线必须设为true
env_version环境版本(develop开发版/trial体验版/release正式版
核心配置
# 微信小程序配置
wx:
   appid: wx64b5b88bd3013081
   secret: 978c53ac1d6d1acaef7d1ef88e15de09
   # Redis中存储access_token的键名
   access-token-key: wx:access_token
 核心实现代码
/**
 *
 * @author DaFu
 * @className WechatMiniUtils
 * @date 2025/6/18
 */
@Slf4j
@Component
public class WeChatUtils {
    @Value("${wx.appid}")
    private String appid;

    @Value("${wx.secret}")
    private String secret;

    @Value("${wx.access-token-key}")
    private String accessTokenKey;
    /**
     * 接口调用凭据
     */
    private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}";
    /**
     * 接口不限制的小程序码
     */
    private static final String WX_ACO_DE_UN_LIMIT_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=";

    @Autowired
    private RedisCache redisCache;

    /**
     * 获取并缓存 AccessToken
     *
     * @author DaFu
     */
    public String getAccessToken() {
        String accessToken = redisCache.getCacheObject(accessTokenKey);
        if (StringUtils.isNotBlank(accessToken)) {
            return accessToken;
        }
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> response = restTemplate.getForEntity(
                ACCESS_TOKEN_URL, String.class, appid, secret
        );
        if (response.getStatusCode() == HttpStatus.OK) {
            JSONObject json = JSONObject.parseObject(response.getBody());
            if (json.containsKey("access_token")) {
                accessToken = json.getString("access_token");
                int expiresIn = json.getIntValue("expires_in");
                redisCache.setCacheObject(
                        accessTokenKey,
                        accessToken,
                        expiresIn - 120,
                        TimeUnit.SECONDS
                );
                return accessToken;
            } else {
                throw new RuntimeException("获取access_token失败: " + json.getString("errmsg"));
            }
        }
        throw new RuntimeException("微信接口调用失败");
    }

    /**
     * 生成不限制的小程序码
     *
     * @param scene 场景值(最大32字符)
     * @param page  页面路径(如:pages/index/index)
     * @return 小程序码的二进制数据
     */
    public byte[] getUnLimitQrCode(String scene, String page) {
        String accessToken = getAccessToken();
        String url = WX_ACO_DE_UN_LIMIT_URL + accessToken;
        Map<String, Object> body = new HashMap<>();
        body.put("scene", StringUtils.defaultString(scene, ""));
        if (StringUtils.isNotEmpty(page)) {
            page = page.startsWith("/") ? page.substring(1) : page;
        }
        body.put("page", StringUtils.defaultString(page, ""));
        body.put("width", 430);
        //TODO 上线必须是true
        body.put("check_path", false);
        //TODO 上线正式版为 "release"
        body.put("env_version", "develop");
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);
        ResponseEntity<byte[]> response = restTemplate.postForEntity(
                url, request, byte[].class
        );
        byte[] responseBody = response.getBody();
        if (response.getStatusCode() == HttpStatus.OK) {
            if (responseBody.length > 1 && responseBody[0] == '{') {
                String jsonResponse = new String(responseBody, StandardCharsets.UTF_8);
                JSONObject json = JSONObject.parseObject(jsonResponse);
                if (json.containsKey("errcode")) {
                    int errcode = json.getIntValue("errcode");
                    String errmsg = json.getString("errmsg");
                    log.error("生成小程序码失败: errcode={}, errmsg={}", errcode, errmsg);
                    if (errcode == 40001 || errcode == 42001) {
                        redisCache.deleteObject(accessTokenKey);
                        return getUnLimitQrCode(scene, page);
                    }
                }
            }
            return responseBody;
        }
        throw new RuntimeException("生成小程序码失败: " + new String(response.getBody()));
    }


}

四、使用示例

生成订单详情页小程序码
@RestController
public class QrCodeController {
    @Autowired
    private WeChatUtils weChatUtils;

    @GetMapping("/order/qrcode")
    public void getOrderQrCode(HttpServletResponse response) throws IOException {
        // 生成携带订单ID的小程序码
        byte[] qrCode = weChatUtils.getUnLimitQrCode(
            "orderId=202507011234", 
            "pages/order/detail"
        );
        
        // 输出图片流
        response.setContentType("image/jpeg");
        OutputStream os = response.getOutputStream();
        os.write(qrCode);
        os.flush();
    }
}

五、常见问题排查

  1. AccessToken获取失败

    • 检查appid/secret是否正确

    • 确认服务器IP已加入微信白名单

    • 查看微信返回的完整错误信息

  2. 小程序码生成报错

    • {"errcode":41030,"errmsg":"invalid page hint"}

      检查page路径是否存在(上线前开启check_path

    • 确认页面路径格式正确(如:pages/index/index

  3. 缓存异常处理

    • Redis不可用时增加降级策略(如本地缓存)

    • 监控Token获取频率,避免超出限额

总结

本文完整实现了微信小程序开发中的两个关键功能:

  1. 通过 Redis缓存+提前刷新 策略保障AccessToken的高可用性

  2. 采用 错误重试+递归调用 机制处理小程序码生成异常

特别提醒:上线前务必修改check_path=trueenv_version=release,否则可能导致生成无效二维码。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值