#193 增加小程序模块weixin-java-miniapp,支持小程序后台开发

This commit is contained in:
Binary Wang
2017-06-16 00:02:45 +08:00
parent 39e79a2c00
commit 1bd9b15c98
50 changed files with 3641 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
package cn.binarywang.wx.miniapp.api;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.exception.WxErrorException;
import java.io.File;
import java.io.InputStream;
/**
* <pre>
* 临时素材接口
* Created by Binary Wang on 2016/7/21.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaMediaService {
String MEDIA_UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/media/upload?type=%s";
String MEDIA_GET_URL = "https://api.weixin.qq.com/cgi-bin/media/get";
/**
* <pre>
* 新增临时素材
* 小程序可以使用本接口把媒体文件(目前仅支持图片)上传到微信服务器,用户发送客服消息或被动回复用户消息。
* 详情请见: <a href="https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/material.html#新增临时素材">新增临时素材</a>
* 接口url格式https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
* </pre>
*
* @param mediaType 媒体类型,
* @param file 文件对象
* @see #uploadMedia(String, String, InputStream)
*/
WxMediaUploadResult uploadMedia(String mediaType, File file) throws WxErrorException;
/**
* <pre>
* 新增临时素材
* 小程序可以使用本接口把媒体文件(目前仅支持图片)上传到微信服务器,用户发送客服消息或被动回复用户消息。
*
* 详情请见: <a href="https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/material.html#新增临时素材">新增临时素材</a>
* 接口url格式https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
* </pre>
*
* @param mediaType 媒体类型
* @param fileType 文件类型
* @param inputStream 输入流
* @see #uploadMedia(java.lang.String, java.io.File)
*/
WxMediaUploadResult uploadMedia(String mediaType, String fileType, InputStream inputStream) throws WxErrorException;
/**
* <pre>
* 获取临时素材
* 小程序可以使用本接口获取客服消息内的临时素材(即下载临时的多媒体文件)。目前小程序仅支持下载图片文件。
*
* 详情请见: <a href="https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/material.html#获取临时素材">获取临时素材</a>
* 接口url格式https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
* </pre>
*
* @param mediaId 媒体Id
* @return 保存到本地的临时文件
*/
File getMedia(String mediaId) throws WxErrorException;
}

View File

@@ -0,0 +1,36 @@
package cn.binarywang.wx.miniapp.api;
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage;
import me.chanjar.weixin.common.exception.WxErrorException;
/**
* <pre>
* 消息发送接口
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaMsgService {
String KEFU_MESSAGE_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send";
String TEMPLATE_MSG_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send";
/**
* <pre>
* 发送客服消息
* 详情请见: <a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140547&token=&lang=zh_CN">发送客服消息</a>
* 接口url格式https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
* </pre>
*/
boolean sendKefuMsg(WxMaKefuMessage message) throws WxErrorException;
/**
* <pre>
* 发送模板消息
* 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN
* </pre>
*
* @return 消息Id
*/
String sendTemplateMsg(WxMaTemplateMessage templateMessage) throws WxErrorException;
}

View File

@@ -0,0 +1,30 @@
package cn.binarywang.wx.miniapp.api;
import me.chanjar.weixin.common.exception.WxErrorException;
import java.io.File;
/**
* <pre>
* 二维码相关操作接口
* 文档地址https://mp.weixin.qq.com/debug/wxadoc/dev/api/qrcode.html
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaQrcodeService {
/**
* <pre>
* 获取小程序页面二维码
* 适用于需要的码数量较少的业务场景
* 通过该接口,仅能生成已发布的小程序的二维码。
* 可以在开发者工具预览时生成开发版的带参二维码。
* 带参二维码只有 100000 个,请谨慎调用。
* </pre>
*
* @param path 不能为空,最大长度 128 字节
* @param width 默认430 二维码的宽度
*/
File createQrcode(String path, int width) throws WxErrorException;
}

View File

