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
+34 -48
View File
@@ -1,66 +1,52 @@
weixin4j-mp
===========
tencent weixin platform java sdk 微信公众平台开发工具包 http://mp.weixin.qq.com/wiki
@(weixin4j)[公众平台]
微信[公众平台](http://mp.weixin.qq.com/wiki)开发工具包
----------------------------------------------------
功能列表
-------
* weixin4j-mp-api
* TokenHolder token的实现
* MediaApi 上传/下载媒体文件API
* NotifyApi 客服消息API
* MassApi 群发消息API
* UserApi 用户管理API
* GroupApi 分组管理API
* MenuApi 底部菜单API
* QrApi 二维码API
* TmplApi 模板消息API
* HelperApi 辅助API
* netty服务器 & 消息分发
如何使用
--------
1.编辑classweixin.properties并填入appid/appsecret,当然也可通过构造函数传入.
2.实例化一个WeixinProxy对象,调用API.
WeixinProxy weixinProxy = new WeixinProxy();
// weixinProxy = new WeixinProxy(appid,appsecret);
weixinProxy.getUser(openId);
3.针对token存储有两种方案,File存储/Redis存储,当然也可自己实现TokenHolder(继承AbstractTokenHolder类并重写getToken方法),默认使用文件(xml)的方式保存token,如果环境中支持redis,建议使用RedisTokenHolder.
WeixinProxy weixinProxy = new WeixinProxy(new RedisTokenHolder());
// weixinProxy = new WeixinProxy(new RedisTokenHolder(appid,appsecret));
4.mvn package,得到一个zip的压缩包,解压到启动目录(见src/main/startup.sh/APP_HOME)
5.启动netty服务
com.foxinmy.weixin4j.mp.startup.WeixinServiceBootstrap
sh startup.sh start
+ MediaApi `上传/下载媒体文件API`
+ NotifyApi `客服消息API`
+ MassApi `群发消息API`
+ UserApi `用户管理API`
+ GroupApi `分组管理API`
+ MenuApi `底部菜单API`
+ QrApi `二维码API`
+ TmplApi `模板消息API`
+ HelperApi `辅助API`
* weixin4j-mp-server
`netty服务器 & 消息分发`
更新LOG
-------
* 2014-10-27
1).用netty构建http服务器并支持消息分发
+ 用netty构建http服务器并支持消息分发
* 2014-10-28
1).调整ActionMapping抽象化
+ 调整`ActionMapping`抽象化
* 2014-10-31
1).weixin.properties切分为API调用地址/公众号信息两部分
+ `weixin.properties`切分为API调用地址/公众号信息两部分
* 2014-11-03
+ `weixin-mp`分离为`weixin-mp-api``weixin-mp-server`两个工程
+ `weixin-mp`:加入支付模块
+8 -39
View File
@@ -8,54 +8,23 @@
<artifactId>weixin4j</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.foxinmy.weixin</groupId>
<artifactId>weixin4j-mp</artifactId>
<name>weixin4j-mp</name>
<packaging>pom</packaging>
<url>https://github.com/foxinmy/weixin4j/tree/master/weixin4j-mp</url>
<description>微信公众号工具包</description>
<modules>
<module>weixin4j-mp-api</module>
<module>weixin4j-mp-server</module>
</modules>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2.1</version>
<configuration>
<descriptors>
<descriptor>src/main/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<finalName>weixin4j-mp</finalName>
</build>
<dependencies>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>${jaxen.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>com.foxinmy.weixin4j</groupId>
<artifactId>weixin4j-base</artifactId>
<version>${weixin4j.version}</version>
<version>${weixin4j.base.version}</version>
</dependency>
</dependencies>
</project>
+28
View File
@@ -0,0 +1,28 @@
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*~
# eclipse ignore
*.settings/*
/.project
/.classpath
/.tomcatplugin
# maven ignore
target/*
# other ignore
*.log
*.tmp
Thumbs.db
/target/
.DS_Store
+87
View File
@@ -0,0 +1,87 @@
weixin4j-mp-api
===============
@(weixin4j)[公众平台]
微信[公众平台](http://mp.weixin.qq.com/wiki)开发工具包
功能列表
-------
* MediaApi `上传/下载媒体文件API`
* NotifyApi `客服消息API`
* MassApi `群发消息API`
* UserApi `用户管理API`
* GroupApi `分组管理API`
* MenuApi `底部菜单API`
* QrApi `二维码API`
* TmplApi `模板消息API`
* HelperApi `辅助API`
* PayApi `支付API`
如何使用
--------
1.API工程可以单独使用,需新增或拷贝`weixin.properties`文件到classpath
weixin.properties
| 属性名 | 说明 |
| :---------- | :-------------- |
| account | 微信公众号信息 `json格式` |
| token_path | 使用FileTokenHolder时token保存的物理路径 |
| qr_path | 调用二维码接口时保存的物理路径 |
示例
>**`account={"appId":"appId","appSecret":"appSecret",
"token":"开放者的token 非必须","openId":"公众号的openid 非必须",
"mchId":"V3.x版本下的微信商户号",
"partnerId":"财付通的商户号","partnerKey":"财付通商户权限密钥Key",
"paySignKey":"微信支付中调用API的密钥"}
token_path=/tmp/weixin/token`
`qr_path=/tmp/weixin/qr`
`media_path=/tmp/weixin/media`
`bill_path=/tmp/weixin/bill`**
2.实例化一个`WeixinProxy`对象,调用API.
``` java
WeixinProxy weixinProxy = new WeixinProxy();
// weixinProxy = new WeixinProxy(appid,appsecret);
weixinProxy.getUser(openId);
```
3.针对`token`存储有两种方案,`File存储`/`Redis存储`,当然也可自己实现`TokenHolder`(继承`AbstractTokenHolder`类并重写`getToken`方法),默认使用文件(xml)的方式保存token,如果环境中支持`redis`,建议使用`RedisTokenHolder`.
``` java
WeixinProxy weixinProxy = new WeixinProxy(new RedisTokenHolder());
// weixinProxy = new WeixinProxy(new RedisTokenHolder(appid,appsecret));
```
4.mvn package.
更新LOG
-------
* 2014-10-27
+ 用netty构建http服务器并支持消息分发
* 2014-10-28
+ 调整`ActionMapping`抽象化
* 2014-10-31
+ `weixin.properties`切分为API调用地址/公众号信息两部分
* 2014-11-03
+ 分离为`weixin-mp-api`和`weixin-mp-server`两个工程
+ 加入支付模块
+59
View File
@@ -0,0 +1,59 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.foxinmy.weixin4j</groupId>
<artifactId>weixin4j-mp</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>weixin4j-mp-api</artifactId>
<name>weixin4j-mp-api</name>
<url>https://github.com/foxinmy/weixin4j/tree/master/weixin4j-mp</url>
<description>微信公众号API</description>
<build>
<finalName>weixin4j-mp-api</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>*.*</exclude>
</excludes>
</resource>
</resources>
</build>
<dependencies>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>${jaxen.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
<exclusions>
<exclusion>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>${poi.version}</version>
</dependency>
</dependencies>
</project>
@@ -2,13 +2,14 @@ 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.BaseResult;
import com.foxinmy.weixin4j.model.WeixinAccountV2;
import com.foxinmy.weixin4j.model.WeixinAccountV3;
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;
@@ -30,7 +31,11 @@ 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;
@@ -74,17 +79,17 @@ public class WeixinProxy {
this(new FileTokenHolder(appid, appsecret));
}
public WeixinProxy(TokenHolder tokenApi) {
this.mediaApi = new MediaApi(tokenApi);
this.notifyApi = new NotifyApi(tokenApi);
this.massApi = new MassApi(tokenApi);
this.userApi = new UserApi(tokenApi);
this.groupApi = new GroupApi(tokenApi);
this.menuApi = new MenuApi(tokenApi);
this.qrApi = new QrApi(tokenApi);
this.tmplApi = new TmplApi(tokenApi);
this.helperApi = new HelperApi(tokenApi);
this.payApi = new PayApi(tokenApi);
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);
}
/**
@@ -182,7 +187,7 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.mp.msg.notify.ArticleNotify
* @see com.foxinmy.weixin4j.mp.api.NotifyApi
*/
public BaseResult sendNotify(BaseNotify notify) throws WeixinException {
public JsonResult sendNotify(BaseNotify notify) throws WeixinException {
return notifyApi.sendNotify(notify);
}
@@ -199,7 +204,7 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.mp.msg.notify.ArticleNotify
* @see com.foxinmy.weixin4j.mp.api.NotifyApi
*/
public BaseResult sendNotify(String touser, List<Article> articles)
public JsonResult sendNotify(String touser, List<Article> articles)
throws WeixinException {
return notifyApi.sendNotify(touser, articles);
}
@@ -220,7 +225,7 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.mp.msg.model.Voice
* @see com.foxinmy.weixin4j.mp.api.NotifyApi
*/
public BaseResult sendNotify(String touser, BaseMsg baseMsg)
public JsonResult sendNotify(String touser, BaseMsg baseMsg)
throws WeixinException {
return notifyApi.sendNotify(touser, baseMsg);
}
@@ -393,7 +398,7 @@ public class WeixinProxy {
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByGroup(JSONObject, String)}
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByOpenIds(JSONObject, String...)
*/
public BaseResult deleteMassNews(String msgid) throws WeixinException {
public JsonResult deleteMassNews(String msgid) throws WeixinException {
return massApi.deleteMassNews(msgid);
}
@@ -498,7 +503,7 @@ public class WeixinProxy {
* 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 BaseResult remarkUserName(String openId, String remark)
public JsonResult remarkUserName(String openId, String remark)
throws WeixinException {
return userApi.remarkUserName(openId, remark);
}
@@ -564,7 +569,7 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.mp.model.Group#toModifyJson()
* @see com.foxinmy.weixin4j.mp.api.GroupApi
*/
public BaseResult modifyGroup(int groupId, String name)
public JsonResult modifyGroup(int groupId, String name)
throws WeixinException {
return groupApi.modifyGroup(groupId, name);
}
@@ -582,7 +587,7 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.mp.model.Group
* @see com.foxinmy.weixin4j.mp.api.GroupApi
*/
public BaseResult moveGroup(String openId, int groupId)
public JsonResult moveGroup(String openId, int groupId)
throws WeixinException {
return groupApi.moveGroup(openId, groupId);
}
@@ -598,7 +603,7 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.type.ButtonType
* @see com.foxinmy.weixin4j.mp.api.MenuApi
*/
public BaseResult createMenu(List<Button> btnList) throws WeixinException {
public JsonResult createMenu(List<Button> btnList) throws WeixinException {
return menuApi.createMenu(btnList);
}
@@ -625,7 +630,7 @@ public class WeixinProxy {
* @see com.foxinmy.weixin4j.mp.model.Button
* @see com.foxinmy.weixin4j.mp.api.MenuApi
*/
public BaseResult deleteMenu() throws WeixinException {
public JsonResult deleteMenu() throws WeixinException {
return menuApi.deleteMenu();
}
@@ -692,7 +697,7 @@ public class WeixinProxy {
* @seee com.foxinmy.weixin4j.msg.event.TemplatesendjobfinishMessage
* @see com.foxinmy.weixin4j.mp.api.TmplApi
*/
public BaseResult sendTmplMessage(TemplateMessage tplMessage)
public JsonResult sendTmplMessage(TemplateMessage tplMessage)
throws WeixinException {
return tmplApi.sendTmplMessage(tplMessage);
}
@@ -714,8 +719,8 @@ public class WeixinProxy {
/**
* 发货通知
*
* @param weixinConfig
* V2版本
* @param weixinAccount
* 商户信息
* @param transid
* 交易单号
* @param orderNo
@@ -724,31 +729,32 @@ public class WeixinProxy {
* 成功|失败
* @param statusMsg
* status为失败时携带的信息
* @return
* @return 调用结果
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.PayApi
*/
public BaseResult deliverNotify(WeixinAccountV2 weixinConfig,
public JsonResult deliverNotify(WeixinAccount weixinAccount,
String transid, String orderNo, boolean status, String statusMsg)
throws WeixinException {
return payApi.deliverNotify(weixinConfig, transid, orderNo, status,
return payApi.deliverNotify(weixinAccount, transid, orderNo, status,
statusMsg);
}
/**
* 订单查询
*
* @param weixinConfig
* V2版本
* @param weixinAccount
* 商户信息
* @param orderNo
* 订单号
* @return
* @return 订单信息
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.PayApi
*/
public Order orderQuery(WeixinAccountV2 weixinConfig, String orderNo)
public Order orderQueryV2(WeixinAccount weixinAccount, String orderNo)
throws WeixinException {
return payApi.orderQuery(weixinConfig, orderNo);
return payApi.orderQueryV2(weixinAccount, new IdQuery(orderNo,
IdType.ORDERNO));
}
/**
@@ -758,28 +764,106 @@ public class WeixinProxy {
* 用户ID
* @param feedbackId
* 维权单号
* @return
* @return 调用结果
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @throws WeixinException
*/
public BaseResult updateFeedback(String openId, String feedbackId)
public JsonResult updateFeedback(String openId, String feedbackId)
throws WeixinException {
return payApi.updateFeedback(openId, feedbackId);
}
/**
* V3订单查询
*
* @param weixinAccount
* 商户信息
* @param idQuery
* 商户系统内部的订单号, transaction_idout_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_idout_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 weixinConfig
* 商户配置
* @param weixinAccount
* 商户信息
* @param url
* native支付URL
* 具有native标识的支付URL
* @return 转换后的短链接
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @throws WeixinException
*/
public String getShorturl(WeixinAccountV3 weixinConfig, String url)
public String getShorturl(WeixinAccount weixinAccount, String url)
throws WeixinException {
return payApi.getShorturl(weixinConfig, url);
return payApi.getShorturl(weixinAccount, url);
}
}
@@ -2,12 +2,16 @@ 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;
/**
*
@@ -19,12 +23,24 @@ import com.foxinmy.weixin4j.xml.XStream;
*/
public class BaseApi {
protected final HttpRequest request = new HttpRequest();
protected final XStream xStream = XStream.get();
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) {
@@ -4,7 +4,7 @@ import java.util.List;
import com.alibaba.fastjson.JSON;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.BaseResult;
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;
@@ -23,9 +23,9 @@ import com.foxinmy.weixin4j.token.TokenHolder;
*/
public class GroupApi extends BaseApi {
private final TokenHolder tokenApi;
public GroupApi(TokenHolder tokenApi) {
this.tokenApi = tokenApi;
private final TokenHolder tokenHolder;
public GroupApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
@@ -42,7 +42,7 @@ public class GroupApi extends BaseApi {
*/
public Group createGroup(String name) throws WeixinException {
String group_create_uri = getRequestUri("group_create_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
Group group = new Group(name);
Response response = request.post(
String.format(group_create_uri, token.getAccessToken()),
@@ -62,7 +62,7 @@ public class GroupApi extends BaseApi {
*/
public List<Group> getGroups() throws WeixinException {
String group_get_uri = getRequestUri("group_get_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
Response response = request.get(String.format(group_get_uri,
token.getAccessToken()));
@@ -83,7 +83,7 @@ public class GroupApi extends BaseApi {
*/
public int getGroupByOpenId(String openId) throws WeixinException {
String group_getid_uri = getRequestUri("group_getid_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(group_getid_uri, token.getAccessToken()),
String.format("{\"openid\":\"%s\"}", openId));
@@ -104,16 +104,16 @@ public class GroupApi extends BaseApi {
* @see com.foxinmy.weixin4j.mp.model.Group
* @see com.foxinmy.weixin4j.mp.model.Group#toModifyJson()
*/
public BaseResult modifyGroup(int groupId, String name)
public JsonResult modifyGroup(int groupId, String name)
throws WeixinException {
String group_modify_uri = getRequestUri("group_modify_uri");
Token token = tokenApi.getToken();
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.getAsResult();
return response.getAsJsonResult();
}
/**
@@ -128,14 +128,14 @@ public class GroupApi extends BaseApi {
* 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 BaseResult moveGroup(String openId, int groupId)
public JsonResult moveGroup(String openId, int groupId)
throws WeixinException {
String group_move_uri = getRequestUri("group_move_uri");
Token token = tokenApi.getToken();
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.getAsResult();
return response.getAsJsonResult();
}
}
@@ -17,10 +17,10 @@ import com.foxinmy.weixin4j.token.TokenHolder;
*/
public class HelperApi extends BaseApi {
private final TokenHolder tokenApi;
private final TokenHolder tokenHolder;
public HelperApi(TokenHolder tokenApi) {
this.tokenApi = tokenApi;
public HelperApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
@@ -34,7 +34,7 @@ public class HelperApi extends BaseApi {
*/
public String getShorturl(String url) throws WeixinException {
String shorturl_uri = getRequestUri("shorturl_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
JSONObject obj = new JSONObject();
obj.put("action", "long2short");
obj.put("long_url", url);
@@ -5,7 +5,7 @@ import java.util.List;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.BaseResult;
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;
@@ -25,10 +25,10 @@ import com.foxinmy.weixin4j.type.MediaType;
*/
public class MassApi extends BaseApi {
private final TokenHolder tokenApi;
private final TokenHolder tokenHolder;
public MassApi(TokenHolder tokenApi) {
this.tokenApi = tokenApi;
public MassApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
@@ -47,7 +47,7 @@ public class MassApi extends BaseApi {
public String uploadArticle(List<MpArticle> articles)
throws WeixinException {
String article_upload_uri = getRequestUri("article_upload_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
JSONObject obj = new JSONObject();
obj.put("articles", articles);
Response response = request.post(
@@ -75,7 +75,7 @@ public class MassApi extends BaseApi {
public String uploadVideo(String mediaId, String title, String desc)
throws WeixinException {
String video_upload_uri = getRequestUri("video_upload_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
JSONObject obj = new JSONObject();
obj.put("media_id", mediaId);
obj.put("title", title);
@@ -109,7 +109,7 @@ public class MassApi extends BaseApi {
private String massByGroup(JSONObject jsonPara, String groupId)
throws WeixinException {
String mass_group_uri = getRequestUri("mass_group_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(mass_group_uri, token.getAccessToken()),
jsonPara.toJSONString());
@@ -133,7 +133,7 @@ public class MassApi extends BaseApi {
private String massByOpenIds(JSONObject jsonPara, String... openIds)
throws WeixinException {
String mass_openid_uri = getRequestUri("mass_openid_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(mass_openid_uri, token.getAccessToken()),
jsonPara.toJSONString());
@@ -255,15 +255,15 @@ public class MassApi extends BaseApi {
* @see {@link com.foxinmy.weixin4j.mp.api.MassApi#massByGroup(JSONObject, String)}
* @see {@link com.foxinmy.weixin4j.mp.api.MassApi#massByOpenIds(JSONObject, String...)
*/
public BaseResult deleteMassNews(String msgid) throws WeixinException {
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 = tokenApi.getToken();
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(mass_delete_uri, token.getAccessToken()),
obj.toJSONString());
return response.getAsResult();
return response.getAsJsonResult();
}
}
@@ -28,10 +28,10 @@ import com.foxinmy.weixin4j.util.IOUtil;
*/
public class MediaApi extends BaseApi {
private final TokenHolder tokenApi;
private final TokenHolder tokenHolder;
public MediaApi(TokenHolder tokenApi) {
this.tokenApi = tokenApi;
public MediaApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
@@ -72,7 +72,7 @@ public class MediaApi extends BaseApi {
*/
public String uploadMedia(String fileName, byte[] bytes, MediaType mediaType)
throws WeixinException {
Token token = tokenApi.getToken();
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(
@@ -107,14 +107,8 @@ public class MediaApi extends BaseApi {
if (file.exists()) {
return file;
}
FileOutputStream out = null;
try {
file.createNewFile();
} catch (IOException e) {
file.getParentFile().mkdirs();
file.createNewFile();
}
out = new FileOutputStream(file);
file.createNewFile();
FileOutputStream out = new FileOutputStream(file);
out.write(datas);
out.close();
return file;
@@ -131,7 +125,7 @@ public class MediaApi extends BaseApi {
*/
public byte[] downloadMediaData(String mediaId, MediaType mediaType)
throws WeixinException {
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
String file_download_uri = getRequestUri("file_download_uri");
Response response = request.get(String.format(file_download_uri,
token.getAccessToken(), mediaId));
@@ -5,7 +5,7 @@ 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.BaseResult;
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;
@@ -22,10 +22,10 @@ import com.foxinmy.weixin4j.token.TokenHolder;
*/
public class MenuApi extends BaseApi {
private final TokenHolder tokenApi;
private final TokenHolder tokenHolder;
public MenuApi(TokenHolder tokenApi) {
this.tokenApi = tokenApi;
public MenuApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
@@ -37,16 +37,16 @@ public class MenuApi extends BaseApi {
* 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 BaseResult createMenu(List<Button> btnList) throws WeixinException {
public JsonResult createMenu(List<Button> btnList) throws WeixinException {
String menu_create_uri = getRequestUri("menu_create_uri");
Token token = tokenApi.getToken();
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.getAsResult();
return response.getAsJsonResult();
}
/**
@@ -60,7 +60,7 @@ public class MenuApi extends BaseApi {
*/
public List<Button> getMenu() throws WeixinException {
String menu_get_uri = getRequestUri("menu_get_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
Response response = request.get(String.format(menu_get_uri,
token.getAccessToken()));
@@ -77,12 +77,12 @@ public class MenuApi extends BaseApi {
* 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 BaseResult deleteMenu() throws WeixinException {
public JsonResult deleteMenu() throws WeixinException {
String menu_delete_uri = getRequestUri("menu_delete_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
Response response = request.get(String.format(menu_delete_uri,
token.getAccessToken()));
return response.getAsResult();
return response.getAsJsonResult();
}
}
@@ -5,7 +5,7 @@ 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.BaseResult;
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;
@@ -33,10 +33,10 @@ import com.foxinmy.weixin4j.token.TokenHolder;
*/
public class NotifyApi extends BaseApi {
private final TokenHolder tokenApi;
private final TokenHolder tokenHolder;
public NotifyApi(TokenHolder tokenApi) {
this.tokenApi = tokenApi;
public NotifyApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
@@ -48,14 +48,14 @@ public class NotifyApi extends BaseApi {
* @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 BaseResult sendNotify(String jsonPara) throws WeixinException {
private JsonResult sendNotify(String jsonPara) throws WeixinException {
String custom_notify_uri = getRequestUri("custom_notify_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(custom_notify_uri, token.getAccessToken()),
jsonPara);
return response.getAsResult();
return response.getAsJsonResult();
}
/**
@@ -74,7 +74,7 @@ public class NotifyApi extends BaseApi {
* @see com.foxinmy.weixin4j.mp.msg.notify.ArticleNotify
* @see {@link com.foxinmy.weixin4j.mp.api.NotifyApi#sendNotify(String)}
*/
public BaseResult sendNotify(BaseNotify notify) throws WeixinException {
public JsonResult sendNotify(BaseNotify notify) throws WeixinException {
return sendNotify(notify.toJson());
}
@@ -90,7 +90,7 @@ public class NotifyApi extends BaseApi {
* @see com.foxinmy.weixin4j.mp.msg.model.Article
* @see com.foxinmy.weixin4j.mp.msg.notify.ArticleNotify
*/
public BaseResult sendNotify(String touser, List<Article> articles)
public JsonResult sendNotify(String touser, List<Article> articles)
throws WeixinException {
ArticleNotify notify = new ArticleNotify(touser);
notify.pushAll(articles);
@@ -114,7 +114,7 @@ public class NotifyApi extends BaseApi {
* @see {@link com.foxinmy.weixin4j.mp.msg.model.BaseMsg#toNotifyJson()}
* @see {@link com.foxinmy.weixin4j.mp.api.NotifyApi#sendNotify(String)}
*/
public BaseResult sendNotify(String touser, BaseMsg baseMsg)
public JsonResult sendNotify(String touser, BaseMsg baseMsg)
throws WeixinException {
StringBuilder jsonPara = new StringBuilder();
String mediaType = baseMsg.getMediaType().name();
@@ -154,7 +154,7 @@ public class NotifyApi extends BaseApi {
obj.put("pagesize", pagesize > 1000 ? 1000 : pagesize);
obj.put("pageindex", pageindex);
String custom_record_uri = getRequestUri("custom_record_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(custom_record_uri, token.getAccessToken()),
obj.toJSONString());
@@ -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)));
}
}
@@ -24,10 +24,10 @@ import com.foxinmy.weixin4j.token.TokenHolder;
*/
public class QrApi extends BaseApi {
private final TokenHolder tokenApi;
private final TokenHolder tokenHolder;
public QrApi(TokenHolder tokenApi) {
this.tokenApi = tokenApi;
public QrApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
@@ -39,7 +39,7 @@ public class QrApi extends BaseApi {
* @see {@link com.foxinmy.weixin4j.mp.api.QrApi#getQR(QRParameter)}
*/
public byte[] getQRData(QRParameter parameter) throws WeixinException {
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
String qr_uri = getRequestUri("qr_ticket_uri");
Response response = request.post(
String.format(qr_uri, token.getAccessToken()),
@@ -97,14 +97,8 @@ public class QrApi extends BaseApi {
return file;
}
byte[] datas = getQRData(parameter);
FileOutputStream out = null;
try {
file.createNewFile();
} catch (IOException e) {
file.getParentFile().mkdirs();
file.createNewFile();
}
out = new FileOutputStream(file);
file.createNewFile();
FileOutputStream out = new FileOutputStream(file);
out.write(datas);
out.close();
return file;
@@ -1,7 +1,7 @@
package com.foxinmy.weixin4j.mp.api;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.BaseResult;
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;
@@ -18,10 +18,10 @@ import com.foxinmy.weixin4j.token.TokenHolder;
*/
public class TmplApi extends BaseApi {
private final TokenHolder tokenApi;
private final TokenHolder tokenHolder;
public TmplApi(TokenHolder tokenApi) {
this.tokenApi = tokenApi;
public TmplApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
@@ -35,14 +35,14 @@ public class TmplApi extends BaseApi {
* @see com.foxinmy.weixin4j.mp.response.TemplateMessage
* @seee com.foxinmy.weixin4j.msg.event.TemplatesendjobfinishMessage
*/
public BaseResult sendTmplMessage(TemplateMessage tplMessage)
public JsonResult sendTmplMessage(TemplateMessage tplMessage)
throws WeixinException {
Token token = tokenApi.getToken();
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.getAsResult();
return response.getAsJsonResult();
}
}
@@ -5,8 +5,9 @@ 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.BaseResult;
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;
@@ -25,10 +26,10 @@ import com.foxinmy.weixin4j.token.TokenHolder;
*/
public class UserApi extends BaseApi {
private final TokenHolder tokenApi;
private final TokenHolder tokenHolder;
public UserApi(TokenHolder tokenApi) {
this.tokenApi = tokenApi;
public UserApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
@@ -46,7 +47,8 @@ public class UserApi extends BaseApi {
String user_token_uri = getRequestUri("sns_user_token_uri");
Response response = request.get(String.format(user_token_uri, code));
return response.getAsObject(UserToken.class);
return response.getAsObject(new TypeReference<UserToken>() {
});
}
/**
@@ -67,7 +69,8 @@ public class UserApi extends BaseApi {
Response response = request.get(String.format(user_info_uri,
token.getAccessToken(), token.getOpenid()));
return response.getAsObject(User.class);
return response.getAsObject(new TypeReference<User>() {
});
}
/**
@@ -87,11 +90,12 @@ public class UserApi extends BaseApi {
*/
public User getUser(String openId) throws WeixinException {
String user_info_uri = getRequestUri("api_user_info_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
Response response = request.get(String.format(user_info_uri,
token.getAccessToken(), openId));
return response.getAsObject(User.class);
return response.getAsObject(new TypeReference<User>() {
});
}
/**
@@ -107,11 +111,13 @@ public class UserApi extends BaseApi {
*/
public Following getFollowing(String nextOpenId) throws WeixinException {
String fllowing_uri = getRequestUri("following_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
Response response = request.get(String.format(fllowing_uri,
token.getAccessToken(), nextOpenId == null ? "" : nextOpenId));
Following following = response.getAsObject(Following.class);
Following following = response
.getAsObject(new TypeReference<Following>() {
});
if (following.getCount() > 0) {
List<String> openIds = JSON.parseArray(following.getDataJson()
@@ -165,10 +171,10 @@ public class UserApi extends BaseApi {
* @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 BaseResult remarkUserName(String openId, String remark)
public JsonResult remarkUserName(String openId, String remark)
throws WeixinException {
String updateremark_uri = getRequestUri("updateremark_uri");
Token token = tokenApi.getToken();
Token token = tokenHolder.getToken();
JSONObject obj = new JSONObject();
obj.put("openid", openId);
obj.put("remark", remark);
@@ -176,6 +182,6 @@ public class UserApi extends BaseApi {
String.format(updateremark_uri, token.getAccessToken()),
obj.toJSONString());
return response.getAsResult();
return response.getAsJsonResult();
}
}
@@ -65,9 +65,17 @@ p_shorturl_uri={mch_base_url}/tools/shorturl
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
# \u67e5\u8be2\u8ba2\u5355
# \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
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,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,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
@@ -7,7 +7,7 @@ import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.BaseResult;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.mp.api.GroupApi;
import com.foxinmy.weixin4j.mp.model.Group;
@@ -24,7 +24,7 @@ public class GroupTest extends TokenTest {
@Before
public void init() {
groupApi = new GroupApi(tokenApi);
groupApi = new GroupApi(tokenHolder);
}
@Test
@@ -50,14 +50,14 @@ public class GroupTest extends TokenTest {
@Test
public void modify() throws WeixinException {
BaseResult result = groupApi.modifyGroup(100, "my1");
Assert.assertEquals(0, result.getErrcode());
JsonResult result = groupApi.modifyGroup(100, "my1");
Assert.assertEquals(0, result.getCode());
}
@Test
public void move() throws WeixinException {
BaseResult result = groupApi.moveGroup("owGBft_vbBbOaQOmpEUE4xDLeRSU",
JsonResult result = groupApi.moveGroup("owGBft_vbBbOaQOmpEUE4xDLeRSU",
100);
Assert.assertEquals(0, result.getErrcode());
Assert.assertEquals(0, result.getCode());
}
}
@@ -26,7 +26,7 @@ public class MediaTest extends TokenTest {
@Before
public void init() {
mediaApi = new MediaApi(tokenApi);
mediaApi = new MediaApi(tokenHolder);
}
@Test
@@ -8,7 +8,7 @@ import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.BaseResult;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.mp.api.MenuApi;
import com.foxinmy.weixin4j.mp.model.Button;
import com.foxinmy.weixin4j.type.ButtonType;
@@ -28,7 +28,7 @@ public class MenuTest extends TokenTest {
@Before
public void init() {
menuApi = new MenuApi(tokenApi);
menuApi = new MenuApi(tokenHolder);
}
@Test
@@ -43,8 +43,8 @@ public class MenuTest extends TokenTest {
b = new Button("qq", "http://www.qq.com");
btnList.add(b);
BaseResult result = menuApi.createMenu(btnList);
Assert.assertEquals(0, result.getErrcode());
JsonResult result = menuApi.createMenu(btnList);
Assert.assertEquals(0, result.getCode());
}
@Test
@@ -58,7 +58,7 @@ public class MenuTest extends TokenTest {
@Test
public void delete() throws WeixinException {
BaseResult result = menuApi.deleteMenu();
Assert.assertEquals(0, result.getErrcode());
JsonResult result = menuApi.deleteMenu();
Assert.assertEquals(0, result.getCode());
}
}
@@ -25,7 +25,7 @@ public class QRTest extends TokenTest {
@Before
public void init() {
qrApi = new QrApi(tokenApi);
qrApi = new QrApi(tokenHolder);
}
@Test
@@ -18,15 +18,15 @@ import com.foxinmy.weixin4j.token.TokenHolder;
*/
public class TokenTest {
protected TokenHolder tokenApi;
protected TokenHolder tokenHolder;
@Before
public void setUp() {
tokenApi = new FileTokenHolder();
tokenHolder = new FileTokenHolder();
}
@Test
public void test() throws WeixinException {
Assert.assertNotNull(tokenApi.getToken());
Assert.assertNotNull(tokenHolder.getToken());
}
}
@@ -7,7 +7,7 @@ import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.BaseResult;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.mp.api.UserApi;
import com.foxinmy.weixin4j.mp.model.User;
@@ -24,13 +24,14 @@ public class UserTest extends TokenTest {
@Before
public void init() {
userApi = new UserApi(tokenApi);
userApi = new UserApi(tokenHolder);
}
@Test
public void getUser() throws WeixinException {
User user = userApi.getUser("owGBft_vbBbOaQOmpEUE4xDLeRSU");
Assert.assertNotNull(user);
System.out.println(user);
}
@Test
@@ -44,8 +45,8 @@ public class UserTest extends TokenTest {
@Test
public void remark() throws WeixinException {
BaseResult result = userApi.remarkUserName(
JsonResult result = userApi.remarkUserName(
"owGBft_vbBbOaQOmpEUE4xDLeRSU", "foo");
Assert.assertEquals(0, result.getErrcode());
Assert.assertEquals(0, result.getCode());
}
}
@@ -10,7 +10,7 @@ import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.BaseResult;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.mp.api.MassApi;
import com.foxinmy.weixin4j.mp.api.MediaApi;
import com.foxinmy.weixin4j.mp.model.MpArticle;
@@ -32,8 +32,8 @@ public class MassMsgTest extends TokenTest {
@Before
public void init() {
this.massApi = new MassApi(tokenApi);
this.mediaApi = new MediaApi(tokenApi);
this.massApi = new MassApi(tokenHolder);
this.mediaApi = new MediaApi(tokenHolder);
}
@Test
@@ -87,7 +87,7 @@ public class MassMsgTest extends TokenTest {
@Test
public void deleteMass() throws WeixinException {
BaseResult result = massApi.deleteMassNews("34182");
Assert.assertEquals(0, result.getErrcode());
JsonResult result = massApi.deleteMassNews("34182");
Assert.assertEquals(0, result.getCode());
}
}

Some files were not shown because too many files have changed in this diff Show More