本人使用java开发
框架:springboot 1.5.7.RELEASE
本次项目所用的服务器已有其他项目在运行,并且占用了443端口。原项目使用nginx实现转发,所以需要重新配置nginx增加域名转发,以满足微信必须使用443端口要求。
原nginx配置文件如下
user www www;
worker_processes auto;
error_log /www/wwwlogs/nginx_error.log crit;
pid /www/server/nginx/logs/nginx.pid;
worker_rlimit_nofile 51200;
events
{
use epoll;
worker_connections 51200;
multi_accept on;
}
http
{
include mime.types;
include proxy.conf;
#include luawaf.conf;
default_type application/octet-stream;
server_names_hash_bucket_size 512;
client_header_buffer_size 32k;
large_client_header_buffers 4 32k;
client_max_body_size 50m;
sendfile on;
tcp_nopush on;
keepalive_timeout 60;
tcp_nodelay on;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server_tokens off;
access_log off;
server
{
listen 888;
server_name www.bt.cn;
index index.html index.htm index.php;
root /www/server/phpmyadmin;
#error_page 404 /404.html;
include enable-php.conf;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
}
location ~ .*\.(js|css)?$
{
expires 12h;
}
location ~ /\.
{
deny all;
}
access_log /www/wwwlogs/access.log;
}
include /www/server/panel/vhost/nginx/*.conf;
}
配置文件最下面 include 这行内容,会将*.conf文件全部引入到配置中。所以我们再 /www/server/panel/vhost/nginx/ 路径下新增本次项目所需配置,如下
server
{
listen 80;
listen 443 ssl;
server_name 项目域名;
root /根路径;
#SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
#error_page 404/404.html;
#HTTP_TO_HTTPS_START
if ($server_port !~ 443){
rewrite ^(/.*)$ https://$host$1 permanent;
}
#HTTP_TO_HTTPS_END
ssl_certificate /ssl证书.crt;
ssl_certificate_key /ssl证书.rsa;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
#SSL-END
#REWRITE-START URL重写规则引用,修改后将导致面板设置的伪静态规则失效
#REWRITE-END
location = /xxx.txt{
}
location / {
proxy_pass https://项目域名:项目端口;
}
access_log /www/wwwlogs/项目域名.log;
error_log /www/wwwlogs/项目域名.error.log;
}
配置文件中的 需要替换成自己的内容。
location中的xxx.txt 是在上一篇中说的配置授权域名时的校验文件,将校验文件放置根目录下既可以访问到。
1.获取openid
本次项目集成了微信支付、公众号模板消息,需要使用到用户openid,所以首先获取openid。
配置公众号菜单 打开 https://项目域名/open/openid 地址。
该接口重定向至微信授权地址,只需要openid所以使用用户无感的 scope=snsapi_base 即可。
代码如下
@Controller
@Slf4j
public class LoginUserController {
@Resource
private ICourseService courseService;
@Resource
private MyWXPayConfig myWXPayConfig;
@Resource
private RestTemplate restTemplate;
@Resource
private IWxAccessTokerService accessTokerService;
@Resource
private CourseLocationMapper locationMapper;
@Autowired
private IDataDictionaryService dataDictionaryService;
@Value("${server.port}")
private String port;
@Value("${wxconfig.domain}")
private String domain;
@RequestMapping("/open/openid")
public void getOpenId(HttpServletRequest request, HttpServletResponse response){
StringBuffer sb = new StringBuffer();
StringBuffer encodeUrl = new StringBuffer("https://");
//公众号中配置的回调域名(网页授权回调域名)
String root = request.getContextPath();
String appId = myWXPayConfig.getAppID();
sb.append("https://open.weixin.qq.com/connect/oauth2/authorize?appid=");
sb.append(appId);
try {
//对重定向url进行编码,官方文档要求
encodeUrl.append(domain).append(root)/*.append(":").append(port)*/;
String url = URLEncoder.encode(encodeUrl.toString(), "utf-8");
sb.append("&redirect_uri=").append(url);
//网页授权的静默授权snsapi_base
sb.append("&response_type=code&scope=snsapi_base#wechat_redirect");
response.sendRedirect(sb.toString());
} catch (UnsupportedEncodingException e) {
log.error("重定向url编码失败:>>" + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
log.error("response重定向失败:>>" + e.getMessage());
e.printStackTrace();
}
}
/**
* @Title: login
* @Description: TODO(首页)
* @param @return 设定文件
* @return String 返回类型
* @throws
*/
@RequestMapping("/")
public String login(Model model, HttpServletRequest request, HttpServletResponse response) throws BusinessException {
//获取重定向携带的code参数值
String code = request.getParameter("code");
String currentPageUrl = request.getRequestURL().toString();
if(StringUtils.isNotBlank(code)) {
currentPageUrl = new StringBuffer("https://").append(domain).append("/").append("?code=").append(code).append("&state=").toString();
String openId = null;
if (null == openId) {
/*
* 根据得到的code参数,内部请求获取openId的方法。
*/
openId = getOpenId(request,code);
}
/*
* 将openId保存到session中,当其他业务获取openId时,
* 可先从session中获取openId.
*/
request.getSession().setAttribute("openid", openId);
}
try {
String jsapi_ticket = accessTokerService.getWeiXinTicket();
Map<String, String> ret = WxJsapiSignUtil.sign(jsapi_ticket, currentPageUrl);
Map<String, String> wxconfig = new HashMap<>();
wxconfig.put("appId", myWXPayConfig.getAppID());
wxconfig.put("timestamp", ret.get("timestamp"));
wxconfig.put("nonceStr", ret.get("nonceStr"));
wxconfig.put("signature", ret.get("signature"));
model.addAttribute("wxconfig", wxconfig);
log.info("wxconfig ="+wxconfig.toString());
} catch (Exception e) {
log.error(e.toString());
}
return "course/index";
}
//发送请求,根据code获取openId
private String getOpenId(HttpServletRequest request,String code) {
String content = "";
String openId = "";
//封装获取openId的微信API
StringBuffer url = new StringBuffer();
url.append("https://api.weixin.qq.com/sns/oauth2/access_token?appid=")
.append(myWXPayConfig.getAppID())
.append("&secret=")
.append(myWXPayConfig.getAppsecret())
.append("&code=")
.append(code)
.append("&grant_type=authorization_code");
try {
content = restTemplate.getForEntity(url.toString(), String.class).getBody();
JSONObject json = JSONObject.parseObject(content);
openId = json.getString("openid");
} catch (Exception e) {
log.error("http获取openId请求失败:", e);
}
return openId;
}
}
2.发送模板消息
以发送公众号模板消息为例,展示openid的使用。
第一步 获取accessToken
@Service
@Slf4j
public class WxAccessTokenServiceImpl implements IWxAccessTokerService {
private static AccessToken accessToken = null;
@Resource
private MyWXPayConfig wxPayConfig;
@Autowired
private RestTemplate restTemplate;
/**
* 获取access_token的接口地址
*/
public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
/**
* 通过APPID 和 APPSECRET
* 获取assess_token
* @return
*/
@Override
public AccessToken getAccessToken() throws BusinessException {
String appid = wxPayConfig.getAppID();
String appsecret = wxPayConfig.getAppsecret();
if(accessToken == null
|| DateUtils.addSeconds(accessToken.getReceiveTime(), accessToken.getExpires_in()).before(new Date())) {
String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret);
JSONObject jsonObject = restTemplate.getForObject(requestUrl,JSONObject.class);
// 如果请求成功
if (null != jsonObject && jsonObject.get("access_token")!=null) {
try {
accessToken = new AccessToken();
accessToken.setAccess_token(jsonObject.getString("access_token"));
accessToken.setExpires_in(jsonObject.getInteger("expires_in"));
accessToken.setReceiveTime(new Date());
log.info("获取token成功!");
} catch (JSONException e) {
accessToken = null;
// 获取token失败
log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
}
} else {
throw new BusinessException(ResponseConstants.FAIL.getRetCode(), "获取access_token失败!");
}
} else {
log.info("token在有效期内,直接返回!");
}
return accessToken;
}
}
@Data
public class AccessToken {
/**
* 获取到的凭证
*/
private String access_token;
/*
* 凭证得到时间
*/
private Date receiveTime;
/**
* 凭证有效时间,单位:秒
*/
private int expires_in;
}
第二步 构建模板参数实体类
/**
* 微信模板类
*/
@Data
@ToString
public class WeChatTemplate implements Serializable {
private static final long serialVersionUID = 612571563869874653L;
/**
* 模板id
*/
private String template_id;
/**
* 接收者 openId
*/
private String touser;
/**
* 模板跳转链接
*/
@JsonSerialize(include= JsonSerialize.Inclusion.NON_EMPTY)
private String url;
/**
* data的数据
*/
private TreeMap<String, TreeMap<String, String>> data;
/**
* data 里的数据
*
* @param value :模板参数
* @param color :颜色 可选
* @return
*/
public static TreeMap<String, String> item(String value, String color) {
TreeMap<String, String> params = new TreeMap<String, String>();
params.put("value", value);
params.put("color", color==null?"#173177":color);
return params;
}
}
第三步 模板消息发送服务
@Component
@Slf4j
public class WeChatTemplateMessageService {
@Autowired
private RestTemplate restTemplate;
@Resource
private WechatTemplateMessageLogMapper templateMessageLogMapper;
/**发送模板消息*/
public static final String SEND_TEMPLATE_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
/**
* 发送模板消息
* @param accessToken
* @param template
* @return
*/
public void sendTemplateMsg(String accessToken, WeChatTemplate template){
log.info("模板消息请求参数"+ template.toString());
String requestUrl =SEND_TEMPLATE_MESSAGE.replace("ACCESS_TOKEN",accessToken);
JSONObject jsonObject = restTemplate.postForObject(requestUrl,template,JSONObject.class);
log.info("返回jsonObject值:"+jsonObject);
if (null != jsonObject) {
int errorCode = jsonObject.getIntValue("errcode");
//发送日志
if (0 == errorCode) {
log.info("模板消息发送成功");
} else {
String errorMsg = jsonObject.getString("errmsg");
log.info("模板消息发送失败,错误是 "+errorCode+",错误信息是"+ errorMsg);
}
}
}
}
最后 发送消息代码
try {
//发送消息
AccessToken accessToken = wxAccessTokerService.getAccessToken();
if(accessToken == null) {
logger.error("accessToken为空,无法发送消息");
} else {
WechatTemplateMessageConfig config = templateMessageConfigMapper.selectByCode("APPLY");
if(StringUtils.isBlank(config.getTemplate_id())) {
logger.error("模板id未配置,无法发送消息");
} else {
String first = "您已报名参加!";
allData.put("first", WeChatTemplate.item(first, null));
allData.put("keyword1",WeChatTemplate.item(TextFormatUtil.towDecimalPlacesFormat(courseApply.getTotal_fee())+"元",null));
allData.put("keyword2",WeChatTemplate.item(courseUser.getName(),null));
allData.put("keyword3",WeChatTemplate.item(courseUser.getMobile(),null));
allData.put("keyword4",WeChatTemplate.item(TextFormatUtil.timeFormatText(courseApply.getApply_time()),null));
allData.put("keyword5",WeChatTemplate.item("微信公众号", null));
allData.put("remark",WeChatTemplate.item("感谢您的参与!", null));
WeChatTemplate template = new WeChatTemplate();
template.setTouser(courseUser.getOpenid());
template.setTemplate_id(config.getTemplate_id());
template.setUrl(null);
template.setData(allData);
weChatTemplateMessageService.sendTemplateMsg(accessToken.getAccess_token(), template);
}
}
}catch (Exception e){
e.printStackTrace();
logger.error("发送消息出现异常"+e.getMessage());
}
至此 获取openid及发送模板消息完成,后续如有时间会完成微信jssdk及微信支付内容!如有发现错误及不足还请帮忙指正,谢谢!