@@ -0,0 +1,135 @@
package cn.binarywang.wx.miniapp.api;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaService {
/**
* 获取access_token
*/
String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
/**
* <pre>
* 验证消息的确来自微信服务器
* 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN
* </pre>
*/
boolean checkSignature(String timestamp, String nonce, String signature);
/**
* 获取access_token, 不强制刷新access_token
*
* @see #getAccessToken(boolean)
*/
String getAccessToken() throws WxErrorException;
/**
* <pre>
* 获取access_token本方法线程安全
* 且在多线程同时刷新时只刷新一次避免超出2000次/日的调用次数上限
*
* 另本service的所有方法都会在access_token过期是调用此方法
*
* 程序员在非必要情况下尽量不要主动调用此方法
*
* 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183&token=&lang=zh_CN
* </pre>
*
* @param forceRefresh 强制刷新
*/
String getAccessToken(boolean forceRefresh) throws WxErrorException;
/**
* 当本Service没有实现某个API的时候可以用这个针对所有微信API中的GET请求
*/
String get(String url, String queryParam) throws WxErrorException;
/**
* 当本Service没有实现某个API的时候可以用这个针对所有微信API中的POST请求
*/
String post(String url, String postData) throws WxErrorException;
/**
* <pre>
* Service没有实现某个API的时候可以用这个
* 比{@link #get}和{@link #post}方法更灵活可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
* 可以参考,{@link MediaUploadRequestExecutor}的实现方法
* </pre>
*/
<T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException;
/**
* <pre>
* 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试
* 默认1000ms
* </pre>
*/
void setRetrySleepMillis(int retrySleepMillis);
/**
* <pre>
* 设置当微信系统响应系统繁忙时,最大重试次数
* 默认5次
* </pre>
*/
void setMaxRetryTimes(int maxRetryTimes);
/**
* 获取WxMaConfig 对象
*
* @return WxMaConfig
*/
WxMaConfig getWxMaConfig();
/**
* 注入 {@link WxMaConfig} 的实现
*/
void setWxMaConfig(WxMaConfig wxConfigProvider);
/**
* 返回消息(客服消息和模版消息)发送接口方法实现类,以方便调用其各个接口
*
* @return WxMaMsgService
*/
WxMaMsgService getMsgService();
/**
* 返回素材相关接口方法的实现类对象,以方便调用其各个接口
*
* @return WxMaMediaService
*/
WxMaMediaService getMediaService();
/**
* 返回用户相关接口方法的实现类对象,以方便调用其各个接口
*
* @return WxMaUserService
*/
WxMaUserService getUserService();
/**
* 返回二维码相关接口方法的实现类对象,以方便调用其各个接口
*
* @return WxMaQrcodeService
*/
WxMaQrcodeService getQrcodeService();
/**
* 初始化http请求对象
*/
void initHttp();
/**
* 请求http请求相关信息
*/
RequestHttp getRequestHttp();
}

View File

@@ -0,0 +1,39 @@
package cn.binarywang.wx.miniapp.api;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import me.chanjar.weixin.common.exception.WxErrorException;
/**
* 用户信息相关操作接口
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMaUserService {
String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session";
/**
* 获取登录后的session信息
*
* @param jsCode 登录时获取的 code
*/
WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException;
/**
* 解密用户敏感数据
*
* @param sessionKey 会话密钥
* @param encryptedData 消息密文
* @param ivStr 加密算法的初始向量
*/
WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr);
/**
* 验证用户信息完整性
*
* @param sessionKey 会话密钥
* @param rawData 微信用户基本信息
* @param signature 数据签名
*/
boolean checkUserInfo(String sessionKey, String rawData, String signature);
}

View File

@@ -0,0 +1,61 @@
package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.WxMaMediaService;
import cn.binarywang.wx.miniapp.api.WxMaService;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.fs.FileUtils;
import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor;
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.UUID;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaMediaServiceImpl implements WxMaMediaService {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private WxMaService wxMaService;
public WxMaMediaServiceImpl(WxMaService wxMaService) {
this.wxMaService = wxMaService;
}
@Override
public WxMediaUploadResult uploadMedia(String mediaType, String fileType, InputStream inputStream) throws WxErrorException {
try {
return this.uploadMedia(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType));
} catch (IOException e) {
e.printStackTrace();
throw new WxErrorException(WxError.newBuilder().setErrorMsg(e.getMessage()).build());
}
}
@Override
public WxMediaUploadResult uploadMedia(String mediaType, File file) throws WxErrorException {
String url = String.format(MEDIA_UPLOAD_URL, mediaType);
return this.wxMaService.execute(MediaUploadRequestExecutor.create(this.wxMaService.getRequestHttp()), url, file);
}
@Override
public File getMedia(String mediaId) throws WxErrorException {
try {
RequestExecutor<File, String> executor = MediaDownloadRequestExecutor
.create(this.wxMaService.getRequestHttp(), Files.createTempDirectory("wxma").toFile());
return this.wxMaService.execute(executor, MEDIA_GET_URL, "media_id=" + mediaId);
} catch (IOException e) {
this.log.error(e.getMessage(), e);
throw new WxErrorException(WxError.newBuilder().setErrorMsg(e.getMessage()).build());
}
}
}

View File

