weixin-mp & pay

This commit is contained in:
jy.hu
2014-11-03 15:44:12 +08:00
parent c908581178
commit 45ce10141a
172 changed files with 5591 additions and 742 deletions
@@ -0,0 +1,869 @@
package com.foxinmy.weixin4j.mp;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.XmlResult;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.mp.api.GroupApi;
import com.foxinmy.weixin4j.mp.api.HelperApi;
import com.foxinmy.weixin4j.mp.api.MassApi;
import com.foxinmy.weixin4j.mp.api.MediaApi;
import com.foxinmy.weixin4j.mp.api.MenuApi;
import com.foxinmy.weixin4j.mp.api.NotifyApi;
import com.foxinmy.weixin4j.mp.api.PayApi;
import com.foxinmy.weixin4j.mp.api.QrApi;
import com.foxinmy.weixin4j.mp.api.TmplApi;
import com.foxinmy.weixin4j.mp.api.UserApi;
import com.foxinmy.weixin4j.mp.model.Button;
import com.foxinmy.weixin4j.mp.model.CustomRecord;
import com.foxinmy.weixin4j.mp.model.Following;
import com.foxinmy.weixin4j.mp.model.Group;
import com.foxinmy.weixin4j.mp.model.MpArticle;
import com.foxinmy.weixin4j.mp.model.QRParameter;
import com.foxinmy.weixin4j.mp.model.User;
import com.foxinmy.weixin4j.mp.model.UserToken;
import com.foxinmy.weixin4j.mp.msg.model.Article;
import com.foxinmy.weixin4j.mp.msg.model.BaseMsg;
import com.foxinmy.weixin4j.mp.msg.notify.BaseNotify;
import com.foxinmy.weixin4j.mp.payment.BillType;
import com.foxinmy.weixin4j.mp.payment.IdQuery;
import com.foxinmy.weixin4j.mp.payment.IdType;
import com.foxinmy.weixin4j.mp.payment.v2.Order;
import com.foxinmy.weixin4j.mp.payment.v3.Refund;
import com.foxinmy.weixin4j.mp.response.TemplateMessage;
import com.foxinmy.weixin4j.token.FileTokenHolder;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.type.MediaType;
/**
* 微信服务实现
*
* @className WeixinProxy
* @author jy.hu
* @date 2014年3月23日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php">api文档</a>
*/
public class WeixinProxy {
private final MediaApi mediaApi;
private final NotifyApi notifyApi;
private final MassApi massApi;
private final UserApi userApi;
private final GroupApi groupApi;
private final MenuApi menuApi;
private final QrApi qrApi;
private final TmplApi tmplApi;
private final HelperApi helperApi;
private final PayApi payApi;
/**
* 默认采用文件存放Token跟配置文件中的appi信息
*/
public WeixinProxy() {
this(new FileTokenHolder());
}
/**
* 也可接受传递过来的appid跟appsecret
*
* @param appid
* @param appsecret
*/
public WeixinProxy(String appid, String appsecret) {
this(new FileTokenHolder(appid, appsecret));
}
public WeixinProxy(TokenHolder tokenHolder) {
this.mediaApi = new MediaApi(tokenHolder);
this.notifyApi = new NotifyApi(tokenHolder);
this.massApi = new MassApi(tokenHolder);
this.userApi = new UserApi(tokenHolder);
this.groupApi = new GroupApi(tokenHolder);
this.menuApi = new MenuApi(tokenHolder);
this.qrApi = new QrApi(tokenHolder);
this.tmplApi = new TmplApi(tokenHolder);
this.helperApi = new HelperApi(tokenHolder);
this.payApi = new PayApi(tokenHolder);
}
/**
* 上传媒体文件
* <p>
* 正常情况下返回{"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789},
* 否则抛出异常.
* </p>
*
* @param file
* 文件对象
* @param mediaType
* 媒体类型
* @return 上传到微信服务器返回的媒体标识
* @throws WeixinException
* @throws IOException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E4%B8%8B%E8%BD%BD%E5%A4%9A%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">上传下载说明</a>
* @see com.foxinmy.weixin4j.type.MediaType
* @see com.foxinmy.weixin4j.mp.api.MediaApi
*/
public String uploadMedia(File file, MediaType mediaType)
throws WeixinException, IOException {
return mediaApi.uploadMedia(file, mediaType);
}
/**
* 上传媒体文件
*
* @param bytes
* 媒体数据包
* @param mediaType
* 媒体类型
* @return 上传到微信服务器返回的媒体标识
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.MediaApi
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#uploadMedia(File, MediaType)}
*/
public String uploadMedia(String fileName, byte[] data, MediaType mediaType)
throws WeixinException {
return mediaApi.uploadMedia(fileName, data, mediaType);
}
/**
* 下载媒体文件
* <p>
* 正常情况下返回表头如Content-Type: image/jpeg,否则抛出异常.
* </p>
*
* @param mediaId
* 存储在微信服务器上的媒体标识
* @param mediaType
* 媒体类型
* @return 写入硬盘后的文件对象
* @throws WeixinException
* @throws IOException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E4%B8%8B%E8%BD%BD%E5%A4%9A%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">上传下载说明</a>
* @see com.foxinmy.weixin4j.type.MediaType
* @see com.foxinmy.weixin4j.mp.api.MediaApi
*/
public File downloadMedia(String mediaId, MediaType mediaType)
throws WeixinException, IOException {
return mediaApi.downloadMedia(mediaId, mediaType);
}
/**
* 下载媒体文件
*
* @param mediaId
* @param mediaType
* @return 二进制数据包
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.MediaApi
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#downloadMedia(String, MediaType)}
*/
public byte[] downloadMediaData(String mediaId, MediaType mediaType)
throws WeixinException {
return mediaApi.downloadMediaData(mediaId, mediaType);
}
/**
* 发送客服消息(在48小时内不限制发送次数)
*
* @param notify
* 客服消息对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF">发送客服消息</a>
* @see com.foxinmy.weixin4j.mp.msg.notify.TextNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.ImageNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.MusicNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.VideoNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.VoiceNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.ArticleNotify
* @see com.foxinmy.weixin4j.mp.api.NotifyApi
*/
public JsonResult sendNotify(BaseNotify notify) throws WeixinException {
return notifyApi.sendNotify(notify);
}
/**
* 发送图文消息
*
* @param touser
* 目标ID
* @param articles
* 图文列表
* @return 发送结果
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.msg.model.Article
* @see com.foxinmy.weixin4j.mp.msg.notify.ArticleNotify
* @see com.foxinmy.weixin4j.mp.api.NotifyApi
*/
public JsonResult sendNotify(String touser, List<Article> articles)
throws WeixinException {
return notifyApi.sendNotify(touser, articles);
}
/**
* 发送客服消息(不包含图文消息)
*
* @param touser
* 目标用户
* @param baseMsg
* 消息类型
* @return 发送结果
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.msg.model.Text
* @see com.foxinmy.weixin4j.mp.msg.model.Image
* @see com.foxinmy.weixin4j.mp.msg.model.Music
* @see com.foxinmy.weixin4j.mp.msg.model.Video
* @see com.foxinmy.weixin4j.mp.msg.model.Voice
* @see com.foxinmy.weixin4j.mp.api.NotifyApi
*/
public JsonResult sendNotify(String touser, BaseMsg baseMsg)
throws WeixinException {
return notifyApi.sendNotify(touser, baseMsg);
}
/**
* 客服聊天记录
*
* @param openId
* 用户标识 可为空
* @param starttime
* 查询开始时间
* @param endtime
* 查询结束时间 每次查询不能跨日查询
* @param pagesize
* 每页大小 每页最多拉取1000条
* @param pageindex
* 查询第几页 从1开始
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.model.CustomRecord
* @see com.foxinmy.weixin4j.mp.api.NotifyApi
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%AE%A2%E6%9C%8D%E8%81%8A%E5%A4%A9%E8%AE%B0%E5%BD%95">查询客服聊天记录</a>
*/
public List<CustomRecord> getCustomRecord(String openId, long starttime,
long endtime, int pagesize, int pageindex) throws WeixinException {
return notifyApi.getCustomRecord(openId, starttime, endtime, pagesize,
pageindex);
}
/**
* 上传图文消息,一个图文消息支持1到10条图文
*
* @param articles
* 图片消息
* @return 媒体ID
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3">高级群发</a>
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3#.E4.B8.8A.E4.BC.A0.E5.9B.BE.E6.96.87.E6.B6.88.E6.81.AF.E7.B4.A0.E6.9D.90">上传图文消息</a>
* @see com.foxinmy.weixin4j.mp.model.MpArticle
* @see com.foxinmy.weixin4j.mp.api.MassApi
*/
public String uploadArticle(List<MpArticle> articles)
throws WeixinException {
return massApi.uploadArticle(articles);
}
/**
* 上传分组群发的视频素材
*
* @param mediaId
* 媒体文件中上传得到的Id
* @param title
* 标题 可为空
* @param desc
* 描述 可为空
* @return 上传后的ID
* @throws WeixinException
*
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3">高级群发</a>
* @see com.foxinmy.weixin4j.mp.api.MassApi
* @see {@link com.foxinmy.weixin4j.mp.api.MediaApi#uploadMedia(File, MediaType)}
*/
public String uploadVideo(String mediaId, String title, String desc)
throws WeixinException {
return massApi.uploadVideo(mediaId, title, desc);
}
/**
* 分组群发
*
* @param mediaId
* 媒体ID 如果为text 则表示content
* @param mediaType
* 媒体类型
* @param groupId
* 分组ID
* @return
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.model.Group
* @see com.foxinmy.weixin4j.type.MediaType
* @see com.foxinmy.weixin4j.mp.api.MassApi
* @see {@link com.foxinmy.weixin4j.mp.api.MediaApi#uploadMedia(File, MediaType)}
* @see {@link com.foxinmy.weixin4j.mp.api.GroupApi#getGroupByOpenId(String)}
* @see {@link com.foxinmy.weixin4j.mp.api.GroupApi#getGroups()}
*/
public String massByGroup(String mediaId, MediaType mediaType,
String groupId) throws WeixinException {
return massApi.massByGroup(mediaId, mediaType, groupId);
}
/**
* 分组群发图文消息
*
* @param articles
* 图文消息列表
* @param groupId
* 分组ID
* @return 发送出去的消息ID
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.model.MpArticle
* @see com.foxinmy.weixin4j.mp.model.Group
* @see com.foxinmy.weixin4j.mp.api.MassApi
* @see {@link com.foxinmy.weixin4j.mp.api.MediaApi#uploadMedia(File, MediaType)}
* @see {@link com.foxinmy.weixin4j.mp.api.MassApi#uploadNews(List)}
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByGroup(String,MediaType,String)}
*/
public String massArticleByGroup(List<MpArticle> articles, String groupId)
throws WeixinException {
return massApi.massArticleByGroup(articles, groupId);
}
/**
* openId群发
*
* @param mediaId
* 媒体ID 如果为text 则表示content
* @param mediaType
* 媒体类型
* @param openIds
* openId列表
* @return
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.model.User
* @see com.foxinmy.weixin4j.type.MediaType
* @see com.foxinmy.weixin4j.mp.api.MassApi
* @see {@link com.foxinmy.weixin4j.mp.api.MediaApi#uploadMedia(File, MediaType)}
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByOpenIds(JSONObject,String...)}
*/
public String massByOpenIds(String mediaId, MediaType mediaType,
String... openIds) throws WeixinException {
return massApi.massByOpenIds(mediaId, mediaType, openIds);
}
/**
* openId图文群发
*
* @param articles
* 图文消息列表
* @param openIds
* 目标ID列表
* @return 发送出去的消息ID
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.model.MpArticle
* @see com.foxinmy.weixin4j.mp.model.User
* @see com.foxinmy.weixin4j.mp.api.MassApi
* @see {@link com.foxinmy.weixin4j.mp.api.MediaApi#uploadMedia(File, MediaType)}
* @see {@link com.foxinmy.weixin4j.mp.api.MediaApi#uploadNews(List)}
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByOpenIds(String,MediaType,String...)}
*/
public String massArticleByOpenIds(List<MpArticle> articles,
String... openIds) throws WeixinException {
return massApi.massArticleByOpenIds(articles, openIds);
}
/**
* 删除群发消息
* <p>
* 请注意,只有已经发送成功的消息才能删除删除消息只是将消息的图文详情页失效,已经收到的用户,还是能在其本地看到消息卡片
* </p>
*
* @param msgid
* 发送出去的消息ID
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3#.E5.88.A0.E9.99.A4.E7.BE.A4.E5.8F.91">删除群发</a>
* @see com.foxinmy.weixin4j.mp.api.MassApi
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByGroup(JSONObject, String)}
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByOpenIds(JSONObject, String...)
*/
public JsonResult deleteMassNews(String msgid) throws WeixinException {
return massApi.deleteMassNews(msgid);
}
/**
* 获取token
*
* @param code
* 用户授权后返回的code
* @return token对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%BD%91%E9%A1%B5%E6%8E%88%E6%9D%83%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF#.E7.AC.AC.E4.BA.8C.E6.AD.A5.EF.BC.9A.E9.80.9A.E8.BF.87code.E6.8D.A2.E5.8F.96.E7.BD.91.E9.A1.B5.E6.8E.88.E6.9D.83access_token">获取用户token</a>
* @see com.foxinmy.weixin4j.mp.model.UserToken
* @see com.foxinmy.weixin4j.mp.api.UserApi
*/
public UserToken getAccessToken(String code) throws WeixinException {
return userApi.getAccessToken(code);
}
/**
* 获取用户信息
*
* @param token
* 授权票据
* @return 用户对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%BD%91%E9%A1%B5%E6%8E%88%E6%9D%83%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF#.E7.AC.AC.E5.9B.9B.E6.AD.A5.EF.BC.9A.E6.8B.89.E5.8F.96.E7.94.A8.E6.88.B7.E4.BF.A1.E6.81.AF.28.E9.9C.80scope.E4.B8.BA_snsapi_userinfo.29">拉取用户信息</a>
* @see com.foxinmy.weixin4j.mp.model.User
* @see com.foxinmy.weixin4j.mp.model.UserToken
* @see com.foxinmy.weixin4j.mp.api.UserApi
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#getAccessToken(String)}
*/
public User getUser(UserToken token) throws WeixinException {
return userApi.getUser(token);
}
/**
* 获取用户信息
* <p>
* 在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的,对于不同公众号,
* 同一用户的openid不同),公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称、头像、性别、所在城市、语言和关注时间
* </p>
*
* @param openId
* 用户对应的ID
* @return 用户对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF">获取用户信息</a>
* @see com.foxinmy.weixin4j.mp.model.User
* @see com.foxinmy.weixin4j.mp.api.UserApi
*/
public User getUser(String openId) throws WeixinException {
return userApi.getUser(openId);
}
/**
* 获取用户一定数量(10000)的关注者列表
*
* @param nextOpenId
* 下一次拉取数据的openid
* @return 关注信息
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%85%B3%E6%B3%A8%E8%80%85%E5%88%97%E8%A1%A8">获取关注者列表</a>
* @see com.foxinmy.weixin4j.mp.model.Following
* @see com.foxinmy.weixin4j.mp.api.UserApi
*/
public Following getFollowing(String nextOpenId) throws WeixinException {
return userApi.getFollowing(nextOpenId);
}
/**
* 获取用户全部的关注者列表
* <p>
* 当公众号关注者数量超过10000时,可通过填写next_openid的值,从而多次拉取列表的方式来满足需求,
* 将上一次调用得到的返回中的next_openid值,作为下一次调用中的next_openid值
* </p>
*
* @return 用户对象集合
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%85%B3%E6%B3%A8%E8%80%85%E5%88%97%E8%A1%A8">获取关注者列表</a>
* @see com.foxinmy.weixin4j.mp.model.Following
* @see com.foxinmy.weixin4j.mp.api.UserApi
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#getFollowing(String)}
*/
public List<User> getAllFollowing() throws WeixinException {
return userApi.getAllFollowing();
}
/**
* 设置用户备注名
*
* @param openId
* 用户ID
* @param remark
* 备注名
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%AE%BE%E7%BD%AE%E7%94%A8%E6%88%B7%E5%A4%87%E6%B3%A8%E5%90%8D%E6%8E%A5%E5%8F%A3">设置用户备注名</a>
* @see com.foxinmy.weixin4j.mp.api.UserApi
*/
public JsonResult remarkUserName(String openId, String remark)
throws WeixinException {
return userApi.remarkUserName(openId, remark);
}
/**
* 创建分组
*
* @param name
* 组名称
* @return group对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E5.88.9B.E5.BB.BA.E5.88.86.E7.BB.84">创建分组</a>
* @see com.foxinmy.weixin4j.mp.model.Group
* @see com.foxinmy.weixin4j.mp.model.Group#toCreateJson()
* @see com.foxinmy.weixin4j.mp.api.GroupApi
*/
public Group createGroup(String name) throws WeixinException {
return groupApi.createGroup(name);
}
/**
* 查询所有分组
*
* @return 组集合
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E6.9F.A5.E8.AF.A2.E6.89.80.E6.9C.89.E5.88.86.E7.BB.84">查询所有分组</a>
* @see com.foxinmy.weixin4j.mp.model.Group
* @see com.foxinmy.weixin4j.mp.api.GroupApi
*/
public List<Group> getGroups() throws WeixinException {
return groupApi.getGroups();
}
/**
* 查询用户所在分组
*
* @param openId
* 用户对应的ID
* @return 组ID
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E6.9F.A5.E8.AF.A2.E7.94.A8.E6.88.B7.E6.89.80.E5.9C.A8.E5.88.86.E7.BB.84">查询用户所在分组</a>
* @see com.foxinmy.weixin4j.mp.model.Group
* @see com.foxinmy.weixin4j.mp.api.GroupApi
*/
public int getGroupByOpenId(String openId) throws WeixinException {
return groupApi.getGroupByOpenId(openId);
}
/**
* 修改分组名
*
* @param groupId
* 组ID
* @param name
* 组名称
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E4.BF.AE.E6.94.B9.E5.88.86.E7.BB.84.E5.90.8D">修改分组名</a>
* @see com.foxinmy.weixin4j.mp.model.Group
* @see com.foxinmy.weixin4j.mp.model.Group#toModifyJson()
* @see com.foxinmy.weixin4j.mp.api.GroupApi
*/
public JsonResult modifyGroup(int groupId, String name)
throws WeixinException {
return groupApi.modifyGroup(groupId, name);
}
/**
* 移动分组
*
* @param openId
* 用户对应的ID
* @param groupId
* 组ID
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E7.A7.BB.E5.8A.A8.E7.94.A8.E6.88.B7.E5.88.86.E7.BB.84">移动分组</a>
* @see com.foxinmy.weixin4j.mp.model.Group
* @see com.foxinmy.weixin4j.mp.api.GroupApi
*/
public JsonResult moveGroup(String openId, int groupId)
throws WeixinException {
return groupApi.moveGroup(openId, groupId);
}
/**
* 自定义菜单
*
* @param btnList
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E5%88%9B%E5%BB%BA%E6%8E%A5%E5%8F%A3">创建自定义菜单</a>
* @see com.foxinmy.weixin4j.mp.model.Button
* @see com.foxinmy.weixin4j.type.ButtonType
* @see com.foxinmy.weixin4j.mp.api.MenuApi
*/
public JsonResult createMenu(List<Button> btnList) throws WeixinException {
return menuApi.createMenu(btnList);
}
/**
* 查询菜单
*
* @return 菜单集合
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E6%9F%A5%E8%AF%A2%E6%8E%A5%E5%8F%A3">查询菜单</a>
* @see com.foxinmy.weixin4j.mp.model.Button
* @see com.foxinmy.weixin4j.mp.api.MenuApi
*/
public List<Button> getMenu() throws WeixinException {
return menuApi.getMenu();
}
/**
* 删除菜单
*
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E5%88%A0%E9%99%A4%E6%8E%A5%E5%8F%A3">删除菜单</a>
* @see com.foxinmy.weixin4j.mp.model.Button
* @see com.foxinmy.weixin4j.mp.api.MenuApi
*/
public JsonResult deleteMenu() throws WeixinException {
return menuApi.deleteMenu();
}
/**
* 生成带参数的二维码
*
* @param parameter
* @return byte数据包
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.QrApi
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy.QrApi#getQR(QRParameter)}
*/
public byte[] getQRData(QRParameter parameter) throws WeixinException {
return qrApi.getQRData(parameter);
}
/**
* 生成带参数的二维码
*
* @param sceneId
* 场景值
* @param expireSeconds
* 过期秒数 如果小于等于0则 视为永久二维码
* @return byte数据包
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.QrApi
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy.QrApi#getQR(QRParameter)}
*/
public byte[] getQRData(int sceneId, int expireSeconds)
throws WeixinException {
return qrApi.getQRData(sceneId, expireSeconds);
}
/**
* 生成带参数的二维码
* <p>
* 二维码分为临时跟永久两种,扫描时触发推送带参数事件
* </p>
*
* @param parameter
* 二维码参数
* @return 硬盘存储的文件对象
* @throws WeixinException
* @throws IOException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%94%9F%E6%88%90%E5%B8%A6%E5%8F%82%E6%95%B0%E7%9A%84%E4%BA%8C%E7%BB%B4%E7%A0%81">二维码</a>
* @see com.foxinmy.weixin4j.mp.model.QRParameter
* @see com.foxinmy.weixin4j.mp.api.QrApi
*/
public File getQR(QRParameter parameter) throws WeixinException,
IOException {
return qrApi.getQR(parameter);
}
/**
* 发送模板消息
*
* @param message
* @return 发送结果
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%A8%A1%E6%9D%BF%E6%B6%88%E6%81%AF%E6%8E%A5%E5%8F%A3">模板消息</a>
* @see com.foxinmy.weixin4j.mp.response.TemplateMessage
* @seee com.foxinmy.weixin4j.msg.event.TemplatesendjobfinishMessage
* @see com.foxinmy.weixin4j.mp.api.TmplApi
*/
public JsonResult sendTmplMessage(TemplateMessage tplMessage)
throws WeixinException {
return tmplApi.sendTmplMessage(tplMessage);
}
/**
* 长链接转短链接
*
* @param url
* @return 短链接
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%95%BF%E9%93%BE%E6%8E%A5%E8%BD%AC%E7%9F%AD%E9%93%BE%E6%8E%A5%E6%8E%A5%E5%8F%A3">长链接转短链接</a>
* @see com.foxinmy.weixin4j.mp.api.HelperApi
*/
public String getShorturl(String url) throws WeixinException {
return helperApi.getShorturl(url);
}
/**
* 发货通知
*
* @param weixinAccount
* 商户信息
* @param transid
* 交易单号
* @param orderNo
* 订单号
* @param status
* 成功|失败
* @param statusMsg
* status为失败时携带的信息
* @return 调用结果
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.PayApi
*/
public JsonResult deliverNotify(WeixinAccount weixinAccount,
String transid, String orderNo, boolean status, String statusMsg)
throws WeixinException {
return payApi.deliverNotify(weixinAccount, transid, orderNo, status,
statusMsg);
}
/**
* 订单查询
*
* @param weixinAccount
* 商户信息
* @param orderNo
* 订单号
* @return 订单信息
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.PayApi
*/
public Order orderQueryV2(WeixinAccount weixinAccount, String orderNo)
throws WeixinException {
return payApi.orderQueryV2(weixinAccount, new IdQuery(orderNo,
IdType.ORDERNO));
}
/**
* 维权处理
*
* @param openId
* 用户ID
* @param feedbackId
* 维权单号
* @return 调用结果
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @throws WeixinException
*/
public JsonResult updateFeedback(String openId, String feedbackId)
throws WeixinException {
return payApi.updateFeedback(openId, feedbackId);
}
/**
* V3订单查询
*
* @param weixinAccount
* 商户信息
* @param idQuery
* 商户系统内部的订单号, transaction_id、out_trade_no 二 选一,如果同时存在优先级:
* transaction_id> out_trade_no
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @throws WeixinException
*/
public com.foxinmy.weixin4j.mp.payment.v3.Order orderQueryV3(
WeixinAccount weixinAccount, IdQuery idQuery)
throws WeixinException {
return payApi.orderQueryV3(weixinAccount, idQuery);
}
/**
* 下载对账单<br>
* 1.微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账 单中,跟原支付单订单号一致,bill_type 为
* REVOKED;<br>
* 2.微信在次日 9 点启动生成前一天的对账单,建议商户 9 点半后再获取;<br>
* 3.对账单中涉及金额的字段单位为“元”。<br>
*
* @param weixinAccount
* 商户配置
* @param billDate
* 下载对账单的日期
* @param billType
* 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单
* REFUND,返回当日退款订单
* @return excel表格
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @throws WeixinException
* @throws IOException
*/
public File downloadbill(WeixinAccount weixinAccount, Date billDate,
BillType billType) throws WeixinException, IOException {
return payApi.downloadbill(weixinAccount, billDate, billType);
}
/**
* 退款查询<br/>
* 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
*
* @param weixinAccount
* @param idQuery
* 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
* 四个参数必填一个,优先级为:
* refund_id>out_refund_no>transaction_id>out_trade_no
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @return 退款记录
* @throws WeixinException
*/
public Refund refundQuery(WeixinAccount weixinAccount, IdQuery idQuery)
throws WeixinException {
return payApi.refundQuery(weixinAccount, idQuery);
}
/**
* 关闭订单<br/>
* 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
* 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
*
* @param weixinAccount
* 商户信息
* @param order
* 商户系统内部的订单号
* @return 执行结果
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @throws WeixinException
*/
public XmlResult closeOrder(WeixinAccount weixinAccount, String orderNo)
throws WeixinException {
return payApi.orderQueryV3(weixinAccount, new IdQuery(orderNo,
IdType.ORDERNO));
}
/**
* native支付URL转短链接
*
* @param weixinAccount
* 商户信息
* @param url
* 具有native标识的支付URL
* @return 转换后的短链接
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @throws WeixinException
*/
public String getShorturl(WeixinAccount weixinAccount, String url)
throws WeixinException {
return payApi.getShorturl(weixinAccount, url);
}
@@ -0,0 +1,60 @@
package com.foxinmy.weixin4j.mp.api;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.foxinmy.weixin4j.http.HttpRequest;
import com.foxinmy.weixin4j.xml.Map2ObjectConverter;
import com.foxinmy.weixin4j.xml.XStream;
import com.thoughtworks.xstream.core.ClassLoaderReference;
import com.thoughtworks.xstream.mapper.DefaultMapper;
/**
*
* @className BaseApi
* @author jy.hu
* @date 2014年9月26日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php">api文档</a>
*/
public class BaseApi {
protected final HttpRequest request = new HttpRequest();
protected final static XStream mapXstream = XStream.get();
protected final Charset utf8 = StandardCharsets.UTF_8;
private final static ResourceBundle weixinBundle;
static {
weixinBundle = ResourceBundle
.getBundle("com/foxinmy/weixin4j/mp/api/weixin");
mapXstream.alias("xml", Map.class);
mapXstream.registerConverter(new Map2ObjectConverter(new DefaultMapper(
new ClassLoaderReference(XStream.class.getClassLoader()))));
}
protected String map2xml(Map<?, ?> map) {
return mapXstream.toXML(map).replaceAll("__", "_");
}
@SuppressWarnings("unchecked")
protected Map<String, String> xml2map(String xml) {
return mapXstream.fromXML(xml,Map.class);
}
protected String getRequestUri(String key) {
String url = weixinBundle.getString(key);
Pattern p = Pattern.compile("(\\{[^\\}]*\\})");
Matcher m = p.matcher(url);
StringBuffer sb = new StringBuffer();
String sub = null;
while (m.find()) {
sub = m.group();
m.appendReplacement(sb,
getRequestUri(sub.substring(1, sub.length() - 1)));
}
m.appendTail(sb);
return sb.toString();
}
}
@@ -0,0 +1,141 @@
package com.foxinmy.weixin4j.mp.api;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.mp.model.Group;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 分组相关API
*
* @className GroupApi
* @author jy.hu
* @date 2014年9月25日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%
* BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3">分组接口</a>
* @see com.foxinmy.weixin4j.mp.model.Group
*/
public class GroupApi extends BaseApi {
private final TokenHolder tokenHolder;
public GroupApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 创建分组
*
* @param name
* 组名称
* @return group对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E5.88.9B.E5.BB.BA.E5.88.86.E7.BB.84">创建分组</a>
* @see com.foxinmy.weixin4j.mp.model.Group
* @see com.foxinmy.weixin4j.mp.model.Group#toCreateJson()
*/
public Group createGroup(String name) throws WeixinException {
String group_create_uri = getRequestUri("group_create_uri");
Token token = tokenHolder.getToken();
Group group = new Group(name);
Response response = request.post(
String.format(group_create_uri, token.getAccessToken()),
group.toCreateJson());
return response.getAsJson().getObject("group", Group.class);
}
/**
* 查询所有分组
*
* @return 组集合
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E6.9F.A5.E8.AF.A2.E6.89.80.E6.9C.89.E5.88.86.E7.BB.84">查询所有分组</a>
* @see com.foxinmy.weixin4j.mp.model.Group
*/
public List<Group> getGroups() throws WeixinException {
String group_get_uri = getRequestUri("group_get_uri");
Token token = tokenHolder.getToken();
Response response = request.get(String.format(group_get_uri,
token.getAccessToken()));
return JSON.parseArray(response.getAsJson().getString("groups"),
Group.class);
}
/**
* 查询用户所在分组
*
* @param openId
* 用户对应的ID
* @return 组ID
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E6.9F.A5.E8.AF.A2.E7.94.A8.E6.88.B7.E6.89.80.E5.9C.A8.E5.88.86.E7.BB.84">查询用户所在分组</a>
* @see com.foxinmy.weixin4j.mp.model.Group
*/
public int getGroupByOpenId(String openId) throws WeixinException {
String group_getid_uri = getRequestUri("group_getid_uri");
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(group_getid_uri, token.getAccessToken()),
String.format("{\"openid\":\"%s\"}", openId));
return response.getAsJson().getIntValue("groupid");
}
/**
* 修改分组名
*
* @param groupId
* 组ID
* @param name
* 组名称
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E4.BF.AE.E6.94.B9.E5.88.86.E7.BB.84.E5.90.8D">修改分组名</a>
* @see com.foxinmy.weixin4j.mp.model.Group
* @see com.foxinmy.weixin4j.mp.model.Group#toModifyJson()
*/
public JsonResult modifyGroup(int groupId, String name)
throws WeixinException {
String group_modify_uri = getRequestUri("group_modify_uri");
Token token = tokenHolder.getToken();
Group group = new Group(groupId, name);
Response response = request.post(
String.format(group_modify_uri, token.getAccessToken()),
group.toModifyJson());
return response.getAsJsonResult();
}
/**
* 移动分组
*
* @param openId
* 用户对应的ID
* @param groupId
* 组ID
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E7.A7.BB.E5.8A.A8.E7.94.A8.E6.88.B7.E5.88.86.E7.BB.84">移动分组</a>
* @see com.foxinmy.weixin4j.mp.model.Group
*/
public JsonResult moveGroup(String openId, int groupId)
throws WeixinException {
String group_move_uri = getRequestUri("group_move_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(group_move_uri,
token.getAccessToken()), String.format(
"{\"openid\":\"%s\",\"to_groupid\":%d}", openId, groupId));
return response.getAsJsonResult();
}
}
@@ -0,0 +1,47 @@
package com.foxinmy.weixin4j.mp.api;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 辅助相关API
*
* @className HelperApi
* @author jy.hu
* @date 2014年9月26日
* @since JDK 1.7
* @see
*/
public class HelperApi extends BaseApi {
private final TokenHolder tokenHolder;
public HelperApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 长链接转短链接
*
* @param url
* @return 短链接
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%95%BF%E9%93%BE%E6%8E%A5%E8%BD%AC%E7%9F%AD%E9%93%BE%E6%8E%A5%E6%8E%A5%E5%8F%A3">长链接转短链接</a>
*/
public String getShorturl(String url) throws WeixinException {
String shorturl_uri = getRequestUri("shorturl_uri");
Token token = tokenHolder.getToken();
JSONObject obj = new JSONObject();
obj.put("action", "long2short");
obj.put("long_url", url);
Response response = request.post(
String.format(shorturl_uri, token.getAccessToken()),
obj.toJSONString());
return response.getAsJson().getString("short_url");
}
}
@@ -0,0 +1,269 @@
package com.foxinmy.weixin4j.mp.api;
import java.io.File;
import java.util.List;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.mp.model.MpArticle;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.type.MediaType;
/**
* 群发相关API
*
* @className MassApi
* @author jy.hu
* @date 2014年9月25日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3">群发接口</a>
* @see com.foxinmy.weixin4j.mp.model.MpArticle
*/
public class MassApi extends BaseApi {
private final TokenHolder tokenHolder;
public MassApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 上传图文消息,一个图文消息支持1到10条图文
*
* @param articles
* 图片消息
* @return 媒体ID
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3">高级群发</a>
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3#.E4.B8.8A.E4.BC.A0.E5.9B.BE.E6.96.87.E6.B6.88.E6.81.AF.E7.B4.A0.E6.9D.90">上传图文消息</a>
* @see com.foxinmy.weixin4j.mp.model.MpArticle
*/
public String uploadArticle(List<MpArticle> articles)
throws WeixinException {
String article_upload_uri = getRequestUri("article_upload_uri");
Token token = tokenHolder.getToken();
JSONObject obj = new JSONObject();
obj.put("articles", articles);
Response response = request.post(
String.format(article_upload_uri, token.getAccessToken()),
obj.toJSONString());
return response.getAsJson().getString("media_id");
}
/**
* 上传分组群发的视频素材
*
* @param mediaId
* 媒体文件中上传得到的Id
* @param title
* 标题 可为空
* @param desc
* 描述 可为空
* @return 上传后的ID
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3">高级群发</a>
* @see {@link com.foxinmy.weixin4j.mp.api.MediaApi#uploadMedia(File, MediaType)}
*/
public String uploadVideo(String mediaId, String title, String desc)
throws WeixinException {
String video_upload_uri = getRequestUri("video_upload_uri");
Token token = tokenHolder.getToken();
JSONObject obj = new JSONObject();
obj.put("media_id", mediaId);
obj.put("title", title);
obj.put("description", desc);
Response response = request.post(
String.format(video_upload_uri, token.getAccessToken()),
obj.toJSONString());
return response.getAsJson().getString("media_id");
}
/**
* 分组群发
* <p>
* 在返回成功时,意味着群发任务提交成功,并不意味着此时群发已经结束,所以,仍有可能在后续的发送过程中出现异常情况导致用户未收到消息,
* 如消息有时会进行审核、服务器不稳定等,此外,群发任务一般需要较长的时间才能全部发送完毕
* </p>
*
* @param jsonPara
* json格式的参数
* @param groupId
* 分组ID
* @return 发送出去的消息ID
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3#.E6.A0.B9.E6.8D.AE.E5.88.86.E7.BB.84.E8.BF.9B.E8.A1.8C.E7.BE.A4.E5.8F.91">分组群发</a>
* @see com.foxinmy.weixin4j.mp.model.Group
* @see {@link com.foxinmy.weixin4j.mp.api.GroupApi#getGroupByOpenId(String)}
* @see {@link com.foxinmy.weixin4j.mp.api.GroupApi#getGroups()}
*/
private String massByGroup(JSONObject jsonPara, String groupId)
throws WeixinException {
String mass_group_uri = getRequestUri("mass_group_uri");
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(mass_group_uri, token.getAccessToken()),
jsonPara.toJSONString());
return response.getAsJson().getString("msg_id");
}
/**
* openId群发
*
* @param jsonPara
* json格式的参数
* @param openIds
* 目标ID列表
* @return 发送出去的消息ID
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3#.E6.A0.B9.E6.8D.AEOpenID.E5.88.97.E8.A1.A8.E7.BE.A4.E5.8F.91">openId群发</a>
* @see com.foxinmy.weixin4j.mp.model.User
*/
private String massByOpenIds(JSONObject jsonPara, String... openIds)
throws WeixinException {
String mass_openid_uri = getRequestUri("mass_openid_uri");
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(mass_openid_uri, token.getAccessToken()),
jsonPara.toJSONString());
return response.getAsJson().getString("msg_id");
}
/**
* 分组群发
*
* @param mediaId
* 媒体ID 如果为text 则表示content
* @param mediaType
* 媒体类型
* @param groupId
* 分组ID
* @return
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.model.Group
* @see com.foxinmy.weixin4j.type.MediaType
* @see {@link com.foxinmy.weixin4j.mp.api.MediaApi#uploadMedia(File, MediaType)}
* @see {@link com.foxinmy.weixin4j.mp.api.GroupApi#getGroupByOpenId(String)}
* @see {@link com.foxinmy.weixin4j.mp.api.GroupApi#getGroups()}
* @see {@link com.foxinmy.weixin4j.mp.api.MassApi#massByGroup(JSONObject,String)}
*/
public String massByGroup(String mediaId, MediaType mediaType,
String groupId) throws WeixinException {
JSONObject jsonPara = new JSONObject();
jsonPara.put("filter", new JSONObject().put("group_id", groupId));
jsonPara.put(mediaType.name(), new JSONObject().put(
mediaType == MediaType.text ? "content" : "media_id", mediaId));
jsonPara.put("msgtype", mediaType.name());
return massByGroup(jsonPara, groupId);
}
/**
* 分组群发图文消息
*
* @param articles
* 图文消息列表
* @param groupId
* 分组ID
* @return 发送出去的消息ID
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.model.MpArticle
* @see com.foxinmy.weixin4j.mp.model.Group
* @see {@link com.foxinmy.weixin4j.mp.api.MediaApi#uploadMedia(File, MediaType)}
* @see {@link com.foxinmy.weixin4j.mp.api.MassApi#uploadNews(List)}
* @see {@link com.foxinmy.weixin4j.mp.api.MassApi#massByGroup(String,MediaType,String)}
*/
public String massArticleByGroup(List<MpArticle> articles, String groupId)
throws WeixinException {
String mediaId = uploadArticle(articles);
return massByGroup(mediaId, MediaType.mpnews, groupId);
}
/**
* openId群发
*
* @param mediaId
* 媒体ID 如果为text 则表示content
* @param mediaType
* 媒体类型
* @param openIds
* openId列表
* @return
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.model.User
* @see com.foxinmy.weixin4j.type.MediaType
* @see {@link com.foxinmy.weixin4j.mp.api.MediaApi#uploadMedia(File, MediaType)}
* @see {@link com.foxinmy.weixin4j.mp.api.MassApi#massByOpenIds(JSONObject,String...)}
*/
public String massByOpenIds(String mediaId, MediaType mediaType,
String... openIds) throws WeixinException {
JSONObject jsonPara = new JSONObject();
jsonPara.put("touser", openIds);
jsonPara.put(mediaType.name(), new JSONObject().put(
mediaType == MediaType.text ? "content" : "media_id", mediaId));
jsonPara.put("msgtype", mediaType.name());
return massByOpenIds(jsonPara, openIds);
}
/**
* openId图文群发
*
* @param articles
* 图文消息列表
* @param openIds
* 目标ID列表
* @return 发送出去的消息ID
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.model.MpArticle
* @see com.foxinmy.weixin4j.mp.model.User
* @see {@link com.foxinmy.weixin4j.mp.api.MediaApi#uploadMedia(File, MediaType)}
* @see {@link com.foxinmy.weixin4j.mp.api.MediaApi#uploadNews(List)}
* @see {@link com.foxinmy.weixin4j.mp.api.MassApi#massByOpenIds(String,MediaType,String...)}
*/
public String massArticleByOpenIds(List<MpArticle> articles,
String... openIds) throws WeixinException {
String mediaId = uploadArticle(articles);
return massByOpenIds(mediaId, MediaType.mpnews, openIds);
}
/**
* 删除群发消息
* <p>
* 请注意,只有已经发送成功的消息才能删除删除消息只是将消息的图文详情页失效,已经收到的用户,还是能在其本地看到消息卡片
* </p>
*
* @param msgid
* 发送出去的消息ID
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3#.E5.88.A0.E9.99.A4.E7.BE.A4.E5.8F.91">删除群发</a>
* @see {@link com.foxinmy.weixin4j.mp.api.MassApi#massByGroup(JSONObject, String)}
* @see {@link com.foxinmy.weixin4j.mp.api.MassApi#massByOpenIds(JSONObject, String...)
*/
public JsonResult deleteMassNews(String msgid) throws WeixinException {
JSONObject obj = new JSONObject();
obj.put("msgid", msgid);
String mass_delete_uri = getRequestUri("mass_delete_uri");
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(mass_delete_uri, token.getAccessToken()),
obj.toJSONString());
return response.getAsJsonResult();
}
@@ -0,0 +1,135 @@
package com.foxinmy.weixin4j.mp.api;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.http.entity.mime.content.ByteArrayBody;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.PartParameter;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.type.MediaType;
import com.foxinmy.weixin4j.util.IOUtil;
/**
* 媒体相关API
*
* @className MediaApi
* @author jy.hu
* @date 2014年9月25日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E4%B8%8B%E8%BD%BD%E5%A4%9A%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">上传多媒体文件</a>
* @see com.foxinmy.weixin4j.type.MediaType
*/
public class MediaApi extends BaseApi {
private final TokenHolder tokenHolder;
public MediaApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 上传媒体文件
* <p>
* 正常情况下返回{"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789},
* 否则抛出异常.
* </p>
*
* @param file
* 文件对象
* @param mediaType
* 媒体类型
* @return 上传到微信服务器返回的媒体标识
* @throws WeixinException
* @throws IOException
* @throws
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E4%B8%8B%E8%BD%BD%E5%A4%9A%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">上传下载说明</a>
* @see com.foxinmy.weixin4j.type.MediaType
*/
public String uploadMedia(File file, MediaType mediaType)
throws WeixinException, IOException {
byte[] datas = IOUtil.toByteArray(new FileInputStream(file));
return uploadMedia(file.getName(), datas, mediaType);
}
/**
* 上传媒体文件
*
* @param bytes
* 媒体数据包
* @param mediaType
* 媒体类型
* @return 上传到微信服务器返回的媒体标识
* @throws WeixinException
* @see {@link com.foxinmy.weixin4j.mp.api.MediaApi#uploadMedia(File, MediaType)}
*/
public String uploadMedia(String fileName, byte[] bytes, MediaType mediaType)
throws WeixinException {
Token token = tokenHolder.getToken();
String file_upload_uri = getRequestUri("file_upload_uri");
Response response = request.post(String.format(file_upload_uri,
token.getAccessToken(), mediaType.name()), new PartParameter(
"media", new ByteArrayBody(bytes, fileName)));
return response.getAsJson().getString("media_id");
}
/**
* 下载媒体文件
* <p>
* 正常情况下返回表头如Content-Type: image/jpeg,否则抛出异常.
* </p>
*
* @param mediaId
* 存储在微信服务器上的媒体标识
* @param mediaType
* 媒体类型
* @return 写入硬盘后的文件对象
* @throws WeixinException
* @throws IOException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E4%B8%8B%E8%BD%BD%E5%A4%9A%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">上传下载说明</a>
* @see com.foxinmy.weixin4j.type.MediaType
*/
public File downloadMedia(String mediaId, MediaType mediaType)
throws WeixinException, IOException {
String media_path = getRequestUri("media_path");
byte[] datas = downloadMediaData(mediaId, mediaType);
String filename = mediaId + "." + mediaType.getFormatType();
File file = new File(media_path + File.separator + filename);
if (file.exists()) {
return file;
}
file.createNewFile();
FileOutputStream out = new FileOutputStream(file);
out.write(datas);
out.close();
return file;
}
/**
* 下载媒体文件
*
* @param mediaId
* @param mediaType
* @return 二进制数据包
* @throws WeixinException
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#downloadMedia(String, MediaType)}
*/
public byte[] downloadMediaData(String mediaId, MediaType mediaType)
throws WeixinException {
Token token = tokenHolder.getToken();
String file_download_uri = getRequestUri("file_download_uri");
Response response = request.get(String.format(file_download_uri,
token.getAccessToken(), mediaId));
return response.getBody();
}
}
@@ -0,0 +1,88 @@
package com.foxinmy.weixin4j.mp.api;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.mp.model.Button;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 菜单相关API
*
* @className MenuApi
* @author jy.hu
* @date 2014年9月25日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.mp.model.Button
*/
public class MenuApi extends BaseApi {
private final TokenHolder tokenHolder;
public MenuApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 自定义菜单
*
* @param btnList
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E5%88%9B%E5%BB%BA%E6%8E%A5%E5%8F%A3">创建自定义菜单</a>
* @see com.foxinmy.weixin4j.mp.model.Button
*/
public JsonResult createMenu(List<Button> btnList) throws WeixinException {
String menu_create_uri = getRequestUri("menu_create_uri");
Token token = tokenHolder.getToken();
JSONObject obj = new JSONObject();
obj.put("button", btnList);
Response response = request.post(
String.format(menu_create_uri, token.getAccessToken()),
obj.toJSONString());
return response.getAsJsonResult();
}
/**
* 查询菜单
*
* @return 菜单集合
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E6%9F%A5%E8%AF%A2%E6%8E%A5%E5%8F%A3">查询菜单</a>
* @see com.foxinmy.weixin4j.mp.model.Button
*/
public List<Button> getMenu() throws WeixinException {
String menu_get_uri = getRequestUri("menu_get_uri");
Token token = tokenHolder.getToken();
Response response = request.get(String.format(menu_get_uri,
token.getAccessToken()));
String text = response.getAsJson().getJSONObject("menu")
.getString("button");
return JSON.parseArray(text, Button.class);
}
/**
* 删除菜单
*
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E5%88%A0%E9%99%A4%E6%8E%A5%E5%8F%A3">删除菜单</a>
* @see com.foxinmy.weixin4j.mp.model.Button
*/
public JsonResult deleteMenu() throws WeixinException {
String menu_delete_uri = getRequestUri("menu_delete_uri");
Token token = tokenHolder.getToken();
Response response = request.get(String.format(menu_delete_uri,
token.getAccessToken()));
return response.getAsJsonResult();
}
}
@@ -0,0 +1,165 @@
package com.foxinmy.weixin4j.mp.api;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.mp.model.CustomRecord;
import com.foxinmy.weixin4j.mp.msg.model.Article;
import com.foxinmy.weixin4j.mp.msg.model.BaseMsg;
import com.foxinmy.weixin4j.mp.msg.notify.ArticleNotify;
import com.foxinmy.weixin4j.mp.msg.notify.BaseNotify;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 客服相关API
*
* @className NotifyApi
* @author jy.hu
* @date 2014年9月26日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF">客服消息</a>
* @see com.foxinmy.weixin4j.mp.msg.notify.TextNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.ImageNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.MusicNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.VideoNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.VoiceNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.ArticleNotify
*/
public class NotifyApi extends BaseApi {
private final TokenHolder tokenHolder;
public NotifyApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 发送客服消息
*
* @param jsonPara
* @return
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF#.E5.8F.91.E9.80.81.E9.9F.B3.E4.B9.90.E6.B6.88.E6.81.AF">发送客服消息</a>
*/
private JsonResult sendNotify(String jsonPara) throws WeixinException {
String custom_notify_uri = getRequestUri("custom_notify_uri");
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(custom_notify_uri, token.getAccessToken()),
jsonPara);
return response.getAsJsonResult();
}
/**
* 发送客服消息(在48小时内不限制发送次数)
*
* @param notify
* 客服消息对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF">发送客服消息</a>
* @see com.foxinmy.weixin4j.mp.msg.notify.TextNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.ImageNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.MusicNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.VideoNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.VoiceNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.ArticleNotify
* @see {@link com.foxinmy.weixin4j.mp.api.NotifyApi#sendNotify(String)}
*/
public JsonResult sendNotify(BaseNotify notify) throws WeixinException {
return sendNotify(notify.toJson());
}
/**
* 发送图文消息
*
* @param touser
* 目标ID
* @param articles
* 图文列表
* @return 发送结果
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.msg.model.Article
* @see com.foxinmy.weixin4j.mp.msg.notify.ArticleNotify
*/
public JsonResult sendNotify(String touser, List<Article> articles)
throws WeixinException {
ArticleNotify notify = new ArticleNotify(touser);
notify.pushAll(articles);
return sendNotify(notify);
}
/**
* 发送客服消息(不包含图文消息)
*
* @param touser
* 目标用户
* @param baseMsg
* 消息类型
* @return 发送结果
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.msg.model.Text
* @see com.foxinmy.weixin4j.mp.msg.model.Image
* @see com.foxinmy.weixin4j.mp.msg.model.Music
* @see com.foxinmy.weixin4j.mp.msg.model.Video
* @see com.foxinmy.weixin4j.mp.msg.model.Voice
* @see {@link com.foxinmy.weixin4j.mp.msg.model.BaseMsg#toNotifyJson()}
* @see {@link com.foxinmy.weixin4j.mp.api.NotifyApi#sendNotify(String)}
*/
public JsonResult sendNotify(String touser, BaseMsg baseMsg)
throws WeixinException {
StringBuilder jsonPara = new StringBuilder();
String mediaType = baseMsg.getMediaType().name();
jsonPara.append("{");
jsonPara.append("\"touser\":\"").append(touser).append("\",");
jsonPara.append("\"msgtype\":\"").append(mediaType).append("\",");
jsonPara.append("\"").append(mediaType).append("\":");
jsonPara.append(baseMsg.toNotifyJson()).append("}");
return sendNotify(jsonPara.toString());
}
/**
* 客服聊天记录
*
* @param openId
* 用户标识 可为空
* @param starttime
* 查询开始时间
* @param endtime
* 查询结束时间 每次查询不能跨日查询
* @param pagesize
* 每页大小 每页最多拉取1000条
* @param pageindex
* 查询第几页 从1开始
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.model.CustomRecord
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%AE%A2%E6%9C%8D%E8%81%8A%E5%A4%A9%E8%AE%B0%E5%BD%95">查询客服聊天记录</a>
*/
public List<CustomRecord> getCustomRecord(String openId, long starttime,
long endtime, int pagesize, int pageindex) throws WeixinException {
JSONObject obj = new JSONObject();
obj.put("openId", openId == null ? "" : openId);
obj.put("starttime", starttime);
obj.put("endtime", endtime);
obj.put("pagesize", pagesize > 1000 ? 1000 : pagesize);
obj.put("pageindex", pageindex);
String custom_record_uri = getRequestUri("custom_record_uri");
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(custom_record_uri, token.getAccessToken()),
obj.toJSONString());
String text = response.getAsJson().getString("recordlist");
return JSON.parseArray(text, CustomRecord.class);
}
}
@@ -0,0 +1,368 @@
package com.foxinmy.weixin4j.mp.api;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.http.XmlResult;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.mp.payment.BillType;
import com.foxinmy.weixin4j.mp.payment.IdQuery;
import com.foxinmy.weixin4j.mp.payment.IdType;
import com.foxinmy.weixin4j.mp.payment.PayUtil;
import com.foxinmy.weixin4j.mp.payment.RefundConverter;
import com.foxinmy.weixin4j.mp.payment.v2.Order;
import com.foxinmy.weixin4j.mp.payment.v3.Refund;
import com.foxinmy.weixin4j.mp.util.ExcelUtil;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.util.ConfigUtil;
import com.foxinmy.weixin4j.util.DateUtil;
import com.foxinmy.weixin4j.util.MapUtil;
import com.foxinmy.weixin4j.util.RandomUtil;
/**
* 支付API
*
* @className PayApi
* @author jy
* @date 2014年10月28日
* @since JDK 1.7
* @see
*/
public class PayApi extends BaseApi {
private final TokenHolder tokenHolder;
public PayApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 发货通知
*
* @param weixinAccount
* @param transid
* 交易单号
* @param orderNo
* 订单号
* @param status
* 成功|失败
* @param statusMsg
* status为失败时携带的信息
* @return
* @throws WeixinException
*/
public JsonResult deliverNotify(WeixinAccount weixinAccount,
String transid, String orderNo, boolean status, String statusMsg)
throws WeixinException {
String delivernotify_uri = getRequestUri("delivernotify_uri");
Token token = tokenHolder.getToken();
Map<String, String> param = new HashMap<String, String>();
param.put("appid", weixinAccount.getAppId());
param.put("appkey", weixinAccount.getPaySignKey());
// 用户购买的openId
param.put("openid", weixinAccount.getOpenId());
param.put("transid", transid);
param.put("out_trade_no", orderNo);
param.put("deliver_timestamp", System.currentTimeMillis() / 1000 + "");
param.put("deliver_status", status ? "1" : "0");
param.put("deliver_msg", statusMsg);
param.put("app_signature", DigestUtils.sha1Hex(MapUtil.toJoinString(
param, false, true, null)));
param.put("sign_method", "sha1");
Response response = request.post(
String.format(delivernotify_uri, token.getAccessToken()),
JSON.toJSONString(param));
return response.getAsJsonResult();
}
/**
* 订单查询
*
* @param weixinAccount
* 商户信息
* @param orderNo
* 订单号
* @return
* @throws WeixinException
*/
public Order orderQueryV2(WeixinAccount weixinAccount, IdQuery idQuery)
throws WeixinException {
String orderquery_uri = getRequestUri("orderquery_uri");
Token token = tokenHolder.getToken();
StringBuilder sb = new StringBuilder();
sb.append(idQuery.getType().getName()).append(idQuery.getId());
sb.append("&partner=").append(weixinAccount.getPartnerId());
String part = sb.toString();
sb.append("&key=").append(weixinAccount.getPartnerKey());
String sign = DigestUtils.md5Hex(sb.toString()).toUpperCase();
sb.delete(0, sb.length());
sb.append(part).append("&sign=").append(sign);
String timestamp = System.currentTimeMillis() / 1000 + "";
JSONObject obj = new JSONObject();
obj.put("appid", weixinAccount.getAppId());
obj.put("appkey", weixinAccount.getPaySignKey());
obj.put("package", sb.toString());
obj.put("timestamp", timestamp);
String signature = DigestUtils.sha1Hex(MapUtil.toJoinString(obj, false,
true, null));
obj = new JSONObject();
obj.put("appid", weixinAccount.getAppId());
obj.put("package", sb.toString());
obj.put("timestamp", timestamp);
obj.put("app_signature", signature);
obj.put("sign_method", "sha1");
Response response = request.post(
String.format(orderquery_uri, token.getAccessToken()),
obj.toJSONString());
String order_info = response.getAsJson().getString("order_info");
Order order = JSON.parseObject(order_info, Order.class,
Feature.IgnoreNotMatch);
order.setMapData(JSON.parseObject(order_info,
new TypeReference<Map<String, String>>() {
}));
return order;
}
/**
* 维权处理
*
* @param openId
* 用户ID
* @param feedbackId
* 维权单号
* @return
* @throws WeixinException
*/
public JsonResult updateFeedback(String openId, String feedbackId)
throws WeixinException {
String payfeedback_update_uri = ConfigUtil
.getValue("payfeedback_update_uri");
Token token = tokenHolder.getToken();
Response response = request.get(String.format(payfeedback_update_uri,
token.getAccessToken(), openId, feedbackId));
return response.getAsJsonResult();
}
/**
* V3订单查询
*
* @param weixinAccount
* 商户信息
* @param idQuery
* 商户系统内部的订单号, transaction_id、out_trade_no 二 选一,如果同时存在优先级:
* transaction_id> out_trade_no
* @throws WeixinException
*/
public com.foxinmy.weixin4j.mp.payment.v3.Order orderQueryV3(
WeixinAccount weixinAccount, IdQuery idQuery)
throws WeixinException {
Map<String, String> map = new HashMap<String, String>();
map.put("appid", weixinAccount.getAppId());
map.put("mch_id", weixinAccount.getMchId());
map.put("nonce_str", RandomUtil.generateString(16));
map.put(idQuery.getType().getName(), idQuery.getId());
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
map.put("sign", sign);
String param = map2xml(map);
String orderquery_uri = getRequestUri("orderquery_v3_uri");
Response response = request.post(orderquery_uri, param);
return response
.getAsObject(new TypeReference<com.foxinmy.weixin4j.mp.payment.v3.Order>() {
});
}
/**
* native支付URL转短链接
*
* @param weixinAccount
* 商户信息
* @param url
* 具有native标识的支付URL
* @return 转换后的短链接
* @throws WeixinException
*/
public String getShorturl(WeixinAccount weixinAccount, String url)
throws WeixinException {
Map<String, String> map = new HashMap<String, String>();
map.put("appid", weixinAccount.getAppId());
map.put("mch_id", weixinAccount.getMchId());
map.put("long_url", url);
map.put("nonce_str", RandomUtil.generateString(16));
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
map.put("sign", sign);
try {
map.put("long_url", URLEncoder.encode(url, utf8.name()));
} catch (UnsupportedEncodingException e) {
;
}
String param = map2xml(map);
String shorturl_uri = getRequestUri("p_shorturl_uri");
Response response = request.post(shorturl_uri, param);
map = xml2map(response.getAsString());
return map.get("short_url");
}
/**
* 关闭订单<br/>
* 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
* 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
*
* @param weixinAccount
* 商户信息
* @param idQuery
* 商户系统内部的订单号
* @return
* @throws WeixinException
*/
public XmlResult closeOrder(WeixinAccount weixinAccount, IdQuery idQuery)
throws WeixinException {
Map<String, String> map = new HashMap<String, String>();
map.put("appid", weixinAccount.getAppId());
map.put("mch_id", weixinAccount.getMchId());
map.put("nonce_str", RandomUtil.generateString(16));
map.put(idQuery.getType().getName(), idQuery.getId());
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
map.put("sign", sign);
String param = map2xml(map);
String closeorder_uri = getRequestUri("closeorder_uri");
Response response = request.post(closeorder_uri, param);
return response.getAsXmlResult();
}
/**
* 下载对账单<br>
* 1.微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账 单中,跟原支付单订单号一致,bill_type 为
* REVOKED;<br>
* 2.微信在次日 9 点启动生成前一天的对账单,建议商户 9 点半后再获取;<br>
* 3.对账单中涉及金额的字段单位为“元”。<br>
*
* @param weixinAccount
* 商户配置
* @param billDate
* 下载对账单的日期
* @param billType
* 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单
* REFUND,返回当日退款订单
* @return excel表格
* @throws WeixinException
* @throws IOException
*/
public File downloadbill(WeixinAccount weixinAccount, Date billDate,
BillType billType) throws WeixinException, IOException {
if (billDate == null) {
Calendar now = Calendar.getInstance();
now.add(Calendar.DAY_OF_MONTH, -10);
billDate = now.getTime();
}
if (billType == null) {
billType = BillType.ALL;
}
String _billDate = DateUtil.fortmatYYYYMMDD(billDate);
String bill_path = ConfigUtil.getValue("bill_path");
String fileName = String.format("%s_%s_%s.xls", _billDate, billType
.name().toLowerCase(), weixinAccount.getAppId());
File file = new File(String.format("%s/%s", bill_path, fileName));
if (file.exists()) {
return file;
}
Map<String, String> map = new HashMap<String, String>();
map.put("appid", weixinAccount.getAppId());
map.put("mch_id", weixinAccount.getMchId());
map.put("nonce_str", RandomUtil.generateString(16));
map.put("device_info", weixinAccount.getDeviceInfo());
map.put("bill_date", _billDate);
map.put("bill_type", billType.name());
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
map.put("sign", sign);
String param = map2xml(map);
String downloadbill_uri = getRequestUri("downloadbill_uri");
Response response = request.post(downloadbill_uri, param);
BufferedReader reader = new BufferedReader(new StringReader(
response.getAsString()));
String line = null;
List<String[]> bills = new LinkedList<String[]>();
while ((line = reader.readLine()) != null) {
bills.add(line.replaceAll("`", "").split(","));
}
reader.close();
List<String> headers = Arrays.asList(bills.remove(0));
List<String> totalDatas = Arrays.asList(bills.remove(bills.size() - 1));
List<String> totalHeaders = Arrays
.asList(bills.remove(bills.size() - 1));
HSSFWorkbook wb = new HSSFWorkbook();
wb.createSheet(_billDate + "对账单");
ExcelUtil.list2excel(wb, headers, bills);
ExcelUtil.list2excel(wb, totalHeaders, totalDatas);
wb.write(new FileOutputStream(file));
return file;
}
/**
* 退款查询<br/>
* 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
*
* @param weixinAccount
* @param idQuery
* 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
* 四个参数必填一个,优先级为:
* refund_id>out_refund_no>transaction_id>out_trade_no
* @return 退款记录
* @throws WeixinException
*/
public Refund refundQuery(WeixinAccount weixinAccount, IdQuery idQuery)
throws WeixinException {
Map<String, String> map = new HashMap<String, String>();
map.put("appid", weixinAccount.getAppId());
map.put("mch_id", weixinAccount.getMchId());
map.put("nonce_str", RandomUtil.generateString(16));
map.put("device_info", weixinAccount.getDeviceInfo());
map.put(idQuery.getType().getName(), idQuery.getId());
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
map.put("sign", sign);
String param = map2xml(map);
String refundquery_uri = getRequestUri("refundquery_uri");
Response response = request.post(refundquery_uri, param);
return new RefundConverter().fromXML(response.getAsString());
}
public static void main(String[] args) throws Exception {
WeixinAccount weixinAccount = new WeixinAccount("wx0d1d598c0c03c999",
null, "GATFzDwbQdbbci3QEQxX2rUBvwTrsMiZ", "10020674");
PayApi payApi = new PayApi(null);
System.out.println(payApi.refundQuery(weixinAccount, new IdQuery(
"T0002", IdType.ORDERNO)));
}
}
@@ -0,0 +1,106 @@
package com.foxinmy.weixin4j.mp.api;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.mp.model.QRParameter;
import com.foxinmy.weixin4j.mp.model.QRParameter.QRType;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 二维码相关API
*
* @className QrApi
* @author jy.hu
* @date 2014年9月25日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%94%9F%E6%88%90%E5%B8%A6%E5%8F%82%E6%95%B0%E7%9A%84%E4%BA%8C%E7%BB%B4%E7%A0%81">二维码支持</a>
*/
public class QrApi extends BaseApi {
private final TokenHolder tokenHolder;
public QrApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 生成带参数的二维码
*
* @param parameter
* @return byte数据包
* @throws WeixinException
* @see {@link com.foxinmy.weixin4j.mp.api.QrApi#getQR(QRParameter)}
*/
public byte[] getQRData(QRParameter parameter) throws WeixinException {
Token token = tokenHolder.getToken();
String qr_uri = getRequestUri("qr_ticket_uri");
Response response = request.post(
String.format(qr_uri, token.getAccessToken()),
parameter.toJson());
String ticket = response.getAsJson().getString("ticket");
qr_uri = getRequestUri("qr_image_uri");
response = request.get(String.format(qr_uri, ticket));
return response.getBody();
}
/**
* 生成带参数的二维码
*
* @param sceneId
* 场景值
* @param expireSeconds
* 过期秒数 如果小于等于0则 视为永久二维码
* @return byte数据包
* @throws WeixinException
* @see {@link com.foxinmy.weixin4j.mp.api.QrApi#getQR(QRParameter)}
*/
public byte[] getQRData(int sceneId, int expireSeconds)
throws WeixinException {
QRParameter parameter = new QRParameter(sceneId, QRType.TEMPORARY,
expireSeconds);
if (expireSeconds <= 0) {
parameter.setQrType(QRType.PERMANENCE);
}
return getQRData(parameter);
}
/**
* 生成带参数的二维码
* <p>
* 二维码分为临时跟永久两种,扫描时触发推送带参数事件
* </p>
*
* @param parameter
* 二维码参数
* @return 硬盘存储的文件对象
* @throws WeixinException
* @throws FileNotFoundException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%94%9F%E6%88%90%E5%B8%A6%E5%8F%82%E6%95%B0%E7%9A%84%E4%BA%8C%E7%BB%B4%E7%A0%81">二维码</a>
* @see com.foxinmy.weixin4j.mp.model.QRParameter
*/
public File getQR(QRParameter parameter) throws WeixinException,
IOException {
String qr_path = getRequestUri("qr_path");
String filename = String.format("%s_%d_%d.jpg", parameter.getQrType()
.name(), parameter.getSceneId(), parameter.getExpireSeconds());
File file = new File(qr_path + File.separator + filename);
if (parameter.getQrType() == QRType.PERMANENCE && file.exists()) {
return file;
}
byte[] datas = getQRData(parameter);
file.createNewFile();
FileOutputStream out = new FileOutputStream(file);
out.write(datas);
out.close();
return file;
}
}
@@ -0,0 +1 @@
API的实现
@@ -0,0 +1,48 @@
package com.foxinmy.weixin4j.mp.api;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.mp.response.TemplateMessage;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 模板消息相关API
*
* @className TemplApi
* @author jy
* @date 2014年9月30日
* @since JDK 1.7
* @see
*/
public class TmplApi extends BaseApi {
private final TokenHolder tokenHolder;
public TmplApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 发送模板消息
*
* @param message
* @return 发送结果
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%A8%A1%E6%9D%BF%E6%B6%88%E6%81%AF%E6%8E%A5%E5%8F%A3">模板消息</a>
* @see com.foxinmy.weixin4j.mp.response.TemplateMessage
* @seee com.foxinmy.weixin4j.msg.event.TemplatesendjobfinishMessage
*/
public JsonResult sendTmplMessage(TemplateMessage tplMessage)
throws WeixinException {
Token token = tokenHolder.getToken();
String template_send_uri = getRequestUri("template_send_uri");
Response response = request.post(
String.format(template_send_uri, token.getAccessToken()),
tplMessage.toJson());
return response.getAsJsonResult();
}
}
@@ -0,0 +1,187 @@
package com.foxinmy.weixin4j.mp.api;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.mp.model.Following;
import com.foxinmy.weixin4j.mp.model.User;
import com.foxinmy.weixin4j.mp.model.UserToken;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 用户相关API
*
* @className UserApi
* @author jy.hu
* @date 2014年9月25日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.mp.model.User
*/
public class UserApi extends BaseApi {
private final TokenHolder tokenHolder;
public UserApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 获取token
*
* @param code
* 用户授权后返回的code
* @return token对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%BD%91%E9%A1%B5%E6%8E%88%E6%9D%83%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF#.E7.AC.AC.E4.BA.8C.E6.AD.A5.EF.BC.9A.E9.80.9A.E8.BF.87code.E6.8D.A2.E5.8F.96.E7.BD.91.E9.A1.B5.E6.8E.88.E6.9D.83access_token">获取用户token</a>
* @see com.foxinmy.weixin4j.mp.model.UserToken
*/
public UserToken getAccessToken(String code) throws WeixinException {
String user_token_uri = getRequestUri("sns_user_token_uri");
Response response = request.get(String.format(user_token_uri, code));
return response.getAsObject(new TypeReference<UserToken>() {
});
}
/**
* 获取用户信息
*
* @param token
* 授权票据
* @return 用户对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%BD%91%E9%A1%B5%E6%8E%88%E6%9D%83%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF#.E7.AC.AC.E5.9B.9B.E6.AD.A5.EF.BC.9A.E6.8B.89.E5.8F.96.E7.94.A8.E6.88.B7.E4.BF.A1.E6.81.AF.28.E9.9C.80scope.E4.B8.BA_snsapi_userinfo.29">拉取用户信息</a>
* @see com.foxinmy.weixin4j.mp.model.User
* @see com.foxinmy.weixin4j.mp.model.UserToken
* {@link com.foxinmy.weixin4j.mp.api.UserApi#getAccessToken(String)}
*/
public User getUser(UserToken token) throws WeixinException {
String user_info_uri = getRequestUri("sns_user_info_uri");
Response response = request.get(String.format(user_info_uri,
token.getAccessToken(), token.getOpenid()));
return response.getAsObject(new TypeReference<User>() {
});
}
/**
* 获取用户信息
* <p>
* 在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的,对于不同公众号,
* 同一用户的openid不同),公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称、头像、性别、所在城市、语言和关注时间
* </p>
*
* @param openId
* 用户对应的ID
* @return 用户对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF">获取用户信息</a>
* @see com.foxinmy.weixin4j.mp.model.User
*/
public User getUser(String openId) throws WeixinException {
String user_info_uri = getRequestUri("api_user_info_uri");
Token token = tokenHolder.getToken();
Response response = request.get(String.format(user_info_uri,
token.getAccessToken(), openId));
return response.getAsObject(new TypeReference<User>() {
});
}
/**
* 获取用户一定数量(10000)的关注者列表
*
* @param nextOpenId
* 下一次拉取数据的openid
* @return 关注信息
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%85%B3%E6%B3%A8%E8%80%85%E5%88%97%E8%A1%A8">获取关注者列表</a>
* @see com.foxinmy.weixin4j.mp.model.Following
*/
public Following getFollowing(String nextOpenId) throws WeixinException {
String fllowing_uri = getRequestUri("following_uri");
Token token = tokenHolder.getToken();
Response response = request.get(String.format(fllowing_uri,
token.getAccessToken(), nextOpenId == null ? "" : nextOpenId));
Following following = response
.getAsObject(new TypeReference<Following>() {
});
if (following.getCount() > 0) {
List<String> openIds = JSON.parseArray(following.getDataJson()
.getString("openid"), String.class);
List<User> userList = new ArrayList<User>();
for (String openId : openIds) {
userList.add(getUser(openId));
}
following.setUserList(userList);
}
return following;
}
/**
* 获取用户全部的关注者列表
* <p>
* 当公众号关注者数量超过10000时,可通过填写next_openid的值,从而多次拉取列表的方式来满足需求,
* 将上一次调用得到的返回中的next_openid值,作为下一次调用中的next_openid值
* </p>
*
* @return 用户对象集合
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%85%B3%E6%B3%A8%E8%80%85%E5%88%97%E8%A1%A8">获取关注者列表</a>
* @see com.foxinmy.weixin4j.mp.model.Following
* @see com.foxinmy.weixin4j.mp.api.UserApi#getFollowing(String)
*/
public List<User> getAllFollowing() throws WeixinException {
List<User> userList = new ArrayList<User>();
String nextOpenId = null;
Following f = null;
for (;;) {
f = getFollowing(nextOpenId);
if (f.getCount() == 0) {
break;
}
userList.addAll(f.getUserList());
nextOpenId = f.getNextOpenId();
}
return userList;
}
/**
* 设置用户备注名
*
* @param openId
* 用户ID
* @param remark
* 备注名
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%AE%BE%E7%BD%AE%E7%94%A8%E6%88%B7%E5%A4%87%E6%B3%A8%E5%90%8D%E6%8E%A5%E5%8F%A3">设置用户备注名</a>
*/
public JsonResult remarkUserName(String openId, String remark)
throws WeixinException {
String updateremark_uri = getRequestUri("updateremark_uri");
Token token = tokenHolder.getToken();
JSONObject obj = new JSONObject();
obj.put("openid", openId);
obj.put("remark", remark);
Response response = request.post(
String.format(updateremark_uri, token.getAccessToken()),
obj.toJSONString());
return response.getAsJsonResult();
}
}
@@ -0,0 +1,81 @@
# ----------------------------------------------------------------------------
# api\u9996\u9875
# http://mp.weixin.qq.com/wiki/index.php
# \u63a5\u53e3\u8c03\u7528\u8bf4\u660e
# http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E5%8F%A3%E9%A2%91%E7%8E%87%E9%99%90%E5%88%B6%E8%AF%B4%E6%98%8E
# ----------------------------------------------------------------------------
api_base_url=https://api.weixin.qq.com/cgi-bin
mp_base_url=https://mp.weixin.qq.com/cgi-bin
file_base_url=http://file.api.weixin.qq.com/cgi-bin
mch_base_url=https://api.mch.weixin.qq.com
# \u7f51\u9875\u6388\u6743\u83b7\u53d6\u7528\u6237\u4fe1\u606f
user_auth_uri=https://open.weixin.qq.com/connect/oauth2/authorize?appid={app_id}&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect
sns_user_token_uri=https://api.weixin.qq.com/sns/oauth2/access_token?appid={app_id}&secret={app_secret}&code=%s&grant_type=authorization_code
sns_user_info_uri=https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN
# \u76f4\u63a5\u83b7\u53d6\u7528\u6237\u4fe1\u606f
api_user_info_uri={api_base_url}/user/info?access_token=%s&openid=%s&lang=zh_CN
# \u83b7\u53d6token
api_token_uri={api_base_url}/token?grant_type=client_credential&appid=%s&secret=%s
# \u83b7\u53d6\u4e8c\u7ef4\u7801
qr_ticket_uri={api_base_url}/qrcode/create?access_token=%s
qr_image_uri={mp_base_url}/showqrcode?ticket=%s
# \u4e0a\u4f20\u5a92\u4f53\u6587\u4ef6
file_upload_uri={file_base_url}/media/upload?access_token=%s&type=%s
# \u4e0b\u8f7d\u5a92\u4f53\u6587\u4ef6
file_download_uri={file_base_url}/media/get?access_token=%s&media_id=%s
# \u53d1\u9001\u5ba2\u670d\u6d88\u606f
custom_notify_uri={api_base_url}/message/custom/send?access_token=%s
# \u521b\u5efa\u5206\u7ec4
group_create_uri={api_base_url}/groups/create?access_token=%s
# \u67e5\u8be2\u5206\u7ec4
group_get_uri={api_base_url}/groups/get?access_token=%s
# \u67e5\u8be2\u7528\u6237\u6240\u5728\u5206\u7ec4
group_getid_uri={api_base_url}/groups/getid?access_token=%s
# \u4fee\u6539\u5206\u7ec4\u540d
group_modify_uri={api_base_url}/groups/update?access_token=%s
# \u79fb\u52a8\u7528\u6237\u5206\u7ec4
group_move_uri={api_base_url}/groups/members/update?access_token=%s
# \u83b7\u53d6\u5173\u6ce8\u7740
following_uri={api_base_url}/user/get?access_token=%s&next_openid=%s
# \u81ea\u5b9a\u4e49\u83dc\u5355
menu_create_uri={api_base_url}/menu/create?access_token=%s
# \u67e5\u8be2\u83dc\u5355
menu_get_uri={api_base_url}/menu/get?access_token=%s
# \u5220\u9664\u83dc\u5355
menu_delete_uri={api_base_url}/menu/delete?access_token=%s
# \u4e0a\u4f20\u56fe\u6587
article_upload_uri={api_base_url}/media/uploadnews?access_token=%s
# \u4e0a\u4f20\u89c6\u9891
video_upload_uri={file_base_url}/media/uploadvideo?access_token=%s
# \u5206\u7ec4\u7fa4\u53d1
mass_group_uri={api_base_url}/message/mass/sendall?access_token=%s
# openId\u7fa4\u53d1
mass_openid_uri={api_base_url}/message/mass/send?access_token=%s
# \u5220\u9664\u7fa4\u53d1
mass_delete_uri={api_base_url}/message/mass/delete?access_token=%s
# \u5ba2\u670d\u804a\u5929\u8bb0\u5f55
custom_record_uri={api_base_url}/customservice/getrecord?access_token=%s
# \u957f\u94fe\u63a5\u8f6c\u77ed\u94fe\u63a5
shorturl_uri={api_base_url}/shorturl?access_token=%s
p_shorturl_uri={mch_base_url}/tools/shorturl
# \u8bbe\u7f6e\u5907\u6ce8\u540d
updateremark_uri={api_base_url}/user/info/updateremark?access_token=%s
# \u6a21\u677f\u6d88\u606f
template_send_uri={api_base_url}/message/template/send?access_token=%s
# \u8ba2\u5355\u67e5\u8be2
orderquery_uri={api_base_url}/pay/orderquery?access_token=%s
# \u53d1\u8d27\u901a\u77e5
delivernotify_uri={api_base_url}/pay/delivernotify?access_token=%s
# \u7ef4\u6743\u5904\u7406
payfeedback_update_uri={api_base_url}/payfeedback/update?access_token=%s&openid=%s&feedbackid=%s
# \u8ba2\u5355\u67e5\u8be2
orderquery_v3_uri={mch_base_url}/pay/orderquery
# \u5173\u95ed\u8ba2\u5355
closeorder_uri={mch_base_url}/pay/closeorder
# \u5bf9\u8d26\u5355\u4e0b\u8f7d
downloadbill_uri={mch_base_url}/pay/downloadbill
# \u9000\u6b3e\u67e5\u8be2
refundquery_uri={mch_base_url}/pay/refundquery
@@ -0,0 +1,98 @@
package com.foxinmy.weixin4j.mp.model;
import java.io.Serializable;
/**
* 网页授权结果
*
* @className AuthResult
* @author jy.hu
* @date 2014年4月8日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%BD%91%E9%A1%B5%E6%8E%88%E6%9D%83%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF">网页授权获取用户基本资料</a>
*/
public class AuthResult implements Serializable {
private static final long serialVersionUID = 654855396163854805L;
/**
* 网页授权获取code
*
* @className AuthScope
* @author jy.hu
* @date 2014年4月8日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%BD%91%E9%A1%B5%E6%8E%88%E6%9D%83%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF#.E7.AC.AC.E4.B8.80.E6.AD.A5.EF.BC.9A.E7.94.A8.E6.88.B7.E5.90.8C.E6.84.8F.E6.8E.88.E6.9D.83.EF.BC.8C.E8.8E.B7.E5.8F.96code">获取code</a>
*/
public enum AuthScope {
BASE("snsapi_base"), USERINFO("snsapi_userinfo");
private String name;
AuthScope(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public enum AuthCode {
OK, FAILED, REDIRECT
}
private AuthCode authCode;
private String location;
private UserToken accessToken;
public AuthResult(String location) {
this.location = location;
this.authCode = AuthCode.REDIRECT;
}
public AuthResult(UserToken accessToken) {
this.authCode = AuthCode.OK;
this.accessToken = accessToken;
}
public AuthResult(String location, AuthCode authCode) {
this.location = location;
this.authCode = authCode;
}
public AuthCode getAuthCode() {
return authCode;
}
public void setAuthCode(AuthCode authCode) {
this.authCode = authCode;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public UserToken getAccessToken() {
return accessToken;
}
public void setAccessToken(UserToken accessToken) {
this.accessToken = accessToken;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[AuthResult authCode=").append(authCode);
sb.append(", location=").append(location);
sb.append(", accessToken=").append(accessToken).append("]");
return sb.toString();
}
}
@@ -0,0 +1,113 @@
package com.foxinmy.weixin4j.mp.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.type.ButtonType;
/**
* 菜单按钮
* <p>
* 目前自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单,一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以"..."代替
* 请注意,创建自定义菜单后,由于微信客户端缓存,需要24小时微信客户端才会展现出来,建议测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果
* </p>
*
* @className Button
* @author jy.hu
* @date 2014年4月5日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.type.ButtonType
*/
public class Button implements Serializable {
private static final long serialVersionUID = -6422234732203854866L;
private String name;
private ButtonType type; // 菜单的响应动作类型
private String key; // click等点击类型必须
private String url; // view类型必须
@JSONField(name = "sub_button")
private List<Button> subs;
public Button() {
}
public Button(String name) {
this.name = name;
}
public Button(String name, String url) {
this.name = name;
this.url = url;
this.type = ButtonType.view;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ButtonType getType() {
return type;
}
public void setType(ButtonType type) {
this.type = type;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public List<Button> getSubs() {
return subs;
}
public void setSubs(List<Button> subs) {
this.subs = subs;
}
public Button pushSub(Button btn) {
if (this.subs == null) {
this.subs = new ArrayList<Button>();
}
this.subs.add(btn);
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[Button name=").append(name);
sb.append(" ,type=").append(type);
sb.append(" ,key=").append(key);
sb.append(" ,url=").append(url);
if (subs != null && !subs.isEmpty()) {
sb.append("{");
for (Button sub : subs) {
sb.append(sub.toString());
}
sb.append("}");
}
sb.append("]");
return sb.toString();
}
}
@@ -0,0 +1,95 @@
package com.foxinmy.weixin4j.mp.model;
import java.io.Serializable;
import java.util.Date;
/**
* 客服聊天记录
*
* @className CustomRecord
* @author jy
* @date 2014年6月28日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%AE%A2%E6%9C%8D%E8%81%8A%E5%A4%A9%E8%AE%B0%E5%BD%95">客服聊天记录</a>
*/
public class CustomRecord implements Serializable {
private static final long serialVersionUID = -4024147769411601325L;
private String worker;// 客服账号
private String openid;// 用户的标识
private Opercode opercode;// 操作ID(会话状态)
private Date time;// 操作时间
private String text;// 聊天记录
public String getWorker() {
return worker;
}
public void setWorker(String worker) {
this.worker = worker;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public Opercode getOpercode() {
return opercode;
}
public void setOpercode(Opercode opercode) {
this.opercode = opercode;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public enum Opercode {
MISS(1000, "创建未接入会话"), ONLINE(1001, "接入会话"), CALL(1002, "主动发起会话"), CLOSE(
1004, "关闭会话"), RASE(1005, "抢接会话"), RECEIVE1(2001, "公众号收到消息"), SEND(
2002, "客服发送消息"), RECEIVE2(2003, "客服收到消息");
private int code;
private String desc;
Opercode(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[CustomRecord worker=").append(worker);
sb.append(" ,openid=").append(openid);
sb.append(" ,opercode=").append(opercode);
sb.append(" ,time=").append(time);
sb.append(" ,text=").append(text);
sb.append("]");
return sb.toString();
}
}
@@ -0,0 +1,86 @@
package com.foxinmy.weixin4j.mp.model;
import java.io.Serializable;
import java.util.List;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 关注信息
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a href=
* "http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%85%B3%E6%B3%A8%E8%80%85%E5%88%97%E8%A1%A8"
* >关注信息</a>
*/
public class Following implements Serializable {
private static final long serialVersionUID = 1917454368271027134L;
private int total;
private int count;
@JSONField(name = "data")
private JSONObject dataJson;
@JSONField(name = "next_openid")
private String nextOpenId;
private List<User> userList;
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
public JSONObject getDataJson() {
return dataJson;
}
public void setDataJson(JSONObject dataJson) {
this.dataJson = dataJson;
}
public String getNextOpenId() {
return nextOpenId;
}
public void setNextOpenId(String nextOpenId) {
this.nextOpenId = nextOpenId;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[Following total=").append(total);
sb.append(", count=").append(count);
if (userList != null && !userList.isEmpty()) {
sb.append(", users={");
for (User u : userList) {
sb.append(u.toString());
}
sb.append("}");
}
sb.append(", nextOpenId=").append(nextOpenId).append("]");
return sb.toString();
}
}
@@ -0,0 +1,88 @@
package com.foxinmy.weixin4j.mp.model;
import java.io.Serializable;
/**
* 分组
*
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3">分组</a>
*/
public class Group implements Serializable {
private static final long serialVersionUID = 6979565973974005954L;
private int id;
private String name;
private int count;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCount() {
return count;
}
public Group(int id, String name) {
this.id = id;
this.name = name;
}
public Group(String name) {
this.name = name;
}
public Group() {
}
/**
* 返回创建分组所需的json格式字符串
*
* @return {"group": {"id": 107, "name": "test"}}
*/
public String toCreateJson() {
return String.format("{\"group\":{\"id\":%s,\"name\":\"%s\"}}", id,
name);
}
/**
* 返回修改分组所需的json格式字符串
*
* @return {"group": {"id": 107, "name": "test"}}
*/
public String toModifyJson() {
return String.format("{\"group\":{\"id\":%s,\"name\":\"%s\"}}", id,
name);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Group) {
return id == ((Group) obj).getId();
}
return super.equals(obj);
}
@Override
public String toString() {
return String.format("[Group id=%d ,name=%s ,count=%d]", id, name,
count);
}
}
@@ -0,0 +1,93 @@
package com.foxinmy.weixin4j.mp.model;
import java.io.Serializable;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 群发图文消息
*
* @author jy.hu
* @date 2014年4月26日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3#.E4.B8.8A.E4.BC.A0.E5.9B.BE.E6.96.87.E6.B6.88.E6.81.AF.E7.B4.A0.E6.9D.90">群发消息描述</a>
*/
public class MpArticle implements Serializable {
private static final long serialVersionUID = 5583479943661639234L;
@JSONField(name = "thumb_media_id")
private String thumbMediaId; // 图文消息缩略图的media_id,可以在基础支持-上传多媒体文件接口中获得 非空
private String author;// 图文消息的作者 可为空
private String title;// 图文消息的标题 非空
@JSONField(name = "content_source_url")
private String url;// 在图文消息页面点击“阅读原文”后的页面 可为空
private String content;// 图文消息页面的内容,支持HTML标签 非空
private String digest;// 图文消息的描述 可为空
@JSONField(name = "show_cover_pic")
private short showCoverPic; // 是否显示封面,1为显示,0为不显示 可为空
public String getThumbMediaId() {
return thumbMediaId;
}
public void setThumbMediaId(String thumbMediaId) {
this.thumbMediaId = thumbMediaId;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getDigest() {
return digest;
}
public void setDigest(String digest) {
this.digest = digest;
}
public short getShowCoverPic() {
return showCoverPic;
}
public void setShowCoverPic(short showCoverPic) {
this.showCoverPic = showCoverPic;
}
public MpArticle(String thumbMediaId, String title, String content) {
this.thumbMediaId = thumbMediaId;
this.title = title;
this.content = content;
}
public MpArticle() {
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[MpArticle thumbMediaId=").append(thumbMediaId);
sb.append(", author=").append(author);
sb.append(", title=").append(title);
sb.append(", url=").append(url);
sb.append(", content=").append(content);
sb.append(", digest=").append(digest);
sb.append(", showCoverPic=").append(showCoverPic).append("]");
return sb.toString();
}
}
@@ -0,0 +1,110 @@
package com.foxinmy.weixin4j.mp.model;
import java.io.Serializable;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
/**
* 二维码参数对象
* <p>
* 目前有2种类型的二维码,分别是临时二维码和永久二维码,前者有过期时间,最大为1800秒,但能够生成较多数量,后者无过期时间,数量较少(目前参数只支持1--
* 100000)
* </p>
*
* @className QRParameter
* @author jy.hu
* @date 2014年4月8日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%94%9F%E6%88%90%E5%B8%A6%E5%8F%82%E6%95%B0%E7%9A%84%E4%BA%8C%E7%BB%B4%E7%A0%81">生成带参数的二维码</a>
*/
public class QRParameter implements Serializable {
private static final long serialVersionUID = 6611187606558274253L;
public enum QRType {
TEMPORARY("QR_SCENE"), // 临时
PERMANENCE("QR_LIMIT_SCENE"); // 永久
private String name;
QRType(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
@XStreamAlias("expire_seconds")
private int expireSeconds; // 该二维码有效时间,以秒为单位。 最大不超过1800。
@XStreamAlias("action_name")
private QRType qrType; // 二维码类型,QR_SCENE为临时,QR_LIMIT_SCENE为永久
@XStreamOmitField
@XStreamAlias("scene_id")
private int sceneId; // 场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1--100000
public int getExpireSeconds() {
return expireSeconds;
}
public void setExpireSeconds(int expireSeconds) {
this.expireSeconds = expireSeconds;
}
public QRType getQrType() {
return qrType;
}
public void setQrType(QRType qrType) {
this.qrType = qrType;
}
public int getSceneId() {
return sceneId;
}
public void setSceneId(int sceneId) {
this.sceneId = sceneId;
}
public QRParameter(int expireSeconds, QRType qrType, int sceneId) {
this.expireSeconds = expireSeconds;
this.qrType = qrType;
this.sceneId = sceneId;
}
public QRParameter(QRType qrType, int sceneId) {
this(0, qrType, sceneId);
}
public QRParameter(int sceneId, int expireSeconds) {
this(0, null, sceneId);
}
public String toJson() {
/*
* XStream xstream = new XStream(new JsonHierarchicalStreamDriver() {
* public HierarchicalStreamWriter createWriter(Writer writer) { return
* new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE); } });
* xstream.setMode(XStream.NO_REFERENCES);
* xstream.autodetectAnnotations(true); if (this.qrType ==
* QRType.QR_LIMIT_SCENE) { xstream.omitField(QRParameter.class,
* "expire_seconds"); } return xstream.toXML(this);
*/
StringBuilder jsonBuilder = new StringBuilder("{");
jsonBuilder.append("\"action_name\":\"").append(qrType.getName()).append("\"");
if (this.qrType == QRType.TEMPORARY) {
jsonBuilder.append(",\"expire_seconds\":").append(expireSeconds);
}
jsonBuilder.append(",\"action_info\":").append(String.format("{\"scene\": {\"scene_id\": %d}}", sceneId));
jsonBuilder.append("}");
return jsonBuilder.toString();
}
@Override
public String toString() {
return "QRParameter [expireSeconds=" + expireSeconds + ", qrType=" + qrType + ", sceneId=" + sceneId + "]";
}
}
@@ -0,0 +1,220 @@
package com.foxinmy.weixin4j.mp.model;
import java.io.Serializable;
import org.apache.commons.lang3.StringUtils;
/**
* 用户对象
* <p>
* 当用户与公众号有交互时,可通过openid获取信息
* </p>
*
* @author jy.hu
* @date 2014年4月8日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF">获取用户基本资料</a>
*/
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String openid; // 用户的唯一标识
private String nickname; // 用户昵称
private int sex; // 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
private String province; // 用户个人资料填写的省份
private String city; // 普通用户个人资料填写的城市
private String country; // 国家,如中国为CN
private String headimgurl; // 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空
private String privilege; // 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom
private int subscribe; // 是否关注
private long subscribe_time; // 关注时间
private Lang language; // 使用语言
private String unionid; // 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段
// 国家地区语言版本
public enum Lang {
zh_CN("简体"), zh_TW("繁体"), en("英语");
private String desc;
Lang(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
// 用户性别 值为1时是男性,值为2时是女性,值为0时是未知
public enum Gender {
male(1), female(2), unknown(0);
private int sex;
Gender(int sex) {
this.sex = sex;
}
public int getInt() {
return sex;
}
}
// (有0、46、64、96、132数值可选,0代表640*640正方形头像)
public enum Size {
small(46), middle1(64), middle2(96), big(132);
private int size;
Size(int size) {
this.size = size;
}
public int getInt() {
return size;
}
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getSex() {
return sex;
}
public Gender getGender() {
if (sex == 1) {
return Gender.male;
} else if (sex == 2) {
return Gender.female;
} else {
return Gender.unknown;
}
}
public void setSex(int sex) {
this.sex = sex;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getHeadimgurl() {
return headimgurl;
}
public String getHeadimgurl(Size size) {
if (StringUtils.isNoneBlank(headimgurl)) {
StringBuilder sb = new StringBuilder(headimgurl);
return sb.replace(0, (headimgurl.length() - 1), size.getInt() + "")
.toString();
}
return "";
}
public void setHeadimgurl(String headimgurl) {
this.headimgurl = headimgurl;
}
public String getPrivilege() {
return privilege;
}
public void setPrivilege(String privilege) {
this.privilege = privilege;
}
public int getSubscribe() {
return subscribe;
}
public void setSubscribe(int subscribe) {
this.subscribe = subscribe;
}
public Lang getLanguage() {
return language;
}
public void setLanguage(Lang language) {
this.language = language;
}
public long getSubscribe_time() {
return subscribe_time;
}
public void setSubscribe_time(long subscribe_time) {
this.subscribe_time = subscribe_time;
}
public String getUnionid() {
return unionid;
}
public void setUnionid(String unionid) {
this.unionid = unionid;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof User) {
return openid.equals(((User) obj).getOpenid());
}
return false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[User openid=").append(openid);
sb.append(", nickname=").append(nickname);
sb.append(", sex=").append(sex);
sb.append(", province=").append(province);
sb.append(", city=").append(city);
sb.append(", country=").append(country);
sb.append(", headimgurl=").append(headimgurl);
sb.append(", privilege=").append(privilege);
sb.append(", language=").append(language);
sb.append(", subscribe_time=").append(subscribe_time);
sb.append(", unionid=").append(unionid);
sb.append(", subscribe=").append(subscribe).append("]");
return sb.toString();
}
}
@@ -0,0 +1,45 @@
package com.foxinmy.weixin4j.mp.model;
import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.model.Token;
/**
* 用户token 一般通过授权页面获得
*
* @className UserToken
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.mp.model.AuthResult
* @see com.foxinmy.weixin4j.mp.model.AuthResult.AuthScope
*/
public class UserToken extends Token {
private static final long serialVersionUID = 1L;
@JSONField(name = "refresh_token")
private String refreshToken;
private String scope;
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
@Override
public String toString() {
return "UserToken [refreshToken=" + refreshToken + ", scope=" + scope + ", getAccessToken()=" + getAccessToken() + ", getExpiresIn()=" + getExpiresIn() + ", getOpenid()=" + getOpenid() + ", getTime()=" + getTime() + "]";
}
}
@@ -0,0 +1,75 @@
package com.foxinmy.weixin4j.mp.msg.model;
import com.foxinmy.weixin4j.type.MediaType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 图文对象
*
* @className Article
* @author jy
* @date 2014年9月29日
* @since JDK 1.7
* @see
*/
public class Article extends BaseMsg {
private static final long serialVersionUID = 1L;
private String title; // 图文消息标题
@XStreamAlias("description")
private String desc; // 图文消息描述
@XStreamAlias("picurl")
private String picUrl; // 图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200
private String url; // 点击图文消息跳转链接
public Article(String title, String desc, String picUrl, String url) {
this.title = title;
this.desc = desc;
this.picUrl = picUrl;
this.url = url;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getPicUrl() {
return picUrl;
}
public void setPicUrl(String picUrl) {
this.picUrl = picUrl;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return "Article [title=" + title + ", desc=" + desc + ", picUrl="
+ picUrl + ", url=" + url + "]";
}
@Override
public MediaType getMediaType() {
return MediaType.mpnews;
}
}
@@ -0,0 +1,39 @@
package com.foxinmy.weixin4j.mp.msg.model;
import java.io.Serializable;
import java.io.Writer;
import com.foxinmy.weixin4j.mp.msg.notify.BaseNotify;
import com.foxinmy.weixin4j.type.MediaType;
import com.foxinmy.weixin4j.util.ClassUtil;
import com.foxinmy.weixin4j.xml.XStream;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;
import com.thoughtworks.xstream.io.json.JsonWriter;
public abstract class BaseMsg implements Serializable {
private static final long serialVersionUID = 1L;
private final static XStream xstream = new XStream(
new JsonHierarchicalStreamDriver() {
public HierarchicalStreamWriter createWriter(Writer writer) {
return new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE);
}
});
static {
xstream.setMode(XStream.NO_REFERENCES);
xstream.autodetectAnnotations(true);
xstream.processAnnotations(ClassUtil.getClasses(
BaseNotify.class.getPackage()).toArray(new Class[0]));
}
public abstract MediaType getMediaType();
/**
* 客服消息json化,适用于客服消息接口
*
* @return {"touser": "to","msgtype": "text","text": {"content": "123"}}
*/
public String toNotifyJson() {
return xstream.toXML(this);
}
}
@@ -0,0 +1,38 @@
package com.foxinmy.weixin4j.mp.msg.model;
import com.foxinmy.weixin4j.type.MediaType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 图片对象
*
* @className Image
* @author jy
* @date 2014年9月29日
* @since JDK 1.7
* @see
*/
public class Image extends BaseMsg {
private static final long serialVersionUID = 1L;
@XStreamAlias("media_id")
private String mediaId;
public Image(String mediaId) {
this.mediaId = mediaId;
}
public String getMediaId() {
return mediaId;
}
public void setMediaId(String mediaId) {
this.mediaId = mediaId;
}
@Override
public MediaType getMediaType() {
return MediaType.image;
}
}
@@ -0,0 +1,93 @@
package com.foxinmy.weixin4j.mp.msg.model;
import com.foxinmy.weixin4j.type.MediaType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 音乐对象
*
* @className Music
* @author jy
* @date 2014年9月29日
* @since JDK 1.7
* @see
*/
public class Music extends BaseMsg {
private static final long serialVersionUID = 1L;
private String title;
@XStreamAlias("description")
private String desc;
@XStreamAlias("musicurl")
private String musicUrl;
@XStreamAlias("hqmusicurl")
private String hqMusicUrl;
@XStreamAlias("thumb_media_id")
private String thumbMediaId;
public Music(String thumbMediaId) {
this(null, null, null, null, thumbMediaId);
}
public Music(String title, String desc, String musicUrl, String hqMusicUrl,
String thumbMediaId) {
this.title = title;
this.desc = desc;
this.musicUrl = musicUrl;
this.hqMusicUrl = hqMusicUrl;
this.thumbMediaId = thumbMediaId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getMusicUrl() {
return musicUrl;
}
public void setMusicUrl(String musicUrl) {
this.musicUrl = musicUrl;
}
public String getHqMusicUrl() {
return hqMusicUrl;
}
public void setHqMusicUrl(String hqMusicUrl) {
this.hqMusicUrl = hqMusicUrl;
}
public String getThumbMediaId() {
return thumbMediaId;
}
public void setThumbMediaId(String thumbMediaId) {
this.thumbMediaId = thumbMediaId;
}
@Override
public String toString() {
return "Music [title=" + title + ", desc=" + desc + ", musicUrl="
+ musicUrl + ", hqMusicUrl=" + hqMusicUrl + ", thumbMediaId="
+ thumbMediaId + "]";
}
@Override
public MediaType getMediaType() {
return MediaType.music;
}
}
@@ -0,0 +1 @@
不同的消息类型中的模型
@@ -0,0 +1,40 @@
package com.foxinmy.weixin4j.mp.msg.model;
import com.foxinmy.weixin4j.type.MediaType;
/**
* 文本对象
* @className Text
* @author jy
* @date 2014年9月29日
* @since JDK 1.7
* @see
*/
public class Text extends BaseMsg {
private static final long serialVersionUID = 1L;
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Text(String content) {
this.content = content;
}
@Override
public String toString() {
return "Text [content=" + content + "]";
}
@Override
public MediaType getMediaType() {
return MediaType.text;
}
}
@@ -0,0 +1,88 @@
package com.foxinmy.weixin4j.mp.msg.model;
import com.foxinmy.weixin4j.type.MediaType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 视频对象
*
* @className Video
* @author jy
* @date 2014年9月29日
* @since JDK 1.7
* @see
*/
public class Video extends BaseMsg {
private static final long serialVersionUID = 1L;
@XStreamAlias("media_id")
private String mediaId;
@XStreamAlias("thumb_media_id")
private String thumbMediaId;
private String title;
@XStreamAlias("description")
private String desc;
public Video(String mediaId) {
this(mediaId, null, null, null);
}
public Video(String mediaId, String thumbMediaId) {
this(mediaId, thumbMediaId, null, null);
}
public Video(String mediaId, String title, String desc) {
this(mediaId, null, title, desc);
}
public Video(String mediaId, String thumbMediaId, String title, String desc) {
this.mediaId = mediaId;
this.thumbMediaId = thumbMediaId;
this.title = title;
this.desc = desc;
}
public String getMediaId() {
return mediaId;
}
public void setMediaId(String mediaId) {
this.mediaId = mediaId;
}
public String getThumbMediaId() {
return thumbMediaId;
}
public void setThumbMediaId(String thumbMediaId) {
this.thumbMediaId = thumbMediaId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "Video [mediaId=" + mediaId + ", thumbMediaId=" + thumbMediaId
+ ", title=" + title + ", desc=" + desc + "]";
}
@Override
public MediaType getMediaType() {
return MediaType.vedio;
}
}
@@ -0,0 +1,38 @@
package com.foxinmy.weixin4j.mp.msg.model;
import com.foxinmy.weixin4j.type.MediaType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 语音对象
*
* @className Image
* @author jy
* @date 2014年9月29日
* @since JDK 1.7
* @see
*/
public class Voice extends BaseMsg {
private static final long serialVersionUID = 1L;
@XStreamAlias("media_id")
private String mediaId;
public Voice(String mediaId) {
this.mediaId = mediaId;
}
public String getMediaId() {
return mediaId;
}
public void setMediaId(String mediaId) {
this.mediaId = mediaId;
}
@Override
public MediaType getMediaType() {
return MediaType.voice;
}
}
@@ -0,0 +1,102 @@
package com.foxinmy.weixin4j.mp.msg.notify;
import java.util.LinkedList;
import java.util.List;
import com.foxinmy.weixin4j.mp.msg.model.Article;
import com.foxinmy.weixin4j.mp.type.ResponseType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
/**
* 客服图文消息
*
* @className ArticleNotify
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF#.E5.8F.91.E9.80.81.E5.9B.BE.E6.96.87.E6.B6.88.E6.81.AF">客服图文消息</a>
* @see com.foxinmy.weixin.msg.model.Article
* @see com.foxinmy.weixin4j.mp.msg.notify.BaseNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.BaseNotify#toJson()
*/
public class ArticleNotify extends BaseNotify {
private static final int MAX_ARTICLE_COUNT = 10;
private static final long serialVersionUID = 1740696901128709998L;
public ArticleNotify() {
this(null);
}
public ArticleNotify(String touser) {
super(touser, ResponseType.news);
}
@XStreamAlias("news")
private News news;
@XStreamOmitField
private int count; // 图文消息个数,限制为10条以内
public void pushArticle(String title, String desc, String picUrl, String url) {
if ((count + 1) > MAX_ARTICLE_COUNT) {
return;
}
if (this.news == null) {
this.news = new News();
}
this.news.pushArticle(title, desc, picUrl, url);
count++;
}
public void pushAll(List<Article> articles) {
count = articles.size();
if (articles.size() > MAX_ARTICLE_COUNT) {
count = MAX_ARTICLE_COUNT;
articles = articles.subList(0, count);
}
if (this.news == null) {
this.news = new News();
}
this.news.setArticles(articles);
}
private static class News {
@XStreamAlias("articles")
private List<Article> articles;
public News() {
this.articles = new LinkedList<Article>();
}
public void pushArticle(String title, String desc, String picUrl,
String url) {
this.articles.add(new Article(title, desc, picUrl, url));
}
public void setArticles(List<Article> articles) {
this.articles = articles;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Article article : articles) {
sb.append("{title=").append(article.getTitle());
sb.append(" ,description=").append(article.getDesc());
sb.append(" ,picUrl=").append(article.getPicUrl());
sb.append(" ,url=").append(article.getUrl()).append("}");
}
return sb.toString();
}
}
@Override
public String toString() {
return String.format(
"[ArticleNotify touser=%s ,msgtype=%s ,articles=%s]",
super.getTouser(), super.getMsgtype().name(),
this.news.toString());
}
}
@@ -0,0 +1,75 @@
package com.foxinmy.weixin4j.mp.msg.notify;
import java.io.Serializable;
import java.io.Writer;
import com.foxinmy.weixin4j.mp.type.ResponseType;
import com.foxinmy.weixin4j.xml.XStream;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;
import com.thoughtworks.xstream.io.json.JsonWriter;
/**
* 客服消息基类(48小时内不限制发送次数)
*
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF">发送客服消息</a>
*/
public class BaseNotify implements Serializable {
private static final long serialVersionUID = 7190233634431087729L;
private String touser;
private ResponseType msgtype;
public BaseNotify(ResponseType msgtype) {
this.msgtype = msgtype;
}
public BaseNotify(String touser, ResponseType msgtype) {
this.touser = touser;
this.msgtype = msgtype;
}
public String getTouser() {
return touser;
}
public void setTouser(String touser) {
this.touser = touser;
}
public ResponseType getMsgtype() {
return msgtype;
}
public void setMsgtype(ResponseType msgtype) {
this.msgtype = msgtype;
}
/**
* 客服消息json化
*
* @return {"touser": "to","msgtype": "text","text": {"content": "123"}}
*/
public String toJson() {
XStream xstream = new XStream(new JsonHierarchicalStreamDriver() {
public HierarchicalStreamWriter createWriter(Writer writer) {
return new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE);
}
});
xstream.setMode(XStream.NO_REFERENCES);
xstream.autodetectAnnotations(true);
xstream.processAnnotations(this.getClass());
return xstream.toXML(this);
}
@Override
public String toString() {
return String.format("[BaseNotify touser=%s ,msgtype=%s]", touser,
msgtype.name());
}
}
@@ -0,0 +1,47 @@
package com.foxinmy.weixin4j.mp.msg.notify;
import com.foxinmy.weixin4j.mp.msg.model.Image;
import com.foxinmy.weixin4j.mp.type.ResponseType;
/**
* 客服图片消息
*
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF#.E5.8F.91.E9.80.81.E5.9B.BE.E7.89.87.E6.B6.88.E6.81.AF">客服图片消息</a>
* @see com.foxinmy.weixin4j.mp.msg.model.Image
* @see com.foxinmy.weixin4j.mp.msg.notify.BaseNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.BaseNotify#toJson()
*/
public class ImageNotify extends BaseNotify {
private static final long serialVersionUID = -7698823863398518425L;
public ImageNotify() {
this(null, null);
}
public ImageNotify(String touser) {
this(null, touser);
}
public ImageNotify(String mediaId, String touser) {
super(touser, ResponseType.image);
this.pushMediaId(mediaId);
}
public void pushMediaId(String mediaId) {
this.image = new Image(mediaId);
}
private Image image;
@Override
public String toString() {
return String.format("[ImageNotify touser=%s ,msgtype=%s ,mediaId=%s]",
super.getTouser(), super.getMsgtype().name(),
this.image.getMediaId());
}
}
@@ -0,0 +1,46 @@
package com.foxinmy.weixin4j.mp.msg.notify;
import com.foxinmy.weixin4j.mp.msg.model.Music;
import com.foxinmy.weixin4j.mp.type.ResponseType;
/**
* 客服音乐消息
*
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF#.E5.8F.91.E9.80.81.E9.9F.B3.E4.B9.90.E6.B6.88.E6.81.AF">客服音乐消息</a>
* @see com.foxinmy.weixin4j.mp.msg.model.Music
* @see com.foxinmy.weixin4j.mp.msg.notify.BaseNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.BaseNotify#toJson()
*/
public class MusicNotify extends BaseNotify {
private static final long serialVersionUID = -7698823863398518425L;
public MusicNotify() {
this(null);
}
public MusicNotify(String touser) {
super(touser, ResponseType.music);
}
public void pushMusic(String musicurl, String hqUrl, String mediaId) {
this.music = new Music(null, null, musicurl, hqUrl, mediaId);
}
private Music music;
public void setMusic(Music music) {
this.music = music;
}
@Override
public String toString() {
return String.format("[MusicNotify touser=%s ,msgtype=%s ,music=%s]",
super.getTouser(), super.getMsgtype().name(),
this.music.toString());
}
}
@@ -0,0 +1,47 @@
package com.foxinmy.weixin4j.mp.msg.notify;
import com.foxinmy.weixin4j.mp.msg.model.Text;
import com.foxinmy.weixin4j.mp.type.ResponseType;
/**
* 客服文本消息
*
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF#.E5.8F.91.E9.80.81.E6.96.87.E6.9C.AC.E6.B6.88.E6.81.AF">客服文本消息</a>
* @see com.foxinmy.weixin.msg.model.Text
* @see com.foxinmy.weixin4j.mp.msg.notify.BaseNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.BaseNotify#toJson()
*/
public class TextNotify extends BaseNotify {
private static final long serialVersionUID = -7698823863398518425L;
public TextNotify() {
this(null, null);
}
public TextNotify(String content) {
this(content,null);
}
public TextNotify(String content, String touser) {
super(touser, ResponseType.text);
this.text = new Text(content);
}
public void setContent(String content) {
this.text = new Text(content);
}
private Text text;
@Override
public String toString() {
return String.format("[TextNotify touser=%s ,msgtype=%s ,content=%s]",
super.getTouser(), super.getMsgtype().name(),
this.text.getContent());
}
}
@@ -0,0 +1,46 @@
package com.foxinmy.weixin4j.mp.msg.notify;
import com.foxinmy.weixin4j.mp.msg.model.Video;
import com.foxinmy.weixin4j.mp.type.ResponseType;
/**
* 客服视频消息
*
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF#.E5.8F.91.E9.80.81.E8.A7.86.E9.A2.91.E6.B6.88.E6.81.AF">客服视频消息</a>
* @see com.foxinmy.weixin4j.mp.msg.model.Video
* @see com.foxinmy.weixin4j.mp.msg.notify.BaseNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.BaseNotify#toJson()
*/
public class VideoNotify extends BaseNotify {
private static final long serialVersionUID = -7698823863398518425L;
public VideoNotify() {
this(null);
}
public VideoNotify(String touser) {
super(touser, ResponseType.video);
}
public void pushVideo(String mediaId, String thumbMediaId) {
this.video = new Video(mediaId, thumbMediaId);
}
private Video video;
public void setVideo(Video video) {
this.video = video;
}
@Override
public String toString() {
return String.format("[VideoNotify touser=%s ,msgtype=%s ,video=%s]",
super.getTouser(), super.getMsgtype().name(),
this.video.toString());
}
}
@@ -0,0 +1,47 @@
package com.foxinmy.weixin4j.mp.msg.notify;
import com.foxinmy.weixin4j.mp.msg.model.Voice;
import com.foxinmy.weixin4j.mp.type.ResponseType;
/**
* 客服语音消息
*
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF#.E5.8F.91.E9.80.81.E8.AF.AD.E9.9F.B3.E6.B6.88.E6.81.AF">客服语音消息</a>
* @see com.foxinmy.weixin4j.mp.msg.model.Voice
* @see com.foxinmy.weixin4j.mp.msg.notify.BaseNotify
* @see com.foxinmy.weixin4j.mp.msg.notify.BaseNotify#toJson()
*/
public class VoiceNotify extends BaseNotify {
private static final long serialVersionUID = -7698823863398518425L;
public VoiceNotify() {
this(null, null);
}
public VoiceNotify(String touser) {
this(null, touser);
}
public VoiceNotify(String mediaId, String touser) {
super(touser, ResponseType.voice);
this.pushMediaId(mediaId);
}
public void pushMediaId(String mediaId) {
this.voice = new Voice(mediaId);
}
private Voice voice;
@Override
public String toString() {
return String.format("[VoiceNotify touser=%s ,msgtype=%s ,mediaId=%s]",
super.getTouser(), super.getMsgtype().name(),
this.voice.getMediaId());
}
}
@@ -0,0 +1,83 @@
package com.foxinmy.weixin4j.mp.payment;
import com.foxinmy.weixin4j.http.XmlResult;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 调用V3.x接口返回的公用字段
*
* @className ApiResult
* @author jy
* @date 2014年10月21日
* @since JDK 1.7
* @see
*/
public class ApiResult extends XmlResult {
private static final long serialVersionUID = -8430005768959715444L;
@XStreamAlias("appid")
private String appId;// 微信分配的公众账号 ID商户号 非空
@XStreamAlias("mch_id")
private String mchId;// 微信支付分配的商户号 非空
@XStreamAlias("nonce_str")
private String nonceStr;// 随机字符串 非空
private String sign;// 签名 非空
@XStreamAlias("device_info")
private String deviceInfo;// 微信支付分配的终端设备号 可能为空
public ApiResult() {
}
public ApiResult(String returnCode, String returnMsg) {
super(returnCode, returnMsg);
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getMchId() {
return mchId;
}
public void setMchId(String mchId) {
this.mchId = mchId;
}
public String getNonceStr() {
return nonceStr;
}
public void setNonceStr(String nonceStr) {
this.nonceStr = nonceStr;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getDeviceInfo() {
return deviceInfo;
}
public void setDeviceInfo(String deviceInfo) {
this.deviceInfo = deviceInfo;
}
@Override
public String toString() {
return "ApiResult [appId=" + appId + ", mchId=" + mchId + ", nonceStr="
+ nonceStr + ", sign=" + sign + ", deviceInfo=" + deviceInfo
+ "]";
}
}
@@ -0,0 +1,13 @@
package com.foxinmy.weixin4j.mp.payment;
/**
* 对账单类型
* @className BillType
* @author jy
* @date 2014年10月31日
* @since JDK 1.7
* @see
*/
public enum BillType {
ALL, SUCCESS, REFUND
}
@@ -0,0 +1,23 @@
package com.foxinmy.weixin4j.mp.payment;
/**
* 币种
*
* @className CurrencyType
* @author jy
* @date 2014年11月2日
* @since JDK 1.7
* @see
*/
public enum CurrencyType {
CNY("人民币"), HKD("港元"), TWD("台币"), EUR("欧元"), USD("美元"), GBP("英镑"), JPY("日元");
private String desc;
CurrencyType(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
@@ -0,0 +1,40 @@
package com.foxinmy.weixin4j.mp.payment;
import java.io.Serializable;
/**
* ID查询
*
* @className IdQuery
* @author jy
* @date 2014年11月1日
* @since JDK 1.7
* @see
*/
public class IdQuery implements Serializable {
private static final long serialVersionUID = -5273675987521807370L;
private String id;
private IdType type;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public IdType getType() {
return type;
}
public void setType(IdType type) {
this.type = type;
}
public IdQuery(String id, IdType idType) {
this.id = id;
this.type = idType;
}
}
@@ -0,0 +1,26 @@
package com.foxinmy.weixin4j.mp.payment;
/**
* ID类型
*
* @className IdType
* @author jy
* @date 2014年11月1日
* @since JDK 1.7
* @see
*/
public enum IdType {
REFUNDID("refund_id"), // 微信退款单号
TRANSACTIONID("transaction_id"), // 微信订单号
ORDERNO("out_trade_no"), // 商户订单号
REFUNDNO("out_refund_no"); // 商户退款号
private String name;
IdType(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
@@ -0,0 +1,109 @@
package com.foxinmy.weixin4j.mp.payment;
import java.io.Serializable;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* JSAPI支付回调时的POST信息
*
* @className JsPayNotify
* @author jy
* @date 2014年8月19日
* @since JDK 1.7
* @see
*/
public class JsPayNotify implements Serializable {
private static final long serialVersionUID = -4659030958445259803L;
@XStreamAlias("AppId")
private String appid; // 公众号ID
@XStreamAlias("TimeStamp")
private String timestamp; // 时间戳
@XStreamAlias("NonceStr")
private String noncestr; // 随机字符串
@XStreamAlias("OpenId")
private String openid; // 用户ID
@XStreamAlias("AppSignature")
private String appsignature; // 签名结果
@XStreamAlias("IsSubscribe")
private int issubscribe;
@XStreamAlias("SignMethod")
private String signmethod; // 签名方式
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public String getNoncestr() {
return noncestr;
}
public void setNoncestr(String noncestr) {
this.noncestr = noncestr;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getAppsignature() {
return appsignature;
}
public void setAppsignature(String appsignature) {
this.appsignature = appsignature;
}
public int getIssubscribe() {
return issubscribe;
}
public void setIssubscribe(int issubscribe) {
this.issubscribe = issubscribe;
}
public String getSignmethod() {
return signmethod;
}
public void setSignmethod(String signmethod) {
this.signmethod = signmethod;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[JsPayNotify appid=").append(appid);
sb.append(", timestamp=").append(timestamp);
sb.append(", noncestr=").append(noncestr);
sb.append(", openid=").append(openid);
sb.append(", appsignature=").append(appsignature);
sb.append(", issubscribe=").append(issubscribe);
sb.append(", signmethod=").append(signmethod).append("]");
return sb.toString();
}
}
@@ -0,0 +1,314 @@
package com.foxinmy.weixin4j.mp.payment;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.PayException;
import com.foxinmy.weixin4j.http.XmlResult;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.mp.payment.v2.NativePayNotifyV2;
import com.foxinmy.weixin4j.mp.payment.v2.NativePayResponseV2;
import com.foxinmy.weixin4j.mp.payment.v2.PayFeedback;
import com.foxinmy.weixin4j.mp.payment.v2.PayPackageV2;
import com.foxinmy.weixin4j.mp.payment.v3.NativePayNotifyV3;
import com.foxinmy.weixin4j.mp.payment.v3.NativePayResponseV3;
import com.foxinmy.weixin4j.mp.payment.v3.PayPackageV3;
import com.foxinmy.weixin4j.util.ConfigUtil;
import com.foxinmy.weixin4j.xml.XStream;
/**
* 支付示例
*
* @className PayAction
* @author jy
* @date 2014年10月28日
* @since JDK 1.7
* @see
*/
public class PayAction {
private final Logger log = LoggerFactory.getLogger(getClass());
/**
* JSAPI支付
*
* @return
*/
public JSONObject jsPay() {
JSONObject obj = new JSONObject();
PayPackage payPackage = null;
// V3 支付
// 此处的openid为微信用户的openid
WeixinAccount weixinAccount = ConfigUtil.getWeixinAccount();
weixinAccount.setOpenId("用户的openId");
payPackage = new PayPackageV3(weixinAccount, "商品描述", "系统内部订单号", 1d,
"IP地址", TradeType.JSAPI);
// V2 支付
payPackage = new PayPackageV2("商品描述", weixinAccount.getPartnerId(),
"系统内部订单号", 1d, "回调地址", "IP地址");
payPackage.setAttach("ID");
String jspay = null;
try {
jspay = PayUtil.createPayJsRequestJson(payPackage, weixinAccount);
jspay = PayUtil.createPayJsRequestJson(payPackage, weixinAccount);
} catch (PayException e) {
log.error("create jspay error,{}", weixinAccount, e);
}
if (StringUtils.isBlank(jspay)) {
obj.put("code", "-2");
obj.put("msg", "创建支付链接失败!");
return obj;
}
obj.put("code", "0");
obj.put("jspay", jspay);
/*
* 编辑收货地址 SnsToken token = (SnsToken) getSession("AccessToken");
* obj.put("editaddress", PayUtil.createAddressRequestJson(
* wx.getAppId(), getFullLoction(), token.getAccess_token()));
*/
log.info("js pay....{}", obj);
return obj;
}
/**
* JSAPI(V2)支付成功时的回调通知=<br>
* &ltxml&gt<br/>
* &ltOpenId&gt&lt![CDATA[111222]]&gt&lt/OpenId&gt<br/>
* &ltAppId&gt&lt![CDATA[wwwwb4f85f3a797777]]&gt&lt/AppId&gt<br/>
* &ltIsSubscribe&gt1&lt/IsSubscribe&gt<br/>
* &ltTimeStamp&gt1369743511&lt/TimeStamp&gt<br/>
* &ltNonceStr&gt&lt![CDATA[jALldRTHAFd5Tgs5]]&gt&lt/NonceStr&gt<br/>
* &ltAppSignature>&lt![CDATA[bafe07f060f22dcda0bfdb4b5ff756f973aecffa]]&gt
* &lt/AppSignature&gt<br/>
* &ltSignMethod>&lt![CDATA[sha1]]&gt&lt/SignMethod&gt<br/>
* &lt/xml&gt<br/>
* 参与签名的字段为: appid、appkey、timestamp、noncestr、openid、issubscribe
*
* @param 订单信息
* @param inputStream
* 用户信息
*
* @see com.foxinmy.weixin4j.mp.payment.JsPayNotify
* @return success或其他
*/
public String jsNotifyV2(InputStream inputStream) {
Map<String, String> objMap = new HashMap<String, String>();
/*
* 收集url中携带的参数 /pay/notify/back?attach=8&bank_billno=201410293351060&
* bank_type=2032&discount=0&fee_type=1&input_charset=UTF-8&
* notify_id=9fKbVf_qg6y-
* wSjtSMV0PLXeEn2oGfTM1s9dWSvR2B9U6iFQRTzmjrMWKUxvh9mpBLvnh8aqFbC_OFk1pTvFnFUO00Lln4fh
* & out_trade_no=D14102900031&partner=1221928801&product_fee=1&sign=
* B9D6E772C271C9B86B8436FC9F5DFC1A&
* sign_type=MD5&time_end=20141029183707
* &total_fee=1&trade_mode=1&trade_state=0&
* transaction_id=1221928801201410296039230054&transport_fee=0
*/
log.info("pay_notify_orderinfo,{}", objMap);
JsPayNotify payNotify = XStream.get(inputStream, JsPayNotify.class);
log.info("pay_notify_userinfo,{}", payNotify);
WeixinAccount weixinAccount = ConfigUtil.getWeixinAccount();
// 验证财付通签名
String sign = objMap.get("sign");
objMap.remove("sign");
String _sign = PayUtil
.paysignMd5(objMap, weixinAccount.getPartnerKey());
log.info("财付通签名----->sign={},vaild_sign={}", sign, _sign);
if (!sign.equals(_sign)) {
return "fail";
}
objMap.clear();
// 验证微信签名
sign = payNotify.getAppsignature();
payNotify.setAppsignature(null);
payNotify.setSignmethod(null);
String vaild_sign = PayUtil.paysignSha(payNotify,
weixinAccount.getPaySignKey());
log.info("微信签名----->sign={},vaild_sign={}", sign, vaild_sign);
if (!sign.equals(vaild_sign)) {
return "fail";
}
// 处理业务逻辑
return "success";
}
/**
* JSAPI(V3)支付成功时的回调通知
*
*
* @param inputStream
* 订单细腻
* @return &ltxml&gt<br>
* &ltreturn_code&gtSUCCESS/FAIL&lt/return_code&gt<br>
* &ltreturn_msg&gt如非空,为错误 原因签名失败参数格式校验错误&lt/return_msg&gt<br>
* &lt/xml&gt
*/
public String jsNotifyV3(InputStream inputStream) {
com.foxinmy.weixin4j.mp.payment.v3.Order order = XStream.get(
inputStream, com.foxinmy.weixin4j.mp.payment.v3.Order.class);
log.info("order_info:", order);
String sign = order.getSign();
order.setSign(null);
WeixinAccount weixinAccount = ConfigUtil.getWeixinAccount();
String valid_sign = PayUtil.paysignMd5(order,
weixinAccount.getPaySignKey());
log.info("微信签名----->sign={},vaild_sign={}", sign, valid_sign);
if (!sign.equals(valid_sign)) {
return XStream.to(new XmlResult(XmlResult.FAIL, "签名错误"));
}
return XStream.to(new XmlResult());
}
/**
* 告警通知 需要成功返回 success <br/>
* &ltxml&gt<br/>
* &ltAppId&gt&lt![CDATA[wxf8b4f85f3a794e77]]&gt&lt/AppId&gt<br/>
* &ltErrorType&gt1001&lt/ErrorType&gt<br/>
* &ltDescription&gt&lt![CDATA[错误描述]]>&lt/Description&gt<br/>
* &ltAlarmContent&gt&lt![CDATA[错误详情]]>&lt/AlarmContent&gt<br/>
* &ltTimeStamp&gt1393860740&lt/TimeStamp&gt<br/>
* &ltAppSignature&gt&lt![CDATA[签名方式跟JsPayRequest中的paySign一样]]&gt&lt/
* AppSignature&gt<br/>
* &ltSignMethod&gt&lt![CDATA[sha1]]&gt&lt/SignMethod&gt<br/>
* &lt/xml&gt<br/>
* 参与签名字段:alarmcontent、appid、appkey、description、errortype、timestamp
*
* @param inputStream
* xml数据
* @see com.foxinmy.weixin4j.mp.payment.PayWarn
* @return
*/
public String warning(InputStream inputStream) {
PayWarn payWarn = XStream.get(inputStream, PayWarn.class);
log.info("pay_warning,{}", payWarn);
WeixinAccount weixinAccount = ConfigUtil.getWeixinAccount();
String sign = payWarn.getAppsignature();
payWarn.setSignmethod(null);
payWarn.setAppsignature(null);
// 验证微信签名
String vaild_sign = PayUtil.paysignSha(payWarn,
weixinAccount.getPaySignKey());
log.info("微信签名----->sign={},vaild_sign={}", sign, vaild_sign);
return "success";
}
/**
* V2.x版本Native支付时POST数据<br>
* &ltxml&gt<br/>
* &ltOpenId&gt&lt![CDATA[111222]]&gt&lt/OpenId&gt<br/>
* &ltAppId&gt&lt![CDATA[wwwwb4f85f3a797777]]&gt&lt/AppId&gt<br/>
* &ltIsSubscribe&gt1&lt/IsSubscribe&gt<br/>
* &ltProductId&gt[CDATA[000000]]&lt/ProductId&gt<br/>
* &ltTimeStamp&gt1369743511&lt/TimeStamp&gt<br/>
* &ltNonceStr&gt&lt![CDATA[jALldRTHAFd5Tgs5]]&gt&lt/NonceStr&gt<br/>
* &ltAppSignature>&lt![CDATA[bafe07f060f22dcda0bfdb4b5ff756f973aecffa]]&gt
* &lt/AppSignature&gt<br/>
* &ltSignMethod>&lt![CDATA[sha1]]&gt&lt/SignMethod&gt<br/>
* &lt/xml&gt<br/>
* 参与签名的字段为: appid、appkey、timestamp、noncestr、openid、issubscribe、productId
*
* @param inputStream
*
* @return 必须返回一个带有Package信息的xml字符串
*/
public String nativeNotifyV2(InputStream inputStream) {
// V2.x版本
NativePayNotifyV2 payNotify = XStream.get(inputStream,
NativePayNotifyV2.class);
log.info("native_pay_notify,{}", payNotify);
WeixinAccount weixinAccount = ConfigUtil.getWeixinAccount();
String sign = payNotify.getAppsignature();
payNotify.setAppsignature(null);
payNotify.setSignmethod(null);
// 验证微信签名
String vaild_sign = PayUtil.paysignSha(payNotify,
weixinAccount.getPaySignKey());
log.info("微信签名----->sign={},vaild_sign={}", sign, vaild_sign);
if (!sign.equals(vaild_sign)) {
return "fail";
}
// 构造订单信息
PayPackageV2 payPackage = new PayPackageV2("商品描述",
weixinAccount.getPartnerId(), "系统内部订单号", 1d, "回调地址", "IP地址");
NativePayResponseV2 payResponse = new NativePayResponseV2(
weixinAccount, payPackage);
return XStream.to(payResponse);
}
/**
* V3.x版本native回调<br>
* &ltxml&gt<br/>
* &ltopenid&gt&lt![CDATA[111222]]&gt&lt/openid&gt<br/>
* &ltappid&gt&lt![CDATA[wwwwb4f85f3a797777]]&gt&lt/appid&gt<br/>
* &ltmch_id&gt&lt![CDATA[1100022]]&gt&lt/mch_id&gt<br/>
* &ltis_subscribe&gt1&lt/is_subscribe&gt<br/>
* &ltproduct_id&gt[CDATA[000000]]&lt/product_id&gt<br/>
* &ltnonce_str&gt&lt![CDATA[jALldRTHAFd5Tgs5]]&gt&lt/nonce_str&gt<br/>
* &ltsign>&lt![CDATA[bafe07f060f22dcda0bfdb4b5ff756f973aecffa]]&gt&lt/sign&
* gt<br/>
* &lt/xml&gt<br/>
*
* @return
* @throws PayException
*/
public String nativeNotifyV3(InputStream inputStream) throws PayException {
NativePayNotifyV3 payNotify = XStream.get(inputStream,
NativePayNotifyV3.class);
String sign = payNotify.getSign();
payNotify.setSign(null);
WeixinAccount weixinAccount = ConfigUtil.getWeixinAccount();
String valid_sign = PayUtil.paysignMd5(payNotify,
weixinAccount.getPaySignKey());
log.info("微信签名----->sign={},vaild_sign={}", sign, valid_sign);
// 生成Package
PayPackageV3 payPackage = new PayPackageV3(weixinAccount, "商品描述",
"系统内部订单号", 1d, "IP地址", TradeType.NATIVE);
payPackage.setProduct_id(payNotify.getProductId());
if (!sign.equals(valid_sign)) {
NativePayResponseV3 payReponse = new NativePayResponseV3(
payPackage, "签名失败", null);
payReponse.setSign(PayUtil.paysignMd5(payReponse,
weixinAccount.getPaySignKey()));
return XStream.to(payReponse);
}
NativePayResponseV3 payReponse = new NativePayResponseV3(payPackage,
null, null);
payReponse.setSign(PayUtil.paysignMd5(payReponse,
weixinAccount.getPaySignKey()));
return XStream.to(payReponse);
}
/**
* 用户维权
*
* @param inputStream
* @see com.foxinmy.weixin4j.mp.payment.v2.PayFeedback
* @return
*/
public String feedback(InputStream inputStream) {
PayFeedback feedback = XStream.get(inputStream, PayFeedback.class);
log.info("pay_feedback_info:{}", feedback);
WeixinAccount weixinAccount = ConfigUtil.getWeixinAccount();
// 验证微信签名
Map<String, String> obj = new HashMap<String, String>();
obj.put("openid", feedback.getOpenId());
obj.put("appid", feedback.getAppId());
obj.put("timestamp", feedback.getTimeStamp());
String sign = PayUtil.paysignSha(obj, weixinAccount.getPaySignKey());
log.info("微信签名----->sign={},vaild_sign={}", sign,
feedback.getAppSignature());
return "success";
}
public static void main(String[] args) {
}
}
@@ -0,0 +1,121 @@
package com.foxinmy.weixin4j.mp.payment;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class PayPackage implements Serializable {
private static final long serialVersionUID = 3450161267802545790L;
protected static final NumberFormat FEE_FORMAT = new DecimalFormat("#");
protected static final DateFormat DATE_FORMAT = new SimpleDateFormat(
"yyyyMMddHHmmss");
private String body; // 商品描述 必须
private String attach; // 附加数据,原样返回 非必须
private String out_trade_no; // 商户系统内部的订单号 ,32 个字符内 、可包含字母 ,确保 在商户系统唯一 必须
private String total_fee; // 订单总金额,单位为分,不 能带小数点 必须
private String spbill_create_ip; // 订单生成的机器 IP 必须
private String time_start; // 订单生成时间,格式 为 yyyyMMddHHmmss,如 2009 年
// 12月25日9点10分10秒表 示为 20091225091010。时区 为 GMT+8
// beijing。该时间取 自商户服务器 非必须
private String time_expire; // 订单失效时间,格式 为 yyyyMMddHHmmss,如 2009 年
// 12月27日9点10分10秒表 示为 20091227091010。时区 为 GMT+8
// beijing。该时间取 自商户服务商品标记 非必须
private String goods_tag; // 商品标记,该字段不能随便 填,不使用请填空 非必须
private String notify_url; // 通知地址接收微信支付成功通知 必须
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getAttach() {
return attach;
}
public void setAttach(String attach) {
this.attach = attach;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public String getTotal_fee() {
return total_fee;
}
public void setTotal_fee(double total_fee) {
this.total_fee = FEE_FORMAT.format(total_fee);
}
public String getSpbill_create_ip() {
return spbill_create_ip;
}
public void setSpbill_create_ip(String spbill_create_ip) {
this.spbill_create_ip = spbill_create_ip;
}
public String getTime_start() {
return time_start;
}
public void setTime_start(String time_start) {
this.time_start = time_start;
}
public void setTime_expire(String time_expire) {
this.time_expire = time_expire;
}
public void setTime_start(Date time_start) {
this.time_start = time_start != null
? DATE_FORMAT.format(time_start)
: null;;
}
public String getTime_expire() {
return time_expire;
}
public void setTime_expire(Date time_expire) {
this.time_expire = time_expire != null ? DATE_FORMAT
.format(time_expire) : null;;
}
public String getGoods_tag() {
return goods_tag;
}
public void setGoods_tag(String goods_tag) {
this.goods_tag = goods_tag;
}
public String getNotify_url() {
return notify_url;
}
public void setNotify_url(String notify_url) {
this.notify_url = notify_url;
}
public PayPackage() {
}
public PayPackage(String body, String attach, String out_trade_no,
double total_fee, String spbill_create_ip, Date time_start,
Date time_expire, String goods_tag, String notify_url) {
this.body = body;
this.attach = attach;
this.out_trade_no = out_trade_no;
this.total_fee = FEE_FORMAT.format(total_fee * 100);
this.spbill_create_ip = spbill_create_ip;
this.time_start = time_start != null
? DATE_FORMAT.format(time_start)
: null;
this.time_expire = time_expire != null ? DATE_FORMAT
.format(time_expire) : null;
this.goods_tag = goods_tag;
this.notify_url = notify_url;
}
@Override
public String toString() {
return "PayPackage [body=" + body + ", attach=" + attach
+ ", out_trade_no=" + out_trade_no + ", total_fee=" + total_fee
+ ", spbill_create_ip=" + spbill_create_ip + ", time_start="
+ time_start + ", time_expire=" + time_expire + ", goods_tag="
+ goods_tag + ", notify_url=" + notify_url + "]";
}
}
@@ -0,0 +1,95 @@
package com.foxinmy.weixin4j.mp.payment;
import java.io.Serializable;
import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.util.RandomUtil;
import com.thoughtworks.xstream.annotations.XStreamAlias;
public class PayRequest implements Serializable {
private static final long serialVersionUID = -453746488398523883L;
// 公众号ID
@XStreamAlias("AppId")
private String appId;
// 当前时间戳
@XStreamAlias("TimeStamp")
private String timeStamp;
// 随机字符串
@XStreamAlias("NonceStr")
private String nonceStr;
// 订单详情扩展 订单信息组成该字符串
@XStreamAlias("Package")
private String packageInfo;
// 签名方式 数取值"SHA1"
@XStreamAlias("SignMethod")
private String signType;
// 商户将接口列表中的参数按照指定方式进行 签名,签名方式使用 signType中标示的签名方式,
@XStreamAlias("Appsignature")
private String paySign;
public PayRequest() {
this.timeStamp = System.currentTimeMillis() / 1000 + "";
this.nonceStr = RandomUtil.generateString(16);
}
public PayRequest(String appId, String packageInfo, SignType signType,
String paySign) {
this.appId = appId;
this.timeStamp = System.currentTimeMillis() / 1000 + "";
this.nonceStr = RandomUtil.generateString(16);
this.packageInfo = packageInfo;
this.signType = signType.name();
this.paySign = paySign;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(String timeStamp) {
this.timeStamp = timeStamp;
}
public String getNonceStr() {
return nonceStr;
}
public void setNonceStr(String nonceStr) {
this.nonceStr = nonceStr;
}
@JSONField(name = "package")
public String getPackageInfo() {
return packageInfo;
}
public void setPackageInfo(String packageInfo) {
this.packageInfo = packageInfo;
}
public String getSignType() {
return signType;
}
public void setSignType(SignType signType) {
this.signType = signType.name();
}
public String getPaySign() {
return paySign;
}
public void setPaySign(String paySign) {
this.paySign = paySign;
}
}
@@ -0,0 +1,369 @@
package com.foxinmy.weixin4j.mp.payment;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.PayException;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.mp.payment.v2.JsPayRequestV2;
import com.foxinmy.weixin4j.mp.payment.v2.NativePayResponseV2;
import com.foxinmy.weixin4j.mp.payment.v2.PayPackageV2;
import com.foxinmy.weixin4j.mp.payment.v3.PayRequestV3;
import com.foxinmy.weixin4j.mp.payment.v3.PayPackageV3;
import com.foxinmy.weixin4j.mp.payment.v3.PrePay;
import com.foxinmy.weixin4j.util.MapUtil;
import com.foxinmy.weixin4j.util.RandomUtil;
import com.foxinmy.weixin4j.xml.XStream;
/**
* 支付工具类
*
* @className PayUtil
* @author jy
* @date 2014年10月28日
* @since JDK 1.7
* @see
*/
public class PayUtil {
private static final String UNIFIEDORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
private static final String NATIVEURLV2 = "weixin://wxpay/bizpayurl?sign=%s&appid=%s&productid=%s&timestamp=%s&noncestr=%s";
private static final String NATIVEURLV3 = "weixin://wxpay/bizpayurl?sign=%s&appid=%s&mch_id=%s&product_id=%s&time_stamp=%s&nonce_str=%s";
/**
* 生成JSAPI字符串
*
* @param payPackage
* 订单信息
* @param weixinConfig
* appid等信息
* @return
* @throws PayException
*/
public static String createPayJsRequestJson(PayPackage payPackage,
WeixinAccount weixinAccount) throws PayException {
if (payPackage instanceof PayPackageV2) {
return createPayJsRequestJsonV2((PayPackageV2) payPackage,
weixinAccount);
} else if (payPackage instanceof PayPackageV3) {
return createPayJsRequestJsonV3((PayPackageV3) payPackage,
weixinAccount);
}
throw new PayException("-1", "unknown pay");
}
/**
* 生成V2.x版本JSAPI支付字符串
*
* @param payPackage
* 订单信息
* @param weixinConfig
* appid等信息
* @return
*/
public static String createPayJsRequestJsonV2(PayPackageV2 payPackage,
WeixinAccount weixinAccount) {
if (StringUtils.isBlank(payPackage.getPartner())) {
payPackage.setPartner(weixinAccount.getPartnerId());
}
JsPayRequestV2 jsPayRequest = new JsPayRequestV2(weixinAccount,
payPackage);
jsPayRequest.setPaySign(paysignSha(jsPayRequest,
weixinAccount.getPaySignKey()));
jsPayRequest.setSignType(SignType.SHA1);
return JSON.toJSONString(jsPayRequest);
}
/**
* 生成V2.x版本JSAPI支付字符串
*
* @param body
* 支付详情
* @param orderNo
* 订单号
* @param orderFee
* 订单总额
* @param ip
* @param weixinConfig
* appid等信息
* @return
*/
public static String createPayJsRequestJsonV2(String body, String orderNo,
double orderFee, String ip, WeixinAccount weixinAccount) {
PayPackageV2 payPackage = new PayPackageV2(body, orderNo, orderFee, ip);
payPackage.setPartner(weixinAccount.getPartnerId());
return createPayJsRequestJsonV2(payPackage, weixinAccount);
}
/**
* V2.x版本的PayRequest签名
*
* @param jsPayRequestV2
* 支付请求
* @param paySignKey
* 支付API的密钥
* @return
*/
public static String paysignSha(Object obj, String paySignKey) {
JSONObject extra = null;
if (StringUtils.isNotBlank(paySignKey)) {
extra = new JSONObject();
extra.put("appKey", paySignKey);
}
return DigestUtils.sha1Hex(MapUtil
.toJoinString(obj, false, true, extra));
}
/**
* V3.x版本的PayRequest签名
*
* @param jsPayRequestV3
* 支付请求
* @param paySignKey
* 支付API的密钥
* @return
*/
public static String paysignMd5(Object obj, String paySignKey) {
StringBuilder sb = new StringBuilder();
// a--->string1
sb.append(MapUtil.toJoinString(obj, false, false, null));
// b--->
// 在 string1 最后拼接上 key=paternerKey 得到 stringSignTemp 字符串,并 对
// stringSignTemp 进行 md5 运算
// 再将得到的 字符串所有字符转换为大写 ,得到 sign 值 signValue。
sb.append("&key=").append(paySignKey);
return DigestUtils.md5Hex(sb.toString()).toUpperCase();
}
/**
* 生成V3.x版本JSAPI支付字符串
*
* @param body
* 订单描述
* @param orderNo
* 订单号
* @param orderFee
* 订单总额
* @param ip
* @param notifyUrl
* 支付通知地址
* @param weixinConfig
* appid等信息
* @return
* @throws PayException
*/
public static String createPayJsRequestJsonV3(String body, String orderNo,
double orderFee, String ip, String notifyUrl,
WeixinAccount weixinAccount) throws PayException {
PayPackageV3 payPackage = new PayPackageV3(weixinAccount, body,
orderNo, orderFee, ip, TradeType.JSAPI);
payPackage.setNotify_url(notifyUrl);
return createPayJsRequestJsonV3(payPackage, weixinAccount);
}
/**
* 生成V3.x版本JSAPI支付字符串
*
* @param payPackage
* 订单信息
* @param weixinConfig
* appid等信息
* @return
* @throws PayException
*/
public static String createPayJsRequestJsonV3(PayPackageV3 payPackage,
WeixinAccount weixinAccount) throws PayException {
String paySignKey = weixinAccount.getPaySignKey();
payPackage.setSign(paysignMd5(payPackage, paySignKey));
PrePay prePay = createPrePay(payPackage);
PayRequestV3 jsPayRequest = new PayRequestV3(prePay);
jsPayRequest.setPaySign(paysignMd5(jsPayRequest, paySignKey));
jsPayRequest.setSignType(SignType.MD5);
return JSON.toJSONString(jsPayRequest);
}
public static PrePay createPrePay(PayPackageV3 payPackage) {
String payJsRequestXml = XStream.to(payPackage).replaceAll("__", "_");
HttpClient client = null;
try {
client = new DefaultHttpClient();
HttpPost post = new HttpPost(UNIFIEDORDER);
post.setEntity(new StringEntity(payJsRequestXml,
StandardCharsets.UTF_8));
HttpResponse response = client.execute(post);
StatusLine statusLine = response.getStatusLine();
if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
return new PrePay("-1", "网络异常[" + statusLine.getStatusCode()
+ "," + statusLine.getReasonPhrase() + "]");
}
String returnXml = EntityUtils.toString(response.getEntity(),
StandardCharsets.UTF_8);
return XStream.get(returnXml, PrePay.class);
} catch (IOException e) {
e.printStackTrace();
} finally {
client.getConnectionManager().shutdown();
}
return new PrePay("-1", "request fail");
}
/**
* <p>
* 生成编辑地址请求
* </p>
*
* err_msg edit_address:ok获取编辑收货地址成功<br/>
* edit_address:fail获取编辑收货地址失败<br/>
* userName 收货人姓名<br/>
* telNumber 收货人电话<br/>
* addressPostalCode 邮编<br/>
* proviceFirstStageName 国标收货地址第一级地址<br/>
* addressCitySecondStageName 国标收货地址第二级地址<br/>
* addressCountiesThirdStageName 国标收货地址第三级地址<br/>
* addressDetailInfo 详细收货地址信息<br/>
* nationalCode 收货地址国家码<br/>
*
* @param appId
* 公众号的ID
* @param url
* 当前访问页的URL
* @param accessToken
* snsapi_base授权时产生的token
* @return
*/
public static String createAddressRequestJson(String appId, String url,
String accessToken) {
Map<String, String> param = new HashMap<String, String>();
param.put("appId", appId);
param.put("url", url);
param.put("timeStamp", System.currentTimeMillis() / 1000 + "");
param.put("nonceStr", RandomUtil.generateString(16));
param.put("accessToken", accessToken);
String sign = paysignSha(param, null);
JSONObject obj = new JSONObject();
obj.put("appId", appId);
obj.put("scope", "jsapi_address");
obj.put("signType", "sha1");
obj.put("addrSign", sign);
obj.put("timeStamp", param.get("timeStamp"));
obj.put("nonceStr", param.get("nonceStr"));
return obj.toJSONString();
}
/**
* 创建V2.x NativePay支付链接
*
* @param weixinConfig
* 支付配置信息
* @param productId
* 与订单ID等价
* @return
*/
public String createNativePayRequestURLV2(WeixinAccount weixinAccount,
String productId) {
Map<String, String> map = new HashMap<String, String>();
String timestamp = System.currentTimeMillis() / 1000 + "";
String noncestr = RandomUtil.generateString(16);
map.put("appid", weixinAccount.getAppId());
map.put("timestamp", timestamp);
map.put("noncestr", noncestr);
map.put("productid", productId);
String sign = paysignSha(map, weixinAccount.getPaySignKey());
return String.format(NATIVEURLV2, sign, weixinAccount.getAppId(),
productId, timestamp, noncestr);
}
/**
* 创建V3.x NativePay支付链接
*
* @param weixinConfig
* 支付配置信息
* @param productId
* 与订单ID等价
* @return
*/
public String createNativePayRequestURLV3(WeixinAccount weixinAccount,
String productId) {
Map<String, String> map = new HashMap<String, String>();
String timestamp = System.currentTimeMillis() / 1000 + "";
String noncestr = RandomUtil.generateString(16);
map.put("appid", weixinAccount.getAppId());
map.put("mch_id", weixinAccount.getMchId());
map.put("time_stamp", timestamp);
map.put("nonce_str", noncestr);
map.put("product_id", productId);
String sign = paysignMd5(map, weixinAccount.getPaySignKey());
return String.format(NATIVEURLV3, sign, weixinAccount.getAppId(),
weixinAccount.getMchId(), productId, timestamp, noncestr);
}
public static String createNativePayRequestV2(WeixinAccount weixinAccount,
PayPackageV2 payPackage) {
NativePayResponseV2 payRequest = new NativePayResponseV2(weixinAccount,
payPackage);
Map<String, String> map = new HashMap<String, String>();
String timestamp = System.currentTimeMillis() / 1000 + "";
String noncestr = RandomUtil.generateString(16);
map.put("appid", weixinAccount.getAppId());
map.put("timestamp", timestamp);
map.put("noncestr", noncestr);
map.put("package", payRequest.getPackageInfo());
map.put("retcode", payRequest.getRetCode());
map.put("reterrmsg", payRequest.getRetMsg());
payRequest.setPaySign(paysignSha(map, weixinAccount.getPaySignKey()));
return XStream.to(payRequest);
}
/**
* 测试js支付请求
*
* @return
*/
private static void createTestPayJsRequestJson() {
// V2.xAPI支付
PayPackageV2 payPackage = new PayPackageV2("pay_test", "1220403701",
"D123456", 0.01, "http://182.92.74.85:8082/pay/notify",
"192.168.1.1");
WeixinAccount weixinAccount = new WeixinAccount("wx0d1d598c0c03c999",
"2270e6c67cf4ff48fe2c6d7cc5a42157",
"GATFzDwbQdbbci3QEQxX2rUBvwTrsMiZ", "1221966601",
"6b506ef5fefba3142653a9affd2648d8");
System.out.println(PayUtil.createPayJsRequestJsonV2(payPackage,
weixinAccount));
// V3.xJSAPI支付
try {
weixinAccount = new WeixinAccount("wx0d1d598c0c03c999",
"2270e6c67cf4ff48fe2c6d7cc5a42157",
"6b506ef5fefba3142653a9affd2648d8", "10020674",
"oyFLst1bqtuTcxK-ojF8hOGtLQao");
System.out.println(PayUtil.createPayJsRequestJsonV3("测试", "T001",
1d, "192.0.0.1", "http://182.92.74.85:8082/pay/notify",
weixinAccount));
} catch (PayException e) {
e.printStackTrace();
}
// V2.xNative支付
System.out.println(PayUtil.createNativePayRequestV2(weixinAccount,
payPackage));
}
public static void main(String[] args) {
createTestPayJsRequestJson();
}
}
@@ -0,0 +1,96 @@
package com.foxinmy.weixin4j.mp.payment;
import java.io.Serializable;
import com.thoughtworks.xstream.annotations.XStreamAlias;
@XStreamAlias("xml")
public class PayWarn implements Serializable {
private static final long serialVersionUID = 2334592957844332640L;
@XStreamAlias("AppId")
private String appid;
@XStreamAlias("ErrorType")
private String errortype;
@XStreamAlias("Description")
private String description;
@XStreamAlias("AlarmContent")
private String alarmcontent;
@XStreamAlias("TimeStamp")
private String timestamp;
@XStreamAlias("AppSignature")
private String appsignature;
@XStreamAlias("SignMethod")
private String signmethod;
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getErrortype() {
return errortype;
}
public void setErrortype(String errortype) {
this.errortype = errortype;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getAlarmcontent() {
return alarmcontent;
}
public void setAlarmcontent(String alarmcontent) {
this.alarmcontent = alarmcontent;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public String getAppsignature() {
return appsignature;
}
public void setAppsignature(String appsignature) {
this.appsignature = appsignature;
}
public String getSignmethod() {
return signmethod;
}
public void setSignmethod(String signmethod) {
this.signmethod = signmethod;
}
@Override
public String toString() {
return "PayWarn [appid=" + appid + ", errortype=" + errortype
+ ", description=" + description + ", alarmcontent="
+ alarmcontent + ", timestamp=" + timestamp + ", appsignature="
+ appsignature + ", signmethod=" + signmethod + "]";
}
}
@@ -0,0 +1 @@
支付模块【JSP AY】【NATIVE PAY】【APP PAY】
@@ -0,0 +1,117 @@
package com.foxinmy.weixin4j.mp.payment;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.foxinmy.weixin4j.mp.payment.v3.Refund;
import com.foxinmy.weixin4j.mp.payment.v3.RefundDetail;
import com.foxinmy.weixin4j.xml.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;
/**
* 退款查询接口调用结果转换类
* @className RefundConverter
* @author jy
* @date 2014年11月2日
* @since JDK 1.7
* @see
*/
public class RefundConverter {
private final static XStream xStream = XStream.get();
private final ReflectionConverter reflectionConverter;
public RefundConverter() {
xStream.processAnnotations(Refund.class);
xStream.registerConverter(new RefundConverter.$());
reflectionConverter = new ReflectionConverter(xStream.getMapper(),
xStream.getReflectionProvider());
}
public String toXML(Refund refund) {
return xStream.toXML(refund);
}
public Refund fromXML(String xml) {
return xStream.fromXML(xml, Refund.class);
}
private class $ implements Converter {
@Override
public boolean canConvert(@SuppressWarnings("rawtypes") Class clazz) {
return clazz.equals(Refund.class);
}
@Override
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
reflectionConverter.marshal(source, writer, context);
}
@SuppressWarnings("unchecked")
@Override
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Refund refund = new Refund();
Mapper mapper = xStream.getMapper();
ReflectionProvider reflectionProvider = xStream
.getReflectionProvider();
Pattern pattern = Pattern.compile("(_\\d)$");
Matcher matcher = null;
Map<String, Map<String, String>> outMap = new HashMap<String, Map<String, String>>();
while (reader.hasMoreChildren()) {
reader.moveDown();
String nodeName = reader.getNodeName();
String fieldName = mapper.realMember(Refund.class, nodeName);
Field field = reflectionProvider.getFieldOrNull(Refund.class,
fieldName);
if (field != null) {
Object value = context.convertAnother(refund,
field.getType());
reflectionProvider.writeField(refund, fieldName, value,
field.getDeclaringClass());
} else if ((matcher = pattern.matcher(nodeName)).find()) {
String key = matcher.group();
Map<String, String> innerMap = null;
if ((innerMap = outMap.get(key)) == null) {
innerMap = new HashMap<String, String>();
outMap.put(key, innerMap);
}
innerMap.put(nodeName.replace(key, ""), reader.getValue());
}
reader.moveUp();
}
StringBuilder detailXml = new StringBuilder();
detailXml.append("<list>");
String detailCanonicalName = RefundDetail.class.getCanonicalName();
for (Iterator<Entry<String, Map<String, String>>> outIt = outMap
.entrySet().iterator(); outIt.hasNext();) {
detailXml.append("<").append(detailCanonicalName).append(">");
for (Iterator<Entry<String, String>> innerIt = outIt.next()
.getValue().entrySet().iterator(); innerIt.hasNext();) {
Entry<String, String> entry = innerIt.next();
detailXml.append("<").append(entry.getKey()).append(">");
detailXml.append(entry.getValue());
detailXml.append("</").append(entry.getKey()).append(">");
}
detailXml.append("</").append(detailCanonicalName).append(">");
}
detailXml.append("</list>");
xStream.processAnnotations(RefundDetail.class);
refund.setDetails(xStream.fromXML(detailXml.toString(), List.class));
return refund;
}
}
}
@@ -0,0 +1,20 @@
package com.foxinmy.weixin4j.mp.payment;
/**
* 退款状态
*
* @className RefundStatus
* @author jy
* @date 2014年11月2日
* @since JDK 1.7
* @see
*/
public enum RefundStatus {
SUCCES, // 退款成功
FAIL, // 退款失败
PROCESSING, // 退款处理中
NOTSURE, // 未确定,需要商户 原退款单号重新发起
// 转入代发,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,资金回流到商户的现金帐号,需要商户人工干
// 预,通过线下或者财付通转 账的方式进行退款。
CHANGE;
}
@@ -0,0 +1,5 @@
package com.foxinmy.weixin4j.mp.payment;
public enum SignType {
SHA1,MD5
}
@@ -0,0 +1,21 @@
package com.foxinmy.weixin4j.mp.payment;
/**
* 交易状态
*
* @className TradeState
* @author jy
* @date 2014年11月2日
* @since JDK 1.7
* @see
*/
public enum TradeState {
SUCCESS, // 支付成功
REFUND, // 转入退款
NOTPAY, // 未支付
CLOSED, // 已关闭
REVOKED, // 已撤销
USERPAYING, // 用户支付中
NOPAY, // 未支付(输入密码或 确认支付超时)
PAYERROR;// 支付失败(其他 原因,如银行返回失败)
}
@@ -0,0 +1,14 @@
package com.foxinmy.weixin4j.mp.payment;
/**
* 微信支付类型
*
* @className TradeType
* @author jy
* @date 2014年10月21日
* @since JDK 1.7
* @see
*/
public enum TradeType {
JSAPI, MICROPAY, NATIVE, APP;
}
@@ -0,0 +1,64 @@
package com.foxinmy.weixin4j.mp.payment.v2;
import org.apache.commons.codec.digest.DigestUtils;
import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.mp.payment.PayRequest;
import com.foxinmy.weixin4j.util.MapUtil;
/**
* 微信JS支付:get_brand_wcpay_request<br/>
* <font color="red">所列参数均为非空字符串</font>
* <p>
* get_brand_wcpay_request:ok 支付成功<br>
* get_brand_wcpay_request:cancel 支付过程中用户取消<br>
* get_brand_wcpay_request:fail 支付失败
* </p>
*
* @className JsPayRequestV2
* @author jy
* @date 2014年8月17日
* @since JDK 1.7
* @see
*/
public class JsPayRequestV2 extends PayRequest {
private static final long serialVersionUID = -5972173459255255197L;
public JsPayRequestV2(WeixinAccount weixinAccount, PayPackageV2 payPackage) {
this.setAppId(weixinAccount.getAppId());
this.setPackageInfo(package2string(payPackage,
weixinAccount.getPartnerKey()));
}
@JSONField(serialize = false)
private String package2string(PayPackageV2 payPackage, String partnerKey) {
StringBuilder sb = new StringBuilder();
// a.对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序) 后,
// 使用 URL 键值 对的格式(即 key1=value1&key2=value2...)拼接成字符串 string1
// 注意:值为空的参数不参与签名
sb.append(MapUtil.toJoinString(payPackage, false, false, null));
// b--->
// 在 string1 最后拼接上 key=paternerKey 得到 stringSignTemp 字符串,并 对
// stringSignTemp 进行 md5 运算
// 再将得到的 字符串所有字符转换为大写 ,得到 sign 值 signValue。
sb.append("&key=").append(partnerKey);
// c---> & d---->
String sign = DigestUtils.md5Hex(sb.toString()).toUpperCase();
sb.delete(0, sb.length());
// c.对传入参数中所有键值对的 value 进行 urlencode 转码后重新拼接成字符串 string2
sb.append(MapUtil.toJoinString(payPackage, true, false, null))
.append("&sign=").append(sign);
return sb.toString();
}
@Override
public String toString() {
return "JsPayRequest [getAppId()=" + getAppId() + ", getTimeStamp()="
+ getTimeStamp() + ", getNonceStr()=" + getNonceStr()
+ ", getPackageInfo()=" + getPackageInfo() + ", getSignType()="
+ getSignType() + ", getPaySign()=" + getPaySign() + "]";
}
}
@@ -0,0 +1,38 @@
package com.foxinmy.weixin4j.mp.payment.v2;
import com.foxinmy.weixin4j.mp.payment.JsPayNotify;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* Native支付回调时POST的信息
*
* @className PayNativeNotifyV2
* @author jy
* @date 2014年10月28日
* @since JDK 1.7
* @see
*/
public class NativePayNotifyV2 extends JsPayNotify {
private static final long serialVersionUID = 1868431159301749988L;
@XStreamAlias("ProductId")
private String productId;
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
@Override
public String toString() {
return "PayNativeNotifyV2 [productId=" + productId + ", getAppid()="
+ getAppid() + ", getTimestamp()=" + getTimestamp()
+ ", getNoncestr()=" + getNoncestr() + ", getOpenid()="
+ getOpenid() + ", getAppsignature()=" + getAppsignature()
+ ", getIssubscribe()=" + getIssubscribe()
+ ", getSignmethod()=" + getSignmethod() + "]";
}
}
@@ -0,0 +1,55 @@
package com.foxinmy.weixin4j.mp.payment.v2;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* Native支付响应
*
* @className NativePayResponseV2
* @author jy
* @date 2014年10月28日
* @since JDK 1.7
* @see
*/
@XStreamAlias("xml")
public class NativePayResponseV2 extends JsPayRequestV2 {
private static final long serialVersionUID = 6119895998783333012L;
@XStreamAlias("RetCode")
private String retCode;
@XStreamAlias("RetErrMsg")
private String retMsg;
public NativePayResponseV2(WeixinAccount weixinAccount,
PayPackageV2 payPackage) {
super(weixinAccount, payPackage);
this.retCode = "0";
this.retMsg = "OK";
}
public String getRetCode() {
return retCode;
}
public void setRetCode(String retCode) {
this.retCode = retCode;
}
public String getRetMsg() {
return retMsg;
}
public void setRetMsg(String retMsg) {
this.retMsg = retMsg;
}
@Override
public String toString() {
return "NativePayResponseV2 [retCode=" + retCode + ", retMsg=" + retMsg
+ ", getAppId()=" + getAppId() + ", getTimeStamp()="
+ getTimeStamp() + ", getNonceStr()=" + getNonceStr()
+ ", getPackageInfo()=" + getPackageInfo() + ", getSignType()="
+ getSignType() + ", getPaySign()=" + getPaySign() + "]";
}
}
@@ -0,0 +1,245 @@
package com.foxinmy.weixin4j.mp.payment.v2;
import java.util.Map;
import com.foxinmy.weixin4j.http.JsonResult;
/**
* 订单信息
*
* @className Order
* @author jy
* @date 2014年11月2日
* @since JDK 1.7
* @see
*/
public class Order extends JsonResult {
private static final long serialVersionUID = 4543552984506609920L;
// 是查询结果状态码,0 表明成功,其他表明错误;
private String ret_code;
// 是查询结果出错信息;
private String ret_msg;
// 是返回信息中的编码方式;
private String input_charset;
// 是订单状态,0 为成功,其他为失败;
private String trade_state;
// 是交易模式,1 为即时到帐,其他保留;
private String trade_mode;
// 是财付通商户号,即前文的 partnerid;
private String partner;
// 是银行类型;
private String bank_type;
// 是银行订单号;
private String bank_billno;
// 是总金额,单位为分;
private String total_fee;
// 是币种,1 为人民币;
private String fee_type;
// 是财付通订单号;
private String transaction_id;
// 是第三方订单号;
private String out_trade_no;
// 表明是否分账,false 为无分账,true 为有分账;
private boolean is_split;
// 表明是否退款,false 为无退款,ture 为退款;
private boolean is_refund;
// attach 是商户数据包,即生成订单package 时商户填入的 attach;
private String attach;
// 支付完成时间;
private String time_end;
// 物流费用,单位为分;
private String transport_fee;
// 物品费用,单位为分;
private String product_fee;
// 折扣价格,单位为分;
private String discount;
// 换算成人民币之后的总金额,单位为分,一般看 total_fee 即可。
private String rmb_total_fee;
public String getRet_code() {
return ret_code;
}
public void setRet_code(String ret_code) {
this.ret_code = ret_code;
}
public String getRet_msg() {
return ret_msg;
}
public void setRet_msg(String ret_msg) {
this.ret_msg = ret_msg;
}
public String getInput_charset() {
return input_charset;
}
public void setInput_charset(String input_charset) {
this.input_charset = input_charset;
}
public String getTrade_state() {
return trade_state;
}
public void setTrade_state(String trade_state) {
this.trade_state = trade_state;
}
public String getTrade_mode() {
return trade_mode;
}
public void setTrade_mode(String trade_mode) {
this.trade_mode = trade_mode;
}
public String getPartner() {
return partner;
}
public void setPartner(String partner) {
this.partner = partner;
}
public String getBank_type() {
return bank_type;
}
public void setBank_type(String bank_type) {
this.bank_type = bank_type;
}
public String getBank_billno() {
return bank_billno;
}
public void setBank_billno(String bank_billno) {
this.bank_billno = bank_billno;
}
public String getTotal_fee() {
return total_fee;
}
public void setTotal_fee(String total_fee) {
this.total_fee = total_fee;
}
public String getFee_type() {
return fee_type;
}
public void setFee_type(String fee_type) {
this.fee_type = fee_type;
}
public String getTransaction_id() {
return transaction_id;
}
public void setTransaction_id(String transaction_id) {
this.transaction_id = transaction_id;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public boolean isIs_split() {
return is_split;
}
public void setIs_split(boolean is_split) {
this.is_split = is_split;
}
public boolean isIs_refund() {
return is_refund;
}
public void setIs_refund(boolean is_refund) {
this.is_refund = is_refund;
}
public String getAttach() {
return attach;
}
public void setAttach(String attach) {
this.attach = attach;
}
public String getTime_end() {
return time_end;
}
public void setTime_end(String time_end) {
this.time_end = time_end;
}
public String getTransport_fee() {
return transport_fee;
}
public void setTransport_fee(String transport_fee) {
this.transport_fee = transport_fee;
}
public String getProduct_fee() {
return product_fee;
}
public void setProduct_fee(String product_fee) {
this.product_fee = product_fee;
}
public String getDiscount() {
return discount;
}
public void setDiscount(String discount) {
this.discount = discount;
}
public String getRmb_total_fee() {
return rmb_total_fee;
}
public void setRmb_total_fee(String rmb_total_fee) {
this.rmb_total_fee = rmb_total_fee;
}
private Map<String, String> mapData;
public Map<String, String> getMapData() {
return mapData;
}
public void setMapData(Map<String, String> mapData) {
this.mapData = mapData;
}
@Override
public String toString() {
return "Order [ret_code=" + ret_code + ", ret_msg=" + ret_msg
+ ", input_charset=" + input_charset + ", trade_state="
+ trade_state + ", trade_mode=" + trade_mode + ", partner="
+ partner + ", bank_type=" + bank_type + ", bank_billno="
+ bank_billno + ", total_fee=" + total_fee + ", fee_type="
+ fee_type + ", transaction_id=" + transaction_id
+ ", out_trade_no=" + out_trade_no + ", is_split=" + is_split
+ ", is_refund=" + is_refund + ", attach=" + attach
+ ", time_end=" + time_end + ", transport_fee=" + transport_fee
+ ", product_fee=" + product_fee + ", discount=" + discount
+ ", rmb_total_fee=" + rmb_total_fee + "]";
}
}
@@ -0,0 +1,140 @@
package com.foxinmy.weixin4j.mp.payment.v2;
import java.io.Serializable;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 维权POST的数据
*
* @className PayFeedback
* @author jy
* @date 2014年10月29日
* @since JDK 1.7
* @see
*/
@XStreamAlias("xml")
public class PayFeedback implements Serializable {
private static final long serialVersionUID = 7230049346213966310L;
@XStreamAlias("FeedBackId")
private String feedbackId;
@XStreamAlias("OpenId")
private String openId;
@XStreamAlias("TransId")
private String transId;
@XStreamAlias("Reason")
private String reason;
@XStreamAlias("Solution")
private String solution;
@XStreamAlias("ExtInfo")
private String extInfo;
@XStreamAlias("PicInfo")
private String picInfo;
@XStreamAlias("MsgType")
private String status;
@XStreamAlias("AppId")
private String appId;
@XStreamAlias("TimeStamp")
private String timeStamp;
@XStreamAlias("AppSignature")
private String appSignature;
public String getFeedbackId() {
return feedbackId;
}
public void setFeedbackId(String feedbackId) {
this.feedbackId = feedbackId;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public String getTransId() {
return transId;
}
public void setTransId(String transId) {
this.transId = transId;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public String getSolution() {
return solution;
}
public void setSolution(String solution) {
this.solution = solution;
}
public String getExtInfo() {
return extInfo;
}
public void setExtInfo(String extInfo) {
this.extInfo = extInfo;
}
public String getPicInfo() {
return picInfo;
}
public void setPicInfo(String picInfo) {
this.picInfo = picInfo;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(String timeStamp) {
this.timeStamp = timeStamp;
}
public String getAppSignature() {
return appSignature;
}
public void setAppSignature(String appSignature) {
this.appSignature = appSignature;
}
@Override
public String toString() {
return "PayFeedback [feedbackId=" + feedbackId + ", openId=" + openId
+ ", transId=" + transId + ", reason=" + reason + ", solution="
+ solution + ", extInfo=" + extInfo + ", picInfo=" + picInfo
+ ", status=" + status + ", appId=" + appId + ", timeStamp="
+ timeStamp + ", appSignature=" + appSignature + "]";
}
}
@@ -0,0 +1,139 @@
package com.foxinmy.weixin4j.mp.payment.v2;
import java.util.Date;
import org.apache.commons.lang3.StringUtils;
import com.foxinmy.weixin4j.mp.payment.PayPackage;
/**
* 微信支付的订单详情
*
* @className PayPackageV2
* @author jy
* @date 2014年8月17日
* @since JDK 1.7
* @see
*/
public class PayPackageV2 extends PayPackage {
private static final long serialVersionUID = 5557542103637795834L;
// 银行通道类型 固定为"WX" 非空
private String bank_type;
// 商户号 注册时分配的财付通商户号 非空
private String partner;
// 支付币种 默认值是"1" 非空
private String fee_type;
// 物流费用 可为空 如果有值,必须保 证 transport_fee + product_fee=total_fee【传进来的参数按照实际金额即可
// 也就是元为单位】
private String transport_fee;
// 商品费用 可为空 商品费用,单位为分。如果有值,必须保 证 transport_fee +
// product_fee=total_fee;【传进来的参数按照实际金额即可 也就是元为单位】
private String product_fee;
// 传入参数字符编码 取值范围:"GBK"、"UTF-8",默认:"GBK" 可为空
private String input_charset;
private String goods_tag;// 􏱙􏴱􏲹􏺯􏱮􏺰􏺱􏺲􏱫􏱥􏵧􏲛􏳙􏱙􏴱􏲹􏺯􏱮􏺰􏺱􏺲􏱫􏱥􏵧􏲛􏳙􏱙􏴱􏲹􏺯􏱮􏺰􏺱􏺲􏱫􏱥􏵧􏲛􏳙􏱙􏴱􏲹􏺯􏱮􏺰􏺱􏺲􏱫􏱥􏵧􏲛􏳙􏱙􏴱􏲹􏺯􏱮􏺰􏺱􏺲􏱫􏱥􏵧􏲛􏳙商品标记优惠券可能用到
// 可为空
public String getBank_type() {
return bank_type;
}
public void setBank_type(String bank_type) {
this.bank_type = bank_type;
}
public void setBody(String body) {
super.setBody(StringUtils.isBlank(body) ? "服务费用" : body);
}
public String getPartner() {
return partner;
}
public void setPartner(String partner) {
this.partner = partner;
}
public String getFee_type() {
return fee_type;
}
public void setFee_type(String fee_type) {
this.fee_type = fee_type;
}
public void setNotify_url(String notify_url) {
super.setNotify_url(notify_url);
}
public String getTransport_fee() {
return transport_fee;
}
public void setTransport_fee(double transport_fee) {
this.transport_fee = FEE_FORMAT.format(transport_fee);
}
public String getProduct_fee() {
return product_fee;
}
public void setProduct_fee(double product_fee) {
this.product_fee = FEE_FORMAT.format(product_fee);
}
public String getGoods_tag() {
return goods_tag;
}
public void setGoods_tag(String goods_tag) {
this.goods_tag = goods_tag;
}
public String getInput_charset() {
return input_charset;
}
public void setInput_charset(String input_charset) {
this.input_charset = input_charset;
}
public PayPackageV2() {
this.bank_type = "WX";
this.fee_type = "1";
this.input_charset = "UTF-8";
}
public PayPackageV2(String out_trade_no, double total_fee,
String spbill_create_ip) {
this(null, null, null, out_trade_no, total_fee, null, spbill_create_ip,
null, null, 0d, 0d, null);
}
public PayPackageV2(String body, String out_trade_no, double total_fee,
String spbill_create_ip) {
this(body, null, null, out_trade_no, total_fee, null, spbill_create_ip,
null, null, 0d, 0d, null);
}
public PayPackageV2(String body, String partner, String out_trade_no,
double total_fee, String notify_url, String spbill_create_ip) {
this(body, null, partner, out_trade_no, total_fee, notify_url,
spbill_create_ip, null, null, 0d, 0d, null);
}
public PayPackageV2(String body, String attach, String partner,
String out_trade_no, double total_fee, String notify_url,
String spbill_create_ip, Date time_start, Date time_expire,
double transport_fee, double product_fee, String goods_tag) {
super(body, attach, out_trade_no, total_fee, spbill_create_ip,
time_start, time_expire, goods_tag, notify_url);
this.bank_type = "WX";
this.fee_type = "1";
this.input_charset = "UTF-8";
this.transport_fee = transport_fee > 0d ? FEE_FORMAT
.format(transport_fee * 100) : null;
this.product_fee = product_fee > 0 ? FEE_FORMAT
.format(product_fee * 100) : null;
}
@Override
public String toString() {
return "PayPackageV2 [bank_type=" + bank_type + ", partner=" + partner
+ ", fee_type=" + fee_type + ", transport_fee=" + transport_fee
+ ", product_fee=" + product_fee + ", input_charset="
+ input_charset + ", goods_tag=" + goods_tag
+ ", getBank_type()=" + getBank_type() + ", getPartner()="
+ getPartner() + ", getFee_type()=" + getFee_type()
+ ", getTransport_fee()=" + getTransport_fee()
+ ", getProduct_fee()=" + getProduct_fee()
+ ", getGoods_tag()=" + getGoods_tag()
+ ", getInput_charset()=" + getInput_charset() + "]";
}
}
@@ -0,0 +1,52 @@
package com.foxinmy.weixin4j.mp.payment.v3;
import com.foxinmy.weixin4j.mp.payment.ApiResult;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* V3 Native支付回调时POST的信息
*
* @className PayNativeNotifyV3
* @author jy
* @date 2014年10月30日
* @since JDK 1.7
* @see
*/
@XStreamAlias("xml")
public class NativePayNotifyV3 extends ApiResult {
private static final long serialVersionUID = 4515471400239795492L;
@XStreamAlias("mch_id")
private String mchId;
@XStreamAlias("product_id")
private String productId;
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public String getMchId() {
return mchId;
}
public void setMchId(String mchId) {
this.mchId = mchId;
}
@Override
public String toString() {
return "NativePayNotifyV3 [mchId=" + mchId + ", productId=" + productId
+ ", getAppId()=" + getAppId() + ", getNonceStr()="
+ getNonceStr() + ", getSign()=" + getSign()
+ ", getDeviceInfo()=" + getDeviceInfo() + ", toString()="
+ super.toString() + ", getReturnCode()=" + getReturnCode()
+ ", getReturnMsg()=" + getReturnMsg() + ", getResultCode()="
+ getResultCode() + ", getErrCode()=" + getErrCode()
+ ", getErrCodeDes()=" + getErrCodeDes() + "]";
}
}
@@ -0,0 +1,58 @@
package com.foxinmy.weixin4j.mp.payment.v3;
import org.apache.commons.lang3.StringUtils;
import com.foxinmy.weixin4j.exception.PayException;
import com.foxinmy.weixin4j.mp.payment.ApiResult;
import com.foxinmy.weixin4j.mp.payment.PayUtil;
import com.foxinmy.weixin4j.util.RandomUtil;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* Native支付响应
*
* @className NativePayResponseV3
* @author jy
* @date 2014年10月28日
* @since JDK 1.7
* @see
*/
@XStreamAlias("xml")
public class NativePayResponseV3 extends ApiResult {
private static final long serialVersionUID = 6119895998783333012L;
private String prepay_id;
public NativePayResponseV3(PayPackageV3 payPackage, String returnMsg,
String resultMsg) throws PayException {
super.setReturnMsg(returnMsg);
super.setReturnCode(StringUtils.isNotBlank(returnMsg) ? FAIL : SUCCESS);
this.setErrCodeDes(resultMsg);
this.setResultCode(StringUtils.isNotBlank(resultMsg) ? FAIL : SUCCESS);
this.setMchId(payPackage.getMch_id());
this.setAppId(payPackage.getAppid());
this.setNonceStr(RandomUtil.generateString(16));
this.prepay_id = PayUtil.createPrePay(payPackage).getPrepayId();
}
public String getPrepay_id() {
return prepay_id;
}
public void setPrepay_id(String prepay_id) {
this.prepay_id = prepay_id;
}
@Override
public String toString() {
return "NativePayResponseV3 [prepay_id=" + prepay_id + ", getAppId()="
+ getAppId() + ", getMchId()=" + getMchId()
+ ", getNonceStr()=" + getNonceStr() + ", getSign()="
+ getSign() + ", getDeviceInfo()=" + getDeviceInfo()
+ ", toString()=" + super.toString() + ", getReturnCode()="
+ getReturnCode() + ", getReturnMsg()=" + getReturnMsg()
+ ", getResultCode()=" + getResultCode() + ", getErrCode()="
+ getErrCode() + ", getErrCodeDes()=" + getErrCodeDes() + "]";
}
}
@@ -0,0 +1,172 @@
package com.foxinmy.weixin4j.mp.payment.v3;
import com.foxinmy.weixin4j.mp.payment.ApiResult;
import com.foxinmy.weixin4j.mp.payment.CurrencyType;
import com.foxinmy.weixin4j.mp.payment.TradeState;
import com.foxinmy.weixin4j.mp.payment.TradeType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 订单信息
*
* @className Order
* @author jy
* @date 2014年11月2日
* @since JDK 1.7
* @see
*/
@XStreamAlias("xml")
public class Order extends ApiResult {
private static final long serialVersionUID = 5636828325595317079L;
// SUCCESS—支付成功 REFUND—转入退款 NOTPAY—未支付 CLOSED—已关闭 REVOKED—已撤销
// USERPAYING--用户支付中 NOPAY--未支付(输入密码或 确认支付超时) PAYERROR--支付失败(其他 原因,如银行返回失败)
// 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
@XStreamAlias("trade_state")
private TradeState tradeState;
// 用户标识ID
@XStreamAlias("openid")
private String openId;
// 用户是否关注公众账号,Y- 关注,N-未关注,仅在公众 账号类型支付有效
private String isSubscribe;
// 交易类型
@XStreamAlias("trade_type")
private TradeType tradeType;
// 银行类型
@XStreamAlias("bank_type")
private String bankType;
// 订单总金额,单位为分
@XStreamAlias("total_fee")
private int totalFee;
// 现金券支付金额<=订单总金 额,订单总金额-现金券金额 为现金支付金额
@XStreamAlias("coupon_fee")
private int couponFee;
// 货币类型,符合 ISO 4217 标准的三位字母代码,默认人民币:CNY
@XStreamAlias("fee_type")
private CurrencyType feeType;
// 微信支付订单号
@XStreamAlias("transaction_id")
private String transactionId;
// 商户订单号
@XStreamAlias("out_rade_no")
private String outTradeNo;
// 商家数据包
@XStreamAlias("attach")
private String attach;
// 支付完成时间,格式为 yyyyMMddhhmmss
@XStreamAlias("time_end")
private String timeEnd;
public TradeState getTradeState() {
return tradeState;
}
public void setTradeState(TradeState tradeState) {
this.tradeState = tradeState;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public String getIsSubscribe() {
return isSubscribe;
}
public void setIsSubscribe(String isSubscribe) {
this.isSubscribe = isSubscribe;
}
public TradeType getTradeType() {
return tradeType;
}
public void setTradeType(TradeType tradeType) {
this.tradeType = tradeType;
}
public String getBankType() {
return bankType;
}
public void setBankType(String bankType) {
this.bankType = bankType;
}
public int getTotalFee() {
return totalFee;
}
public void setTotalFee(int totalFee) {
this.totalFee = totalFee;
}
public int getCouponFee() {
return couponFee;
}
public void setCouponFee(int couponFee) {
this.couponFee = couponFee;
}
public CurrencyType getFeeType() {
return feeType;
}
public void setFeeType(CurrencyType feeType) {
this.feeType = feeType;
}
public String getTransactionId() {
return transactionId;
}
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
public String getOutTradeNo() {
return outTradeNo;
}
public void setOutTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
}
public String getAttach() {
return attach;
}
public void setAttach(String attach) {
this.attach = attach;
}
public String getTimeEnd() {
return timeEnd;
}
public void setTimeEnd(String timeEnd) {
this.timeEnd = timeEnd;
}
@Override
public String toString() {
return "Order [tradeState=" + tradeState + ", openId=" + openId
+ ", isSubscribe=" + isSubscribe + ", tradeType=" + tradeType
+ ", bankType=" + bankType + ", totalFee=" + totalFee
+ ", couponFee=" + couponFee + ", feeType=" + feeType
+ ", transactionId=" + transactionId + ", outTradeNo="
+ outTradeNo + ", attach=" + attach + ", timeEnd=" + timeEnd
+ ", getAppId()=" + getAppId() + ", getMchId()=" + getMchId()
+ ", getNonceStr()=" + getNonceStr() + ", getSign()="
+ getSign() + ", getDeviceInfo()=" + getDeviceInfo()
+ ", toString()=" + super.toString() + ", getReturnCode()="
+ getReturnCode() + ", getReturnMsg()=" + getReturnMsg()
+ ", getResultCode()=" + getResultCode() + ", getErrCode()="
+ getErrCode() + ", getErrCodeDes()=" + getErrCodeDes() + "]";
}
}
@@ -0,0 +1,161 @@
package com.foxinmy.weixin4j.mp.payment.v3;
import java.util.Date;
import org.apache.commons.lang3.StringUtils;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.mp.payment.PayPackage;
import com.foxinmy.weixin4j.mp.payment.TradeType;
import com.foxinmy.weixin4j.util.RandomUtil;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 微信支付V3<br/>
* 注意:
* <font color="red">total_fee字段传入时单位为元,创建支付时会转换为分</font>
* @className PayPackageV3
* @author jy
* @date 2014年10月21日
* @since JDK 1.7
* @see
*/
@XStreamAlias("xml")
public class PayPackageV3 extends PayPackage {
private static final long serialVersionUID = 8944928173669656177L;
private String appid; // 微信分配的公众账号 必须
private String mch_id; // 微信支付分配的商户号 必须
private String device_info; // 微信支付分配的终端设备号 非必须
private String nonce_str; // 随机字符串,不长于 32 位 必须
private String sign; // 签名 必须
private String trade_type; // 交易类型JSAPI、NATIVE、APP 必须
private String openid; // 用户在商户 appid 下的唯一 标识, trade_type 为 JSAPI 时,此参数必传
private String product_id; // 只在 trade_type 为 NATIVE 时需要填写 非必须
public PayPackageV3() {
}
public PayPackageV3(WeixinAccount weixinAccount, String body,
String out_trade_no, double total_fee, String spbill_create_ip,
TradeType tradeType) {
this(weixinAccount.getAppId(), weixinAccount.getMchId(), null,
RandomUtil.generateString(16), body, null, out_trade_no,
total_fee, spbill_create_ip, null, null, null, null, tradeType,
weixinAccount.getOpenId(), null, weixinAccount.getPaySignKey());
}
public PayPackageV3(WeixinAccount weixinAccount, String body,
String attach, String out_trade_no, double total_fee,
String spbill_create_ip, String notify_url, TradeType tradeType) {
this(weixinAccount.getAppId(), weixinAccount.getMchId(), null, RandomUtil
.generateString(16), body, attach, out_trade_no, total_fee,
spbill_create_ip, null, null, null, notify_url, tradeType,
weixinAccount.getOpenId(), null, weixinAccount.getPaySignKey());
}
public PayPackageV3(String appid, String mch_id, String device_info,
String nonce_str, String body, String attach, String out_trade_no,
double total_fee, String spbill_create_ip, Date time_start,
Date time_expire, String goods_tag, String notify_url,
TradeType tradeType, String openid, String product_id,
String paySignKey) {
super(body, attach, out_trade_no, total_fee, spbill_create_ip,
time_start, time_expire, goods_tag, notify_url);
this.appid = appid;
this.mch_id = mch_id;
this.device_info = device_info;
this.nonce_str = nonce_str;
this.trade_type = tradeType.name();
this.openid = openid;
this.product_id = product_id;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getDevice_info() {
return device_info;
}
public void setDevice_info(String device_info) {
this.device_info = device_info;
}
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public void setBody(String body) {
super.setBody(StringUtils.isBlank(body) ? "服务费用" : body);
}
public void setNotify_url(String notify_url) {
super.setNotify_url(notify_url);
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(TradeType tradeType) {
this.trade_type = tradeType.name();
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getProduct_id() {
return product_id;
}
public void setProduct_id(String product_id) {
this.product_id = product_id;
}
@Override
public String toString() {
return "PayPackageV3 [appid=" + appid + ", mch_id=" + mch_id
+ ", device_info=" + device_info + ", nonce_str=" + nonce_str
+ ", sign=" + sign + ", trade_type=" + trade_type + ", openid="
+ openid + ", product_id=" + product_id + ", getAppid()="
+ getAppid() + ", getMch_id()=" + getMch_id()
+ ", getDevice_info()=" + getDevice_info()
+ ", getNonce_str()=" + getNonce_str() + ", getSign()="
+ getSign() + ", getTrade_type()=" + getTrade_type()
+ ", getOpenid()=" + getOpenid() + ", getProduct_id()="
+ getProduct_id() + "]";
}
}
@@ -0,0 +1,62 @@
package com.foxinmy.weixin4j.mp.payment.v3;
import java.beans.Transient;
import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.exception.PayException;
import com.foxinmy.weixin4j.http.XmlResult;
import com.foxinmy.weixin4j.mp.payment.PayRequest;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
/**
* JS支付:get_brand_wcpay_request<br/>
* <p>
* get_brand_wcpay_request:ok 支付成功<br>
* get_brand_wcpay_request:cancel 支付过程中用户取消<br>
* get_brand_wcpay_request:fail 支付失败
* </p>
* <p>
* NATIVE支付:PayRequest.TradeType=NATIVE
* </p>
*
* @className PayRequestV3
* @author jy
* @date 2014年8月17日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.mp.payment.v3.PayRequestV3.PrePay
*/
public class PayRequestV3 extends PayRequest {
private static final long serialVersionUID = -5972173459255255197L;
@XStreamOmitField
private PrePay prePay;
public PayRequestV3(PrePay prePay) throws PayException {
if (!prePay.getReturnCode().equalsIgnoreCase(XmlResult.SUCCESS)) {
throw new PayException(prePay.getReturnMsg(),
prePay.getReturnCode());
}
if (!prePay.getResultCode().equalsIgnoreCase(XmlResult.SUCCESS)) {
throw new PayException(prePay.getResultCode(),
prePay.getErrCodeDes());
}
this.prePay = prePay;
this.setAppId(prePay.getAppId());
this.setPackageInfo("prepay_id=" + prePay.getPrepayId());
}
@Transient
@JSONField(serialize = false)
public PrePay getPrePay() {
return prePay;
}
@Override
public String toString() {
return "PayRequestV3 [getAppId()=" + getAppId() + ", getTimeStamp()="
+ getTimeStamp() + ", getNonceStr()=" + getNonceStr()
+ ", getPackageInfo()=" + getPackageInfo() + ", getSignType()="
+ getSignType() + "]";
}
}
@@ -0,0 +1,73 @@
package com.foxinmy.weixin4j.mp.payment.v3;
import com.foxinmy.weixin4j.mp.payment.ApiResult;
import com.foxinmy.weixin4j.mp.payment.TradeType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 预生成订单信息
*
* @className PrePay
* @author jy
* @date 2014年10月21日
* @since JDK 1.7
* @see
*/
@XStreamAlias("xml")
public class PrePay extends ApiResult {
private static final long serialVersionUID = -8430005768959715444L;
@XStreamAlias("trade_type")
private TradeType tradeType;// 交易类型JSAPI、NATIVE、APP 非空
@XStreamAlias("prepay_id")
private String prepayId;// 微信生成的预支付 ID,用于后续接口调用中使用二维码链接 非空
@XStreamAlias("code_url")
private String codeUrl;// trade_type 为 NATIVE 是有 返回,此参数可直接生成二 维码展示出来进行扫码支付
// 可能为空
public PrePay() {
}
public PrePay(String returnCode, String returnMsg) {
super(returnCode, returnMsg);
}
public TradeType getTradeType() {
return tradeType;
}
public void setTradeType(TradeType tradeType) {
this.tradeType = tradeType;
}
public String getPrepayId() {
return prepayId;
}
public void setPrepayId(String prepayId) {
this.prepayId = prepayId;
}
public String getCodeUrl() {
return codeUrl;
}
public void setCodeUrl(String codeUrl) {
this.codeUrl = codeUrl;
}
@Override
public String toString() {
return "PrePay [tradeType=" + tradeType + ", prepayId=" + prepayId
+ ", codeUrl=" + codeUrl + ", getAppId()=" + getAppId()
+ ", getMchId()=" + getMchId() + ", getNonceStr()="
+ getNonceStr() + ", getSign()=" + getSign()
+ ", getDeviceInfo()=" + getDeviceInfo() + ", toString()="
+ super.toString() + ", getReturnCode()=" + getReturnCode()
+ ", getReturnMsg()=" + getReturnMsg() + ", getResultCode()="
+ getResultCode() + ", getErrCode()=" + getErrCode()
+ ", getErrCodeDes()=" + getErrCodeDes() + "]";
}
}
@@ -0,0 +1,87 @@
package com.foxinmy.weixin4j.mp.payment.v3;
import java.util.List;
import com.foxinmy.weixin4j.mp.payment.ApiResult;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
/**
* 退款记录
*
* @className Refund
* @author jy
* @date 2014年11月1日
* @since JDK 1.7
* @see
*/
@XStreamAlias("xml")
public class Refund extends ApiResult {
private static final long serialVersionUID = -2971132874939642721L;
@XStreamAlias("transaction_id")
private String transactionId;// 微信订单号
@XStreamAlias("out_trade_no")
private String orderNo;// 商户订单号
@XStreamAlias("sub_mch_id")
private String subMchId; //
@XStreamAlias("refund_count")
private int count;// 退款笔数
@XStreamOmitField
private List<RefundDetail> details;
public String getTransactionId() {
return transactionId;
}
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public String getSubMchId() {
return subMchId;
}
public void setSubMchId(String subMchId) {
this.subMchId = subMchId;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public List<RefundDetail> getDetails() {
return details;
}
public void setDetails(List<RefundDetail> details) {
this.details = details;
}
@Override
public String toString() {
return "Refund [transactionId=" + transactionId + ", subMchId="
+ subMchId + ", orderNo=" + orderNo + ", count=" + count
+ ", details=" + details + ", getAppId()=" + getAppId()
+ ", getMchId()=" + getMchId() + ", getNonceStr()="
+ getNonceStr() + ", getSign()=" + getSign()
+ ", getDeviceInfo()=" + getDeviceInfo() + ", toString()="
+ super.toString() + ", getReturnCode()=" + getReturnCode()
+ ", getReturnMsg()=" + getReturnMsg() + ", getResultCode()="
+ getResultCode() + ", getErrCode()=" + getErrCode()
+ ", getErrCodeDes()=" + getErrCodeDes() + "]";
}
}
@@ -0,0 +1,89 @@
package com.foxinmy.weixin4j.mp.payment.v3;
import java.io.Serializable;
import com.foxinmy.weixin4j.mp.payment.RefundStatus;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 退款详细
*
* @className RefundDetail
* @author jy
* @date 2014年11月2日
* @since JDK 1.7
* @see
*/
public class RefundDetail implements Serializable {
private static final long serialVersionUID = 2828640496307351988L;
@XStreamAlias("out_refund_no")
private String outRefundNo; // 商户退款单号
@XStreamAlias("refund_id")
private String refundId; // 微信退款单号
@XStreamAlias("refund_channel")
private String refundChannel; // 退款渠道 ORIGINAL—原路退款 BALANCE—退回到余额
@XStreamAlias("refund_fee")
private int refundFee; // 退款总金额,单位为分,可以做部分退款
@XStreamAlias("coupon_refund_fee")
private int couponRefundFee; // 现金券退款金额<=退款金额,退款金额-现金券退款金额为现金
@XStreamAlias("refund_status")
private RefundStatus refundStatus; // 退款状态
public String getOutRefundNo() {
return outRefundNo;
}
public void setOutRefundNo(String outRefundNo) {
this.outRefundNo = outRefundNo;
}
public String getRefundId() {
return refundId;
}
public void setRefundId(String refundId) {
this.refundId = refundId;
}
public String getRefundChannel() {
return refundChannel;
}
public void setRefundChannel(String refundChannel) {
this.refundChannel = refundChannel;
}
public int getRefundFee() {
return refundFee;
}
public void setRefundFee(int refundFee) {
this.refundFee = refundFee;
}
public int getCouponRefundFee() {
return couponRefundFee;
}
public void setCouponRefundFee(int couponRefundFee) {
this.couponRefundFee = couponRefundFee;
}
public RefundStatus getRefundStatus() {
return refundStatus;
}
public void setRefundStatus(RefundStatus refundStatus) {
this.refundStatus = refundStatus;
}
@Override
public String toString() {
return "RefundDetail [outRefundNo=" + outRefundNo + ", refundId="
+ refundId + ", refundChannel=" + refundChannel
+ ", refundFee=" + refundFee + ", couponRefundFee="
+ couponRefundFee + ", refundStatus=" + refundStatus + "]";
}
}
@@ -0,0 +1,103 @@
package com.foxinmy.weixin4j.mp.response;
import java.util.LinkedList;
import com.foxinmy.weixin4j.mp.msg.model.Article;
import com.foxinmy.weixin4j.mp.type.ResponseType;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 回复图文消息
*
* @className ArticleResponse
* @author jy.hu
* @date 2014年3月23日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E8%A2%AB%E5%8A%A8%E5%93%8D%E5%BA%94%E6%B6%88%E6%81%AF#.E5.9B.9E.E5.A4.8D.E5.9B.BE.E6.96.87.E6.B6.88.E6.81.AF">回复图文消息</a>
* @see com.foxinmy.weixin.msg.model.Article
* @see com.foxinmy.weixin4j.mp.response.BaseResponse
* @see com.foxinmy.weixin4j.mp.response.BaseResponse#toXml()
*/
@XStreamAlias("xml")
public class ArticleResponse extends BaseResponse {
private static final int MAX_ARTICLE_COUNT = 10;
private static final long serialVersionUID = -7331603018352309317L;
public ArticleResponse(BaseMessage inMessage) {
super(ResponseType.news, inMessage);
}
@XStreamAlias("ArticleCount")
private int count; // 图文消息个数,限制为10条以内
@XStreamAlias("Articles")
private LinkedList<Article> articles;
public void pushArticle(String title, String desc, String picUrl, String url) {
if (this.articles == null) {
this.articles = new LinkedList<Article>();
}
if ((articles.size() + 1) > MAX_ARTICLE_COUNT) {
return;
}
this.articles.add(new Article(title, desc, picUrl, url));
}
public void pushFirstArticle(String title, String desc, String picUrl,
String url) {
if (this.articles == null) {
this.articles = new LinkedList<Article>();
}
this.articles.addFirst(new Article(title, desc, picUrl, url));
}
public void pushLastArticle(String title, String desc, String picUrl,
String url) {
if (this.articles == null) {
this.articles = new LinkedList<Article>();
}
this.articles.addLast(new Article(title, desc, picUrl, url));
}
public Article removeLastArticle() {
Article article = null;
if (this.articles != null) {
article = this.articles.removeLast();
}
return article;
}
public Article removeFirstArticle() {
if (this.articles != null) {
return this.articles.removeFirst();
}
return null;
}
public boolean isMaxCount() {
return this.articles.size() == MAX_ARTICLE_COUNT;
}
@Override
public String toXml() {
this.count = articles.size();
xmlStream.alias("item", Article.class);
xmlStream.aliasField("Title", Article.class, "title");
xmlStream.aliasField("Description", Article.class, "desc");
xmlStream.aliasField("PicUrl", Article.class, "picUrl");
xmlStream.aliasField("Url", Article.class, "url");
return xmlStream.toXML(this);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[BaseResponse ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,articles=").append(this.articles.toString());
sb.append(" ,createTime=").append(super.getCreateTime()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,122 @@
package com.foxinmy.weixin4j.mp.response;
import java.io.Serializable;
import java.io.Writer;
import com.foxinmy.weixin4j.mp.type.ResponseType;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.foxinmy.weixin4j.util.ClassUtil;
import com.foxinmy.weixin4j.xml.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;
import com.thoughtworks.xstream.io.json.JsonWriter;
/**
* 响应消息基类
* <p>
* <font color="red">回复图片等多媒体消息时需要预先上传多媒体文件到微信服务器,
* 假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试</font>
* </p>
*
* @className BaseResponse
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
*/
public class BaseResponse implements Serializable {
private static final long serialVersionUID = 7761192742840031607L;
protected final static XStream xmlStream = XStream.get();
private final static XStream jsonStream = new XStream(
new JsonHierarchicalStreamDriver() {
public HierarchicalStreamWriter createWriter(Writer writer) {
return new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE);
}
});
@XStreamAlias("ToUserName")
private String toUserName; // 开发者微信号
@XStreamAlias("FromUserName")
private String fromUserName; // 发送方帐号(一个OpenID
@XStreamAlias("CreateTime")
private long createTime = System.currentTimeMillis(); // 消息创建时间 (整型)
@XStreamAlias("MsgType")
private ResponseType msgType; // 消息类型
static {
Class<?>[] classes = ClassUtil.getClasses(
BaseResponse.class.getPackage()).toArray(new Class[0]);
xmlStream.processAnnotations(classes);
jsonStream.setMode(XStream.NO_REFERENCES);
jsonStream.autodetectAnnotations(true);
jsonStream.processAnnotations(classes);
}
public BaseResponse(ResponseType msgType) {
this.msgType = msgType;
}
public BaseResponse(ResponseType msgType, BaseMessage inMessage) {
this(msgType, inMessage.getFromUserName(), inMessage.getToUserName());
}
public BaseResponse(ResponseType msgType, String toUserName,
String fromUserName) {
this.msgType = msgType;
this.toUserName = toUserName;
this.fromUserName = fromUserName;
}
public String getToUserName() {
return toUserName;
}
public void setToUserName(String toUserName) {
this.toUserName = toUserName;
}
public String getFromUserName() {
return fromUserName;
}
public void setFromUserName(String fromUserName) {
this.fromUserName = fromUserName;
}
public long getCreateTime() {
return createTime;
}
public void setCreateTime(long createTime) {
this.createTime = createTime;
}
public ResponseType getMsgType() {
return msgType;
}
public void setMsgType(ResponseType msgType) {
this.msgType = msgType;
}
/**
* 消息对象转换为微信服务器接受的xml格式消息
*
* @return xml字符串
*/
public String toXml() {
return xmlStream.toXML(this);
}
/**
* 消息对象转换为微信服务器接受的json格式字符串
*
* @return json字符串
*/
public String toJson() {
return jsonStream.toXML(this);
}
}
@@ -0,0 +1,59 @@
package com.foxinmy.weixin4j.mp.response;
import com.foxinmy.weixin4j.mp.msg.model.Image;
import com.foxinmy.weixin4j.mp.type.ResponseType;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 回复图片消息
*
* @className ImageResponse
* @author jy.hu
* @date 2014年3月23日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E8%A2%AB%E5%8A%A8%E5%93%8D%E5%BA%94%E6%B6%88%E6%81%AF#.E5.9B.9E.E5.A4.8D.E5.9B.BE.E7.89.87.E6.B6.88.E6.81.AF">回复图片消息</a>
* @see com.foxinmy.weixin4j.mp.msg.model.Image
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.mp.response.BaseResponse
* @see com.foxinmy.weixin4j.mp.response.BaseResponse#toXml()
*/
@XStreamAlias("xml")
public class ImageResponse extends BaseResponse {
private static final long serialVersionUID = 6998255203997554731L;
public ImageResponse(BaseMessage inMessage) {
this(null, inMessage);
}
public ImageResponse(String mediaId, BaseMessage inMessage) {
super(ResponseType.image, inMessage);
super.getMsgType().setMessageClass(ImageResponse.class);
this.pushMediaId(mediaId);
}
@XStreamAlias("Image")
private Image image;
public void pushMediaId(String mediaId) {
this.image = new Image(mediaId);
}
@Override
public String toXml() {
xmlStream.aliasField("MediaId", Image.class, "mediaId");
return xmlStream.toXML(this);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[ImageResponse ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,mediaId=").append(this.image.getMediaId());
sb.append(" ,createTime=").append(super.getCreateTime()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,61 @@
package com.foxinmy.weixin4j.mp.response;
import com.foxinmy.weixin4j.mp.msg.model.Music;
import com.foxinmy.weixin4j.mp.type.ResponseType;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 回复音乐消息
*
* @className MusicResponse
* @author jy.hu
* @date 2014年3月23日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E8%A2%AB%E5%8A%A8%E5%93%8D%E5%BA%94%E6%B6%88%E6%81%AF#.E5.9B.9E.E5.A4.8D.E9.9F.B3.E4.B9.90.E6.B6.88.E6.81.AF">回复音乐消息</a>
* @see com.foxinmy.weixin4j.mp.msg.model.Music
* @see com.foxinmy.weixin4j.mp.response.BaseResponse
* @see com.foxinmy.weixin4j.mp.response.BaseResponse#toXml()
*/
@XStreamAlias("xml")
public class MusicResponse extends BaseResponse {
private static final long serialVersionUID = 4384403772658796395L;
public MusicResponse(BaseMessage inMessage) {
super(ResponseType.music, inMessage);
}
@XStreamAlias("Music")
private Music music;
public void pushMusic(String mediaId) {
this.music = new Music(mediaId);
}
public void setMusic(Music music) {
this.music = music;
}
@Override
public String toXml() {
xmlStream.aliasField("MediaId", Music.class, "musicUrl");
xmlStream.aliasField("Title", Music.class, "title");
xmlStream.aliasField("Description", Music.class, "desc");
xmlStream.aliasField("HQMusicUrl", Music.class, "hqMusicUrl");
xmlStream.aliasField("ThumbMediaId", Music.class, "thumbMediaId");
return xmlStream.toXML(this);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[MusicResponse ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,music=").append(music.toString());
sb.append(" ,createTime=").append(super.getCreateTime()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,122 @@
package com.foxinmy.weixin4j.mp.response;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 模板消息
*
* @className TemplateMessage
* @author jy
* @date 2014年9月29日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%A8%A1%E6%9D%BF%E6%B6%88%E6%81%AF%E6%8E%A5%E5%8F%A3">模板消息</a>
*/
public class TemplateMessage implements Serializable {
private static final long serialVersionUID = 7950608393821661436L;
private String touser;
private String template_id;
private String url;
private String topcolor = "#FF0000";
private Map<String, Item> data;
public void pushData(String key, String value) {
this.data.put(key, new Item(value));
}
public TemplateMessage(String touser, String template_id, String title,
String url) {
this.touser = touser;
this.template_id = template_id;
this.url = url;
this.data = new HashMap<String, Item>();
pushData("first", title);
}
private static class Item implements Serializable {
private static final long serialVersionUID = 1L;
private String value;
private String color;
public Item(String value) {
this(value, "#173177");
}
public Item(String value, String color) {
this.value = value;
this.color = color;
}
public String getValue() {
return value;
}
public String getColor() {
return color;
}
@Override
public String toString() {
return "$ [value=" + getValue() + ", color=" + getColor() + "]";
}
}
public String getTouser() {
return touser;
}
public void setTouser(String touser) {
this.touser = touser;
}
public String getTemplate_id() {
return template_id;
}
public void setTemplate_id(String template_id) {
this.template_id = template_id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getTopcolor() {
return topcolor;
}
public void setTopcolor(String topcolor) {
this.topcolor = topcolor;
}
public Map<String, Item> getData() {
return data;
}
public void setData(Map<String, Item> data) {
this.data = data;
}
@Override
public String toString() {
return "TemplateMessage [touser=" + touser + ", template_id="
+ template_id + ", url=" + url + ", topcolor=" + topcolor
+ ", data=" + data + "]";
}
@JSONField(serialize = false)
public String toJson() {
return JSON.toJSONString(this);
}
}
@@ -0,0 +1,57 @@
package com.foxinmy.weixin4j.mp.response;
import com.foxinmy.weixin4j.mp.type.ResponseType;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 回复文本消息
*
* @className TextResponse
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%99%AE%E9%80%9A%E6%B6%88%E6%81%AF#.E6.96.87.E6.9C.AC.E6.B6.88.E6.81.AF">接收文本消息</a>
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E8%A2%AB%E5%8A%A8%E5%93%8D%E5%BA%94%E6%B6%88%E6%81%AF#.E5.9B.9E.E5.A4.8D.E6.96.87.E6.9C.AC.E6.B6.88.E6.81.AF">回复文本消息</a>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.mp.response.BaseResponse
* @see com.foxinmy.weixin4j.mp.response.BaseResponse#toXml()
*/
@XStreamAlias("xml")
public class TextResponse extends BaseResponse {
private static final long serialVersionUID = -7018053906644190260L;
public TextResponse() {
super(ResponseType.text);
}
public TextResponse(String content, BaseMessage inMessage) {
super(ResponseType.text, inMessage);
this.content = content;
}
@XStreamAlias("Content")
private String content; // 消息内容
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[TextResponse ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,content=").append(content);
sb.append(" ,createTime=").append(super.getCreateTime()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,27 @@
package com.foxinmy.weixin4j.mp.response;
import com.foxinmy.weixin4j.mp.type.ResponseType;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 转移消息到多客服端消息
*
* @className TransferResponse
* @author jy.hu
* @date 2014年6月28日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%B0%86%E6%B6%88%E6%81%AF%E8%BD%AC%E5%8F%91%E5%88%B0%E5%A4%9A%E5%AE%A2%E6%9C%8D">多客服转移消息</a>
* @see com.foxinmy.weixin4j.mp.response.BaseResponse
* @see com.foxinmy.weixin4j.mp.response.BaseResponse#toXml()
*/
@XStreamAlias("xml")
public class TransferResponse extends BaseResponse {
private static final long serialVersionUID = -5479496746108594940L;
public TransferResponse(BaseMessage inMessage) {
super(ResponseType.transfer_customer_service, inMessage);
}
}
@@ -0,0 +1,61 @@
package com.foxinmy.weixin4j.mp.response;
import com.foxinmy.weixin4j.mp.msg.model.Video;
import com.foxinmy.weixin4j.mp.type.ResponseType;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 回复视频消息
*
* @className VideoResponse
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E8%A2%AB%E5%8A%A8%E5%93%8D%E5%BA%94%E6%B6%88%E6%81%AF#.E5.9B.9E.E5.A4.8D.E8.A7.86.E9.A2.91.E6.B6.88.E6.81.AF">回复视频消息</a>
* @see com.foxinmy.weixin4j.mp.msg.model.Video
* @see com.foxinmy.weixin4j.mp.response.BaseResponse
* @see com.foxinmy.weixin4j.mp.response.BaseResponse#toXml()
*/
@XStreamAlias("xml")
public class VideoResponse extends BaseResponse {
private static final long serialVersionUID = -1013075358679078381L;
public VideoResponse(BaseMessage inMessage) {
super(ResponseType.video, inMessage);
super.getMsgType().setMessageClass(VideoResponse.class);
}
@XStreamAlias("Video")
private Video video;
public void pushVideo(String mediaId) {
this.video = new Video(mediaId);
}
public void setVideo(Video video) {
this.video = video;
}
@Override
public String toXml() {
xmlStream.aliasField("MediaId", Video.class, "mediaId");
xmlStream.aliasField("Title", Video.class, "title");
xmlStream.aliasField("Description", Video.class, "desc");
xmlStream.omitField(Video.class, "thumbMediaId");
return xmlStream.toXML(this);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[VideoResponse ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,video=").append(video.toString());
sb.append(" ,createTime=").append(super.getCreateTime()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,59 @@
package com.foxinmy.weixin4j.mp.response;
import com.foxinmy.weixin4j.mp.msg.model.Voice;
import com.foxinmy.weixin4j.mp.type.ResponseType;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 回复语音消息
*
* @className VoiceResponse
* @author jy.hu
* @date 2014年3月23日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E8%A2%AB%E5%8A%A8%E5%93%8D%E5%BA%94%E6%B6%88%E6%81%AF#.E5.9B.9E.E5.A4.8D.E8.AF.AD.E9.9F.B3.E6.B6.88.E6.81.AF">回复语音消息</a>
* @see com.foxinmy.weixin4j.mp.msg.model.Voice
* @see com.foxinmy.weixin4j.mp.response.BaseResponse
* @see com.foxinmy.weixin4j.mp.response.BaseResponse#toXml()
*/
@XStreamAlias("xml")
public class VoiceResponse extends BaseResponse {
private static final long serialVersionUID = -7944926238652243793L;
public VoiceResponse(BaseMessage inMessage) {
this(null, inMessage);
}
public VoiceResponse(String mediaId, BaseMessage inMessage) {
super(ResponseType.voice, inMessage);
super.getMsgType().setMessageClass(VoiceResponse.class);
this.pushMediaId(mediaId);
}
@XStreamAlias("Voice")
private Voice voice;
public void pushMediaId(String mediaId) {
this.voice = new Voice(mediaId);
}
@Override
public String toXml() {
xmlStream.aliasField("MediaId", Voice.class, "mediaId");
return xmlStream.toXML(this);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[VoiceResponse ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,mediaId=").append(voice.getMediaId());
sb.append(" ,createTime=").append(super.getCreateTime()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,3 @@
模拟微信公众平台登陆
(模拟登录|启用开发者模式|修改服务器配置|修改回调地址|创建自定义菜单....more)
@@ -0,0 +1,670 @@
package com.foxinmy.weixin4j.mp.spider;
import java.io.Serializable;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.util.IOUtil;
import com.foxinmy.weixin4j.util.RandomUtil;
/**
* 模拟微信WEB登陆
*
* <p>
* (模拟登录|启用开发者模式|修改服务器配置|修改回调地址|创建自定义菜单....more)
* </p>
*
* @className WeixinExecutor
* @author jy
* @date 2014年8月15日
* @since JDK 1.7
* @see
*/
public class WeixinExecutor implements Serializable {
private static final long serialVersionUID = 4253859892138066462L;
private final Logger logger = LoggerFactory.getLogger(getClass());
private final static Charset charset = StandardCharsets.UTF_8;
private final static Map<String, String> accountMap = new HashMap<String, String>() {
private static final long serialVersionUID = 1L;
{
put("名称", "name");
put("头像", "avatar");
put("登录邮箱", "loginEmail");
put("原始ID", "originalId");
put("微信号", "weixinNo");
put("类型", "accountType");
put("认证情况", "weixinVerify");
put("主体信息", "bodyInfo");
put("介绍", "introduce");
put("所在地址", "address");
put("二维码", "qrcodeUrl");
}
};
private AbstractHttpClient client;
private HttpHost host;
private JSONObject weixin;
// 服务器响应地址
private String pushurl;
// oauth授权回调地址
private String backurl;
// 服务器校验token
private String token;
// 公众号用户名
private String uname;
// 公众号密码
private String pwd;
// 登录时验证码(如果有)
private String imgcode;
// 当要求输入验证码时,cookie需带上
private String sig;
public WeixinExecutor(String backurl, String pushurl, String token,
String uname, String pwd, String imgcode, String sig) {
this.backurl = backurl;
this.pushurl = pushurl;
this.token = token;
this.uname = uname;
this.pwd = pwd;
this.imgcode = StringUtils.isBlank(imgcode) ? "" : imgcode;
this.sig = sig;
weixin = new JSONObject();
weixin.put("host", "mp.weixin.qq.com");
weixin.put("base", "https://mp.weixin.qq.com");
weixin.put("auth", "https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN");
weixin.put(
"call",
"https://mp.weixin.qq.com/advanced/callbackprofile?t=ajax-response&token=%s&lang=zh_CN");
weixin.put("start",
"https://mp.weixin.qq.com/misc/skeyform?form=advancedswitchform");
weixin.put("back",
"https://mp.weixin.qq.com/merchant/myservice?action=set_oauth_domain&f=json");
weixin.put("verifycode",
"https://mp.weixin.qq.com/cgi-bin/verifycode?username=" + uname
+ "&r=%s");
weixin.put("bedeveloper",
"https://mp.weixin.qq.com/advanced/advanced?action=agreement");
List<BasicHeader> headers = new ArrayList<BasicHeader>();
headers.add(new BasicHeader("Origin", weixin.getString("base")));
headers.add(new BasicHeader("Connection", "keep-alive"));
headers.add(new BasicHeader(
"User-Agent",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36"));
client = new DefaultHttpClient();
client.getParams().setParameter(ClientPNames.COOKIE_POLICY,
CookiePolicy.BROWSER_COMPATIBILITY);
client.getParams().setBooleanParameter(
"http.protocol.single-cookie-header", true);
client.getParams().setParameter(ClientPNames.DEFAULT_HEADERS, headers);
host = new HttpHost(weixin.getString("host"), -1, "https");
}
public JSONObject process() {
// 1.登陆微信公众号
step1_login();
int code = weixin.getIntValue("code");
if (code == 0) {
// 2.收集相关信息
step2_collect();
code = weixin.getIntValue("code");
// 3.填写服务器配置
// 3-1.未初始化账号
// 3-2.已配置账号
if (code == 0) {
step3_setting();
}
code = weixin.getIntValue("code");
// 4.修改网页授权地址
if (code == 0) {
step4_back();
}
// 5.创建底部菜单 (调用封装好的API)
// 5-1.订阅号
// 5-2.服务号
// 6.完成
}
return weixin;
}
/**
* step1:登录操作
*/
private void step1_login() {
HttpPost method = new HttpPost(weixin.getString("auth"));
try {
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
parameters.add(new BasicNameValuePair("username", uname));
parameters.add(new BasicNameValuePair("pwd", DigestUtils.md5Hex(pwd
.getBytes())));
parameters.add(new BasicNameValuePair("f", "json"));
parameters.add(new BasicNameValuePair("imgcode", imgcode));
if (!StringUtils.isBlank(imgcode)) {
method.addHeader("Cookie", "sig=" + sig);
}
method.setEntity(new UrlEncodedFormEntity(parameters, charset));
method.addHeader("Referer", weixin.getString("base"));
HttpResponse response = client.execute(host, method);
HttpEntity entity = response.getEntity();
Document root = Jsoup.parse(entity.getContent(), charset.name(),
weixin.getString("base"));
StatusLine line = response.getStatusLine();
logger.info("step1_login--->status={},body=\n{}", line,
root.toString());
if (line.getStatusCode() == HttpStatus.SC_OK) {
JSONObject body = JSON.parseObject(root.body().text());
String msg = "";
int code = 0;
switch (body.getIntValue("ret")
+ body.getJSONObject("base_resp").getIntValue("ret")) {
case -1:
msg = "系统错误,请稍候再试。";
code = -1;
break;
case -2:
msg = "帐号或密码错误。";
code = 100;
break;
case -23:
msg = "您输入的帐号或者密码不正确,请重新输入。";
code = 101;
break;
case -21:
msg = "不存在该帐户。";
code = 102;
break;
case -7:
msg = "您目前处于访问受限状态。";
code = 103;
break;
case -8:
msg = "请输入图中的验证码";
code = 104;
break;
case -27:
msg = "您输入的验证码不正确,请重新输入";
code = 105;
break;
case -26:
msg = "该公众会议号已经过期,无法再登录使用。";
code = 106;
break;
case 0:
msg = "成功登录,正在跳转...";
break;
case -25:
msg = "海外帐号请在公众平台海外版登录,<a href=\"http://admin.wechat.com/\">点击登录</a>";
code = 107;
break;
default:
msg = "未知错误";
code = 108;
break;
}
if (code == 0) {
weixin.put(
"urlToken",
getQueryMap(body.getString("redirect_url")).get(
"token"));
weixin.put("indexUrl", String.format("%s%s",
weixin.getString("base"),
body.getString("redirect_url")));
weixin.put("step", "1");
} else {
if (code == 104 || code == 105) {
// 下载验证码
HttpGet get = new HttpGet(String.format(
weixin.getString("verifycode"),
System.currentTimeMillis()));
get.setHeaders(method.getAllHeaders());
response = client.execute(host, get);
StringBuffer base64 = new StringBuffer();
base64.append("data:")
.append(response.getFirstHeader("Content-Type")
.getValue()).append(";base64,");
base64.append(new String(
Base64.encodeBase64(IOUtil.toByteArray(response
.getEntity().getContent())), charset));
weixin.put("verifydata", base64.toString());
List<Cookie> cookieList = client.getCookieStore()
.getCookies();
for (Cookie cookie : cookieList) {
if (cookie.getName().equals("sig")) {
weixin.put("sig", cookie.getValue());
break;
}
}
}
weixin.put("code", code);
weixin.put("msg", msg);
}
} else {
weixin.put("code", "-3");
weixin.put("msg", "网络异常,请稍后重试!");
}
} catch (Exception e) {
weixin.put("code", "-2");
weixin.put("msg", "服务器繁忙,请稍后重试!");
weixin.put("exception", e.getMessage());
logger.error("step1_login catch error", e);
} finally {
if (weixin.getIntValue("code") != 0) {
client.getConnectionManager().shutdown();
}
}
}
/**
* step2:收集信息
*/
private void step2_collect() {
String url = weixin.getString("indexUrl");
HttpGet method = new HttpGet(url);
try {
method.addHeader("Referer", weixin.getString("base"));
HttpResponse response = client.execute(host, method);
HttpEntity entity = response.getEntity();
Document root = Jsoup.parse(entity.getContent(), charset.name(),
weixin.getString("base"));
StatusLine line = response.getStatusLine();
logger.info("step2_setting--->status={},body=\n{}", line,
root.toString());
if (line.getStatusCode() == HttpStatus.SC_OK) {
Element ele = root.getElementById("menuBar")
.getElementsByTag("dl").last();
url = ele.getElementsByTag("a").last().absUrl("href");
weixin.put("developerUrl", url);
method.addHeader("Referer", url);
url = ele.previousElementSibling().getElementsByTag("a")
.first().absUrl("href");
weixin.put("settingUrl", url);
method.setURI(URI.create(url));
response = client.execute(host, method);
entity = response.getEntity();
root = Jsoup.parse(entity.getContent(), charset.name(),
weixin.getString("base"));
line = response.getStatusLine();
weixin.put("step", "2-1");
// 公众号配置页面
if (line.getStatusCode() == HttpStatus.SC_OK) {
Elements eles = root.getElementById("settingArea")
.getElementsByTag("li");
String key, value;
for (Element element : eles) {
key = element.getElementsByTag("h4").first().text();
ele = element.getElementsByClass("meta_content")
.first();
if (ele.children().isEmpty()) {
value = ele.text();
} else {
if (ele.child(0).tagName().equalsIgnoreCase("a")) {
value = ele.child(0).absUrl("href");
} else if (ele.child(0).tagName()
.equalsIgnoreCase("img")) {
value = ele.child(0).absUrl("src");
} else {
value = ele.text();
}
}
weixin.put(accountMap.get(key), value);
}
weixin.put("isVerify", weixin.getString("weixinVerify")
.contains("微信认证"));
weixin.put("isService", weixin.getString("accountType")
.contains("服务号"));
weixin.put("isSubscribe", weixin.getString("accountType")
.contains("订阅号"));
value = weixin.getString("qrcodeUrl");
method.setURI(URI.create(value));
response = client.execute(host, method);
weixin.put("qrcodeData", IOUtil.toByteArray(response
.getEntity().getContent()));
weixin.put("step", "2-2");
// 开发者页面
method.addHeader("Referer", url);
method.setURI(URI.create(weixin.getString("developerUrl")));
response = client.execute(host, method);
entity = response.getEntity();
root = Jsoup.parse(entity.getContent(), charset.name(),
weixin.getString("base"));
line = response.getStatusLine();
if (line.getStatusCode() == HttpStatus.SC_OK) {
// 还没有成为开发者 2014.10-06 jy.hu
// 触发成为开发者动作
ele = root.getElementById("js_toBeDeveloper");
if (ele != null && ele.hasText()) {
HttpPost post = new HttpPost(URI.create(weixin
.getString("bedeveloper")));
post.addHeader("Referer", url);
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
parameters = new ArrayList<NameValuePair>();
parameters.add(new BasicNameValuePair("token",
weixin.getString("urlToken")));
parameters.add(new BasicNameValuePair("f", "json"));
parameters.add(new BasicNameValuePair("ajax", "1"));
parameters.add(new BasicNameValuePair("lang",
"zh_CN"));
parameters.add(new BasicNameValuePair("random",
System.currentTimeMillis() + ""));
post.setEntity(new UrlEncodedFormEntity(parameters,
charset));
response = client.execute(host, post);
entity = response.getEntity();
root = Jsoup.parse(entity.getContent(),
charset.name(), weixin.getString("base"));
line = response.getStatusLine();
logger.info(
"step2_bedeveloper--->status={},body=\n{}",
line, root.toString());
if (line.getStatusCode() == HttpStatus.SC_OK) {
JSONObject body = JSON.parseObject(root.body()
.text());
if (body.getIntValue("ret") == 0) {
method.addHeader("Referer", url);
method.setURI(URI.create(weixin
.getString("developerUrl")));
response = client.execute(host, method);
entity = response.getEntity();
root = Jsoup.parse(entity.getContent(),
charset.name(),
weixin.getString("base"));
} else {
weixin.put("code", "-100");
weixin.put("msg", "成为开发者失败!");
return;
}
}
}
// 初始化状态
// 配置未启用状态
// 配置已启用状态
eles = root.getElementsByClass("developer_info_opr");
if (eles != null && eles.hasText()) {
weixin.put("developerModifyUrl", eles.first()
.children().first().absUrl("href"));
weixin.put("status",
eles.text().contains("启用") ? "READY"
: "RUNNING");
} else {
weixin.put("status", "INIT");
}
// appid&appsecret
if (weixin.getBooleanValue("isService")
|| (weixin.getBooleanValue("isSubscribe") && weixin
.getBooleanValue("isVerify"))) {
eles = root
.getElementsByClass("developer_info_item")
.first().children().last()
.getElementsByClass("frm_controls");
weixin.put("appId", eles.first().text());
weixin.put("appSecret",
eles.last().text().replace("重置", "").trim());
}
weixin.put("step", "2-3");
}
} else {
weixin.put("code", "-3");
weixin.put("msg", "网络异常,请稍后重试!");
}
} else {
weixin.put("code", "-3");
weixin.put("msg", "网络异常,请稍后重试!");
}
} catch (Exception e) {
weixin.put("code", "-2");
weixin.put("msg", "服务器繁忙,请稍后重试!");
weixin.put("exception", e.getMessage());
logger.error("step2_collect catch error", e);
} finally {
if (weixin.getIntValue("code") != 0) {
client.getConnectionManager().shutdown();
}
}
}
/**
* step3:填写配置
*/
private void step3_setting() {
HttpPost method = new HttpPost(String.format(weixin.getString("call"),
weixin.getString("urlToken")));
try {
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
parameters.add(new BasicNameValuePair("url", pushurl));
parameters.add(new BasicNameValuePair("callback_token", token));
// EncodingAESKey | 消息加解密方式(明文0,兼容1,安全2)
parameters.add(new BasicNameValuePair("encoding_aeskey", RandomUtil
.generateString(43)));
parameters
.add(new BasicNameValuePair("callback_encrypt_mode", "0"));
parameters.add(new BasicNameValuePair("operation_seq", RandomUtil
.generateStringByNumberChar(9)));
method.setEntity(new UrlEncodedFormEntity(parameters, charset));
method.addHeader("Referer", weixin.getString("developerModifyUrl"));
HttpResponse response = client.execute(host, method);
HttpEntity entity = response.getEntity();
Document root = Jsoup.parse(entity.getContent(), charset.name(),
weixin.getString("base"));
StatusLine line = response.getStatusLine();
logger.info("step3_setting--->status={},body=\n{}", line,
root.toString());
if (line.getStatusCode() == HttpStatus.SC_OK) {
JSONObject body = JSON.parseObject(root.body().text());
String msg = "";
int code = 0;
switch (body.getIntValue("ret")
+ body.getJSONObject("base_resp").getIntValue("ret")) {
case -201:
msg = "无效的URL";
code = 200;
break;
case -202:
msg = "无效的Token";
code = 201;
break;
case -203:
msg = "操作频率太快,请休息一下。";
code = 202;
break;
case -204:
msg = "请先在设置页面完善当前帐号信息";
code = 203;
break;
case -205:
msg = "该URL可能存在安全风险,请检查";
code = 207;
case -301:
msg = "请求URL超时";
code = 204;
break;
case -302:
msg = "服务器没有正确响应Token验证,请稍后重试";
code = 205;
break;
case -104:
msg = "参数错误,请重新填写。";
code = 206;
break;
case 0:
msg = "配置成功..";
break;
default:
msg = "未知错误";
code = 108;
break;
}
if (code == 0) {
// 触发启用按钮
if (!weixin.getString("status").equals("RUNNING")) {
parameters = new ArrayList<NameValuePair>();
parameters.add(new BasicNameValuePair("token", weixin
.getString("urlToken")));
parameters.add(new BasicNameValuePair("f", "json"));
parameters.add(new BasicNameValuePair("ajax", "1"));
parameters.add(new BasicNameValuePair("flag", "1"));
parameters.add(new BasicNameValuePair("type", "2"));
parameters.add(new BasicNameValuePair("lang", "zh_CN"));
parameters.add(new BasicNameValuePair("random", System
.currentTimeMillis() + ""));
method.setEntity(new UrlEncodedFormEntity(parameters,
charset));
method.setURI(URI.create(weixin.getString("start")));
response = client.execute(host, method);
entity = response.getEntity();
root = Jsoup.parse(entity.getContent(), charset.name(),
weixin.getString("base"));
line = response.getStatusLine();
logger.info("step3_setting--->status={},body=\n{}",
line, root.toString());
if (line.getStatusCode() == HttpStatus.SC_OK) {
body = JSON.parseObject(root.body().text());
if (body.getIntValue("ret")
+ body.getJSONObject("base_resp")
.getIntValue("ret") != 0) {
weixin.put("code", 300);
weixin.put("msg", "启用开发者模式失败,请稍后再试!");
}
}
}
weixin.put("step", "3");
} else {
weixin.put("code", code);
weixin.put("msg", msg);
}
} else {
weixin.put("code", "-3");
weixin.put("msg", "网络异常,请稍后重试!");
}
} catch (Exception e) {
weixin.put("code", "-2");
weixin.put("msg", "服务器繁忙,请稍后重试!");
weixin.put("exception", e.getMessage());
logger.error("step3_setting catch error", e);
} finally {
if (weixin.getIntValue("code") != 0) {
client.getConnectionManager().shutdown();
}
}
}
/**
* step4:修改回调
*/
private void step4_back() {
try {
if (weixin.getBooleanValue("isVerify")) {
HttpPost method = new HttpPost(weixin.getString("back"));
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
parameters.add(new BasicNameValuePair("domain", backurl));
parameters.add(new BasicNameValuePair("token", weixin
.getString("urlToken")));
parameters.add(new BasicNameValuePair("f", "json"));
parameters.add(new BasicNameValuePair("ajax", "1"));
parameters.add(new BasicNameValuePair("flag", "1"));
parameters.add(new BasicNameValuePair("lang", "zh_CN"));
parameters.add(new BasicNameValuePair("random", System
.currentTimeMillis() + ""));
method.setEntity(new UrlEncodedFormEntity(parameters, charset));
method.addHeader("Referer", weixin.getString("developerUrl"));
HttpResponse response = client.execute(host, method);
HttpEntity entity = response.getEntity();
Document root = Jsoup.parse(entity.getContent(),
charset.name(), weixin.getString("base"));
StatusLine line = response.getStatusLine();
logger.info("step4_back--->status={},body=\n{}", line,
root.toString());
if (line.getStatusCode() == HttpStatus.SC_OK) {
JSONObject body = JSON.parseObject(root.body().text());
if (body.getIntValue("ret")
+ body.getJSONObject("base_resp")
.getIntValue("ret") != 0) {
weixin.put("code", "400");
weixin.put("msg", "修改授权回调地址失败!");
}
weixin.put("step", "4");
}
} else {
logger.info("公众号尚未认证,放弃本次修改授权回调地址操作。{}", weixin);
}
} catch (Exception e) {
weixin.put("code", "-2");
weixin.put("msg", "服务器繁忙,请稍后重试!");
weixin.put("exception", e.getMessage());
logger.error("step4_back catch error", e);
} finally {
client.getConnectionManager().shutdown();
}
}
private Map<String, String> getQueryMap(String query) {
String[] params = query.split("&");
Map<String, String> map = new HashMap<String, String>();
for (String param : params) {
String name = param.split("=")[0];
String value = param.split("=")[1];
map.put(name, value);
}
return map;
}
}
@@ -0,0 +1,37 @@
package com.foxinmy.weixin4j.mp.type;
import com.foxinmy.weixin4j.mp.response.ArticleResponse;
import com.foxinmy.weixin4j.mp.response.BaseResponse;
import com.foxinmy.weixin4j.mp.response.ImageResponse;
import com.foxinmy.weixin4j.mp.response.MusicResponse;
import com.foxinmy.weixin4j.mp.response.TextResponse;
import com.foxinmy.weixin4j.mp.response.TransferResponse;
import com.foxinmy.weixin4j.mp.response.VideoResponse;
import com.foxinmy.weixin4j.mp.response.VoiceResponse;
/**
*
* 响应类型
*
* @author jy.hu
*
*/
public enum ResponseType {
text(TextResponse.class), image(ImageResponse.class), voice(
VoiceResponse.class), video(VideoResponse.class), music(
MusicResponse.class), news(ArticleResponse.class), transfer_customer_service(
TransferResponse.class);
private Class<? extends BaseResponse> messageClass;
ResponseType(Class<? extends BaseResponse> messageClass) {
this.messageClass = messageClass;
}
public void setMessageClass(Class<? extends BaseResponse> messageClass) {
this.messageClass = messageClass;
}
public Class<? extends BaseResponse> getMessageClass() {
return messageClass;
}
}
@@ -0,0 +1,434 @@
package com.foxinmy.weixin4j.mp.util;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
/**
* excel工具类
* @className ExcelUtil
* @author jy
* @date 2014年11月1日
* @since JDK 1.7
* @see
*/
public class ExcelUtil {
/**
* 读取Excel2003,2007的内容,第一维数组存储的是一行中格列的值,二维数组存储的是多少个行
*
* @param file
* 输入流
* @param fileName
* 是2003还是2007 xls2003xlsx2007
* @throws Exception
*/
public static String[][] read(File file) throws Exception {
String fileExt = getExtension(file.getName());
if (null != fileExt && fileExt.toLowerCase().equals("xls")) {// 2003
BufferedInputStream in = new BufferedInputStream(
new FileInputStream(file));
// 打开HSSFWorkbook
POIFSFileSystem fs = new POIFSFileSystem(in);
Workbook wb = new HSSFWorkbook(fs);
in.close();
return readExcel(wb);
} else if (null != fileExt && fileExt.toLowerCase().equals("xlsx")) {// 2007
Workbook wb = new XSSFWorkbook(new FileInputStream(file));
return readExcel(wb);
}
return null;
}
public static String[][] read4Special(File file, String fileName,
int columnSize) throws Exception {
String fileExt = getExtension(fileName);
if (null != fileExt && fileExt.toLowerCase().equals("xls")) {// 2003
BufferedInputStream in = new BufferedInputStream(
new FileInputStream(file));
// 打开HSSFWorkbook
POIFSFileSystem fs = new POIFSFileSystem(in);
Workbook wb = new HSSFWorkbook(fs);
in.close();
return readExcel4Special(wb, columnSize);
} else if (null != fileExt && fileExt.toLowerCase().equals("xlsx")) {// 2007
Workbook wb = new XSSFWorkbook(new FileInputStream(file));
return readExcel4Special(wb, columnSize);
}
return null;
}
/**
* 读取Excel文件中的值
*
* @param wb
* @return String[][]
*/
private static String[][] readExcel(Workbook wb) throws Exception {
List<String[]> result = new ArrayList<String[]>();
int rowSize = 0;
Cell cell = null;
for (int sheetIndex = 0; sheetIndex < wb.getNumberOfSheets(); sheetIndex++) {
Sheet st = wb.getSheetAt(sheetIndex);
// 第一行为标题,不取
for (int rowIndex = 1; rowIndex <= st.getLastRowNum(); rowIndex++) {
Row row = st.getRow(rowIndex);
if (row == null) {
continue;
}
int tempRowSize = row.getLastCellNum() + 1;
if (tempRowSize > rowSize) {
rowSize = tempRowSize;
}
String[] values = new String[rowSize];
Arrays.fill(values, "");
boolean hasValue = false;
for (short columnIndex = 0; columnIndex <= row.getLastCellNum(); columnIndex++) {
String value = "";
cell = row.getCell(columnIndex);
if (cell != null) {
// 注意:一定要设成这个,否则可能会出现乱码
// cell.setEncoding(HSSFCell.ENCODING_UTF_16);
switch (cell.getCellType()) {
case Cell.CELL_TYPE_STRING:
value = cell.getStringCellValue();
break;
case Cell.CELL_TYPE_NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
Date date = cell.getDateCellValue();
if (date != null) {
value = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss").format(date);
} else {
value = "";
}
} else {
value = getRightStr(cell.getNumericCellValue()
+ "");
// value =
// String.valueOf(cell.getNumericCellValue());
}
break;
case Cell.CELL_TYPE_FORMULA:
// 导入时如果为公式生成的数据则无值
if (!("").equals(cell.getStringCellValue())) {
value = cell.getStringCellValue();
} else {
value = cell.getNumericCellValue() + "";
}
break;
case Cell.CELL_TYPE_BLANK:
break;
case Cell.CELL_TYPE_ERROR:
value = "";
break;
case Cell.CELL_TYPE_BOOLEAN:
value = (cell.getBooleanCellValue() == true ? "true"
: "false");
break;
default:
value = "";
}
}
if (columnIndex == 0 && value.trim().equals("")) {
break;
}
values[columnIndex] = rightTrim(value);
hasValue = true;
}
if (hasValue) {
result.add(values);
}
}
}
String[][] returnArray = new String[result.size()][rowSize];
for (int i = 0; i < returnArray.length; i++) {
returnArray[i] = result.get(i);
}
return returnArray;
}
/*
* 读取excel数据
*/
private static String[][] readExcel4Special(Workbook wb, int columnSize)
throws Exception {
List<String[]> result = new ArrayList<String[]>();
int rowSize = 0;
Cell cell = null;
for (int sheetIndex = 0; sheetIndex < wb.getNumberOfSheets(); sheetIndex++) {
Sheet st = wb.getSheetAt(sheetIndex);
// 第一行为标题,不取
for (int rowIndex = 1; rowIndex <= st.getLastRowNum(); rowIndex++) {
Row row = st.getRow(rowIndex);
if (row == null) {
continue;
}
int tempRowSize = row.getLastCellNum() + 1;
if (tempRowSize > rowSize) {
rowSize = tempRowSize;
}
String[] values = new String[columnSize];
Arrays.fill(values, "");
boolean hasValue = false;
for (short columnIndex = 0; columnIndex < columnSize; columnIndex++) {
String value = "";
cell = row.getCell(columnIndex);
if (cell != null) {
// 注意:一定要设成这个,否则可能会出现乱码
// cell.setEncoding(HSSFCell.ENCODING_UTF_16);
switch (cell.getCellType()) {
case Cell.CELL_TYPE_STRING:
value = cell.getStringCellValue();
break;
case Cell.CELL_TYPE_NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
Date date = cell.getDateCellValue();
if (date != null) {
value = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss").format(date);
} else {
value = "";
}
} else {
value = getRightStr(cell.getNumericCellValue()
+ "");
// value =
// String.valueOf(cell.getNumericCellValue());
}
break;
case Cell.CELL_TYPE_FORMULA:
// 导入时如果为公式生成的数据则无值
if (!("").equals(cell.getStringCellValue())) {
value = cell.getStringCellValue();
} else {
value = cell.getNumericCellValue() + "";
}
break;
case Cell.CELL_TYPE_BLANK:
break;
case Cell.CELL_TYPE_ERROR:
value = "";
break;
case Cell.CELL_TYPE_BOOLEAN:
value = (cell.getBooleanCellValue() == true ? "true"
: "false");
break;
default:
value = "";
}
} else {
value = "";
}
if (columnIndex == 0 && value.trim().equals("")) {
break;
}
values[columnIndex] = rightTrim(value);
hasValue = true;
}
if (hasValue) {
result.add(values);
}
}
}
String[][] returnArray = new String[result.size()][columnSize];
for (int i = 0; i < returnArray.length; i++) {
returnArray[i] = result.get(i);
}
return returnArray;
}
/**
* double 类型数据转换
*
* @param sNum
* @return
*/
private static String getRightStr(String sNum) {
DecimalFormat decimalFormat = new DecimalFormat("#.000000");
String resultStr = decimalFormat.format(new Double(sNum));
if (resultStr.equals(".000000"))
return String.valueOf(0d);
if (resultStr.matches("^[-+]?\\d+\\.[0]+$")) {
resultStr = resultStr.substring(0, resultStr.indexOf("."));
}
return resultStr;
}
/**
* 去掉字符串右边的空格
*
* @param str要处理的字符串
* @return 处理后的字符串
*/
public static String rightTrim(String str) {
if (str == null) {
return "";
}
int length = str.length();
for (int i = length - 1; i >= 0; i--) {
if (str.charAt(i) != 0x20) {
break;
}
length--;
}
return str.substring(0, length);
}
/**
* 获取文件扩展名
*
* @param filename
* @return
*/
private static String getExtension(String filename) {
if ((filename != null) && (filename.length() > 0)) {
int i = filename.lastIndexOf('.');
if ((i > 0) && (i < (filename.length() - 1))) {
return filename.substring(i + 1);
}
}
return "";
}
public static void list2excel(HSSFWorkbook workbook, List<String> headers,
Collection<?> datas) {
JSONArray arrays = null; //
String[] strings = null; //
HSSFSheet sheet = null; // 工作表
HSSFRow row = null; // 单元行
HSSFCell cell = null; // 单元格
HSSFRichTextString richText = null; // 单元格内容
sheet = workbook.getSheetAt(0); // 创建表格
int rowNum = sheet.getLastRowNum(); // 数据行号
if (rowNum != 0) {
rowNum++;
sheet.createRow(rowNum);
rowNum++;
}
HSSFCellStyle cellStyle = workbook.createCellStyle();// 创建单元格样式(用于表头)
cellStyle.setFillForegroundColor(HSSFColor.SKY_BLUE.index);// 设置单元格样式
cellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
cellStyle.setBorderBottom(CellStyle.BORDER_THIN);
cellStyle.setBorderLeft(CellStyle.BORDER_THIN);
cellStyle.setBorderRight(CellStyle.BORDER_THIN);
cellStyle.setBorderTop(CellStyle.BORDER_THIN);
cellStyle.setAlignment(CellStyle.ALIGN_CENTER);
HSSFFont f = workbook.createFont();
f.setColor(HSSFColor.BLUE.index);
HSSFFont font = workbook.createFont(); // 创建字体
font.setColor(HSSFColor.VIOLET.index); // 设置字体属性
font.setFontHeightInPoints((short) 12);
font.setBoldweight(Font.BOLDWEIGHT_BOLD);
cellStyle.setFont(font); // 设置单元格的字体
row = sheet.createRow(rowNum); // 创建表格标题行
for (int i = 0; i < headers.size(); i++) {
// 填充标题行的单元格数据
cell = row.createCell(i);
cell.setCellStyle(cellStyle);
richText = new HSSFRichTextString(headers.get(i));
cell.setCellValue(richText);
sheet.autoSizeColumn(i);
}
rowNum++;
HSSFCellStyle contentStyle = workbook.createCellStyle();// 创建单元格样式(用于表内容)
contentStyle.setFillForegroundColor(HSSFColor.LIGHT_YELLOW.index);
contentStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
contentStyle.setBorderBottom(CellStyle.BORDER_THIN);
contentStyle.setBorderLeft(CellStyle.BORDER_THIN);
contentStyle.setBorderRight(CellStyle.BORDER_THIN);
contentStyle.setBorderTop(CellStyle.BORDER_THIN);
contentStyle.setAlignment(CellStyle.ALIGN_CENTER);
contentStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
font = workbook.createFont(); // 创建字体
font.setBoldweight(Font.BOLDWEIGHT_NORMAL); // 设置字体粗细
contentStyle.setFont(font); // 设置单元格的字体样式
int j = 0;
for (Object obj : datas) {
row = sheet.getRow(rowNum);
if (row == null) {
row = sheet.createRow(rowNum);
}
if (obj instanceof JSONArray) {
arrays = (JSONArray) obj;
// 只能是 JSONObject
for (int i = 0; i < arrays.size(); i++) {
JSONObject jsonObj = arrays.getJSONObject(i);
cell = row.createCell(i);
cell.setCellStyle(contentStyle);
String val = jsonObj.getString(headers.get(i));
richText = new HSSFRichTextString(val);
richText.applyFont(f);
cell.setCellValue(richText);
}
rowNum++;
} else if (obj instanceof String[]) {
strings = (String[]) obj;
for (int i = 0; i < strings.length; i++) {
cell = row.createCell(i);
cell.setCellStyle(contentStyle);
richText = new HSSFRichTextString(strings[i]);
richText.applyFont(f);
cell.setCellValue(richText);
}
rowNum++;
} else {
cell = row.createCell(j);
cell.setCellStyle(contentStyle);
richText = new HSSFRichTextString((String) obj);
richText.applyFont(f);
cell.setCellValue(richText);
j++;
}
}
}
}
@@ -0,0 +1,16 @@
# \u7f16\u7801\u6d4b\u8bd5\u4e4b\u7528 \u6b63\u5f0f\u73af\u5883\u4e0bcopy\u4e00\u4efd\u5230classpath
# \u516c\u4f17\u53f7\u4fe1\u606f
account={"appId":"wx4ab8f8de58159a57","appSecret":"1d4eb0f4bf556aaed539f30ed05ca795",\
"token":"\u5f00\u653e\u8005\u7684token \u975e\u5fc5\u987b","openId":"\u516c\u4f17\u53f7\u7684openid \u975e\u5fc5\u987b",\
"mchId":"V3.x\u7248\u672c\u4e0b\u7684\u5fae\u4fe1\u5546\u6237\u53f7",\
"partnerId":"\u8d22\u4ed8\u901a\u7684\u5546\u6237\u53f7","partnerKey":"\u8d22\u4ed8\u901a\u5546\u6237\u6743\u9650\u5bc6\u94a5Key",\
"paySignKey":"\u5fae\u4fe1\u652f\u4ed8\u4e2d\u8c03\u7528API\u7684\u5bc6\u94a5"}
# \u4f7f\u7528FileTokenHolder\u65f6token\u7684\u5b58\u653e\u8def\u5f84
token_path=/tmp/weixin/token
# \u4e8c\u7ef4\u7801\u4fdd\u5b58\u8def\u5f84
qr_path=/tmp/weixin/qr
# \u5a92\u4f53\u6587\u4ef6\u4fdd\u5b58\u8def\u5f84
media_path=/tmp/weixin/media
# \u5bf9\u8d26\u5355\u4fdd\u5b58\u8def\u5f84
bill_path=/tmp/weixin/bill