新增退款接口

This commit is contained in:
jy.hu 2014-11-06 20:46:35 +08:00
parent f489667109
commit 26d04798c5
34 changed files with 752 additions and 502 deletions

View File

@ -48,10 +48,14 @@ weixin4j
+ 优化了代码
* 2014-11-06
+ **weixin-base**: 删除`WeixinConfig`类只保留`WeixinAccount`
+ **weixin-mp**: 新增`退款接口`
接下来
------
* 公众号退款接口
* 公众号智能接口
* 微信消息加密

View File

@ -17,4 +17,8 @@ weixin4j-base
+ `TokenApi`重命名为`TokenHolder`
+ 新增`WeixinConfig`等类
+ 新增`WeixinConfig`等类
* 2014-11-06
+ 删除`WeixinConfig`类只保留`WeixinAccount`

View File

@ -47,7 +47,7 @@ import com.foxinmy.weixin4j.exception.WeixinException;
*/
public class HttpRequest {
private final String SUCCESS = "success";
private AbstractHttpClient client;
protected AbstractHttpClient client;
public HttpRequest() {
this(150, 100, 10000, 10000);
@ -155,17 +155,17 @@ public class HttpRequest {
HttpResponse httpResponse = client.execute(request);
StatusLine statusLine = httpResponse.getStatusLine();
HttpEntity httpEntity = httpResponse.getEntity();
int status = statusLine.getStatusCode();
if (status != HttpStatus.SC_OK) {
throw new WeixinException(status + "", "request fail");
throw new WeixinException(Integer.toString(status),
"request fail");
}
// 301或者302
if (status == HttpStatus.SC_MOVED_PERMANENTLY
|| status == HttpStatus.SC_MOVED_TEMPORARILY) {
throw new WeixinException(status + "", String.format(
"the page was redirected to %s",
httpResponse.getFirstHeader("location")));
throw new WeixinException(Integer.toString(status),
String.format("the page was redirected to %s",
httpResponse.getFirstHeader("location")));
}
byte[] data = EntityUtils.toByteArray(httpEntity);
response = new Response();
@ -186,8 +186,8 @@ public class HttpRequest {
JsonResult jsonResult = response.getAsJsonResult();
response.setJsonResult(true);
if (jsonResult.getCode() != 0) {
throw new WeixinException(jsonResult.getCode() + "",
jsonResult.getDesc());
throw new WeixinException(Integer.toString(jsonResult
.getCode()), jsonResult.getDesc());
}
return response;
} catch (JSONException e) {
@ -205,7 +205,7 @@ public class HttpRequest {
}
}
} catch (IOException e) {
throw new WeixinException(e.getMessage());
throw new WeixinException("-1", e.getMessage());
} finally {
request.releaseConnection();
}

View File

@ -0,0 +1,48 @@
package com.foxinmy.weixin4j.http;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
/**
* ssl请求
*
* @className SSLHttpRequest
* @author jy
* @date 2014年11月6日
* @since JDK 1.7
* @see
*/
public class SSLHttpRequest extends HttpRequest {
public SSLHttpRequest(String password, File file) throws IOException {
this(password, new FileInputStream(file));
}
public SSLHttpRequest(String password, InputStream inputStream) {
super();
try {
KeyStore trustStore = KeyStore.getInstance("PKCS12");
trustStore.load(inputStream, password.toCharArray());
SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore,
password);
client.getConnectionManager().getSchemeRegistry()
.register(new Scheme("https", 443, socketFactory));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (Exception ignore) {
;
}
}
}
}

View File

@ -24,7 +24,6 @@ public class Token implements Serializable {
private String accessToken;
@JSONField(name = "expires_in")
private int expiresIn;
private String openid;
private long time;
public String getAccessToken() {
@ -43,14 +42,6 @@ public class Token implements Serializable {
this.expiresIn = expiresIn;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public long getTime() {
return time;
}
@ -69,6 +60,7 @@ public class Token implements Serializable {
@Override
public String toString() {
return "Token [accessToken=" + accessToken + ", expiresIn=" + expiresIn + ", openid=" + openid + ", time=" + time + "]";
return "Token [accessToken=" + accessToken + ", expiresIn=" + expiresIn
+ ", time=" + time + "]";
}
}

View File

@ -1,5 +1,7 @@
package com.foxinmy.weixin4j.model;
import java.io.Serializable;
/**
* 微信账户信息
*
@ -9,9 +11,16 @@ package com.foxinmy.weixin4j.model;
* @since JDK 1.7
* @see
*/
public class WeixinAccount extends WeixinConfig {
public class WeixinAccount implements Serializable {
private static final long serialVersionUID = 3689999353867189585L;
private String token;
// 支付场景下为用户的openid 其余情况可能是公众号的原始ID
private String openId;
// 公众号身份的唯一标识
private String appId;
// 公众平台接口 API 的权限获取所需密钥 Key
private String appSecret;
// 公众号支付请求中用于加密的密钥 Key,可验证商户唯一身份,PaySignKey 对应于支付场景中的 appKey
private String paySignKey;
// 财付通商户身份的标识
@ -30,6 +39,38 @@ public class WeixinAccount extends WeixinConfig {
// 是否是订阅号
private boolean isSubscribe;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getAppSecret() {
return appSecret;
}
public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}
public String getPaySignKey() {
return paySignKey;
}
@ -98,19 +139,51 @@ public class WeixinAccount extends WeixinConfig {
}
public WeixinAccount(String appId, String appSecret) {
this.appId = appId;
this.appSecret = appSecret;
}
/**
* V3版本字段
*
* @param appId
* @param appSecret
* @param paySignKey
* @param mchId
*/
public WeixinAccount(String appId, String appSecret, String paySignKey,
String mchId) {
super(appId, appSecret);
this(appId, appSecret);
this.paySignKey = paySignKey;
this.mchId = mchId;
}
/**
* V2版本字段
*
* @param appId
* @param appSecret
* @param paySignKey
* @param partnerId
* @param partnerKey
*/
public WeixinAccount(String appId, String appSecret, String paySignKey,
String partnerId, String partnerKey) {
super(appId, appSecret);
this(appId, appSecret);
this.paySignKey = paySignKey;
this.partnerId = partnerId;
this.partnerKey = partnerKey;
}
@Override
public String toString() {
return "WeixinAccount [token=" + token + ", openId=" + openId
+ ", appId=" + appId + ", appSecret=" + appSecret
+ ", paySignKey=" + paySignKey + ", partnerId=" + partnerId
+ ", partnerKey=" + partnerKey + ", mchId=" + mchId
+ ", deviceInfo=" + deviceInfo + ", isAlive=" + isAlive
+ ", isService=" + isService + ", isSubscribe=" + isSubscribe
+ "]";
}
}

View File

@ -1,64 +0,0 @@
package com.foxinmy.weixin4j.model;
import java.io.Serializable;
public class WeixinConfig implements Serializable {
private static final long serialVersionUID = -3454743453282851243L;
private String token;
// 支付场景下为用户的openid 其余情况可能是公众号的原始ID
private String openId;
// 公众号身份的唯一标识
private String appId;
// 除了支付请求需要用到 paySignKey,公众平台接口 API 的权限获取所需密 Key
private String appSecret;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getAppSecret() {
return appSecret;
}
public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}
public WeixinConfig() {
}
public WeixinConfig(String appId, String appSecret) {
this.appId = appId;
this.appSecret = appSecret;
}
@Override
public String toString() {
return "WeixinConfig [token=" + token + ", openId=" + openId
+ ", appId=" + appId + ", appSecret=" + appSecret + "]";
}
}

View File

@ -1,7 +1,7 @@
package com.foxinmy.weixin4j.token;
import com.foxinmy.weixin4j.http.HttpRequest;
import com.foxinmy.weixin4j.model.WeixinConfig;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
@ -16,17 +16,17 @@ import com.foxinmy.weixin4j.util.ConfigUtil;
public abstract class AbstractTokenHolder implements TokenHolder {
protected final String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
protected final HttpRequest request = new HttpRequest();
private final WeixinConfig weixinConfig;
private final WeixinAccount weixinAccount;
public AbstractTokenHolder() {
this.weixinConfig = ConfigUtil.getWeixinConfig();
this.weixinAccount = ConfigUtil.getWeixinAccount();
}
public AbstractTokenHolder(String appid, String appsecret) {
this.weixinConfig = new WeixinConfig(appid, appsecret);
public AbstractTokenHolder(WeixinAccount weixinAccount) {
this.weixinAccount = weixinAccount;
}
public WeixinConfig getConfig() {
return this.weixinConfig;
public WeixinAccount getAccount() {
return weixinAccount;
}
}

View File

@ -12,6 +12,7 @@ import com.alibaba.fastjson.TypeReference;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.util.ConfigUtil;
import com.foxinmy.weixin4j.xml.XStream;
@ -32,8 +33,12 @@ public class FileTokenHolder extends AbstractTokenHolder {
super();
}
public FileTokenHolder(WeixinAccount weixinAccount) {
super(weixinAccount);
}
public FileTokenHolder(String appid, String appsecret) {
super(appid, appsecret);
this(new WeixinAccount(appid, appsecret));
}
/**
@ -50,8 +55,8 @@ public class FileTokenHolder extends AbstractTokenHolder {
*/
@Override
public Token getToken() throws WeixinException {
String appid = getConfig().getAppId();
String appsecret = getConfig().getAppSecret();
String appid = getAccount().getAppId();
String appsecret = getAccount().getAppSecret();
if (StringUtils.isBlank(appid) || StringUtils.isBlank(appsecret)) {
throw new IllegalArgumentException(
"appid or appsecret not be null!");
@ -65,7 +70,6 @@ public class FileTokenHolder extends AbstractTokenHolder {
if (token_file.exists()) {
token = XStream.get(new FileInputStream(token_file),
Token.class);
long expise_time = token.getTime()
+ (token.getExpiresIn() * 1000) - 3;
if (expise_time > now_time) {
@ -79,7 +83,6 @@ public class FileTokenHolder extends AbstractTokenHolder {
token = response.getAsObject(new TypeReference<Token>() {
});
token.setTime(now_time);
token.setOpenid(appid);
XStream.to(token, new FileOutputStream(token_file));
} catch (IOException e) {
throw new WeixinException("-1", "IO ERROR");

View File

@ -10,6 +10,7 @@ import redis.clients.jedis.exceptions.JedisException;
import com.alibaba.fastjson.TypeReference;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.model.WeixinAccount;
/**
* 基于redis保存的Token获取类
@ -26,17 +27,7 @@ public class RedisTokenHolder extends AbstractTokenHolder {
private JedisPool jedisPool;
public RedisTokenHolder() {
super();
}
public RedisTokenHolder(String appid, String appsecret) {
this(appid, appsecret, "localhost", 6379);
}
public RedisTokenHolder(String appid, String appsecret, String host,
int port) {
super(appid, appsecret);
private void createPool(String host, int port) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(50);
poolConfig.setMaxIdle(5);
@ -46,10 +37,33 @@ public class RedisTokenHolder extends AbstractTokenHolder {
this.jedisPool = new JedisPool(poolConfig, host, port);
}
public RedisTokenHolder() {
this("localhost", 6379);
}
public RedisTokenHolder(String host, int port) {
super();
createPool(host, port);
}
public RedisTokenHolder(WeixinAccount weixinAccount) {
this(weixinAccount, "localhost", 6379);
}
public RedisTokenHolder(String appId, String appSecret, String host,
int port) {
this(new WeixinAccount(appId, appSecret), host, port);
}
public RedisTokenHolder(WeixinAccount weixinAccount, String host, int port) {
super(weixinAccount);
createPool(host, port);
}
@Override
public Token getToken() throws WeixinException {
String appid = getConfig().getAppId();
String appsecret = getConfig().getAppSecret();
String appid = getAccount().getAppId();
String appsecret = getAccount().getAppSecret();
if (StringUtils.isBlank(appid) || StringUtils.isBlank(appsecret)) {
throw new IllegalArgumentException(
"appid or appsecret not be null!");
@ -68,13 +82,12 @@ public class RedisTokenHolder extends AbstractTokenHolder {
});
jedis.setex(key, token.getExpiresIn() - 3,
token.getAccessToken());
token.setTime(System.currentTimeMillis());
} else {
token = new Token();
token.setAccessToken(accessToken);
token.setExpiresIn(jedis.ttl(key).intValue());
}
token.setTime(System.currentTimeMillis());
token.setOpenid(appid);
} catch (JedisException e) {
jedisPool.returnBrokenResource(jedis);
} finally {
@ -82,4 +95,4 @@ public class RedisTokenHolder extends AbstractTokenHolder {
}
return token;
}
}
}

View File

@ -2,7 +2,7 @@ package com.foxinmy.weixin4j.token;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.model.WeixinConfig;
import com.foxinmy.weixin4j.model.WeixinAccount;
/**
* 获取Token接口
@ -17,7 +17,7 @@ import com.foxinmy.weixin4j.model.WeixinConfig;
* @see com.foxinmy.weixin4j.token.RedisTokenHolder
*/
public interface TokenHolder {
public WeixinConfig getConfig();
public WeixinAccount getAccount();
public Token getToken() throws WeixinException;
}

View File

@ -6,7 +6,6 @@ import java.util.Set;
import com.alibaba.fastjson.JSON;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.model.WeixinConfig;
/**
* 商户配置工具类
@ -38,9 +37,4 @@ public class ConfigUtil {
String text = getValue("account");
return JSON.parseObject(text, WeixinAccount.class);
}
public static WeixinConfig getWeixinConfig() {
String text = getValue("account");
return JSON.parseObject(text, WeixinConfig.class);
}
}

View File

@ -1,6 +1,7 @@
package com.foxinmy.weixin4j.util;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@ -25,7 +26,16 @@ public class DateUtil {
return new SimpleDateFormat(yyyyMMddHHmmss).format(date);
}
public static String format2fee(double fee) {
public static Date parse2yyyyMMddHHmmss(String date) {
try {
return new SimpleDateFormat(yyyyMMddHHmmss).parse(date);
} catch (ParseException e) {
;
}
return null;
}
public static String formaFee2Fen(double fee) {
return new DecimalFormat("#").format(fee * 100);
}
}

View File

@ -28,7 +28,7 @@ weixin4j-mp
* **weixin4j-mp-server**
`netty服务器 & 消息分发`
`netty服务器` & `消息分发`
更新LOG
-------
@ -48,4 +48,8 @@ weixin4j-mp
+ `weixin-mp`分离为`weixin-mp-api``weixin-mp-server`两个工程
+ **weixin-mp**: 加入`支付`模块
+ **weixin-mp**: 新增`支付`模块
* 2014-11-06
+ **weixin-mp**: 新增`退款接口`

View File

@ -41,7 +41,7 @@ weixin.properties说明
| media_path | 调用媒体接口时保存媒体文件的物理路径 |
| bill_path | 调用支付(`V3`)下载对账单接口保存excel文件的物理路径 |
示例(properties中换行用右斜杆\)
示例(properties中换行用右斜杆\\)
> account={"appId":"appId","appSecret":"appSecret",
> "token":"开放者的token 非必须","openId":"公众号的openid 非必须",
@ -84,4 +84,8 @@ weixin.properties说明
+ 分离为`weixin-mp-api``weixin-mp-server`两个工程
+ 加入`支付模块`
+ 新增`支付模块`
* 2014-11-06
+ 新增`退款申请`接口

View File

@ -2,6 +2,7 @@ package com.foxinmy.weixin4j.mp;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
@ -25,18 +26,18 @@ import com.foxinmy.weixin4j.mp.model.CustomRecord;
import com.foxinmy.weixin4j.mp.model.Following;
import com.foxinmy.weixin4j.mp.model.Group;
import com.foxinmy.weixin4j.mp.model.MpArticle;
import com.foxinmy.weixin4j.mp.model.OauthToken;
import com.foxinmy.weixin4j.mp.model.QRParameter;
import com.foxinmy.weixin4j.mp.model.User;
import com.foxinmy.weixin4j.mp.model.OauthToken;
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.v2.Order;
import com.foxinmy.weixin4j.mp.payment.v3.Refund;
import com.foxinmy.weixin4j.mp.payment.v3.RefundResult;
import com.foxinmy.weixin4j.mp.response.TemplateMessage;
import com.foxinmy.weixin4j.mp.type.BillType;
import com.foxinmy.weixin4j.mp.type.IdQuery;
import com.foxinmy.weixin4j.mp.type.IdType;
import com.foxinmy.weixin4j.token.FileTokenHolder;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.type.MediaType;
@ -396,8 +397,7 @@ public class WeixinProxy {
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3#.E5.88.A0.E9.99.A4.E7.BE.A4.E5.8F.91">删除群发</a>
* @see com.foxinmy.weixin4j.mp.api.MassApi
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByGroup(JSONObject, String)}
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByOpenIds(JSONObject, String...)
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByOpenIds(JSONObject, String...)
*/
public JsonResult deleteMassNews(String msgid) throws WeixinException {
return massApi.deleteMassNews(msgid);
@ -720,8 +720,6 @@ public class WeixinProxy {
/**
* 发货通知
*
* @param weixinAccount
* 商户信息
* @param openId
* 用户ID
* @param transid
@ -736,28 +734,25 @@ public class WeixinProxy {
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.PayApi
*/
public JsonResult deliverNotify(WeixinAccount weixinAccount, String openId,
String transid, String orderNo, boolean status, String statusMsg)
public JsonResult deliverNotify(String openId, String transid,
String outTradeNo, boolean status, String statusMsg)
throws WeixinException {
return payApi.deliverNotify(weixinAccount, openId, transid, orderNo,
status, statusMsg);
return payApi.deliverNotify(openId, transid, outTradeNo, status,
statusMsg);
}
/**
* 订单查询
*
* @param weixinAccount
* 商户信息
* @param orderNo
* @param outTradeNo
* 订单号
* @return 订单信息
* @throws WeixinException
* @see com.foxinmy.weixin4j.mp.api.PayApi
*/
public Order orderQueryV2(WeixinAccount weixinAccount, String orderNo)
public Order orderQueryV2(WeixinAccount weixinAccount, String outTradeNo)
throws WeixinException {
return payApi.orderQueryV2(weixinAccount, new IdQuery(orderNo,
IdType.ORDERNO));
return payApi.orderQueryV2(outTradeNo);
}
/**
@ -779,18 +774,15 @@ public class WeixinProxy {
/**
* 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)
public com.foxinmy.weixin4j.mp.payment.v3.Order orderQueryV3(IdQuery idQuery)
throws WeixinException {
return payApi.orderQueryV3(weixinAccount, idQuery);
return payApi.orderQueryV3(idQuery);
}
/**
@ -800,8 +792,6 @@ public class WeixinProxy {
* 2.微信在次日 9 点启动生成前一天的对账单,建议商户 9 点半后再获取;<br>
* 3.对账单中涉及金额的字段单位为<br>
*
* @param weixinAccount
* 商户配置
* @param billDate
* 下载对账单的日期
* @param billType
@ -812,16 +802,60 @@ public class WeixinProxy {
* @throws WeixinException
* @throws IOException
*/
public File downloadbill(WeixinAccount weixinAccount, Date billDate,
BillType billType) throws WeixinException, IOException {
return payApi.downloadbill(weixinAccount, billDate, billType);
public File downloadbill(Date billDate, BillType billType)
throws WeixinException, IOException {
return payApi.downloadbill(billDate, billType);
}
/**
* 申请退款(请求需要双向证书)<br/>
* <p style="color:red">
* 交易时间超过 1 年的订单无法提交退款; <br/>
* 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no一笔退款失 败后重新提交,要采用原来的
* out_refund_no总退款金额不能超过用户实际支付金额<br/>
* </p>
*
* @param ca
* 证书文件
* @param idQuery
* ) 商户系统内部的订单号, transaction_id out_trade_no 二选一,如果同时存在优先级:
* transaction_id> out_trade_no
* @param outRefundNo
* 商户系统内部的退款单号, 户系统内部唯一,同一退款单号多次请求只退一笔
* @param totalFee
* 订单总金额,单位为元
* @param refundFee
* 退款总金额,单位为元,可以做部分退款
* @param opUserId
* 操作员帐号, 默认为商户号
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @throws WeixinException
* @throws IOException
* @return 退款结果
*/
public RefundResult refund(InputStream ca, IdQuery idQuery,
String outRefundNo, double totalFee, double refundFee,
String opUserId) throws WeixinException, IOException {
return payApi.refund(idQuery, outRefundNo, totalFee, refundFee,
opUserId);
}
/**
* 使用properties中配置的ca文件
*
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#refund(InputStream, IdQuery, String, double, double, String)}
*/
public RefundResult refund(IdQuery idQuery, String outRefundNo,
double totalFee, double refundFee, String opUserId)
throws WeixinException, IOException {
return payApi.refund(idQuery, outRefundNo, totalFee, refundFee,
opUserId);
}
/**
* 退款查询<br/>
* 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
*
* @param weixinAccount
* @param idQuery
* 单号 refund_idout_refund_no out_trade_no transaction_id
* 四个参数必填一个,优先级为:
@ -830,9 +864,8 @@ public class WeixinProxy {
* @return 退款记录
* @throws WeixinException
*/
public Refund refundQuery(WeixinAccount weixinAccount, IdQuery idQuery)
throws WeixinException {
return payApi.refundQuery(weixinAccount, idQuery);
public Refund refundQuery(IdQuery idQuery) throws WeixinException {
return payApi.refundQuery(idQuery);
}
/**
@ -840,33 +873,26 @@ public class WeixinProxy {
* 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
* 成支付请按正常支付处理如果出现银行掉单,调用关单成功后,微信后台会主动发起退款
*
* @param weixinAccount
* 商户信息
* @param order
* @param outTradeNo
* 商户系统内部的订单号
* @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));
public XmlResult closeOrder(String outTradeNo) throws WeixinException {
return payApi.closeOrder(outTradeNo);
}
/**
* native支付URL转短链接
*
* @param weixinAccount
* 商户信息
* @param url
* 具有native标识的支付URL
* @return 转换后的短链接
* @see com.foxinmy.weixin4j.mp.api.PayApi
* @throws WeixinException
*/
public String getPayShorturl(WeixinAccount weixinAccount, String url)
throws WeixinException {
return payApi.getShorturl(weixinAccount, url);
public String getPayShorturl(String url) throws WeixinException {
return payApi.getShorturl(url);
}
}

View File

@ -2,8 +2,10 @@ package com.foxinmy.weixin4j.mp.api;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
@ -16,6 +18,7 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import com.alibaba.fastjson.JSON;
@ -25,6 +28,7 @@ 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.SSLHttpRequest;
import com.foxinmy.weixin4j.http.XmlResult;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.model.WeixinAccount;
@ -32,8 +36,10 @@ import com.foxinmy.weixin4j.mp.payment.PayUtil;
import com.foxinmy.weixin4j.mp.payment.v2.Order;
import com.foxinmy.weixin4j.mp.payment.v3.Refund;
import com.foxinmy.weixin4j.mp.payment.v3.RefundConverter;
import com.foxinmy.weixin4j.mp.payment.v3.RefundResult;
import com.foxinmy.weixin4j.mp.type.BillType;
import com.foxinmy.weixin4j.mp.type.IdQuery;
import com.foxinmy.weixin4j.mp.type.IdType;
import com.foxinmy.weixin4j.mp.util.ExcelUtil;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.util.ConfigUtil;
@ -60,24 +66,23 @@ public class PayApi extends BaseApi {
/**
* 发货通知
*
* @param weixinAccount
* 商户信息
* @param openId
* 用户ID
* @param transid
* 交易单号
* @param orderNo
* @param outTradeNo
* 订单号
* @param status
* 成功|失败
* @param statusMsg
* status为失败时携带的信息
* @return
* @return 发货处理结果
* @throws WeixinException
*/
public JsonResult deliverNotify(WeixinAccount weixinAccount, String openId,
String transid, String orderNo, boolean status, String statusMsg)
public JsonResult deliverNotify(String openId, String transid,
String outTradeNo, boolean status, String statusMsg)
throws WeixinException {
WeixinAccount weixinAccount = tokenHolder.getAccount();
String delivernotify_uri = getRequestUri("delivernotify_uri");
Token token = tokenHolder.getToken();
@ -87,7 +92,7 @@ public class PayApi extends BaseApi {
// 用户购买的openId
param.put("openid", openId);
param.put("transid", transid);
param.put("out_trade_no", orderNo);
param.put("out_trade_no", outTradeNo);
param.put("deliver_timestamp", System.currentTimeMillis() / 1000 + "");
param.put("deliver_status", status ? "1" : "0");
param.put("deliver_msg", statusMsg);
@ -105,20 +110,17 @@ public class PayApi extends BaseApi {
/**
* 订单查询
*
* @param weixinAccount
* 商户信息
* @param orderNo
* 订单号
* @return
* @throws WeixinException
*/
public Order orderQueryV2(WeixinAccount weixinAccount, IdQuery idQuery)
throws WeixinException {
public Order orderQueryV2(String orderNo) throws WeixinException {
WeixinAccount weixinAccount = tokenHolder.getAccount();
String orderquery_uri = getRequestUri("orderquery_uri");
Token token = tokenHolder.getToken();
StringBuilder sb = new StringBuilder();
sb.append(idQuery.getType().getName()).append(idQuery.getId());
sb.append("out_trade_no=").append(orderNo);
sb.append("&partner=").append(weixinAccount.getPartnerId());
String part = sb.toString();
sb.append("&key=").append(weixinAccount.getPartnerKey());
@ -149,6 +151,10 @@ public class PayApi extends BaseApi {
String order_info = response.getAsJson().getString("order_info");
Order order = JSON.parseObject(order_info, Order.class,
Feature.IgnoreNotMatch);
if (order.getRetCode() != 0) {
throw new WeixinException(Integer.toString(order.getRetCode()),
order.getRetMsg());
}
order.setMapData(JSON.parseObject(order_info,
new TypeReference<Map<String, String>>() {
}));
@ -177,21 +183,15 @@ public class PayApi extends BaseApi {
/**
* V3订单查询
*
* @param weixinAccount
* 商户信息
* @param idQuery
* 商户系统内部的订单号, transaction_idout_trade_no 选一,如果同时存在优先级:
* transaction_id> out_trade_no
* @throws WeixinException
*/
public com.foxinmy.weixin4j.mp.payment.v3.Order orderQueryV3(
WeixinAccount weixinAccount, IdQuery idQuery)
public com.foxinmy.weixin4j.mp.payment.v3.Order orderQueryV3(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());
WeixinAccount weixinAccount = tokenHolder.getAccount();
Map<String, String> map = baseMap2V3(idQuery);
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
map.put("sign", sign);
String param = map2xml(map);
@ -202,6 +202,67 @@ public class PayApi extends BaseApi {
});
}
/**
* 申请退款(请求需要双向证书)<br/>
* <p style="color:red">
* 交易时间超过 1 年的订单无法提交退款; <br/>
* 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no一笔退款失 败后重新提交,要采用原来的
* out_refund_no总退款金额不能超过用户实际支付金额<br/>
* </p>
*
* @param ca
* 证书文件
* @param idQuery
* ) 商户系统内部的订单号, transaction_id out_trade_no 二选一,如果同时存在优先级:
* transaction_id> out_trade_no
* @param outRefundNo
* 商户系统内部的退款单号, 户系统内部唯一,同一退款单号多次请求只退一笔
* @param totalFee
* 订单总金额,单位为元
* @param refundFee
* 退款总金额,单位为元,可以做部分退款
* @param opUserId
* 操作员帐号, 默认为商户号
* @throws WeixinException
* @throws IOException
* @return 退款结果
*/
public RefundResult refund(InputStream ca, IdQuery idQuery,
String outRefundNo, double totalFee, double refundFee,
String opUserId) throws WeixinException, IOException {
WeixinAccount weixinAccount = tokenHolder.getAccount();
Map<String, String> map = baseMap2V3(idQuery);
map.put("out_refund_no", outRefundNo);
map.put("total_fee", DateUtil.formaFee2Fen(totalFee));
map.put("refund_fee", DateUtil.formaFee2Fen(refundFee));
if (StringUtils.isBlank(opUserId)) {
opUserId = weixinAccount.getMchId();
}
map.put("op_user_id", opUserId);
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
map.put("sign", sign);
String param = map2xml(map);
String refund_uri = getRequestUri("refund_uri");
SSLHttpRequest request = new SSLHttpRequest(weixinAccount.getMchId(),
ca);
Response response = request.post(refund_uri, param);
return response.getAsObject(new TypeReference<RefundResult>() {
});
}
/**
* 使用properties中配置的ca文件
*
* @see {@link com.foxinmy.weixin4j.mp.api.PayApi#refund(InputStream, IdQuery, String, double, double, String)}
*/
public RefundResult refund(IdQuery idQuery, String outRefundNo,
double totalFee, double refundFee, String opUserId)
throws WeixinException, IOException {
File caFile = new File(ConfigUtil.getValue("ca_file"));
return refund(new FileInputStream(caFile), idQuery, outRefundNo,
totalFee, refundFee, opUserId);
}
/**
* native支付URL转短链接
*
@ -212,13 +273,10 @@ public class PayApi extends BaseApi {
* @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());
public String getShorturl(String url) throws WeixinException {
WeixinAccount weixinAccount = tokenHolder.getAccount();
Map<String, String> map = baseMap2V3(null);
map.put("long_url", url);
map.put("nonce_str", RandomUtil.generateString(16));
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
map.put("sign", sign);
try {
@ -238,20 +296,15 @@ public class PayApi extends BaseApi {
* 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
* 成支付请按正常支付处理如果出现银行掉单,调用关单成功后,微信后台会主动发起退款
*
* @param weixinAccount
* 商户信息
* @param idQuery
* @param outTradeNo
* 商户系统内部的订单号
* @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());
public XmlResult closeOrder(String outTradeNo) throws WeixinException {
WeixinAccount weixinAccount = tokenHolder.getAccount();
Map<String, String> map = baseMap2V3(new IdQuery(outTradeNo,
IdType.TRADENO));
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
map.put("sign", sign);
String param = map2xml(map);
@ -267,8 +320,6 @@ public class PayApi extends BaseApi {
* 2.微信在次日 9 点启动生成前一天的对账单,建议商户 9 点半后再获取;<br>
* 3.对账单中涉及金额的字段单位为<br>
*
* @param weixinAccount
* 商户配置
* @param billDate
* 下载对账单的日期
* @param billType
@ -278,9 +329,9 @@ public class PayApi extends BaseApi {
* @throws WeixinException
* @throws IOException
*/
public File downloadbill(WeixinAccount weixinAccount, Date billDate,
BillType billType) throws WeixinException, IOException {
public File downloadbill(Date billDate, BillType billType)
throws WeixinException, IOException {
WeixinAccount weixinAccount = tokenHolder.getAccount();
if (billDate == null) {
Calendar now = Calendar.getInstance();
now.add(Calendar.DAY_OF_MONTH, -10);
@ -297,11 +348,7 @@ public class PayApi extends BaseApi {
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<String, String> map = baseMap2V3(null);
map.put("bill_date", _billDate);
map.put("bill_type", billType.name());
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
@ -334,7 +381,6 @@ public class PayApi extends BaseApi {
* 退款查询<br/>
* 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
*
* @param weixinAccount 商户信息
* @param idQuery
* 单号 refund_idout_refund_no out_trade_no transaction_id
* 四个参数必填一个,优先级为:
@ -342,14 +388,9 @@ public class PayApi extends BaseApi {
* @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());
public Refund refundQuery(IdQuery idQuery) throws WeixinException {
WeixinAccount weixinAccount = tokenHolder.getAccount();
Map<String, String> map = baseMap2V3(idQuery);
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
map.put("sign", sign);
String param = map2xml(map);
@ -357,4 +398,24 @@ public class PayApi extends BaseApi {
Response response = request.post(refundquery_uri, param);
return new RefundConverter().fromXML(response.getAsString());
}
/**
* V3接口请求基本数据
*
* @return
*/
private Map<String, String> baseMap2V3(IdQuery idQuery) {
WeixinAccount weixinAccount = tokenHolder.getAccount();
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));
if (StringUtils.isNotBlank(weixinAccount.getDeviceInfo())) {
map.put("device_info", weixinAccount.getDeviceInfo());
}
if (idQuery != null) {
map.put(idQuery.getType().getName(), idQuery.getId());
}
return map;
}
}

View File

@ -10,7 +10,7 @@ import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.model.WeixinConfig;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.mp.model.Following;
import com.foxinmy.weixin4j.mp.model.OauthToken;
import com.foxinmy.weixin4j.mp.model.User;
@ -45,10 +45,10 @@ public class UserApi extends BaseApi {
* @see com.foxinmy.weixin4j.mp.model.OauthToken
*/
public OauthToken getOauthToken(String code) throws WeixinException {
WeixinAccount weixinAccount = tokenHolder.getAccount();
String user_token_uri = getRequestUri("sns_user_token_uri");
WeixinConfig weixinConfig = tokenHolder.getConfig();
Response response = request.get(String.format(user_token_uri,
weixinConfig.getAppId(), weixinConfig.getAppSecret(), code));
weixinAccount.getAppId(), weixinAccount.getAppSecret(), code));
return response.getAsObject(new TypeReference<OauthToken>() {
});

View File

@ -17,11 +17,21 @@ public class OauthToken extends Token {
private static final long serialVersionUID = 1L;
private String openid;
@JSONField(name = "refresh_token")
private String refreshToken;
private String scope;
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getRefreshToken() {
return refreshToken;
}
@ -40,6 +50,9 @@ public class OauthToken extends Token {
@Override
public String toString() {
return "UserToken [refreshToken=" + refreshToken + ", scope=" + scope + ", getAccessToken()=" + getAccessToken() + ", getExpiresIn()=" + getExpiresIn() + ", getOpenid()=" + getOpenid() + ", getTime()=" + getTime() + "]";
return "OauthToken [openid=" + openid + ", refreshToken="
+ refreshToken + ", scope=" + scope + ", getAccessToken()="
+ getAccessToken() + ", getExpiresIn()=" + getExpiresIn()
+ ", getTime()=" + getTime() + "]";
}
}

View File

@ -20,6 +20,8 @@ public class ApiResult extends XmlResult {
private String appId;// 微信分配的公众账号 ID商户号 非空
@XStreamAlias("mch_id")
private String mchId;// 微信支付分配的商户号 非空
@XStreamAlias("sub_mch_id")
private String subMchId; // 未知 可能为空
@XStreamAlias("nonce_str")
private String nonceStr;// 随机字符串 非空
private String sign;// 签名 非空
@ -50,6 +52,14 @@ public class ApiResult extends XmlResult {
this.mchId = mchId;
}
public String getSubMchId() {
return subMchId;
}
public void setSubMchId(String subMchId) {
this.subMchId = subMchId;
}
public String getNonceStr() {
return nonceStr;
}
@ -76,8 +86,11 @@ public class ApiResult extends XmlResult {
@Override
public String toString() {
return "ApiResult [appId=" + appId + ", mchId=" + mchId + ", nonceStr="
+ nonceStr + ", sign=" + sign + ", deviceInfo=" + deviceInfo
+ "]";
return "ApiResult [appId=" + appId + ", mchId=" + mchId + ", subMchId="
+ subMchId + ", nonceStr=" + nonceStr + ", sign=" + sign
+ ", deviceInfo=" + deviceInfo + ", getReturnCode()="
+ getReturnCode() + ", getReturnMsg()=" + getReturnMsg()
+ ", getResultCode()=" + getResultCode() + ", getErrCode()="
+ getErrCode() + ", getErrCodeDes()=" + getErrCodeDes() + "]";
}
}

View File

@ -52,7 +52,7 @@ public class PayPackage implements Serializable {
}
public void setTotal_fee(double total_fee) {
this.total_fee = DateUtil.format2fee(total_fee);
this.total_fee = DateUtil.formaFee2Fen(total_fee);
}
public String getSpbill_create_ip() {
@ -78,7 +78,6 @@ public class PayPackage implements Serializable {
public void setTime_start(Date time_start) {
this.time_start = time_start != null ? DateUtil
.fortmat2yyyyMMddHHmmss(time_start) : null;
;
}
public String getTime_expire() {
@ -88,7 +87,6 @@ public class PayPackage implements Serializable {
public void setTime_expire(Date time_expire) {
this.time_expire = time_expire != null ? DateUtil
.fortmat2yyyyMMddHHmmss(time_expire) : null;
;
}
public String getGoods_tag() {
@ -116,7 +114,7 @@ public class PayPackage implements Serializable {
this.body = body;
this.attach = attach;
this.out_trade_no = out_trade_no;
this.total_fee = DateUtil.format2fee(total_fee);
this.total_fee = DateUtil.formaFee2Fen(total_fee);
this.spbill_create_ip = spbill_create_ip;
this.time_start = time_start != null ? DateUtil
.fortmat2yyyyMMddHHmmss(time_start) : null;

View File

@ -1,8 +1,11 @@
package com.foxinmy.weixin4j.mp.payment.v2;
import java.util.Date;
import java.util.Map;
import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.util.DateUtil;
/**
* 订单信息
@ -18,84 +21,104 @@ public class Order extends JsonResult {
private static final long serialVersionUID = 4543552984506609920L;
// 是查询结果状态码,0 表明成功,其他表明错误;
private String ret_code;
@JSONField(name = "ret_code")
private int retCode;
// 是查询结果出错信息;
private String ret_msg;
@JSONField(name = "ret_msg")
private String retMsg;
// 是返回信息中的编码方式;
private String input_charset;
@JSONField(name = "input_charset")
private String inputCharset;
// 是订单状态,0 为成功,其他为失败;
private String trade_state;
@JSONField(name = "trade_state")
private int tradeState;
// 是交易模式,1 为即时到帐,其他保留;
private String trade_mode;
@JSONField(name = "trade_mode")
private int tradeMode;
// 是财付通商户号,即前文的 partnerid;
private String partner;
// 是银行类型;
private String bank_type;
@JSONField(name = "bank_type")
private String bankType;
// 是银行订单号;
private String bank_billno;
@JSONField(name = "bank_billno")
private String bankBillno;
// 是总金额,单位为分;
private String total_fee;
@JSONField(name = "total_fee")
private int totalFee;
// 是币种,1 为人民币;
private String fee_type;
@JSONField(name = "fee_type")
private String feeType;
// 是财付通订单号;
private String transaction_id;
@JSONField(name = "transaction_id")
private String transactionId;
// 是第三方订单号;
private String out_trade_no;
@JSONField(name = "out_trade_no")
private String outTradeNo;
// 表明是否分账,false 为无分账,true 为有分账;
private boolean is_split;
@JSONField(name = "is_split")
private boolean isSplit;
// 表明是否退款,false 为无退款,ture 为退款;
private boolean is_refund;
@JSONField(name = "is_refund")
private boolean isRefund;
// attach 是商户数据包,即生成订单package 时商户填入的 attach;
private String attach;
// 支付完成时间;
private String time_end;
@JSONField(name = "time_end")
private String timeEnd;
// 物流费用,单位为分;
private String transport_fee;
@JSONField(name = "transport_fee")
private int transportFee;
// 物品费用,单位为分;
private String product_fee;
@JSONField(name = "product_fee")
private int productFee;
// 折扣价格,单位为分;
private String discount;
private int discount;
// 换算成人民币之后的总金额,单位为分,一般看 total_fee 即可
private String rmb_total_fee;
@JSONField(name = "rmb_total_fee")
private int rmbTotalFee;
public String getRet_code() {
return ret_code;
@JSONField(serialize = false)
private Map<String, String> mapData;
public int getRetCode() {
return retCode;
}
public void setRet_code(String ret_code) {
this.ret_code = ret_code;
public void setRetCode(int retCode) {
this.retCode = retCode;
}
public String getRet_msg() {
return ret_msg;
public String getRetMsg() {
return retMsg;
}
public void setRet_msg(String ret_msg) {
this.ret_msg = ret_msg;
public void setRetMsg(String retMsg) {
this.retMsg = retMsg;
}
public String getInput_charset() {
return input_charset;
public String getInputCharset() {
return inputCharset;
}
public void setInput_charset(String input_charset) {
this.input_charset = input_charset;
public void setInputCharset(String inputCharset) {
this.inputCharset = inputCharset;
}
public String getTrade_state() {
return trade_state;
public int getTradeState() {
return tradeState;
}
public void setTrade_state(String trade_state) {
this.trade_state = trade_state;
public void setTradeState(int tradeState) {
this.tradeState = tradeState;
}
public String getTrade_mode() {
return trade_mode;
public int getTradeMode() {
return tradeMode;
}
public void setTrade_mode(String trade_mode) {
this.trade_mode = trade_mode;
public void setTradeMode(int tradeMode) {
this.tradeMode = tradeMode;
}
public String getPartner() {
@ -106,68 +129,73 @@ public class Order extends JsonResult {
this.partner = partner;
}
public String getBank_type() {
return bank_type;
public String getBankType() {
return bankType;
}
public void setBank_type(String bank_type) {
this.bank_type = bank_type;
public void setBankType(String bankType) {
this.bankType = bankType;
}
public String getBank_billno() {
return bank_billno;
public String getBankBillno() {
return bankBillno;
}
public void setBank_billno(String bank_billno) {
this.bank_billno = bank_billno;
public void setBankBillno(String bankBillno) {
this.bankBillno = bankBillno;
}
public String getTotal_fee() {
return total_fee;
/**
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
*
* @return 元单位
*/
public double getTotalFee() {
return totalFee / 100d;
}
public void setTotal_fee(String total_fee) {
this.total_fee = total_fee;
public void setTotalFee(int totalFee) {
this.totalFee = totalFee;
}
public String getFee_type() {
return fee_type;
public String getFeeType() {
return feeType;
}
public void setFee_type(String fee_type) {
this.fee_type = fee_type;
public void setFeeType(String feeType) {
this.feeType = feeType;
}
public String getTransaction_id() {
return transaction_id;
public String getTransactionId() {
return transactionId;
}
public void setTransaction_id(String transaction_id) {
this.transaction_id = transaction_id;
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
public String getOut_trade_no() {
return out_trade_no;
public String getOutTradeNo() {
return outTradeNo;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
public void setOutTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
}
public boolean isIs_split() {
return is_split;
public boolean isSplit() {
return isSplit;
}
public void setIs_split(boolean is_split) {
this.is_split = is_split;
public void setSplit(boolean isSplit) {
this.isSplit = isSplit;
}
public boolean isIs_refund() {
return is_refund;
public boolean isRefund() {
return isRefund;
}
public void setIs_refund(boolean is_refund) {
this.is_refund = is_refund;
public void setRefund(boolean isRefund) {
this.isRefund = isRefund;
}
public String getAttach() {
@ -178,48 +206,66 @@ public class Order extends JsonResult {
this.attach = attach;
}
public String getTime_end() {
return time_end;
public Date getTimeEnd() {
return DateUtil.parse2yyyyMMddHHmmss(timeEnd);
}
public void setTime_end(String time_end) {
this.time_end = time_end;
public void setTimeEnd(String timeEnd) {
this.timeEnd = timeEnd;
}
public String getTransport_fee() {
return transport_fee;
/**
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
*
* @return 元单位
*/
public double getTransportFee() {
return transportFee / 100d;
}
public void setTransport_fee(String transport_fee) {
this.transport_fee = transport_fee;
public void setTransportFee(int transportFee) {
this.transportFee = transportFee;
}
public String getProduct_fee() {
return product_fee;
/**
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
*
* @return 元单位
*/
public double getProductFee() {
return productFee / 100d;
}
public void setProduct_fee(String product_fee) {
this.product_fee = product_fee;
public void setProductFee(int productFee) {
this.productFee = productFee;
}
public String getDiscount() {
return discount;
/**
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
*
* @return 元单位
*/
public double getDiscount() {
return discount / 100d;
}
public void setDiscount(String discount) {
public void setDiscount(int discount) {
this.discount = discount;
}
public String getRmb_total_fee() {
return rmb_total_fee;
/**
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
*
* @return 元单位
*/
public double getRmbTotalFee() {
return rmbTotalFee / 100d;
}
public void setRmb_total_fee(String rmb_total_fee) {
this.rmb_total_fee = rmb_total_fee;
public void setRmbTotalFee(int rmbTotalFee) {
this.rmbTotalFee = rmbTotalFee;
}
private Map<String, String> mapData;
public Map<String, String> getMapData() {
return mapData;
}
@ -230,16 +276,16 @@ public class Order extends JsonResult {
@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 + "]";
return "Order [retCode=" + retCode + ", retMsg=" + retMsg
+ ", inputCharset=" + inputCharset + ", tradeState="
+ tradeState + ", tradeMode=" + tradeMode + ", partner="
+ partner + ", bankType=" + bankType + ", bankBillno="
+ bankBillno + ", totalFee=" + totalFee + ", feeType="
+ feeType + ", transactionId=" + transactionId
+ ", outTradeNo=" + outTradeNo + ", isSplit=" + isSplit
+ ", isRefund=" + isRefund + ", attach=" + attach
+ ", timeEnd=" + timeEnd + ", transportFee=" + transportFee
+ ", productFee=" + productFee + ", discount=" + discount
+ ", rmbTotalFee=" + rmbTotalFee + ", mapData=" + mapData + "]";
}
}

View File

@ -38,66 +38,34 @@ public class PayFeedback extends PayBaseInfo {
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;
}
@Override
public String toString() {
return "PayFeedback [feedbackId=" + feedbackId + ", openId=" + openId

View File

@ -72,7 +72,7 @@ public class PayPackageV2 extends PayPackage {
}
public void setTransport_fee(double transport_fee) {
this.transport_fee = DateUtil.format2fee(transport_fee);
this.transport_fee = DateUtil.formaFee2Fen(transport_fee);
}
public String getProduct_fee() {
@ -80,7 +80,7 @@ public class PayPackageV2 extends PayPackage {
}
public void setProduct_fee(double product_fee) {
this.product_fee = DateUtil.format2fee(product_fee);
this.product_fee = DateUtil.formaFee2Fen(product_fee);
}
public String getInput_charset() {
@ -125,8 +125,8 @@ public class PayPackageV2 extends PayPackage {
this.fee_type = "1";
this.input_charset = "UTF-8";
this.transport_fee = transport_fee > 0d ? DateUtil
.format2fee(transport_fee) : null;
this.product_fee = product_fee > 0 ? DateUtil.format2fee(product_fee)
.formaFee2Fen(transport_fee) : null;
this.product_fee = product_fee > 0 ? DateUtil.formaFee2Fen(product_fee)
: null;
}

View File

@ -21,26 +21,14 @@ public class PayWarn extends PayBaseInfo {
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;
}
@Override
public String toString() {
return "PayWarn [errortype=" + errortype + ", description="

View File

@ -1,9 +1,12 @@
package com.foxinmy.weixin4j.mp.payment.v3;
import java.util.Date;
import com.foxinmy.weixin4j.mp.payment.ApiResult;
import com.foxinmy.weixin4j.mp.type.CurrencyType;
import com.foxinmy.weixin4j.mp.type.TradeState;
import com.foxinmy.weixin4j.mp.type.TradeType;
import com.foxinmy.weixin4j.util.DateUtil;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
@ -51,7 +54,6 @@ public class Order extends ApiResult {
@XStreamAlias("out_rade_no")
private String outTradeNo;
// 商家数据包
@XStreamAlias("attach")
private String attach;
// 支付完成时间,格式为 yyyyMMddhhmmss
@XStreamAlias("time_end")
@ -61,96 +63,58 @@ public class Order extends ApiResult {
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;
/**
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
*
* @return 元单位
*/
public double getTotalFee() {
return totalFee / 100d;
}
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;
/**
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
*
* @return 元单位
*/
public double getCouponFee() {
return couponFee / 100d;
}
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;
public Date getTimeEnd() {
return DateUtil.parse2yyyyMMddHHmmss(timeEnd);
}
@Override

View File

@ -33,34 +33,18 @@ public class Refund extends ApiResult {
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;
}
@ -71,15 +55,14 @@ public class Refund extends ApiResult {
@Override
public String toString() {
return "Refund [transactionId=" + transactionId + ", subMchId="
+ subMchId + ", orderNo=" + orderNo + ", count=" + count
return "Refund [transactionId=" + transactionId + ", orderNo="
+ orderNo + ", subMchId=" + subMchId + ", 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() + "]";
+ ", getDeviceInfo()=" + getDeviceInfo() + ", getReturnCode()="
+ getReturnCode() + ", getReturnMsg()=" + getReturnMsg()
+ ", getResultCode()=" + getResultCode() + ", getErrCode()="
+ getErrCode() + ", getErrCodeDes()=" + getErrCodeDes() + "]";
}
}

View File

@ -2,6 +2,9 @@ package com.foxinmy.weixin4j.mp.payment.v3;
import java.io.Serializable;
import org.apache.commons.lang3.StringUtils;
import com.foxinmy.weixin4j.mp.type.RefundChannel;
import com.foxinmy.weixin4j.mp.type.RefundStatus;
import com.thoughtworks.xstream.annotations.XStreamAlias;
@ -35,50 +38,37 @@ public class RefundDetail implements Serializable {
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;
return StringUtils.isBlank(refundChannel) ? RefundChannel.BALANCE
.name() : refundChannel;
}
public void setRefundChannel(String refundChannel) {
this.refundChannel = refundChannel;
/**
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
*
* @return 元单位
*/
public double getRefundFee() {
return refundFee / 100d;
}
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;
/**
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
*
* @return 元单位
*/
public double getCouponRefundFee() {
return couponRefundFee / 100d;
}
public RefundStatus getRefundStatus() {
return refundStatus;
}
public void setRefundStatus(RefundStatus refundStatus) {
this.refundStatus = refundStatus;
}
@Override
public String toString() {
return "RefundDetail [outRefundNo=" + outRefundNo + ", refundId="

View File

@ -0,0 +1,98 @@
package com.foxinmy.weixin4j.mp.payment.v3;
import org.apache.commons.lang3.StringUtils;
import com.foxinmy.weixin4j.mp.payment.ApiResult;
import com.foxinmy.weixin4j.mp.type.RefundChannel;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 退款申请结果
*
* @className RefundResult
* @author jy
* @date 2014年11月6日
* @since JDK 1.7
* @see
*/
@XStreamAlias("xml")
public class RefundResult extends ApiResult {
private static final long serialVersionUID = -3687863914168618620L;
// 微信订单号
@XStreamAlias("transaction_id")
private String transactionId;
// 商户系统内部的订单号
@XStreamAlias("out_trade_no")
private String outTradeNo;
// 商户退款单号
@XStreamAlias("out_refund_no")
private String outRefundNo;
// 微信退款单号
@XStreamAlias("refund_id")
private String refundId;
// 退款渠道 ORIGINAL原路退款,默认 BALANCE退回到余额
@XStreamAlias("refund_channel")
private String refundChannel;
// 退款总金额,单位为元,可以做部分退款
@XStreamAlias("refund_fee")
private int refundFee;
// 现金券退款金额<=退款金 ,退款金额-现金券退款金 额为现金
@XStreamAlias("coupon_refund_fee")
private int couponRefundFee;
public String getTransactionId() {
return transactionId;
}
public String getOutTradeNo() {
return outTradeNo;
}
public String getOutRefundNo() {
return outRefundNo;
}
public String getRefundId() {
return refundId;
}
public String getRefundChannel() {
return StringUtils.isBlank(refundChannel) ? RefundChannel.BALANCE
.name() : refundChannel;
}
/**
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
*
* @return 元单位
*/
public double getRefundFee() {
return refundFee / 100d;
}
/**
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
*
* @return 元单位
*/
public double getCouponRefundFee() {
return couponRefundFee / 100d;
}
@Override
public String toString() {
return "RefundResult [transactionId=" + transactionId + ", outTradeNo="
+ outTradeNo + ", outRefundNo=" + outRefundNo + ", refundId="
+ refundId + ", refundChannel=" + refundChannel
+ ", refundFee=" + refundFee + ", couponRefundFee="
+ couponRefundFee + ", getAppId()=" + getAppId()
+ ", getMchId()=" + getMchId() + ", getNonceStr()="
+ getNonceStr() + ", getSign()=" + getSign()
+ ", getDeviceInfo()=" + getDeviceInfo() + ", getReturnCode()="
+ getReturnCode() + ", getReturnMsg()=" + getReturnMsg()
+ ", getResultCode()=" + getResultCode() + ", getErrCode()="
+ getErrCode() + ", getErrCodeDes()=" + getErrCodeDes() + "]";
}
}

View File

@ -12,7 +12,7 @@ package com.foxinmy.weixin4j.mp.type;
public enum IdType {
REFUNDID("refund_id"), // 微信退款单号
TRANSACTIONID("transaction_id"), // 微信订单号
ORDERNO("out_trade_no"), // 商户订单号
TRADENO("out_trade_no"), // 商户订单号
REFUNDNO("out_refund_no"); // 商户退款号
private String name;

View File

@ -0,0 +1,15 @@
package com.foxinmy.weixin4j.mp.type;
/**
* 退款渠道
*
* @className RefundChannel
* @author jy
* @date 2014年11月6日
* @since JDK 1.7
* @see
*/
public enum RefundChannel {
ORIGINAL, // 原路退款
BALANCE;// 退回到余额
}

View File

@ -36,11 +36,11 @@ public class MessagePush {
int status = statusLine.getStatusCode();
if (status != HttpStatus.SC_OK) {
throw new WeixinException(status + "", "request fail");
throw new WeixinException(Integer.toString(status), "request fail");
}
if (status == HttpStatus.SC_MOVED_PERMANENTLY
|| status == HttpStatus.SC_MOVED_TEMPORARILY) {
throw new WeixinException(status + "", "uri moved");
throw new WeixinException(Integer.toString(status), "uri moved");
}
return EntityUtils.toString(httpResponse.getEntity(),
StandardCharsets.UTF_8);

View File

@ -23,7 +23,7 @@ weixin4j-mp-server
| media_path | 调用媒体接口时保存媒体文件的物理路径 |
| bill_path | 调用支付(`V3`)下载对账单接口保存excel文件的物理路径 |
示例(properties中换行用右斜杆\)
示例(properties中换行用右斜杆\\)
> account={"appId":"appId","appSecret":"appSecret",
> "token":"开放者的token 非必须","openId":"公众号的openid 非必须",

View File

@ -12,4 +12,6 @@ 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
bill_path=/tmp/weixin/bill
# ca\u8bc1\u4e66\u5b58\u653e\u7684\u5b8c\u6574\u8def\u5f84
ca_file=/tmp/weixin/xxxxx.p12