@@ -0,0 +1,40 @@
package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.WxMaMsgService;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.exception.WxErrorException;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaMsgServiceImpl implements WxMaMsgService {
private static final JsonParser JSON_PARSER = new JsonParser();
private WxMaService wxMaService;
public WxMaMsgServiceImpl(WxMaService wxMaService) {
this.wxMaService = wxMaService;
}
@Override
public boolean sendKefuMsg(WxMaKefuMessage message) throws WxErrorException {
String responseContent = this.wxMaService.post(KEFU_MESSAGE_SEND_URL, message.toJson());
return responseContent != null;
}
@Override
public String sendTemplateMsg(WxMaTemplateMessage templateMessage) throws WxErrorException {
String responseContent = this.wxMaService.post(TEMPLATE_MSG_SEND_URL, templateMessage.toJson());
JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject();
if (jsonObject.get("errcode").getAsInt() == 0) {
return jsonObject.get("msgid").getAsString();
}
throw new WxErrorException(WxError.fromJson(responseContent));
}
}

View File

@@ -0,0 +1,28 @@
package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.WxMaQrcodeService;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaQrcode;
import cn.binarywang.wx.miniapp.util.http.QrCodeRequestExecutor;
import me.chanjar.weixin.common.exception.WxErrorException;
import java.io.File;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaQrcodeServiceImpl implements WxMaQrcodeService {
private WxMaService wxMaService;
public WxMaQrcodeServiceImpl(WxMaService wxMaService) {
this.wxMaService = wxMaService;
}
@Override
public File createQrcode(String path, int width) throws WxErrorException {
String url = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode";
return this.wxMaService.execute(new QrCodeRequestExecutor(this.wxMaService.getRequestHttp()),
url, new WxMaQrcode(path, width));
}
}

View File

@@ -0,0 +1,265 @@
package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.*;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import com.google.gson.JsonParser;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.crypto.SHA1;
import me.chanjar.weixin.common.util.http.*;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.concurrent.locks.Lock;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpClient, HttpHost> {
private static final JsonParser JSON_PARSER = new JsonParser();
private final Logger log = LoggerFactory.getLogger(this.getClass());
private CloseableHttpClient httpClient;
private HttpHost httpProxy;
private WxMaConfig wxMaConfig;
private WxMaMsgService kefuService = new WxMaMsgServiceImpl(this);
private WxMaMediaService materialService = new WxMaMediaServiceImpl(this);
private WxMaUserService userService = new WxMaUserServiceImpl(this);
private WxMaQrcodeService qrCodeService = new WxMaQrcodeServiceImpl(this);
private int retrySleepMillis = 1000;
private int maxRetryTimes = 5;
@Override
public CloseableHttpClient getRequestHttpClient() {
return httpClient;
}
@Override
public HttpHost getRequestHttpProxy() {
return httpProxy;
}
@Override
public HttpType getRequestType() {
return HttpType.APACHE_HTTP;
}
@Override
public void initHttp() {
WxMaConfig configStorage = this.getWxMaConfig();
ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder();
if (null == apacheHttpClientBuilder) {
apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get();
}
apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost())
.httpProxyPort(configStorage.getHttpProxyPort())
.httpProxyUsername(configStorage.getHttpProxyUsername())
.httpProxyPassword(configStorage.getHttpProxyPassword());
if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) {
this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort());
}
this.httpClient = apacheHttpClientBuilder.build();
}
@Override
public RequestHttp getRequestHttp() {
return this;
}
@Override
public String getAccessToken(boolean forceRefresh) throws WxErrorException {
Lock lock = this.getWxMaConfig().getAccessTokenLock();
try {
lock.lock();
if (forceRefresh) {
this.getWxMaConfig().expireAccessToken();
}
if (this.getWxMaConfig().isAccessTokenExpired()) {
String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(),
this.getWxMaConfig().getSecret());
try {
HttpGet httpGet = new HttpGet(url);
if (this.getRequestHttpProxy() != null) {
RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build();
httpGet.setConfig(config);
}
try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) {
String resultContent = new BasicResponseHandler().handleResponse(response);
WxError error = WxError.fromJson(resultContent);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
this.getWxMaConfig().updateAccessToken(accessToken.getAccessToken(),
accessToken.getExpiresIn());
} finally {
httpGet.releaseConnection();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} finally {
lock.unlock();
}
return this.getWxMaConfig().getAccessToken();
}
@Override
public boolean checkSignature(String timestamp, String nonce, String signature) {
try {
return SHA1.gen(this.getWxMaConfig().getToken(), timestamp, nonce).equals(signature);
} catch (Exception e) {
this.log.error("Checking signature failed, and the reason is :" + e.getMessage());
return false;
}
}
@Override
public String getAccessToken() throws WxErrorException {
return getAccessToken(false);
}
@Override
public String get(String url, String queryParam) throws WxErrorException {
return execute(SimpleGetRequestExecutor.create(this), url, queryParam);
}
@Override
public String post(String url, String postData) throws WxErrorException {
return execute(SimplePostRequestExecutor.create(this), url, postData);
}
/**
* 向微信端发送请求在这里执行的策略是当发生access_token过期时才去刷新然后重新执行请求而不是全局定时请求
*/
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
int retryTimes = 0;
do {
try {
return this.executeInternal(executor, uri, data);
} catch (WxErrorException e) {
if (retryTimes + 1 > this.maxRetryTimes) {
this.log.warn("重试达到最大次数【{}】", maxRetryTimes);
//最后一次重试失败后,直接抛出异常,不再等待
throw new RuntimeException("微信服务端异常,超出重试次数");
}
WxError error = e.getError();
// -1 系统繁忙, 1000ms后重试
if (error.getErrorCode() == -1) {
int sleepMillis = this.retrySleepMillis * (1 << retryTimes);
try {
this.log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
Thread.sleep(sleepMillis);
} catch (InterruptedException e1) {
throw new RuntimeException(e1);
}
} else {
throw e;
}
}
} while (retryTimes++ < this.maxRetryTimes);
this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
throw new RuntimeException("微信服务端异常,超出重试次数");
}
public synchronized <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
if (uri.contains("access_token=")) {
throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri);
}
String accessToken = getAccessToken(false);
String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "access_token=" + accessToken;
try {
T result = executor.execute(uriWithAccessToken, data);
this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uriWithAccessToken, data, result);
return result;
} catch (WxErrorException e) {
WxError error = e.getError();
/*
* 发生以下情况时尝试刷新access_token
* 40001 获取access_token时AppSecret错误或者access_token无效
* 42001 access_token超时
* 40014 不合法的access_token请开发者认真比对access_token的有效性如是否过期
*/
if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001 || error.getErrorCode() == 40014) {
// 强制设置wxMpConfigStorage它的access token过期了这样在下一次请求里就会刷新access token
this.getWxMaConfig().expireAccessToken();
if (this.getWxMaConfig().autoRefreshToken()) {
return this.execute(executor, uri, data);
}
}
if (error.getErrorCode() != 0) {
this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uriWithAccessToken, data, error);
throw new WxErrorException(error);
}
return null;
} catch (IOException e) {
this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", uriWithAccessToken, data, e.getMessage());
throw new RuntimeException(e);
}
}
@Override
public WxMaConfig getWxMaConfig() {
return this.wxMaConfig;
}
@Override
public void setWxMaConfig(WxMaConfig wxConfigProvider) {
this.wxMaConfig = wxConfigProvider;
this.initHttp();
}
@Override
public void setRetrySleepMillis(int retrySleepMillis) {
this.retrySleepMillis = retrySleepMillis;
}
@Override
public void setMaxRetryTimes(int maxRetryTimes) {
this.maxRetryTimes = maxRetryTimes;
}
@Override
public WxMaMsgService getMsgService() {
return this.kefuService;
}
@Override
public WxMaMediaService getMediaService() {
return this.materialService;
}
@Override
public WxMaUserService getUserService() {
return this.userService;
}
@Override
public WxMaQrcodeService getQrcodeService() {
return this.qrCodeService;
}
}

View File

@@ -0,0 +1,51 @@
package cn.binarywang.wx.miniapp.api.impl;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils;
import com.google.common.base.Joiner;
import me.chanjar.weixin.common.exception.WxErrorException;
import org.apache.commons.codec.digest.DigestUtils;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMaUserServiceImpl implements WxMaUserService {
private WxMaService service;
WxMaUserServiceImpl(WxMaService service) {
this.service = service;
}
@Override
public WxMaJscode2SessionResult getSessionInfo(String jsCode) throws WxErrorException {
final WxMaConfig config = service.getWxMaConfig();
Map<String, String> params = new HashMap<>();
params.put("appid", config.getAppid());
params.put("secret", config.getSecret());
params.put("js_code", jsCode);
params.put("grant_type", "authorization_code");
String result = this.service.get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params));
return WxMaJscode2SessionResult.fromJson(result);
}
@Override
public WxMaUserInfo getUserInfo(String sessionKey, String encryptedData, String ivStr) {
return WxMaUserInfo.fromJson(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr));
}
@Override
public boolean checkUserInfo(String sessionKey, String rawData, String signature) {
final String generatedSignature = DigestUtils.sha1Hex(rawData + sessionKey);
System.out.println(generatedSignature);
return generatedSignature.equals(signature);
}
}