支付模块拆分为V2跟V3
This commit is contained in:
parent
75dc019b1f
commit
5b40a3728c
12
README.md
12
README.md
@ -207,6 +207,18 @@ netty的代码没有放到maven中心仓库,也没什么意义,因为最终需
|
||||
+ **weixin4j-qy**: 新增获取微信服务器IP接口
|
||||
|
||||
+ **weixin4j-qy**: 调整回调模式下的首次验证的签名方式
|
||||
|
||||
* 2015-01-04
|
||||
|
||||
+ **weixin4j-base**: 新增获取classpath目录下的资源路径的方法
|
||||
|
||||
+ **weixin4j-mp**: 支付模块拆分为V2跟V3,新增WeixinPayProxy类
|
||||
|
||||
+ **weixin4j-mp**: 退款相关类拆分V2跟V3
|
||||
|
||||
+ **weixin4j-mp**: 新增接口上报接口
|
||||
|
||||
+ **weixin4j-qy**: 新增批量删除员工接口
|
||||
|
||||
接下来
|
||||
------
|
||||
|
||||
6
pom.xml
6
pom.xml
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.foxinmy</groupId>
|
||||
<artifactId>weixin4j</artifactId>
|
||||
@ -209,6 +210,9 @@
|
||||
<include>**/*.xml</include>
|
||||
<include>**/*.properties</include>
|
||||
<include>**/*.pem</include>
|
||||
<include>**/*.p12</include>
|
||||
<include>**/*.pfx</include>
|
||||
<include>**/*.pem</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
|
||||
@ -37,4 +37,8 @@ weixin4j-base
|
||||
|
||||
* 2014-11-24
|
||||
|
||||
+ 将Action跟Mapping基础类并入到项目
|
||||
+ 将Action跟Mapping基础类并入到项目
|
||||
|
||||
* 2015-01-04
|
||||
|
||||
+ ConfigUtil类新增获取classpath目录下的资源路径的方法
|
||||
@ -13,6 +13,7 @@ import com.thoughtworks.xstream.mapper.DefaultMapper;
|
||||
|
||||
/**
|
||||
* API基础
|
||||
*
|
||||
* @className BaseApi
|
||||
* @author jy.hu
|
||||
* @date 2014年9月26日
|
||||
@ -37,7 +38,9 @@ public abstract class BaseApi {
|
||||
protected Map<String, String> xml2map(String xml) {
|
||||
return mapXstream.fromXML(xml, Map.class);
|
||||
}
|
||||
|
||||
protected abstract ResourceBundle getWeixinBundle();
|
||||
|
||||
protected String getRequestUri(String key) {
|
||||
String url = getWeixinBundle().getString(key);
|
||||
Pattern p = Pattern.compile("(\\{[^\\}]*\\})");
|
||||
|
||||
@ -188,7 +188,6 @@ public class HttpRequest {
|
||||
response.setStatusText(statusLine.getReasonPhrase());
|
||||
response.setStream(new ByteArrayInputStream(data));
|
||||
response.setText(new String(data, Consts.UTF_8));
|
||||
|
||||
EntityUtils.consume(httpEntity);
|
||||
Header contentType = httpResponse
|
||||
.getFirstHeader(HttpHeaders.CONTENT_TYPE);
|
||||
|
||||
@ -168,10 +168,9 @@ public class Response {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[Response text=").append(text);
|
||||
sb.append(", statusCode=").append(statusCode);
|
||||
sb.append(", statusText=").append(statusText).append("]");
|
||||
return sb.toString();
|
||||
return "Response [text=" + text + ", statusCode=" + statusCode
|
||||
+ ", statusText=" + statusText + ", stream=" + stream
|
||||
+ ", isJsonResult=" + isJsonResult + ", isXmlResult="
|
||||
+ isXmlResult + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
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;
|
||||
|
||||
@ -23,10 +20,6 @@ import org.apache.http.conn.ssl.SSLSocketFactory;
|
||||
*/
|
||||
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 {
|
||||
|
||||
@ -2,7 +2,9 @@ package com.foxinmy.weixin4j.http;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.foxinmy.weixin4j.model.Consts;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
@ -19,16 +21,29 @@ public class XmlResult implements Serializable {
|
||||
private static final long serialVersionUID = -6185313616955051150L;
|
||||
|
||||
@XStreamAlias("return_code")
|
||||
@JSONField(name = "return_code")
|
||||
private String returnCode;// 此字段是通信标识,非交易 标识,交易是否成功需要查 看 result_code 来判断 非空
|
||||
@XStreamAlias("return_msg")
|
||||
@JSONField(name = "return_msg")
|
||||
private String returnMsg;// 返回信息,如非 空,为错误原因 可能为空
|
||||
@XStreamAlias("result_code")
|
||||
@JSONField(name = "result_code")
|
||||
private String resultCode;// 业务结果SUCCESS/FAIL 非空
|
||||
@XStreamAlias("err_code")
|
||||
@JSONField(name = "err_code")
|
||||
private String errCode;// 错误代码 可为空
|
||||
@XStreamAlias("err_code_des")
|
||||
@JSONField(name = "err_code_des")
|
||||
private String errCodeDes;// 结果信息描述 可为空
|
||||
|
||||
public XmlResult() {
|
||||
}
|
||||
|
||||
public XmlResult(String returnCode, String returnMsg) {
|
||||
this.returnCode = returnCode;
|
||||
this.returnMsg = returnMsg;
|
||||
}
|
||||
|
||||
public String getReturnCode() {
|
||||
return returnCode;
|
||||
}
|
||||
@ -38,7 +53,7 @@ public class XmlResult implements Serializable {
|
||||
}
|
||||
|
||||
public String getReturnMsg() {
|
||||
return returnMsg;
|
||||
return StringUtils.isNotBlank(returnMsg) ? returnMsg : null;
|
||||
}
|
||||
|
||||
public void setReturnMsg(String returnMsg) {
|
||||
@ -62,27 +77,13 @@ public class XmlResult implements Serializable {
|
||||
}
|
||||
|
||||
public String getErrCodeDes() {
|
||||
return errCodeDes;
|
||||
return StringUtils.isNotBlank(errCodeDes) ? errCodeDes : null;
|
||||
}
|
||||
|
||||
public void setErrCodeDes(String errCodeDes) {
|
||||
this.errCodeDes = errCodeDes;
|
||||
}
|
||||
|
||||
public XmlResult() {
|
||||
this(Consts.SUCCESS.toLowerCase(), "");
|
||||
}
|
||||
|
||||
public XmlResult(String returnCode, String returnMsg) {
|
||||
this.returnCode = returnCode;
|
||||
this.returnMsg = returnMsg;
|
||||
if (returnCode.equalsIgnoreCase(Consts.SUCCESS)) {
|
||||
this.resultCode = Consts.SUCCESS.toLowerCase();
|
||||
this.errCode = Consts.SUCCESS.toLowerCase();
|
||||
this.errCodeDes = "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "returnCode=" + returnCode + ", returnMsg=" + returnMsg
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
package com.foxinmy.weixin4j.model;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* 常量类
|
||||
* @className Consts
|
||||
@ -8,6 +11,8 @@ package com.foxinmy.weixin4j.model;
|
||||
* @see
|
||||
*/
|
||||
public final class Consts {
|
||||
public static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
public static final Charset GBK = Charset.forName("GBK");
|
||||
public static final String SUCCESS = "SUCCESS";
|
||||
public static final String FAIL = "FAIL";
|
||||
public static final String SunX509 = "SunX509";
|
||||
|
||||
@ -19,16 +19,21 @@ import com.foxinmy.weixin4j.model.WeixinQyAccount;
|
||||
* @see
|
||||
*/
|
||||
public class ConfigUtil {
|
||||
private final static String CLASSPATH_PREFIX = "classpath:";
|
||||
private final static String CLASSPATH_VALUE;
|
||||
private final static ResourceBundle weixinBundle;
|
||||
static {
|
||||
weixinBundle = ResourceBundle.getBundle("weixin");
|
||||
Set<String> keySet = weixinBundle.keySet();
|
||||
File file = null;
|
||||
CLASSPATH_VALUE = Thread.currentThread().getContextClassLoader()
|
||||
.getResource("").getPath();
|
||||
for (String key : keySet) {
|
||||
if (!key.endsWith("_path")) {
|
||||
continue;
|
||||
}
|
||||
file = new File(getValue(key));
|
||||
file = new File(getValue(key).replaceFirst(CLASSPATH_PREFIX,
|
||||
CLASSPATH_VALUE));
|
||||
if (!file.exists() && !file.mkdirs()) {
|
||||
System.err.append(String.format("%s create fail.%n",
|
||||
file.getAbsolutePath()));
|
||||
@ -36,10 +41,27 @@ public class ConfigUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取weixin.properties文件中的key值
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String getValue(String key) {
|
||||
return weixinBundle.getString(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断属性是否存在[classpath:]如果存在则拼接项目路径后返回 一般用于文件的绝对路径获取
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String getClassPathValue(String key) {
|
||||
return new File(getValue(key).replaceFirst(CLASSPATH_PREFIX,
|
||||
CLASSPATH_VALUE)).getPath();
|
||||
}
|
||||
|
||||
public static <T extends WeixinAccount> T getWeixinAccount(Class<T> clazz) {
|
||||
String text = getValue("account");
|
||||
return JSON.parseObject(text, clazz);
|
||||
|
||||
@ -65,6 +65,7 @@ weixin4j-mp
|
||||
media_path=/tmp/weixin/media
|
||||
bill_path=/tmp/weixin/bill
|
||||
ca_file=/tmp/weixin/xxxxx.p12 | xxxx.pfx
|
||||
#classpath路径下:ca_file=classpath:xxxxx.p12
|
||||
|
||||
3.在项目根目录下执行`mvn package`命令后得到jar包,将`weixin4j-mp-full`包或者`weixin4j-base`和`weixin4j-mp-api`两个包引入到自己的工程.
|
||||
|
||||
@ -150,4 +151,12 @@ weixin4j-mp
|
||||
|
||||
+ **weixin4j-mp-api**: 新增群发消息预览、状态查询接口
|
||||
|
||||
+ **weixin4j-mp-api**: 新增多客服添加账号、更新账号、上传头像、删除账号接口
|
||||
+ **weixin4j-mp-api**: 新增多客服添加账号、更新账号、上传头像、删除账号接口
|
||||
|
||||
* 2015-01-04
|
||||
|
||||
+ **weixin4j-mp-api**: 支付模块拆分为V2跟V3,新增WeixinPayProxy类
|
||||
|
||||
+ **weixin4j-mp-api**: 退款相关类拆分为V2跟V3
|
||||
|
||||
+ **weixin4j-mp-api**: 新增接口上报接口
|
||||
@ -60,6 +60,7 @@ weixin.properties说明
|
||||
media_path=/tmp/weixin/media
|
||||
bill_path=/tmp/weixin/bill
|
||||
ca_file=/tmp/weixin/xxxxx.p12 | xxxx.pfx
|
||||
#classpath路径下:ca_file=classpath:xxxxx.p12
|
||||
|
||||
2.实例化一个`WeixinProxy`对象,调用API,需要强调的是如果只传入appid,appsecret两个参数将无法调用支付相关接口
|
||||
|
||||
@ -131,4 +132,12 @@ weixin.properties说明
|
||||
|
||||
+ 新增群发消息预览、状态查询接口
|
||||
|
||||
+ 新增多客服添加账号、更新账号、上传头像、删除账号接口
|
||||
+ 新增多客服添加账号、更新账号、上传头像、删除账号接口
|
||||
|
||||
* 2015-01-04
|
||||
|
||||
+ 支付模块拆分为V2跟V3,新增WeixinPayProxy类
|
||||
|
||||
+ 退款相关类拆分为V2跟V3
|
||||
|
||||
+ 新增接口上报接口
|
||||
@ -0,0 +1,449 @@
|
||||
package com.foxinmy.weixin4j.mp;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.http.JsonResult;
|
||||
import com.foxinmy.weixin4j.http.XmlResult;
|
||||
import com.foxinmy.weixin4j.model.WeixinMpAccount;
|
||||
import com.foxinmy.weixin4j.mp.api.Pay2Api;
|
||||
import com.foxinmy.weixin4j.mp.api.Pay3Api;
|
||||
import com.foxinmy.weixin4j.mp.api.PayApi;
|
||||
import com.foxinmy.weixin4j.mp.payment.v3.ApiResult;
|
||||
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.type.RefundType;
|
||||
import com.foxinmy.weixin4j.token.FileTokenHolder;
|
||||
import com.foxinmy.weixin4j.token.TokenHolder;
|
||||
import com.foxinmy.weixin4j.type.AccountType;
|
||||
import com.foxinmy.weixin4j.util.ConfigUtil;
|
||||
|
||||
/**
|
||||
* 微信支付接口实现
|
||||
*
|
||||
* @className WeixinPayProxy
|
||||
* @author jy
|
||||
* @date 2015年1月3日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay2Api
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
|
||||
*/
|
||||
public class WeixinPayProxy {
|
||||
private final PayApi payApi;
|
||||
private final Pay2Api pay2Api;
|
||||
private final Pay3Api pay3Api;
|
||||
|
||||
/**
|
||||
* 默认采用文件存放Token信息
|
||||
*/
|
||||
public WeixinPayProxy() {
|
||||
this(new FileTokenHolder(AccountType.MP));
|
||||
}
|
||||
|
||||
/**
|
||||
* WeixinAccount对象
|
||||
*
|
||||
* @param weixinAccount
|
||||
* 微信账户
|
||||
*/
|
||||
public WeixinPayProxy(WeixinMpAccount weixinAccount) {
|
||||
this(new FileTokenHolder(weixinAccount));
|
||||
}
|
||||
|
||||
/**
|
||||
* TokenHolder对象
|
||||
*
|
||||
* @param tokenHolder
|
||||
*/
|
||||
public WeixinPayProxy(TokenHolder tokenHolder) {
|
||||
this.pay2Api = new Pay2Api(tokenHolder);
|
||||
this.pay3Api = new Pay3Api(tokenHolder);
|
||||
int version = ((WeixinMpAccount) tokenHolder.getAccount()).getVersion();
|
||||
if (version == 2) {
|
||||
this.payApi = this.pay2Api;
|
||||
} else if (version == 3) {
|
||||
this.payApi = this.pay3Api;
|
||||
} else {
|
||||
this.payApi = this.pay3Api;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发货通知
|
||||
*
|
||||
* @param openId
|
||||
* 用户ID
|
||||
* @param transid
|
||||
* 交易单号
|
||||
* @param outTradeNo
|
||||
* 订单号
|
||||
* @param status
|
||||
* 成功|失败
|
||||
* @param statusMsg
|
||||
* status为失败时携带的信息
|
||||
* @return 发货处理结果
|
||||
* @since V2 & V3
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public JsonResult deliverNotify(String openId, String transid,
|
||||
String outTradeNo, boolean status, String statusMsg)
|
||||
throws WeixinException {
|
||||
return payApi.deliverNotify(openId, transid, outTradeNo, status,
|
||||
statusMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 维权处理
|
||||
*
|
||||
* @param openId
|
||||
* 用户ID
|
||||
* @param feedbackId
|
||||
* 维权单号
|
||||
* @return 调用结果
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @since V2 & V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public JsonResult updateFeedback(String openId, String feedbackId)
|
||||
throws WeixinException {
|
||||
return payApi.updateFeedback(openId, feedbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* V2订单查询
|
||||
*
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id、out_trade_no 二 选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @since V2
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.Order
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay2Api
|
||||
* @return 订单详情
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public com.foxinmy.weixin4j.mp.payment.v2.Order orderQueryV2(
|
||||
String outTradeNo) throws WeixinException {
|
||||
return pay2Api.orderQuery(new IdQuery(outTradeNo, IdType.TRADENO));
|
||||
}
|
||||
|
||||
/**
|
||||
* V3订单查询
|
||||
*
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id、out_trade_no 二 选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @since V3
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v3.Order
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
|
||||
* @return 订单详情
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public com.foxinmy.weixin4j.mp.payment.v3.Order orderQueryV3(IdQuery idQuery)
|
||||
throws WeixinException {
|
||||
return pay3Api.orderQuery(idQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* V2申请退款(请求需要双向证书)</br>
|
||||
* <p style="color:red">
|
||||
* 交易时间超过 1 年的订单无法提交退款; </br> 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失
|
||||
* 败后重新提交,要采用原来的 out_refund_no。总退款金额不能超过用户实际支付金额。</br>
|
||||
* </p>
|
||||
*
|
||||
* @param caFile
|
||||
* 证书文件(后缀为*.pfx)
|
||||
* @param idQuery
|
||||
* ) 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @param outRefundNo
|
||||
* 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
|
||||
* @param totalFee
|
||||
* 订单总金额,单位为元
|
||||
* @param refundFee
|
||||
* 退款总金额,单位为元,可以做部分退款
|
||||
* @param opUserId
|
||||
* 操作员帐号, 默认为商户号
|
||||
* @param opUserPasswd
|
||||
* 操作员密码
|
||||
*
|
||||
* @return 退款申请结果
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay2Api
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.RefundResult
|
||||
* @since V2
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public com.foxinmy.weixin4j.mp.payment.v2.RefundResult refundV2(
|
||||
File caFile, IdQuery idQuery, String outRefundNo, double totalFee,
|
||||
double refundFee, String opUserId, String opUserPasswd)
|
||||
throws WeixinException {
|
||||
return pay2Api.refund(caFile, idQuery, outRefundNo, totalFee,
|
||||
refundFee, opUserId, opUserPasswd);
|
||||
}
|
||||
|
||||
/**
|
||||
* V2退款申请采用properties中配置的ca文件
|
||||
*
|
||||
* @see {@link com.foxinmy.weixin4j.mp.WeixinPayProxy#refundV2(File, IdQuery, String, double, double, String,String)}
|
||||
*/
|
||||
public com.foxinmy.weixin4j.mp.payment.v2.RefundResult refundV2(
|
||||
IdQuery idQuery, String outRefundNo, double totalFee,
|
||||
double refundFee, String opUserId, String opUserPasswd)
|
||||
throws WeixinException {
|
||||
File caFile = new File(ConfigUtil.getClassPathValue("ca_file"));
|
||||
return refundV2(caFile, idQuery, outRefundNo, totalFee, refundFee,
|
||||
opUserId, opUserPasswd);
|
||||
}
|
||||
|
||||
/**
|
||||
* V2退款申请
|
||||
*
|
||||
* @param caFile
|
||||
* 证书文件(V2版本后缀为*.pfx)
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @param outRefundNo
|
||||
* 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
|
||||
* @param totalFee
|
||||
* 订单总金额,单位为元
|
||||
* @param refundFee
|
||||
* 退款总金额,单位为元,可以做部分退款
|
||||
* @param opUserId
|
||||
* 操作员帐号, 默认为商户号
|
||||
* @param opUserPasswd
|
||||
* 操作员密码,默认为商户后台登录密码
|
||||
* @param recvUserId
|
||||
* 转账退款接收退款的财付通帐号。 一般无需填写,只有退银行失败,资金转入商 户号现金账号时(即状态为转入代发,查询返 回的
|
||||
* refund_status 是 7 或 11),填写原退款 单号并填写此字段,资金才会退到指定财付通
|
||||
* 账号。其他情况此字段忽略
|
||||
* @param reccvUserName
|
||||
* 转账退款接收退款的姓名(需与接收退款的财 付通帐号绑定的姓名一致)
|
||||
* @param refundType
|
||||
* 为空或者填 1:商户号余额退款;2:现金帐号 退款;3:优先商户号退款,若商户号余额不足, 再做现金帐号退款。使用 2 或
|
||||
* 3 时,需联系财 付通开通此功能
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay2Api
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.RefundResult
|
||||
* @return 退款结果
|
||||
*/
|
||||
public com.foxinmy.weixin4j.mp.payment.v2.RefundResult refundV2(
|
||||
File caFile, IdQuery idQuery, String outRefundNo, double totalFee,
|
||||
double refundFee, String opUserId, String opUserPasswd,
|
||||
String recvUserId, String reccvUserName, RefundType refundType)
|
||||
throws WeixinException {
|
||||
return pay2Api.refund(caFile, idQuery, outRefundNo, totalFee,
|
||||
refundFee, opUserId, opUserPasswd, recvUserId, reccvUserName,
|
||||
refundType);
|
||||
}
|
||||
|
||||
/**
|
||||
* V2退款查询</br> 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
|
||||
*
|
||||
* @param idQuery
|
||||
* 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
|
||||
* 四个参数必填一个,优先级为:
|
||||
* refund_id>out_refund_no>transaction_id>out_trade_no
|
||||
* @return 退款记录
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.RefundRecord
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay2Api
|
||||
* @since V2
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public com.foxinmy.weixin4j.mp.payment.v2.RefundRecord refundQueryV2(
|
||||
IdQuery idQuery) throws WeixinException {
|
||||
return pay2Api.refundQuery(idQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3申请退款(请求需要双向证书)</br>
|
||||
* <p style="color:red">
|
||||
* 交易时间超过 1 年的订单无法提交退款; </br> 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失
|
||||
* 败后重新提交,要采用原来的 out_refund_no。总退款金额不能超过用户实际支付金额。</br>
|
||||
* </p>
|
||||
*
|
||||
* @param caFile
|
||||
* 证书文件(后缀为*.p12)
|
||||
* @param idQuery
|
||||
* ) 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @param outRefundNo
|
||||
* 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
|
||||
* @param totalFee
|
||||
* 订单总金额,单位为元
|
||||
* @param refundFee
|
||||
* 退款总金额,单位为元,可以做部分退款
|
||||
* @param opUserId
|
||||
* 操作员帐号, 默认为商户号
|
||||
*
|
||||
* @return 退款申请结果
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundResult
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
|
||||
* @since V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public com.foxinmy.weixin4j.mp.payment.v3.RefundResult refundV3(
|
||||
File caFile, IdQuery idQuery, String outRefundNo, double totalFee,
|
||||
double refundFee, String opUserId) throws WeixinException {
|
||||
return pay3Api.refund(caFile, idQuery, outRefundNo, totalFee,
|
||||
refundFee, opUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3退款申请采用properties中配置的ca文件
|
||||
*
|
||||
* @see {@link com.foxinmy.weixin4j.mp.WeixinPayProxy#refundV3(File, IdQuery, String, double, double, String)}
|
||||
*/
|
||||
public com.foxinmy.weixin4j.mp.payment.v3.RefundResult refundV3(
|
||||
IdQuery idQuery, String outRefundNo, double totalFee,
|
||||
double refundFee, String opUserId) throws WeixinException {
|
||||
File caFile = new File(ConfigUtil.getClassPathValue("ca_file"));
|
||||
return pay3Api.refund(caFile, idQuery, outRefundNo, totalFee,
|
||||
refundFee, opUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3退款查询</br> 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
|
||||
*
|
||||
* @param idQuery
|
||||
* 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
|
||||
* 四个参数必填一个,优先级为:
|
||||
* refund_id>out_refund_no>transaction_id>out_trade_no
|
||||
* @return 退款记录
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundRecord
|
||||
* @since V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public com.foxinmy.weixin4j.mp.payment.v3.RefundRecord refundQueryV3(
|
||||
IdQuery idQuery) throws WeixinException {
|
||||
return pay3Api.refundQuery(idQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载对账单<br>
|
||||
* 1.微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账 单中,跟原支付单订单号一致,bill_type 为
|
||||
* REVOKED;<br>
|
||||
* 2.微信在次日 9 点启动生成前一天的对账单,建议商户 9 点半后再获取;<br>
|
||||
* 3.对账单中涉及金额的字段单位为“元”。<br>
|
||||
*
|
||||
* @param billDate
|
||||
* 下载对账单的日期
|
||||
* @param billType
|
||||
* 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单
|
||||
* REFUND,返回当日退款订单
|
||||
* @return excel表格
|
||||
* @since V2 & V3
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public File downloadbill(Date billDate, BillType billType)
|
||||
throws WeixinException {
|
||||
return payApi.downloadbill(billDate, billType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 冲正订单(需要证书)</br> 当支付返回失败,或收银系统超时需要取消交易,可以调用该接口</br> 接口逻辑:支
|
||||
* 付失败的关单,支付成功的撤销支付</br> <font color="red">7天以内的单可撤销,其他正常支付的单
|
||||
* 如需实现相同功能请调用退款接口</font></br> <font
|
||||
* color="red">调用扣款接口后请勿立即调用撤销,需要等待5秒以上。先调用查单接口,如果没有确切的返回,再调用撤销</font></br>
|
||||
*
|
||||
* @param ca
|
||||
* 证书文件(V2版本后缀为*.pfx,V3版本后缀为*.p12)
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @return 撤销结果
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay2Api
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
|
||||
* @since V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public ApiResult reverse(File caFile, IdQuery idQuery)
|
||||
throws WeixinException {
|
||||
return payApi.reverse(caFile, idQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 冲正撤销:默认采用properties中配置的ca文件
|
||||
*
|
||||
* @param idQuery
|
||||
* transaction_id、out_trade_no 二选一
|
||||
* @return 撤销结果
|
||||
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#reverse(File, IdQuery)}
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public ApiResult reverse(IdQuery idQuery) throws WeixinException {
|
||||
File caFile = new File(ConfigUtil.getClassPathValue("ca_file"));
|
||||
return payApi.reverse(caFile, idQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭订单</br> 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
|
||||
* 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
|
||||
*
|
||||
* @param outTradeNo
|
||||
* 商户系统内部的订单号
|
||||
* @return 执行结果
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
|
||||
* @since V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public ApiResult closeOrder(String outTradeNo) throws WeixinException {
|
||||
return payApi.closeOrder(outTradeNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* native支付URL转短链接
|
||||
*
|
||||
* @param url
|
||||
* 具有native标识的支付URL
|
||||
* @return 转换后的短链接
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay2Api
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
|
||||
* @since V2 & V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public String getPayShorturl(String url) throws WeixinException {
|
||||
return payApi.getShorturl(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口上报
|
||||
*
|
||||
* @param interfaceUrl
|
||||
* 上报对应的接口的完整 URL, 类似: https://api.mch.weixin.q
|
||||
* q.com/pay/unifiedorder
|
||||
* @param executeTime
|
||||
* 接口耗时情况,单位为毫秒
|
||||
* @param outTradeNo
|
||||
* 商户系统内部的订单号,商 户可以在上报时提供相关商户订单号方便微信支付更好 的提高服务质量。
|
||||
* @param ip
|
||||
* 发起接口调用时的机器 IP
|
||||
* @param time
|
||||
* 商户调用该接口时商户自己 系统的时间
|
||||
* @param returnXml
|
||||
* 调用接口返回的基本数据
|
||||
* @return 处理结果
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public XmlResult interfaceReport(String interfaceUrl, int executeTime,
|
||||
String outTradeNo, String ip, Date time, XmlResult returnXml)
|
||||
throws WeixinException {
|
||||
return pay3Api.interfaceReport(interfaceUrl, executeTime, outTradeNo,
|
||||
ip, time, returnXml);
|
||||
}
|
||||
}
|
||||
@ -2,13 +2,11 @@ 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;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.http.JsonResult;
|
||||
import com.foxinmy.weixin4j.http.XmlResult;
|
||||
import com.foxinmy.weixin4j.model.Button;
|
||||
import com.foxinmy.weixin4j.model.WeixinMpAccount;
|
||||
import com.foxinmy.weixin4j.mp.api.CustomApi;
|
||||
@ -18,7 +16,6 @@ import com.foxinmy.weixin4j.mp.api.MassApi;
|
||||
import com.foxinmy.weixin4j.mp.api.MediaApi;
|
||||
import com.foxinmy.weixin4j.mp.api.MenuApi;
|
||||
import com.foxinmy.weixin4j.mp.api.NotifyApi;
|
||||
import com.foxinmy.weixin4j.mp.api.PayApi;
|
||||
import com.foxinmy.weixin4j.mp.api.QrApi;
|
||||
import com.foxinmy.weixin4j.mp.api.TmplApi;
|
||||
import com.foxinmy.weixin4j.mp.api.UserApi;
|
||||
@ -33,12 +30,6 @@ import com.foxinmy.weixin4j.mp.model.QRParameter;
|
||||
import com.foxinmy.weixin4j.mp.model.SemQuery;
|
||||
import com.foxinmy.weixin4j.mp.model.SemResult;
|
||||
import com.foxinmy.weixin4j.mp.model.User;
|
||||
import com.foxinmy.weixin4j.mp.payment.ApiResult;
|
||||
import com.foxinmy.weixin4j.mp.payment.Refund;
|
||||
import com.foxinmy.weixin4j.mp.payment.RefundResult;
|
||||
import com.foxinmy.weixin4j.mp.payment.v2.Order;
|
||||
import com.foxinmy.weixin4j.mp.type.BillType;
|
||||
import com.foxinmy.weixin4j.mp.type.IdQuery;
|
||||
import com.foxinmy.weixin4j.mp.type.IndustryType;
|
||||
import com.foxinmy.weixin4j.mp.type.Lang;
|
||||
import com.foxinmy.weixin4j.msg.model.Base;
|
||||
@ -70,7 +61,6 @@ public class WeixinProxy {
|
||||
private final QrApi qrApi;
|
||||
private final TmplApi tmplApi;
|
||||
private final HelperApi helperApi;
|
||||
private final PayApi payApi;
|
||||
|
||||
/**
|
||||
* 默认采用文件存放Token信息
|
||||
@ -116,7 +106,6 @@ public class WeixinProxy {
|
||||
this.qrApi = new QrApi(tokenHolder);
|
||||
this.tmplApi = new TmplApi(tokenHolder);
|
||||
this.helperApi = new HelperApi(tokenHolder);
|
||||
this.payApi = new PayApi(tokenHolder);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -502,8 +491,7 @@ public class WeixinProxy {
|
||||
* href="http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html#.E5.88.A0.E9.99.A4.E7.BE.A4.E5.8F.91.E3.80.90.E8.AE.A2.E9.98.85.E5.8F.B7.E4.B8.8E.E6.9C.8D.E5.8A.A1.E5.8F.B7.E8.AE.A4.E8.AF.81.E5.90.8E.E5.9D.87.E5.8F.AF.E7.94.A8.E3.80.91">删除群发</a>
|
||||
* @see com.foxinmy.weixin4j.mp.api.MassApi
|
||||
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByGroupId(Base, int)}
|
||||
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByOpenIds(Base, String...)
|
||||
|
||||
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#massByOpenIds(Base, String...)
|
||||
*/
|
||||
public JsonResult deleteMassNews(String msgid) throws WeixinException {
|
||||
return massApi.deleteMassNews(msgid);
|
||||
@ -756,7 +744,8 @@ public class WeixinProxy {
|
||||
/**
|
||||
* 自定义菜单
|
||||
*
|
||||
* @param btnList 菜单列表
|
||||
* @param btnList
|
||||
* 菜单列表
|
||||
* @throws WeixinException
|
||||
* @see <a
|
||||
* href="http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html">创建自定义菜单</a>
|
||||
@ -798,7 +787,8 @@ public class WeixinProxy {
|
||||
/**
|
||||
* 生成带参数的二维码
|
||||
*
|
||||
* @param parameter 二维码参数
|
||||
* @param parameter
|
||||
* 二维码参数
|
||||
* @return byte数据包
|
||||
* @throws WeixinException
|
||||
* @see com.foxinmy.weixin4j.mp.api.QrApi
|
||||
@ -900,7 +890,8 @@ public class WeixinProxy {
|
||||
/**
|
||||
* 长链接转短链接
|
||||
*
|
||||
* @param url 待转换的链接
|
||||
* @param url
|
||||
* 待转换的链接
|
||||
* @return 短链接
|
||||
* @throws WeixinException
|
||||
* @see <a
|
||||
@ -911,195 +902,6 @@ public class WeixinProxy {
|
||||
return helperApi.getShorturl(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发货通知
|
||||
*
|
||||
* @param openId
|
||||
* 用户ID
|
||||
* @param transid
|
||||
* 交易单号
|
||||
* @param outTradeNo
|
||||
* 订单号
|
||||
* @param status
|
||||
* 成功|失败
|
||||
* @param statusMsg
|
||||
* status为失败时携带的信息
|
||||
* @return 发货处理结果
|
||||
* @since V2 & V3
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public JsonResult deliverNotify(String openId, String transid,
|
||||
String outTradeNo, boolean status, String statusMsg)
|
||||
throws WeixinException {
|
||||
return payApi.deliverNotify(openId, transid, outTradeNo, status,
|
||||
statusMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单查询
|
||||
*
|
||||
* @param outTradeNo
|
||||
* 订单号
|
||||
* @return 订单信息
|
||||
* @throws WeixinException
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
*/
|
||||
public Order orderQueryV2(WeixinMpAccount weixinAccount, String outTradeNo)
|
||||
throws WeixinException {
|
||||
return payApi.orderQueryV2(outTradeNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 维权处理
|
||||
*
|
||||
* @param openId
|
||||
* 用户ID
|
||||
* @param feedbackId
|
||||
* 维权单号
|
||||
* @return 调用结果
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @since V2 & V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public JsonResult updateFeedback(String openId, String feedbackId)
|
||||
throws WeixinException {
|
||||
return payApi.updateFeedback(openId, feedbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* V3订单查询
|
||||
*
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id、out_trade_no 二 选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public com.foxinmy.weixin4j.mp.payment.v3.Order orderQueryV3(IdQuery idQuery)
|
||||
throws WeixinException {
|
||||
return payApi.orderQueryV3(idQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载对账单<br>
|
||||
* 1.微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账 单中,跟原支付单订单号一致,bill_type 为
|
||||
* REVOKED;<br>
|
||||
* 2.微信在次日 9 点启动生成前一天的对账单,建议商户 9 点半后再获取;<br>
|
||||
* 3.对账单中涉及金额的字段单位为“元”。<br>
|
||||
*
|
||||
* @param billDate
|
||||
* 下载对账单的日期
|
||||
* @param billType
|
||||
* 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单
|
||||
* REFUND,返回当日退款订单
|
||||
* @return excel表格
|
||||
* @since V2 & V3
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @throws WeixinException
|
||||
* @throws IOException
|
||||
*/
|
||||
public File downloadbill(Date billDate, BillType billType)
|
||||
throws WeixinException, IOException {
|
||||
return payApi.downloadbill(billDate, billType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请退款(V3请求需要双向证书)</br>
|
||||
* <p style="color:red">
|
||||
* 交易时间超过 1 年的订单无法提交退款; </br> 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失
|
||||
* 败后重新提交,要采用原来的 out_refund_no。总退款金额不能超过用户实际支付金额。</br>
|
||||
* </p>
|
||||
*
|
||||
* @param ca
|
||||
* 证书文件<font color="red">V2版本时无需传入</font>
|
||||
* @param idQuery
|
||||
* ) 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @param outRefundNo
|
||||
* 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
|
||||
* @param totalFee
|
||||
* 订单总金额,单位为元
|
||||
* @param refundFee
|
||||
* 退款总金额,单位为元,可以做部分退款
|
||||
* @param opUserId
|
||||
* 操作员帐号, 默认为商户号
|
||||
* @param opUserPasswd
|
||||
* <font color="red">V3版本留空,V2版本需传入值</font>
|
||||
*
|
||||
* @return 退款申请结果
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @see com.foxinmy.weixin4j.mp.payment.RefundResult
|
||||
* @since V2 & V3
|
||||
* @throws WeixinException
|
||||
* @throws IOException
|
||||
*/
|
||||
public RefundResult refund(InputStream ca, IdQuery idQuery,
|
||||
String outRefundNo, double totalFee, double refundFee,
|
||||
String opUserId, String opUserPasswd) throws WeixinException,
|
||||
IOException {
|
||||
return payApi.refund(ca, idQuery, outRefundNo, totalFee, refundFee,
|
||||
opUserId, opUserPasswd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 不同的退款接口选择</br> V3支付则采用properties中配置的ca文件</br>
|
||||
* V2支付则需要传入opUserPasswd参数</br>
|
||||
*
|
||||
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#refund(InputStream, IdQuery, String, double, double, String,String)}
|
||||
*/
|
||||
public RefundResult refund(IdQuery idQuery, String outRefundNo,
|
||||
double totalFee, double refundFee, String opUserId,
|
||||
String opUserPasswd) throws WeixinException, IOException {
|
||||
return payApi.refund(idQuery, outRefundNo, totalFee, refundFee,
|
||||
opUserId, opUserPasswd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款查询</br> 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
|
||||
*
|
||||
* @param idQuery
|
||||
* 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
|
||||
* 四个参数必填一个,优先级为:
|
||||
* refund_id>out_refund_no>transaction_id>out_trade_no
|
||||
* @return 退款记录
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @see com.foxinmy.weixin4j.mp.payment.Refund
|
||||
* @since V2 & V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public Refund refundQuery(IdQuery idQuery) throws WeixinException {
|
||||
return payApi.refundQuery(idQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭订单</br> 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
|
||||
* 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
|
||||
*
|
||||
* @param outTradeNo
|
||||
* 商户系统内部的订单号
|
||||
* @return 执行结果
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @since V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public XmlResult closeOrder(String outTradeNo) throws WeixinException {
|
||||
return payApi.closeOrder(outTradeNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* native支付URL转短链接
|
||||
*
|
||||
* @param url
|
||||
* 具有native标识的支付URL
|
||||
* @return 转换后的短链接
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public String getPayShorturl(String url) throws WeixinException {
|
||||
return payApi.getShorturl(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 语义理解
|
||||
*
|
||||
@ -1129,39 +931,4 @@ public class WeixinProxy {
|
||||
public List<String> getcallbackip() throws WeixinException {
|
||||
return helperApi.getcallbackip();
|
||||
}
|
||||
|
||||
/**
|
||||
* 冲正订单(需要证书)</br> 当支付返回失败,或收银系统超时需要取消交易,可以调用该接口</br> 接口逻辑:支
|
||||
* 付失败的关单,支付成功的撤销支付</br> <font color="red">7天以内的单可撤销,其他正常支付的单
|
||||
* 如需实现相同功能请调用退款接口</font></br> <font
|
||||
* color="red">调用扣款接口后请勿立即调用撤销,需要等待5秒以上。先调用查单接口,如果没有确切的返回,再调用撤销</font></br>
|
||||
*
|
||||
* @param ca
|
||||
* 证书文件
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @return 撤销结果
|
||||
* @see com.foxinmy.weixin4j.mp.api.PayApi
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public ApiResult reverse(InputStream ca, IdQuery idQuery)
|
||||
throws WeixinException {
|
||||
return payApi.reverse(ca, idQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 冲正撤销:默认采用properties中配置的ca文件
|
||||
*
|
||||
* @param idQuery
|
||||
* transaction_id、out_trade_no 二选一
|
||||
* @return 撤销结果
|
||||
* @see {@link com.foxinmy.weixin4j.mp.WeixinProxy#reverse(InputStream, IdQuery)}
|
||||
* @throws WeixinException
|
||||
* @throws IOException
|
||||
*/
|
||||
public ApiResult reverse(IdQuery idQuery) throws WeixinException,
|
||||
IOException {
|
||||
return payApi.reverse(idQuery);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,465 @@
|
||||
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.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.Consts;
|
||||
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.Response;
|
||||
import com.foxinmy.weixin4j.http.SSLHttpRequest;
|
||||
import com.foxinmy.weixin4j.model.Token;
|
||||
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.v2.RefundRecord;
|
||||
import com.foxinmy.weixin4j.mp.payment.v2.RefundResult;
|
||||
import com.foxinmy.weixin4j.mp.payment.v3.ApiResult;
|
||||
import com.foxinmy.weixin4j.mp.type.BillType;
|
||||
import com.foxinmy.weixin4j.mp.type.IdQuery;
|
||||
import com.foxinmy.weixin4j.mp.type.RefundType;
|
||||
import com.foxinmy.weixin4j.mp.type.SignType;
|
||||
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;
|
||||
|
||||
/**
|
||||
* V2支付API
|
||||
*
|
||||
* @className PayApi
|
||||
* @author jy
|
||||
* @date 2014年10月28日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class Pay2Api extends PayApi {
|
||||
|
||||
private final HelperApi helperApi;
|
||||
|
||||
public Pay2Api(TokenHolder tokenHolder) {
|
||||
super(tokenHolder);
|
||||
this.helperApi = new HelperApi(tokenHolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单查询
|
||||
*
|
||||
* @param idQuery
|
||||
* 订单号
|
||||
* @return 订单信息
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.Order
|
||||
* @since V2
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public Order orderQuery(IdQuery idQuery) throws WeixinException {
|
||||
String orderquery_uri = getRequestUri("orderquery_uri");
|
||||
Token token = tokenHolder.getToken();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(idQuery.getType().getName()).append("=")
|
||||
.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 = DateUtil.timestamp2string();
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("appid", weixinAccount.getId());
|
||||
obj.put("appkey", weixinAccount.getPaySignKey());
|
||||
obj.put("package", sb.toString());
|
||||
obj.put("timestamp", timestamp);
|
||||
String signature = PayUtil.paysignSha(obj);
|
||||
|
||||
obj.clear();
|
||||
obj.put("appid", weixinAccount.getId());
|
||||
obj.put("package", sb.toString());
|
||||
obj.put("timestamp", timestamp);
|
||||
obj.put("app_signature", signature);
|
||||
obj.put("sign_method", SignType.SHA1.name().toLowerCase());
|
||||
|
||||
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);
|
||||
if (order.getRetCode() != 0) {
|
||||
throw new WeixinException(Integer.toString(order.getRetCode()),
|
||||
order.getRetMsg());
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请退款(需要证书)</br>
|
||||
* <p style="color:red">
|
||||
* 交易时间超过 1 年的订单无法提交退款; </br> 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失
|
||||
* 败后重新提交,要采用原来的 out_refund_no。总退款金额不能超过用户实际支付金额。</br>
|
||||
* </p>
|
||||
*
|
||||
* @param caFile
|
||||
* 证书文件(V2版本后缀为*.pfx)
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @param outRefundNo
|
||||
* 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
|
||||
* @param totalFee
|
||||
* 订单总金额,单位为元
|
||||
* @param refundFee
|
||||
* 退款总金额,单位为元,可以做部分退款
|
||||
* @param opUserId
|
||||
* 操作员帐号, 默认为商户号
|
||||
* @param mopara
|
||||
* 如 opUserPasswd
|
||||
*
|
||||
* @return 退款申请结果
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.RefundResult
|
||||
* @since V2
|
||||
* @throws WeixinException
|
||||
*/
|
||||
@Override
|
||||
protected RefundResult refund(File caFile, IdQuery idQuery,
|
||||
String outRefundNo, double totalFee, double refundFee,
|
||||
String opUserId, Map<String, String> mopara) throws WeixinException {
|
||||
String refund_uri = getRequestUri("refund_v2_uri");
|
||||
Response response = null;
|
||||
InputStream ca = null;
|
||||
try {
|
||||
ca = new FileInputStream(caFile);
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("input_charset", Consts.UTF_8.name());
|
||||
// 版本号
|
||||
// 填写为 1.0 时,操作员密码为明文
|
||||
// 填写为 1.1 时,操作员密码为 MD5(密码)值
|
||||
map.put("service_version", "1.1");
|
||||
map.put("partner", weixinAccount.getPartnerId());
|
||||
map.put("out_refund_no", outRefundNo);
|
||||
map.put("total_fee", DateUtil.formaFee2Fen(totalFee));
|
||||
map.put("refund_fee", DateUtil.formaFee2Fen(refundFee));
|
||||
map.put(idQuery.getType().getName(), idQuery.getId());
|
||||
if (StringUtils.isBlank(opUserId)) {
|
||||
opUserId = weixinAccount.getPartnerId();
|
||||
}
|
||||
map.put("op_user_id", opUserId);
|
||||
if (mopara != null && !mopara.isEmpty()) {
|
||||
map.putAll(mopara);
|
||||
}
|
||||
String sign = PayUtil
|
||||
.paysignMd5(map, weixinAccount.getPartnerKey());
|
||||
map.put("sign", sign.toLowerCase());
|
||||
|
||||
SSLContext ctx = null;
|
||||
KeyStore ks = null;
|
||||
String jksPwd = "";
|
||||
File jksFile = new File(String.format("%s/tenpay_cacert.jks",
|
||||
caFile.getParent()));
|
||||
// create jks ca
|
||||
if (!jksFile.exists()) {
|
||||
CertificateFactory cf = CertificateFactory
|
||||
.getInstance(com.foxinmy.weixin4j.model.Consts.X509);
|
||||
java.security.cert.Certificate cert = cf
|
||||
.generateCertificate(PayUtil.class
|
||||
.getResourceAsStream("cacert.pem"));
|
||||
ks = KeyStore
|
||||
.getInstance(com.foxinmy.weixin4j.model.Consts.JKS);
|
||||
ks.load(null, null);
|
||||
ks.setCertificateEntry("tenpay", cert);
|
||||
ks.store(new FileOutputStream(jksFile), jksPwd.toCharArray());
|
||||
}
|
||||
// load jks ca
|
||||
TrustManagerFactory tmf = TrustManagerFactory
|
||||
.getInstance(com.foxinmy.weixin4j.model.Consts.SunX509);
|
||||
ks = KeyStore.getInstance(com.foxinmy.weixin4j.model.Consts.JKS);
|
||||
ks.load(new FileInputStream(jksFile), jksPwd.toCharArray());
|
||||
tmf.init(ks);
|
||||
// load pfx ca
|
||||
KeyManagerFactory kmf = KeyManagerFactory
|
||||
.getInstance(com.foxinmy.weixin4j.model.Consts.SunX509);
|
||||
ks = KeyStore.getInstance(com.foxinmy.weixin4j.model.Consts.PKCS12);
|
||||
ks.load(ca, weixinAccount.getPartnerId().toCharArray());
|
||||
kmf.init(ks, weixinAccount.getPartnerId().toCharArray());
|
||||
|
||||
ctx = SSLContext.getInstance(com.foxinmy.weixin4j.model.Consts.TLS);
|
||||
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
|
||||
new SecureRandom());
|
||||
|
||||
SSLHttpRequest request = new SSLHttpRequest(ctx);
|
||||
response = request.get(refund_uri, map);
|
||||
} catch (WeixinException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new WeixinException(e.getMessage());
|
||||
} finally {
|
||||
if (ca != null) {
|
||||
try {
|
||||
ca.close();
|
||||
} catch (IOException e) {
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
return response.getAsObject(new TypeReference<RefundResult>() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款申请
|
||||
*
|
||||
* @param caFile
|
||||
* 证书文件(V2版本后缀为*.pfx)
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @param outRefundNo
|
||||
* 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
|
||||
* @param totalFee
|
||||
* 订单总金额,单位为元
|
||||
* @param refundFee
|
||||
* 退款总金额,单位为元,可以做部分退款
|
||||
* @param opUserId
|
||||
* 操作员帐号, 默认为商户号
|
||||
* @param opUserPasswd
|
||||
* 操作员密码,默认为商户后台登录密码
|
||||
* @see {@link com.foxinmy.weixin4j.mp.api.Pay2Api#refund(File, IdQuery, String, double, double, String, Map)}
|
||||
*/
|
||||
public RefundResult refund(File caFile, IdQuery idQuery,
|
||||
String outRefundNo, double totalFee, double refundFee,
|
||||
String opUserId, String opUserPasswd) throws WeixinException {
|
||||
Map<String, String> mopara = new HashMap<String, String>();
|
||||
mopara.put("op_user_passwd", DigestUtils.md5Hex(opUserPasswd));
|
||||
return refund(caFile, idQuery, outRefundNo, totalFee, refundFee,
|
||||
opUserId, mopara);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款申请
|
||||
*
|
||||
* @param caFile
|
||||
* 证书文件(V2版本后缀为*.pfx)
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @param outRefundNo
|
||||
* 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
|
||||
* @param totalFee
|
||||
* 订单总金额,单位为元
|
||||
* @param refundFee
|
||||
* 退款总金额,单位为元,可以做部分退款
|
||||
* @param opUserId
|
||||
* 操作员帐号, 默认为商户号
|
||||
* @param opUserPasswd
|
||||
* 操作员密码,默认为商户后台登录密码
|
||||
* @param recvUserId
|
||||
* 转账退款接收退款的财付通帐号。 一般无需填写,只有退银行失败,资金转入商 户号现金账号时(即状态为转入代发,查询返 回的
|
||||
* refund_status 是 7 或 11),填写原退款 单号并填写此字段,资金才会退到指定财付通
|
||||
* 账号。其他情况此字段忽略
|
||||
* @param reccvUserName
|
||||
* 转账退款接收退款的姓名(需与接收退款的财 付通帐号绑定的姓名一致)
|
||||
* @param refundType
|
||||
* 为空或者填 1:商户号余额退款;2:现金帐号 退款;3:优先商户号退款,若商户号余额不足, 再做现金帐号退款。使用 2 或
|
||||
* 3 时,需联系财 付通开通此功能
|
||||
* @see {@link com.foxinmy.weixin4j.mp.api.Pay2Api#refund(File, IdQuery, String, double, double, String, Map)}
|
||||
* @return 退款结果
|
||||
*/
|
||||
public RefundResult refund(File caFile, IdQuery idQuery,
|
||||
String outRefundNo, double totalFee, double refundFee,
|
||||
String opUserId, String opUserPasswd, String recvUserId,
|
||||
String reccvUserName, RefundType refundType) throws WeixinException {
|
||||
Map<String, String> mopara = new HashMap<String, String>();
|
||||
mopara.put("op_user_passwd", DigestUtils.md5Hex(opUserPasswd));
|
||||
if (StringUtils.isNotBlank(recvUserId)) {
|
||||
mopara.put("recv_user_id", recvUserId);
|
||||
}
|
||||
if (StringUtils.isNotBlank(reccvUserName)) {
|
||||
mopara.put("reccv_user_name", reccvUserName);
|
||||
}
|
||||
if (refundType != null) {
|
||||
mopara.put("refund_type", Integer.toString(refundType.getVal()));
|
||||
}
|
||||
return refund(caFile, idQuery, outRefundNo, totalFee, refundFee,
|
||||
opUserId, mopara);
|
||||
}
|
||||
|
||||
/**
|
||||
* 冲正订单(需要证书)</br><font color="red">V2暂不支持</font>
|
||||
*
|
||||
* @param caFile
|
||||
* 证书文件(V2版本后缀为*.pfx)
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @since V2
|
||||
* @return 撤销结果
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public ApiResult reverse(File caFile, IdQuery idQuery)
|
||||
throws WeixinException {
|
||||
throw new WeixinException("V2 unsupport reverse api");
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭订单</br> 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
|
||||
* 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
|
||||
*
|
||||
* @param outTradeNo
|
||||
* 商户系统内部的订单号
|
||||
* @return 处理结果
|
||||
* @since V2
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public ApiResult closeOrder(String outTradeNo) throws WeixinException {
|
||||
throw new WeixinException("V2 unsupport closeOrder api");
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载对账单<br>
|
||||
* 1.微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账 单中,跟原支付单订单号一致,bill_type 为
|
||||
* REVOKED;<br>
|
||||
* 2.微信在次日 9 点启动生成前一天的对账单,建议商户 9 点半后再获取;<br>
|
||||
* 3.对账单中涉及金额的字段单位为“元”。<br>
|
||||
*
|
||||
* @param billDate
|
||||
* 下载对账单的日期 为空则取前一天
|
||||
* @param billType
|
||||
* 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单
|
||||
* REFUND,返回当日退款订单
|
||||
* @return excel表格
|
||||
* @since V2
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public File downloadbill(Date billDate, BillType billType)
|
||||
throws WeixinException {
|
||||
if (billDate == null) {
|
||||
Calendar now = Calendar.getInstance();
|
||||
now.add(Calendar.DAY_OF_MONTH, -1);
|
||||
billDate = now.getTime();
|
||||
}
|
||||
if (billType == null) {
|
||||
billType = BillType.ALL;
|
||||
}
|
||||
String formatBillDate = DateUtil.fortmat2yyyyMMdd(billDate);
|
||||
String bill_path = ConfigUtil.getValue("bill_path");
|
||||
String fileName = String.format("%s_%s_%s.xls", formatBillDate,
|
||||
billType.name().toLowerCase(), weixinAccount.getId());
|
||||
File file = new File(String.format("%s/%s", bill_path, fileName));
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
}
|
||||
String downloadbill_uri = getRequestUri("downloadbill_v2_uri");
|
||||
|
||||
Map<String, String> map = new LinkedHashMap<String, String>();
|
||||
map.put("spid", weixinAccount.getPartnerId());
|
||||
map.put("trans_time", DateUtil.fortmat2yyyy_MM_dd(billDate));
|
||||
map.put("stamp", DateUtil.timestamp2string());
|
||||
map.put("cft_signtype", "0");
|
||||
map.put("mchtype", Integer.toString(billType.getVal()));
|
||||
map.put("key", weixinAccount.getPartnerKey());
|
||||
String sign = DigestUtils.md5Hex(MapUtil
|
||||
.toJoinString(map, false, false));
|
||||
map.put("sign", sign.toLowerCase());
|
||||
Response response = request.get(downloadbill_uri, map);
|
||||
BufferedReader reader = null;
|
||||
OutputStream os = null;
|
||||
try {
|
||||
reader = new BufferedReader(
|
||||
new InputStreamReader(response.getStream(),
|
||||
com.foxinmy.weixin4j.model.Consts.GBK));
|
||||
String line = null;
|
||||
List<String[]> bills = new LinkedList<String[]>();
|
||||
while ((line = reader.readLine()) != null) {
|
||||
bills.add(line.replaceAll("`", "").split(","));
|
||||
}
|
||||
|
||||
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(formatBillDate + "对账单");
|
||||
ExcelUtil.list2excel(wb, headers, bills);
|
||||
ExcelUtil.list2excel(wb, totalHeaders, totalDatas);
|
||||
os = new FileOutputStream(file);
|
||||
wb.write(os);
|
||||
} catch (IOException e) {
|
||||
throw new WeixinException(e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
if (os != null) {
|
||||
os.close();
|
||||
}
|
||||
} catch (IOException ignore) {
|
||||
;
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款查询</br> 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
|
||||
*
|
||||
* @param idQuery
|
||||
* 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
|
||||
* 四个参数必填一个,优先级为:
|
||||
* refund_id>out_refund_no>transaction_id>out_trade_no
|
||||
* @return 退款记录
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.RefundRecord
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.RefundDetail
|
||||
* @since V2
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public RefundRecord refundQuery(IdQuery idQuery) throws WeixinException {
|
||||
String refundquery_uri = getRequestUri("refundquery_v2_uri");
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("input_charset", Consts.UTF_8.name());
|
||||
map.put("partner", weixinAccount.getPartnerId());
|
||||
map.put(idQuery.getType().getName(), idQuery.getId());
|
||||
String sign = PayUtil.paysignMd5(map, weixinAccount.getPartnerKey());
|
||||
map.put("sign", sign.toLowerCase());
|
||||
Response response = request.get(refundquery_uri, map);
|
||||
return RefundConverter.fromXML(response.getAsString(),
|
||||
RefundRecord.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShorturl(String url) throws WeixinException {
|
||||
return helperApi.getShorturl(url);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,430 @@
|
||||
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.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
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.lang3.StringUtils;
|
||||
import org.apache.http.Consts;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.http.Response;
|
||||
import com.foxinmy.weixin4j.http.SSLHttpRequest;
|
||||
import com.foxinmy.weixin4j.http.XmlResult;
|
||||
import com.foxinmy.weixin4j.mp.payment.PayUtil;
|
||||
import com.foxinmy.weixin4j.mp.payment.RefundConverter;
|
||||
import com.foxinmy.weixin4j.mp.payment.v3.ApiResult;
|
||||
import com.foxinmy.weixin4j.mp.payment.v3.Order;
|
||||
import com.foxinmy.weixin4j.mp.payment.v3.RefundRecord;
|
||||
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;
|
||||
import com.foxinmy.weixin4j.util.DateUtil;
|
||||
import com.foxinmy.weixin4j.util.RandomUtil;
|
||||
|
||||
/**
|
||||
* V3支付API
|
||||
*
|
||||
* @className PayApi
|
||||
* @author jy
|
||||
* @date 2014年10月28日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class Pay3Api extends PayApi {
|
||||
|
||||
public Pay3Api(TokenHolder tokenHolder) {
|
||||
super(tokenHolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单查询
|
||||
*
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id、out_trade_no 二 选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @return 订单信息
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v3.Order
|
||||
* @since V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public Order orderQuery(IdQuery idQuery) throws WeixinException {
|
||||
Map<String, String> map = baseMap(idQuery);
|
||||
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<Order>() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请退款(请求需要双向证书)</br>
|
||||
* <p style="color:red">
|
||||
* 交易时间超过 1 年的订单无法提交退款; </br> 支持部分退款,部分退需要设置相同的订单号和不同的 out_refund_no。一笔退款失
|
||||
* 败后重新提交,要采用原来的 out_refund_no。总退款金额不能超过用户实际支付金额。</br>
|
||||
* </p>
|
||||
*
|
||||
* @param caFile
|
||||
* 证书文件(V3版本后缀为*.p12)
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @param outRefundNo
|
||||
* 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
|
||||
* @param totalFee
|
||||
* 订单总金额,单位为元
|
||||
* @param refundFee
|
||||
* 退款总金额,单位为元,可以做部分退款
|
||||
* @param opUserId
|
||||
* 操作员帐号, 默认为商户号
|
||||
*
|
||||
* @return 退款申请结果
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundResult
|
||||
* @since V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
protected RefundResult refund(File caFile, IdQuery idQuery,
|
||||
String outRefundNo, double totalFee, double refundFee,
|
||||
String opUserId, Map<String, String> mopara) throws WeixinException {
|
||||
String refund_uri = getRequestUri("refund_v3_uri");
|
||||
Response response = null;
|
||||
InputStream ca = null;
|
||||
try {
|
||||
ca = new FileInputStream(caFile);
|
||||
|
||||
Map<String, String> map = baseMap(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);
|
||||
if (mopara != null && !mopara.isEmpty()) {
|
||||
map.putAll(mopara);
|
||||
}
|
||||
String sign = PayUtil
|
||||
.paysignMd5(map, weixinAccount.getPaySignKey());
|
||||
map.put("sign", sign);
|
||||
String param = map2xml(map);
|
||||
SSLHttpRequest request = new SSLHttpRequest(
|
||||
weixinAccount.getMchId(), ca);
|
||||
response = request.post(refund_uri, param);
|
||||
} catch (WeixinException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new WeixinException(e.getMessage());
|
||||
} finally {
|
||||
if (ca != null) {
|
||||
try {
|
||||
ca.close();
|
||||
} catch (IOException e) {
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
return response.getAsObject(new TypeReference<RefundResult>() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款申请
|
||||
*
|
||||
* @param caFile
|
||||
* 证书文件(V2版本后缀为*.pfx)
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @param outRefundNo
|
||||
* 商户系统内部的退款单号,商 户系统内部唯一,同一退款单号多次请求只退一笔
|
||||
* @param totalFee
|
||||
* 订单总金额,单位为元
|
||||
* @param refundFee
|
||||
* 退款总金额,单位为元,可以做部分退款
|
||||
* @param opUserId
|
||||
* 操作员帐号, 默认为商户号
|
||||
* @see {@link com.foxinmy.weixin4j.mp.api.Pay3Api#refund(File, IdQuery, String, double, double, String, Map)}
|
||||
*/
|
||||
public RefundResult refund(File caFile, IdQuery idQuery,
|
||||
String outRefundNo, double totalFee, double refundFee,
|
||||
String opUserId) throws WeixinException {
|
||||
return refund(caFile, idQuery, outRefundNo, totalFee, refundFee,
|
||||
opUserId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 冲正订单(需要证书)</br> 当支付返回失败,或收银系统超时需要取消交易,可以调用该接口</br> 接口逻辑:支
|
||||
* 付失败的关单,支付成功的撤销支付</br> <font color="red">7天以内的单可撤销,其他正常支付的单
|
||||
* 如需实现相同功能请调用退款接口</font></br> <font
|
||||
* color="red">调用扣款接口后请勿立即调用撤销,需要等待5秒以上。先调用查单接口,如果没有确切的返回,再调用撤销</font></br>
|
||||
*
|
||||
* @param caFile
|
||||
* 证书文件(V3版本后缀为*.p12)
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @return 撤销结果
|
||||
* @since V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public ApiResult reverse(File caFile, IdQuery idQuery)
|
||||
throws WeixinException {
|
||||
InputStream ca = null;
|
||||
try {
|
||||
ca = new FileInputStream(caFile);
|
||||
SSLHttpRequest request = new SSLHttpRequest(
|
||||
weixinAccount.getMchId(), ca);
|
||||
String reverse_uri = getRequestUri("reverse_uri");
|
||||
Map<String, String> map = baseMap(idQuery);
|
||||
String sign = PayUtil
|
||||
.paysignMd5(map, weixinAccount.getPaySignKey());
|
||||
map.put("sign", sign);
|
||||
String param = map2xml(map);
|
||||
Response response = request.post(reverse_uri, param);
|
||||
return response.getAsObject(new TypeReference<ApiResult>() {
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new WeixinException(e.getMessage());
|
||||
} finally {
|
||||
if (ca != null) {
|
||||
try {
|
||||
ca.close();
|
||||
} catch (IOException e) {
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* native支付URL转短链接
|
||||
*
|
||||
* @param url
|
||||
* 具有native标识的支付URL
|
||||
* @return 转换后的短链接
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public String getShorturl(String url) throws WeixinException {
|
||||
Map<String, String> map = baseMap(null);
|
||||
try {
|
||||
map.put("long_url", URLEncoder.encode(url, Consts.UTF_8.name()));
|
||||
} catch (UnsupportedEncodingException ignore) {
|
||||
;
|
||||
}
|
||||
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
|
||||
map.put("sign", sign);
|
||||
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 outTradeNo
|
||||
* 商户系统内部的订单号
|
||||
* @return 处理结果
|
||||
* @since V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public ApiResult closeOrder(String outTradeNo) throws WeixinException {
|
||||
Map<String, String> map = baseMap(new IdQuery(outTradeNo,
|
||||
IdType.TRADENO));
|
||||
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.getAsObject(new TypeReference<ApiResult>() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载对账单<br>
|
||||
* 1.微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账 单中,跟原支付单订单号一致,bill_type 为
|
||||
* REVOKED;<br>
|
||||
* 2.微信在次日 9 点启动生成前一天的对账单,建议商户 9 点半后再获取;<br>
|
||||
* 3.对账单中涉及金额的字段单位为“元”。<br>
|
||||
*
|
||||
* @param billDate
|
||||
* 下载对账单的日期
|
||||
* @param billType
|
||||
* 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单
|
||||
* REFUND,返回当日退款订单
|
||||
* @return excel表格
|
||||
* @since V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public File downloadbill(Date billDate, BillType billType)
|
||||
throws WeixinException {
|
||||
if (billDate == null) {
|
||||
Calendar now = Calendar.getInstance();
|
||||
now.add(Calendar.DAY_OF_MONTH, -1);
|
||||
billDate = now.getTime();
|
||||
}
|
||||
if (billType == null) {
|
||||
billType = BillType.ALL;
|
||||
}
|
||||
String formatBillDate = DateUtil.fortmat2yyyyMMdd(billDate);
|
||||
String bill_path = ConfigUtil.getValue("bill_path");
|
||||
String fileName = String.format("%s_%s_%s.xls", formatBillDate,
|
||||
billType.name().toLowerCase(), weixinAccount.getId());
|
||||
File file = new File(String.format("%s/%s", bill_path, fileName));
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
}
|
||||
String downloadbill_uri = getRequestUri("downloadbill_v3_uri");
|
||||
Map<String, String> map = baseMap(null);
|
||||
map.put("bill_date", formatBillDate);
|
||||
map.put("bill_type", billType.name());
|
||||
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
|
||||
map.put("sign", sign);
|
||||
String param = map2xml(map);
|
||||
Response response = request.post(downloadbill_uri, param);
|
||||
|
||||
BufferedReader reader = null;
|
||||
OutputStream os = null;
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(
|
||||
response.getStream(), Consts.UTF_8));
|
||||
String line = null;
|
||||
List<String[]> bills = new LinkedList<String[]>();
|
||||
while ((line = reader.readLine()) != null) {
|
||||
bills.add(line.replaceAll("`", "").split(","));
|
||||
}
|
||||
|
||||
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(formatBillDate + "对账单");
|
||||
ExcelUtil.list2excel(wb, headers, bills);
|
||||
ExcelUtil.list2excel(wb, totalHeaders, totalDatas);
|
||||
os = new FileOutputStream(file);
|
||||
wb.write(os);
|
||||
} catch (IOException e) {
|
||||
throw new WeixinException(e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
if (os != null) {
|
||||
os.close();
|
||||
}
|
||||
} catch (IOException ignore) {
|
||||
;
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款查询</br> 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
|
||||
*
|
||||
* @param idQuery
|
||||
* 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
|
||||
* 四个参数必填一个,优先级为:
|
||||
* refund_id>out_refund_no>transaction_id>out_trade_no
|
||||
* @return 退款记录
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundRecord
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundDetail
|
||||
* @since V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public RefundRecord refundQuery(IdQuery idQuery) throws WeixinException {
|
||||
String refundquery_uri = getRequestUri("refundquery_v3_uri");
|
||||
Map<String, String> map = baseMap(idQuery);
|
||||
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
|
||||
map.put("sign", sign);
|
||||
String param = map2xml(map);
|
||||
Response response = request.post(refundquery_uri, param);
|
||||
return RefundConverter.fromXML(response.getAsString(),
|
||||
RefundRecord.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口上报
|
||||
*
|
||||
* @param interfaceUrl
|
||||
* 上报对应的接口的完整 URL, 类似: https://api.mch.weixin.q
|
||||
* q.com/pay/unifiedorder
|
||||
* @param executeTime
|
||||
* 接口耗时情况,单位为毫秒
|
||||
* @param outTradeNo
|
||||
* 商户系统内部的订单号,商 户可以在上报时提供相关商户订单号方便微信支付更好 的提高服务质量。
|
||||
* @param ip
|
||||
* 发起接口调用时的机器 IP
|
||||
* @param time
|
||||
* 商户调用该接口时商户自己 系统的时间
|
||||
* @param returnXml
|
||||
* 调用接口返回的基本数据
|
||||
* @return 处理结果
|
||||
* @throws WeixinException
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public XmlResult interfaceReport(String interfaceUrl, int executeTime,
|
||||
String outTradeNo, String ip, Date time, XmlResult returnXml)
|
||||
throws WeixinException {
|
||||
String pay_report_uri = getRequestUri("pay_report_uri");
|
||||
Map<String, String> map = baseMap(null);
|
||||
map.put("interface_url", interfaceUrl);
|
||||
map.put("execute_time_", Integer.toString(executeTime));
|
||||
map.put("out_trade_no", outTradeNo);
|
||||
map.put("user_ip", ip);
|
||||
map.put("time", DateUtil.fortmat2yyyyMMddHHmmss(time));
|
||||
map.putAll((Map<String, String>) JSON.toJSON(returnXml));
|
||||
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
|
||||
map.put("sign", sign);
|
||||
String param = map2xml(map);
|
||||
Response response = request.post(pay_report_uri, param);
|
||||
return response.getAsXmlResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* V3接口请求基本数据
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Map<String, String> baseMap(IdQuery idQuery) {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("appid", weixinAccount.getId());
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,64 +1,23 @@
|
||||
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.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.KeyStore;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.Consts;
|
||||
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.SSLHttpRequest;
|
||||
import com.foxinmy.weixin4j.http.XmlResult;
|
||||
import com.foxinmy.weixin4j.model.Token;
|
||||
import com.foxinmy.weixin4j.model.WeixinMpAccount;
|
||||
import com.foxinmy.weixin4j.mp.payment.ApiResult;
|
||||
import com.foxinmy.weixin4j.mp.payment.PayUtil;
|
||||
import com.foxinmy.weixin4j.mp.payment.Refund;
|
||||
import com.foxinmy.weixin4j.mp.payment.RefundConverter;
|
||||
import com.foxinmy.weixin4j.mp.payment.RefundResult;
|
||||
import com.foxinmy.weixin4j.mp.payment.v2.Order;
|
||||
import com.foxinmy.weixin4j.mp.payment.v3.ApiResult;
|
||||
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.type.SignType;
|
||||
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
|
||||
@ -67,11 +26,12 @@ import com.foxinmy.weixin4j.util.RandomUtil;
|
||||
* @author jy
|
||||
* @date 2014年10月28日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay2Api
|
||||
* @see com.foxinmy.weixin4j.mp.api.Pay3Api
|
||||
*/
|
||||
public class PayApi extends MpApi {
|
||||
private final TokenHolder tokenHolder;
|
||||
private final WeixinMpAccount weixinAccount;
|
||||
public abstract class PayApi extends MpApi {
|
||||
protected final TokenHolder tokenHolder;
|
||||
protected final WeixinMpAccount weixinAccount;
|
||||
|
||||
public PayApi(TokenHolder tokenHolder) {
|
||||
this.tokenHolder = tokenHolder;
|
||||
@ -92,7 +52,6 @@ public class PayApi extends MpApi {
|
||||
* @param statusMsg
|
||||
* status为失败时携带的信息
|
||||
* @return 发货处理结果
|
||||
* @since V2 & V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public JsonResult deliverNotify(String openId, String transid,
|
||||
@ -116,63 +75,9 @@ public class PayApi extends MpApi {
|
||||
Response response = request.post(
|
||||
String.format(delivernotify_uri, token.getAccessToken()),
|
||||
JSON.toJSONString(map));
|
||||
|
||||
return response.getAsJsonResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* V2订单查询
|
||||
*
|
||||
* @param orderNo
|
||||
* 订单号
|
||||
* @return 订单信息
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.Order
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public Order orderQueryV2(String orderNo) throws WeixinException {
|
||||
String orderquery_uri = getRequestUri("orderquery_uri");
|
||||
Token token = tokenHolder.getToken();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("out_trade_no=").append(orderNo);
|
||||
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 = DateUtil.timestamp2string();
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("appid", weixinAccount.getId());
|
||||
obj.put("appkey", weixinAccount.getPaySignKey());
|
||||
obj.put("package", sb.toString());
|
||||
obj.put("timestamp", timestamp);
|
||||
String signature = PayUtil.paysignSha(obj);
|
||||
|
||||
obj.clear();
|
||||
obj.put("appid", weixinAccount.getId());
|
||||
obj.put("package", sb.toString());
|
||||
obj.put("timestamp", timestamp);
|
||||
obj.put("app_signature", signature);
|
||||
obj.put("sign_method", SignType.SHA1.name().toLowerCase());
|
||||
|
||||
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);
|
||||
if (order.getRetCode() != 0) {
|
||||
throw new WeixinException(Integer.toString(order.getRetCode()),
|
||||
order.getRetMsg());
|
||||
}
|
||||
order.setMapData(JSON.parseObject(order_info,
|
||||
new TypeReference<Map<String, String>>() {
|
||||
}));
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* 维权处理
|
||||
*
|
||||
@ -181,7 +86,6 @@ public class PayApi extends MpApi {
|
||||
* @param feedbackId
|
||||
* 维权单号
|
||||
* @return 维权处理结果
|
||||
* @since V2 & V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public JsonResult updateFeedback(String openId, String feedbackId)
|
||||
@ -194,27 +98,17 @@ public class PayApi extends MpApi {
|
||||
}
|
||||
|
||||
/**
|
||||
* V3订单查询
|
||||
* 订单查询
|
||||
*
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id、out_trade_no 二 选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @return 订单信息
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.Order
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v3.Order
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public com.foxinmy.weixin4j.mp.payment.v3.Order orderQueryV3(IdQuery idQuery)
|
||||
throws WeixinException {
|
||||
Map<String, String> map = baseMapV3(idQuery);
|
||||
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>() {
|
||||
});
|
||||
}
|
||||
public abstract Object orderQuery(IdQuery idQuery) throws WeixinException;
|
||||
|
||||
/**
|
||||
* 申请退款(请求需要双向证书)</br>
|
||||
@ -223,8 +117,8 @@ public class PayApi extends MpApi {
|
||||
* 败后重新提交,要采用原来的 out_refund_no。总退款金额不能超过用户实际支付金额。</br>
|
||||
* </p>
|
||||
*
|
||||
* @param ca
|
||||
* 证书文件
|
||||
* @param caFile
|
||||
* 证书文件(V2版本后缀为*.pfx,V3版本后缀为*.p12)
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
@ -236,236 +130,45 @@ public class PayApi extends MpApi {
|
||||
* 退款总金额,单位为元,可以做部分退款
|
||||
* @param opUserId
|
||||
* 操作员帐号, 默认为商户号
|
||||
* @param opUserPasswd
|
||||
* <font color="red">V3版本留空,V2版本需传入值</font>
|
||||
* @param mopara
|
||||
* 更多参数 如V2版本的opUserPasswd
|
||||
*
|
||||
* @return 退款申请结果
|
||||
* @see com.foxinmy.weixin4j.mp.payment.RefundResult
|
||||
* @since V2 & V3
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.RefundResult
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundResult
|
||||
* @throws WeixinException
|
||||
* @throws IOException
|
||||
*/
|
||||
public RefundResult refund(InputStream ca, IdQuery idQuery,
|
||||
protected abstract Object refund(File caFile, IdQuery idQuery,
|
||||
String outRefundNo, double totalFee, double refundFee,
|
||||
String opUserId, String opUserPasswd) throws WeixinException{
|
||||
int version = weixinAccount.getVersion();
|
||||
String refund_uri = getRequestUri(String.format("refund_v%d_uri",
|
||||
version));
|
||||
Response response = null;
|
||||
if (version == 2) {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("input_charset", Consts.UTF_8.name());
|
||||
// 版本号
|
||||
// 填写为 1.0 时,操作员密码为明文
|
||||
// 填写为 1.1 时,操作员密码为 MD5(密码)值
|
||||
map.put("service_version", "1.1");
|
||||
map.put("partner", weixinAccount.getPartnerId());
|
||||
map.put("out_refund_no", outRefundNo);
|
||||
map.put("total_fee", DateUtil.formaFee2Fen(totalFee));
|
||||
map.put("refund_fee", DateUtil.formaFee2Fen(refundFee));
|
||||
map.put(idQuery.getType().getName(), idQuery.getId());
|
||||
if (StringUtils.isBlank(opUserId)) {
|
||||
opUserId = weixinAccount.getPartnerId();
|
||||
}
|
||||
map.put("op_user_id", opUserId);
|
||||
map.put("op_user_passwd", DigestUtils.md5Hex(opUserPasswd));
|
||||
// 以下几个字段可能用不到 记录下来
|
||||
// [接收人帐号]
|
||||
// recv_user_id
|
||||
// 转账退款接收退款的财付通帐号。
|
||||
// 一般无需填写,只有退银行失败,资金转入商 户号现金账号时(即状态为转入代发,查询返 回的 refund_status 是 7 或
|
||||
// 11),填写原退款 单号并填写此字段,资金才会退到指定财付通 账号。其他情况此字段忽略
|
||||
// ---------------------------------------------------------------------------------
|
||||
// [接收人姓名]
|
||||
// reccv_user_name
|
||||
// 转账退款接收退款的姓名(需与接收退款的财 付通帐号绑定的姓名一致)
|
||||
// ---------------------------------------------------------------------------------
|
||||
// [通过商户订单号退款]
|
||||
// use_spbill_no_flag
|
||||
// 若通过接口 (https://www.tenpay.com/cgi-bin/v1.0/pay _gate.cgi)
|
||||
// 支付的商户订单号来退款,则取值 为 1;而通过本文档支付接口的,则无需传值。
|
||||
// ---------------------------------------------------------------------------------
|
||||
// [退款类型]
|
||||
// refund_type
|
||||
// 为空或者填 1:商户号余额退款;2:现金帐号 退款;3:优先商户号退款,若商户号余额不足, 再做现金帐号退款。使用 2 或 3
|
||||
// 时,需联系财 付通开通此功能。
|
||||
String sign = PayUtil
|
||||
.paysignMd5(map, weixinAccount.getPartnerKey());
|
||||
map.put("sign", sign.toLowerCase());
|
||||
|
||||
SSLContext ctx = null;
|
||||
try {
|
||||
File file = new File(ConfigUtil.getValue("ca_file"));
|
||||
|
||||
String jksPwd = "";
|
||||
File jksFile = new File(String.format("%s/tenpay_cacert.jks",
|
||||
file.getParent()));
|
||||
KeyStore ks = null;
|
||||
// create jks ca
|
||||
if (!jksFile.exists()) {
|
||||
CertificateFactory cf = CertificateFactory
|
||||
.getInstance(com.foxinmy.weixin4j.model.Consts.X509);
|
||||
java.security.cert.Certificate cert = cf
|
||||
.generateCertificate(PayUtil.class
|
||||
.getResourceAsStream("cacert.pem"));
|
||||
ks = KeyStore
|
||||
.getInstance(com.foxinmy.weixin4j.model.Consts.JKS);
|
||||
ks.load(null, null);
|
||||
ks.setCertificateEntry("tenpay", cert);
|
||||
ks.store(new FileOutputStream(jksFile),
|
||||
jksPwd.toCharArray());
|
||||
}
|
||||
// load jks ca
|
||||
TrustManagerFactory tmf = TrustManagerFactory
|
||||
.getInstance(com.foxinmy.weixin4j.model.Consts.SunX509);
|
||||
ks = KeyStore
|
||||
.getInstance(com.foxinmy.weixin4j.model.Consts.JKS);
|
||||
ks.load(new FileInputStream(jksFile), jksPwd.toCharArray());
|
||||
tmf.init(ks);
|
||||
// load pfx ca
|
||||
KeyManagerFactory kmf = KeyManagerFactory
|
||||
.getInstance(com.foxinmy.weixin4j.model.Consts.SunX509);
|
||||
ks = KeyStore
|
||||
.getInstance(com.foxinmy.weixin4j.model.Consts.PKCS12);
|
||||
ks.load(ca, weixinAccount.getPartnerId().toCharArray());
|
||||
kmf.init(ks, weixinAccount.getPartnerId().toCharArray());
|
||||
|
||||
ctx = SSLContext
|
||||
.getInstance(com.foxinmy.weixin4j.model.Consts.TLS);
|
||||
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
|
||||
new SecureRandom());
|
||||
} catch (Exception e) {
|
||||
throw new WeixinException(e.getMessage());
|
||||
}
|
||||
SSLHttpRequest request = new SSLHttpRequest(ctx);
|
||||
response = request.get(refund_uri, map);
|
||||
} else if (version == 3) {
|
||||
Map<String, String> map = baseMapV3(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);
|
||||
SSLHttpRequest request = new SSLHttpRequest(
|
||||
weixinAccount.getMchId(), ca);
|
||||
response = request.post(refund_uri, param);
|
||||
} else {
|
||||
throw new WeixinException(String.format("unknown version:%d",
|
||||
version));
|
||||
}
|
||||
return response.getAsObject(new TypeReference<RefundResult>() {
|
||||
});
|
||||
}
|
||||
String opUserId, Map<String, String> mopara) throws WeixinException;
|
||||
|
||||
/**
|
||||
* 退款申请:默认采用properties中配置的ca文件</br> <font
|
||||
* color="red">V2支付则需要传入opUserPasswd参数</font>
|
||||
* 退款查询</br> 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
|
||||
*
|
||||
* @see {@link com.foxinmy.weixin4j.mp.api.PayApi#refund(InputStream, IdQuery, String, double, double, String, String)}
|
||||
* @param idQuery
|
||||
* 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
|
||||
* 四个参数必填一个,优先级为:
|
||||
* refund_id>out_refund_no>transaction_id>out_trade_no
|
||||
* @return 退款记录
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.RefundRecord
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundRecord
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public RefundResult refund(IdQuery idQuery, String outRefundNo,
|
||||
double totalFee, double refundFee, String opUserId,
|
||||
String opUserPasswd) throws WeixinException, IOException {
|
||||
File ca = new File(ConfigUtil.getValue("ca_file"));
|
||||
return refund(new FileInputStream(ca), idQuery, outRefundNo, totalFee,
|
||||
refundFee, opUserId, opUserPasswd);
|
||||
}
|
||||
public abstract Object refundQuery(IdQuery idQuery) throws WeixinException;
|
||||
|
||||
/**
|
||||
* 冲正订单(需要证书)</br> 当支付返回失败,或收银系统超时需要取消交易,可以调用该接口</br> 接口逻辑:支
|
||||
* 付失败的关单,支付成功的撤销支付</br> <font color="red">7天以内的单可撤销,其他正常支付的单
|
||||
* 如需实现相同功能请调用退款接口</font></br> <font
|
||||
* color="red">调用扣款接口后请勿立即调用撤销,需要等待5秒以上。先调用查单接口,如果没有确切的返回,再调用撤销</font></br>
|
||||
* 冲正订单(需要证书)
|
||||
*
|
||||
* @param ca
|
||||
* 证书文件
|
||||
* @param caFile
|
||||
* 证书文件 (V2版本后缀为*.pfx,V3版本后缀为*.p12)
|
||||
* @param idQuery
|
||||
* 商户系统内部的订单号, transaction_id 、 out_trade_no 二选一,如果同时存在优先级:
|
||||
* transaction_id> out_trade_no
|
||||
* @return 撤销结果
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public ApiResult reverse(InputStream ca, IdQuery idQuery)
|
||||
throws WeixinException {
|
||||
SSLHttpRequest request = new SSLHttpRequest(weixinAccount.getMchId(),
|
||||
ca);
|
||||
String reverse_uri = getRequestUri("reverse_uri");
|
||||
Map<String, String> map = baseMapV3(idQuery);
|
||||
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
|
||||
map.put("sign", sign);
|
||||
String param = map2xml(map);
|
||||
Response response = request.post(reverse_uri, param);
|
||||
return response.getAsObject(new TypeReference<ApiResult>() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 冲正撤销:默认采用properties中配置的ca文件
|
||||
*
|
||||
* @param idQuery
|
||||
* transaction_id、out_trade_no 二选一
|
||||
* @return 撤销结果
|
||||
* @see {@link com.foxinmy.weixin4j.mp.api.PayApi#reverse(InputStream, IdQuery)}
|
||||
* @throws WeixinException
|
||||
* @throws IOException
|
||||
*/
|
||||
public ApiResult reverse(IdQuery idQuery) throws WeixinException,
|
||||
IOException {
|
||||
File ca = new File(ConfigUtil.getValue("ca_file"));
|
||||
return reverse(new FileInputStream(ca), idQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* native支付URL转短链接
|
||||
*
|
||||
* @param url
|
||||
* 具有native标识的支付URL
|
||||
* @return 转换后的短链接
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public String getShorturl(String url) throws WeixinException {
|
||||
Map<String, String> map = baseMapV3(null);
|
||||
map.put("long_url", url);
|
||||
String sign = PayUtil.paysignMd5(map, weixinAccount.getPaySignKey());
|
||||
map.put("sign", sign);
|
||||
try {
|
||||
map.put("long_url", URLEncoder.encode(url, Consts.UTF_8.name()));
|
||||
} catch (UnsupportedEncodingException ignore) {
|
||||
;
|
||||
}
|
||||
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 outTradeNo
|
||||
* 商户系统内部的订单号
|
||||
* @return 处理结果
|
||||
* @since V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public XmlResult closeOrder(String outTradeNo) throws WeixinException {
|
||||
Map<String, String> map = baseMapV3(new IdQuery(outTradeNo,
|
||||
IdType.TRADENO));
|
||||
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();
|
||||
}
|
||||
public abstract ApiResult reverse(File caFile, IdQuery idQuery)
|
||||
throws WeixinException;
|
||||
|
||||
/**
|
||||
* 下载对账单<br>
|
||||
@ -480,154 +183,30 @@ public class PayApi extends MpApi {
|
||||
* 下载对账单的类型 ALL,返回当日所有订单信息, 默认值 SUCCESS,返回当日成功支付的订单
|
||||
* REFUND,返回当日退款订单
|
||||
* @return excel表格
|
||||
* @since V2 & V3
|
||||
* @throws WeixinException
|
||||
* @throws IOException
|
||||
*/
|
||||
public File downloadbill(Date billDate, BillType billType)
|
||||
throws WeixinException {
|
||||
if (billDate == null) {
|
||||
Calendar now = Calendar.getInstance();
|
||||
now.add(Calendar.DAY_OF_MONTH, -1);
|
||||
billDate = now.getTime();
|
||||
}
|
||||
if (billType == null) {
|
||||
billType = BillType.ALL;
|
||||
}
|
||||
String _billDate = DateUtil.fortmat2yyyyMMdd(billDate);
|
||||
String bill_path = ConfigUtil.getValue("bill_path");
|
||||
String fileName = String.format("%s_%s_%s.xls", _billDate, billType
|
||||
.name().toLowerCase(), weixinAccount.getId());
|
||||
File file = new File(String.format("%s/%s", bill_path, fileName));
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
}
|
||||
int version = weixinAccount.getVersion();
|
||||
String downloadbill_uri = getRequestUri(String.format(
|
||||
"downloadbill_v%d_uri", version));
|
||||
Response response = null;
|
||||
Charset charset = Consts.UTF_8;
|
||||
if (version == 2) {
|
||||
Map<String, String> map = new LinkedHashMap<String, String>();
|
||||
map.put("spid", weixinAccount.getPartnerId());
|
||||
map.put("trans_time", DateUtil.fortmat2yyyy_MM_dd(billDate));
|
||||
map.put("stamp", DateUtil.timestamp2string());
|
||||
map.put("cft_signtype", "0");
|
||||
map.put("mchtype", Integer.toString(billType.getVal()));
|
||||
map.put("key", weixinAccount.getPartnerKey());
|
||||
String sign = DigestUtils.md5Hex(MapUtil.toJoinString(map, false,
|
||||
false));
|
||||
map.put("sign", sign.toLowerCase());
|
||||
response = request.get(downloadbill_uri, map);
|
||||
charset = Charset.forName("GBK");
|
||||
} else if (version == 3) {
|
||||
Map<String, String> map = baseMapV3(null);
|
||||
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);
|
||||
response = request.post(downloadbill_uri, param);
|
||||
} else {
|
||||
throw new WeixinException(String.format("unknown version:%d",
|
||||
version));
|
||||
}
|
||||
BufferedReader reader = null;
|
||||
OutputStream os = null;
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(
|
||||
response.getStream(), charset));
|
||||
String line = null;
|
||||
List<String[]> bills = new LinkedList<String[]>();
|
||||
while ((line = reader.readLine()) != null) {
|
||||
bills.add(line.replaceAll("`", "").split(","));
|
||||
}
|
||||
|
||||
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);
|
||||
os = new FileOutputStream(file);
|
||||
wb.write(os);
|
||||
} catch (IOException e) {
|
||||
throw new WeixinException(e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
if (os != null) {
|
||||
os.close();
|
||||
}
|
||||
} catch (IOException ignore) {
|
||||
;
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款查询</br> 退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款 3 个工作日后重新查询退款状态
|
||||
*
|
||||
* @param idQuery
|
||||
* 单号 refund_id、out_refund_no、 out_trade_no 、 transaction_id
|
||||
* 四个参数必填一个,优先级为:
|
||||
* refund_id>out_refund_no>transaction_id>out_trade_no
|
||||
* @return 退款记录
|
||||
* @see com.foxinmy.weixin4j.mp.payment.Refund
|
||||
* @since V2 & V3
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public Refund refundQuery(IdQuery idQuery) throws WeixinException {
|
||||
int version = weixinAccount.getVersion();
|
||||
String refundquery_uri = getRequestUri(String.format(
|
||||
"refundquery_v%d_uri", version));
|
||||
Response response = null;
|
||||
if (version == 2) {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("input_charset", Consts.UTF_8.name());
|
||||
map.put("partner", weixinAccount.getPartnerId());
|
||||
map.put(idQuery.getType().getName(), idQuery.getId());
|
||||
String sign = PayUtil
|
||||
.paysignMd5(map, weixinAccount.getPartnerKey());
|
||||
map.put("sign", sign.toLowerCase());
|
||||
response = request.get(refundquery_uri, map);
|
||||
} else if (version == 3) {
|
||||
Map<String, String> map = baseMapV3(idQuery);
|
||||
String sign = PayUtil
|
||||
.paysignMd5(map, weixinAccount.getPaySignKey());
|
||||
map.put("sign", sign);
|
||||
String param = map2xml(map);
|
||||
response = request.post(refundquery_uri, param);
|
||||
} else {
|
||||
throw new WeixinException(String.format("unknown version:%d",
|
||||
version));
|
||||
}
|
||||
return RefundConverter.fromXML(response.getAsString());
|
||||
}
|
||||
public abstract File downloadbill(Date billDate, BillType billType)
|
||||
throws WeixinException;
|
||||
|
||||
/**
|
||||
* V3接口请求基本数据
|
||||
* 关闭订单</br> 当订单支付失败,调用关单接口后用新订单号重新发起支付,如果关单失败,返回已完
|
||||
* 成支付请按正常支付处理。如果出现银行掉单,调用关单成功后,微信后台会主动发起退款。
|
||||
*
|
||||
* @return
|
||||
* @param outTradeNo
|
||||
* 商户系统内部的订单号
|
||||
* @return 处理结果
|
||||
* @throws WeixinException
|
||||
*/
|
||||
private Map<String, String> baseMapV3(IdQuery idQuery) {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("appid", weixinAccount.getId());
|
||||
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;
|
||||
}
|
||||
public abstract ApiResult closeOrder(String outTradeNo)
|
||||
throws WeixinException;
|
||||
|
||||
/**
|
||||
* native支付URL转短链接
|
||||
*
|
||||
* @param url
|
||||
* 具有native标识的支付URL
|
||||
* @return 转换后的短链接
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public abstract String getShorturl(String url) throws WeixinException;
|
||||
}
|
||||
|
||||
@ -121,6 +121,8 @@ refund_v3_uri={mch_base_url}/secapi/pay/refund
|
||||
reverse_uri={mch_base_url}/secapi/pay/reverse
|
||||
# \u88ab\u626b\u652f\u4ed8
|
||||
micropay_uri={mch_base_url}/pay/micropay
|
||||
# \u63a5\u53e3\u4e0a\u62a5
|
||||
pay_report_uri={mch_base_url}/payitil/report
|
||||
|
||||
|
||||
# \u7edf\u4e00\u8ba2\u5355\u751f\u6210
|
||||
|
||||
@ -51,7 +51,7 @@ public class CustomRecord implements Serializable {
|
||||
}
|
||||
|
||||
public void setTime(long time) {
|
||||
this.time = new Date(time * 1000);
|
||||
this.time = new Date(time * 1000l);
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
package com.foxinmy.weixin4j.mp.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.foxinmy.weixin4j.model.Gender;
|
||||
import com.foxinmy.weixin4j.mp.type.FaceSize;
|
||||
import com.foxinmy.weixin4j.mp.type.Lang;
|
||||
@ -24,22 +26,20 @@ public class User implements Serializable {
|
||||
|
||||
private String openid; // 用户的唯一标识
|
||||
private String nickname; // 用户昵称
|
||||
private int sex; // 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
|
||||
@JSONField(name = "sex")
|
||||
private Gender gender; // 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
|
||||
private String province; // 用户个人资料填写的省份
|
||||
private String city; // 普通用户个人资料填写的城市
|
||||
private String country; // 国家,如中国为CN
|
||||
private String headimgurl; // 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空
|
||||
private String privilege; // 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
|
||||
private int subscribe; // 是否关注
|
||||
private long subscribe_time; // 关注时间
|
||||
@JSONField(name = "subscribe")
|
||||
private boolean isSubscribe; // 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。
|
||||
@JSONField(name = "subscribe_time")
|
||||
private Date subscribeTime; // 关注时间
|
||||
private Lang language; // 使用语言
|
||||
private String unionid; // 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段
|
||||
|
||||
public User() {
|
||||
this.sex = 0;
|
||||
this.language = Lang.zh_CN;
|
||||
}
|
||||
|
||||
public String getOpenid() {
|
||||
return openid;
|
||||
}
|
||||
@ -56,22 +56,18 @@ public class User implements Serializable {
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
public int getSex() {
|
||||
return sex;
|
||||
}
|
||||
|
||||
public Gender getGender() {
|
||||
if (sex == 1) {
|
||||
return Gender.male;
|
||||
} else if (sex == 2) {
|
||||
return Gender.female;
|
||||
} else {
|
||||
return Gender.unknown;
|
||||
}
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setSex(int sex) {
|
||||
this.sex = sex;
|
||||
public void setGender(int sex) {
|
||||
if (sex == 1) {
|
||||
this.gender = Gender.male;
|
||||
} else if (sex == 2) {
|
||||
this.gender = Gender.female;
|
||||
} else {
|
||||
this.gender = Gender.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
public String getProvince() {
|
||||
@ -123,14 +119,6 @@ public class User implements Serializable {
|
||||
this.privilege = privilege;
|
||||
}
|
||||
|
||||
public int getSubscribe() {
|
||||
return subscribe;
|
||||
}
|
||||
|
||||
public void setSubscribe(int subscribe) {
|
||||
this.subscribe = subscribe;
|
||||
}
|
||||
|
||||
public Lang getLanguage() {
|
||||
return language;
|
||||
}
|
||||
@ -139,12 +127,20 @@ public class User implements Serializable {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public long getSubscribe_time() {
|
||||
return subscribe_time;
|
||||
public boolean isSubscribe() {
|
||||
return isSubscribe;
|
||||
}
|
||||
|
||||
public void setSubscribe_time(long subscribe_time) {
|
||||
this.subscribe_time = subscribe_time;
|
||||
public void setSubscribe(boolean isSubscribe) {
|
||||
this.isSubscribe = isSubscribe;
|
||||
}
|
||||
|
||||
public Date getSubscribeTime() {
|
||||
return (Date) subscribeTime.clone();
|
||||
}
|
||||
|
||||
public void setSubscribeTime(long subscribeTime) {
|
||||
this.subscribeTime = new Date(subscribeTime * 1000l);
|
||||
}
|
||||
|
||||
public String getUnionid() {
|
||||
@ -163,46 +159,21 @@ public class User implements Serializable {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((city == null) ? 0 : city.hashCode());
|
||||
result = prime * result + ((country == null) ? 0 : country.hashCode());
|
||||
result = prime * result
|
||||
+ ((headimgurl == null) ? 0 : headimgurl.hashCode());
|
||||
result = prime * result
|
||||
+ ((language == null) ? 0 : language.hashCode());
|
||||
result = prime * result
|
||||
+ ((nickname == null) ? 0 : nickname.hashCode());
|
||||
result = prime * result + ((openid == null) ? 0 : openid.hashCode());
|
||||
result = prime * result
|
||||
+ ((privilege == null) ? 0 : privilege.hashCode());
|
||||
result = prime * result
|
||||
+ ((province == null) ? 0 : province.hashCode());
|
||||
result = prime * result + sex;
|
||||
result = prime * result + subscribe;
|
||||
result = prime * result
|
||||
+ (int) (subscribe_time ^ (subscribe_time >>> 32));
|
||||
result = prime * result + ((unionid == null) ? 0 : unionid.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[User openid=").append(openid);
|
||||
sb.append(", nickname=").append(nickname);
|
||||
sb.append(", sex=").append(sex);
|
||||
sb.append(", gender=").append(gender);
|
||||
sb.append(", province=").append(province);
|
||||
sb.append(", city=").append(city);
|
||||
sb.append(", country=").append(country);
|
||||
sb.append(", headimgurl=").append(headimgurl);
|
||||
sb.append(", privilege=").append(privilege);
|
||||
sb.append(", language=").append(language);
|
||||
sb.append(", subscribe_time=").append(subscribe_time);
|
||||
sb.append(", subscribeTime=").append(subscribeTime);
|
||||
sb.append(", unionid=").append(unionid);
|
||||
sb.append(", subscribe=").append(subscribe).append("]");
|
||||
sb.append(", isSubscribe=").append(isSubscribe).append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@ public class PayUtil {
|
||||
* 订单信息
|
||||
* @param WeixinMpAccount
|
||||
* 商户信息
|
||||
* @since V2 & V3
|
||||
* @return 支付json串
|
||||
* @throws PayException
|
||||
*/
|
||||
@ -56,8 +57,9 @@ public class PayUtil {
|
||||
} else if (payPackage instanceof PayPackageV3) {
|
||||
return createPayJsRequestJsonV3((PayPackageV3) payPackage,
|
||||
weixinAccount);
|
||||
} else {
|
||||
throw new PayException("unknown pay");
|
||||
}
|
||||
throw new PayException("unknown pay");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
package com.foxinmy.weixin4j.mp.payment;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* 退款记录
|
||||
*
|
||||
* @className Refund
|
||||
* @author jy
|
||||
* @date 2014年11月1日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.mp.payment.RefundDetail
|
||||
*/
|
||||
@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;// 退款笔数
|
||||
private String partner; // 商户号V2
|
||||
private List<RefundDetail> details;
|
||||
|
||||
public String getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
public String getOrderNo() {
|
||||
return orderNo;
|
||||
}
|
||||
|
||||
public String getSubMchId() {
|
||||
return subMchId;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public String getPartner() {
|
||||
return partner;
|
||||
}
|
||||
|
||||
public List<RefundDetail> getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
public void setDetails(List<RefundDetail> details) {
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Refund [transactionId=" + transactionId + ", orderNo="
|
||||
+ orderNo + ", subMchId=" + subMchId + ", count=" + count
|
||||
+ ", partner=" + partner + ", details=" + details
|
||||
+ ", " + super.toString() + "]";
|
||||
}
|
||||
}
|
||||
@ -26,34 +26,39 @@ import com.thoughtworks.xstream.mapper.Mapper;
|
||||
* @author jy
|
||||
* @date 2014年11月2日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.mp.payment.Refund
|
||||
* @see com.foxinmy.weixin4j.mp.payment.RefundDetail
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.RefundRecord
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.RefundDetail
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundRecord
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundDetail
|
||||
*/
|
||||
public class RefundConverter {
|
||||
private final static XmlStream xStream = XmlStream.get();
|
||||
private final static Mapper mapper;
|
||||
private final static ReflectionProvider reflectionProvider;
|
||||
private final static Pattern pattern = Pattern.compile("(_\\d)$");
|
||||
private static Class<?> clazz;
|
||||
private final static Class<com.foxinmy.weixin4j.mp.payment.v2.RefundRecord> REFUNDRECORD2 = com.foxinmy.weixin4j.mp.payment.v2.RefundRecord.class;
|
||||
private final static Class<com.foxinmy.weixin4j.mp.payment.v3.RefundRecord> REFUNDRECORD3 = com.foxinmy.weixin4j.mp.payment.v3.RefundRecord.class;
|
||||
|
||||
static {
|
||||
xStream.processAnnotations(Refund.class);
|
||||
xStream.processAnnotations(RefundDetail.class);
|
||||
xStream.processAnnotations(new Class[] { REFUNDRECORD2, REFUNDRECORD3,
|
||||
com.foxinmy.weixin4j.mp.payment.v2.RefundDetail.class,
|
||||
com.foxinmy.weixin4j.mp.payment.v3.RefundDetail.class });
|
||||
xStream.aliasField("refund_state", com.foxinmy.weixin4j.mp.payment.v2.RefundDetail.class, "refundStatus");
|
||||
xStream.registerConverter(new $());
|
||||
mapper = xStream.getMapper();
|
||||
reflectionProvider = xStream.getReflectionProvider();
|
||||
}
|
||||
|
||||
public static String toXML(Refund refund) {
|
||||
return xStream.toXML(refund);
|
||||
}
|
||||
|
||||
public static Refund fromXML(String xml) {
|
||||
return xStream.fromXML(xml, Refund.class);
|
||||
public static <T> T fromXML(String xml, Class<T> clazz) {
|
||||
RefundConverter.clazz = clazz;
|
||||
return xStream.fromXML(xml, clazz);
|
||||
}
|
||||
|
||||
private static class $ implements Converter {
|
||||
@Override
|
||||
public boolean canConvert(@SuppressWarnings("rawtypes") Class clazz) {
|
||||
return clazz.equals(Refund.class);
|
||||
return clazz.equals(REFUNDRECORD2) || clazz.equals(REFUNDRECORD3);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -63,18 +68,24 @@ public class RefundConverter {
|
||||
writer, context);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object unmarshal(HierarchicalStreamReader reader,
|
||||
UnmarshallingContext context) {
|
||||
Refund refund = new Refund();
|
||||
Object refund = null;
|
||||
try {
|
||||
refund = clazz.newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
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,
|
||||
String fieldName = mapper.realMember(clazz, nodeName);
|
||||
Field field = reflectionProvider.getFieldOrNull(clazz,
|
||||
fieldName);
|
||||
if (field != null) {
|
||||
Object value = context.convertAnother(refund,
|
||||
@ -94,7 +105,8 @@ public class RefundConverter {
|
||||
}
|
||||
StringBuilder detailXml = new StringBuilder();
|
||||
detailXml.append("<list>");
|
||||
String detailCanonicalName = RefundDetail.class.getCanonicalName();
|
||||
String detailCanonicalName = clazz.getCanonicalName().replaceFirst(
|
||||
"RefundRecord", "RefundDetail");
|
||||
for (Iterator<Entry<String, Map<String, String>>> outIt = outMap
|
||||
.entrySet().iterator(); outIt.hasNext();) {
|
||||
detailXml.append("<").append(detailCanonicalName).append(">");
|
||||
@ -108,7 +120,9 @@ public class RefundConverter {
|
||||
detailXml.append("</").append(detailCanonicalName).append(">");
|
||||
}
|
||||
detailXml.append("</list>");
|
||||
refund.setDetails(xStream.fromXML(detailXml.toString(), List.class));
|
||||
reflectionProvider.writeField(refund, "details",
|
||||
xStream.fromXML(detailXml.toString(), List.class),
|
||||
List.class.getDeclaringClass());
|
||||
return refund;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,127 +0,0 @@
|
||||
package com.foxinmy.weixin4j.mp.payment;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 退款详细
|
||||
*
|
||||
* @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 String refundStatus; // 退款状态
|
||||
@XStreamAlias("recv_user_id")
|
||||
private String recvUserId;// 转账退款接收退款的财付通帐号
|
||||
@XStreamAlias("reccv_user_name")
|
||||
private String reccvUserName;// 转账退款接收退款的姓名(需与接收退款的财 付通帐号绑定的姓名一致)
|
||||
@XStreamAlias("sign_key_index")
|
||||
private String signKeyIndex;// 多密钥支持的密钥序号,默认 1
|
||||
@XStreamAlias("sign_type")
|
||||
private String signType;// 签名类型,取值:MD5、RSA,默认:MD5
|
||||
|
||||
public String getOutRefundNo() {
|
||||
return outRefundNo;
|
||||
}
|
||||
|
||||
public String getRefundId() {
|
||||
return refundId;
|
||||
}
|
||||
|
||||
public String getRefundChannel() {
|
||||
if (StringUtils.isBlank(refundChannel)) {
|
||||
return RefundChannel.BALANCE.name();
|
||||
}
|
||||
// V2
|
||||
if (refundChannel.equals("0")) {
|
||||
return RefundChannel.TENPAY.name();
|
||||
} else if (refundChannel.equals("1")) {
|
||||
return RefundChannel.BALANCE.name();
|
||||
}
|
||||
return 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;
|
||||
}
|
||||
|
||||
public RefundStatus getRefundStatus() {
|
||||
// V2
|
||||
if ("4,10,".contains(refundStatus + ",")) {
|
||||
return RefundStatus.SUCCES;
|
||||
} else if ("3,5,6,".contains(refundStatus + ",")) {
|
||||
return RefundStatus.FAIL;
|
||||
} else if ("8,9,10,".contains(refundStatus + ",")) {
|
||||
return RefundStatus.PROCESSING;
|
||||
} else if ("1,2,".contains(refundStatus + ",")) {
|
||||
return RefundStatus.NOTSURE;
|
||||
} else if ("7,".contains(refundStatus + ",")) {
|
||||
return RefundStatus.CHANGE;
|
||||
} else {
|
||||
return RefundStatus.valueOf(refundStatus);
|
||||
}
|
||||
}
|
||||
|
||||
public String getRecvUserId() {
|
||||
return recvUserId;
|
||||
}
|
||||
|
||||
public String getReccvUserName() {
|
||||
return reccvUserName;
|
||||
}
|
||||
|
||||
public String getSignKeyIndex() {
|
||||
return signKeyIndex;
|
||||
}
|
||||
|
||||
public String getSignType() {
|
||||
return signType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RefundDetail [outRefundNo=" + outRefundNo + ", refundId="
|
||||
+ refundId + ", refundChannel=" + refundChannel
|
||||
+ ", refundFee=" + refundFee + ", couponRefundFee="
|
||||
+ couponRefundFee + ", refundStatus=" + refundStatus
|
||||
+ ", recvUserId=" + recvUserId + ", reccvUserName="
|
||||
+ reccvUserName + ", signKeyIndex=" + signKeyIndex
|
||||
+ ", signType=" + signType + "]";
|
||||
}
|
||||
}
|
||||
@ -1,118 +0,0 @@
|
||||
package com.foxinmy.weixin4j.mp.payment;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
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;
|
||||
@XStreamAlias("recv_user_id")
|
||||
private String recvUserId;// 转账退款接收退款的财付通帐号
|
||||
@XStreamAlias("reccv_user_name")
|
||||
private String reccvUserName;// 转账退款接收退款的姓名(需与接收退款的财 付通帐号绑定的姓名一致)
|
||||
@XStreamAlias("sign_key_index")
|
||||
private String signKeyIndex;// 多密钥支持的密钥序号,默认 1
|
||||
@XStreamAlias("sign_type")
|
||||
private String signType;// 签名类型,取值:MD5、RSA,默认:MD5
|
||||
|
||||
public String getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
public String getOutTradeNo() {
|
||||
return outTradeNo;
|
||||
}
|
||||
|
||||
public String getOutRefundNo() {
|
||||
return outRefundNo;
|
||||
}
|
||||
|
||||
public String getRefundId() {
|
||||
return refundId;
|
||||
}
|
||||
|
||||
public String getRefundChannel() {
|
||||
if (StringUtils.isBlank(refundChannel)) {
|
||||
return RefundChannel.BALANCE.name();
|
||||
}
|
||||
// V2
|
||||
if (refundChannel.equals("0")) {
|
||||
return RefundChannel.TENPAY.name();
|
||||
} else if (refundChannel.equals("1")) {
|
||||
return RefundChannel.BALANCE.name();
|
||||
}
|
||||
return 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;
|
||||
}
|
||||
|
||||
public String getSignKeyIndex() {
|
||||
return signKeyIndex;
|
||||
}
|
||||
|
||||
public String getSignType() {
|
||||
return signType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RefundResult [transactionId=" + transactionId + ", outTradeNo="
|
||||
+ outTradeNo + ", outRefundNo=" + outRefundNo + ", refundId="
|
||||
+ refundId + ", refundChannel=" + refundChannel
|
||||
+ ", refundFee=" + refundFee + ", couponRefundFee="
|
||||
+ couponRefundFee + ", recvUserId=" + recvUserId
|
||||
+ ", reccvUserName=" + reccvUserName + ", signKeyIndex="
|
||||
+ signKeyIndex + ", signType=" + signType + ", "
|
||||
+ super.toString() + "]";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
package com.foxinmy.weixin4j.mp.payment.v2;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.foxinmy.weixin4j.mp.type.SignType;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* 调用V2.x接口返回的公用字段
|
||||
*
|
||||
* @className ApiResult
|
||||
* @author jy
|
||||
* @date 2014年12月30日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class ApiResult implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -2876899595643466203L;
|
||||
// 是查询结果状态码,0 表明成功,其他表明错误;
|
||||
@JSONField(name = "ret_code")
|
||||
@XStreamAlias("retcode")
|
||||
private int retCode;
|
||||
// 是查询结果出错信息;
|
||||
@JSONField(name = "ret_msg")
|
||||
@XStreamAlias("retmsg")
|
||||
private String retMsg;
|
||||
// 是返回信息中的编码方式;
|
||||
@JSONField(name = "input_charset")
|
||||
@XStreamAlias("input_charset")
|
||||
private String inputCharset;
|
||||
// 是财付通商户号,即前文的 partnerid;
|
||||
private String partner;
|
||||
@XStreamAlias("sign_key_index")
|
||||
@JSONField(name = "sign_key_index")
|
||||
private Integer signKeyIndex; // 多密钥支持的密钥序号,默认 1
|
||||
private String sign;// 签名
|
||||
@JSONField(name = "sign_type")
|
||||
@XStreamAlias("sign_type")
|
||||
private SignType signType; // 签名类型,取值:MD5、RSA
|
||||
|
||||
public int getRetCode() {
|
||||
return retCode;
|
||||
}
|
||||
|
||||
public void setRetCode(int retCode) {
|
||||
this.retCode = retCode;
|
||||
}
|
||||
|
||||
public String getRetMsg() {
|
||||
return StringUtils.isNotBlank(retMsg) ? retMsg : null;
|
||||
}
|
||||
|
||||
public void setRetMsg(String retMsg) {
|
||||
this.retMsg = retMsg;
|
||||
}
|
||||
|
||||
public String getInputCharset() {
|
||||
return inputCharset;
|
||||
}
|
||||
|
||||
public void setInputCharset(String inputCharset) {
|
||||
this.inputCharset = inputCharset;
|
||||
}
|
||||
|
||||
public String getPartner() {
|
||||
return partner;
|
||||
}
|
||||
|
||||
public void setPartner(String partner) {
|
||||
this.partner = partner;
|
||||
}
|
||||
|
||||
public String getSign() {
|
||||
return sign;
|
||||
}
|
||||
|
||||
public void setSign(String sign) {
|
||||
this.sign = sign;
|
||||
}
|
||||
|
||||
public SignType getSignType() {
|
||||
return signType;
|
||||
}
|
||||
|
||||
public void setSignType(SignType signType) {
|
||||
this.signType = signType;
|
||||
}
|
||||
|
||||
public Integer getSignKeyIndex() {
|
||||
return signKeyIndex;
|
||||
}
|
||||
|
||||
public void setSignKeyIndex(Integer signKeyIndex) {
|
||||
this.signKeyIndex = signKeyIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "retCode=" + retCode + ", retMsg=" + retMsg + ", inputCharset="
|
||||
+ inputCharset + ", partner=" + partner + ", sign=" + sign
|
||||
+ ", signType=" + signType + ", signKeyIndex=" + signKeyIndex;
|
||||
}
|
||||
}
|
||||
@ -10,7 +10,7 @@ import com.foxinmy.weixin4j.mp.payment.PayRequest;
|
||||
import com.foxinmy.weixin4j.util.MapUtil;
|
||||
|
||||
/**
|
||||
* 微信JS支付:get_brand_wcpay_request</br>
|
||||
* V2微信JS支付:get_brand_wcpay_request</br>
|
||||
* <font color="red">所列参数均为非空字符串</font>
|
||||
* <p>
|
||||
* get_brand_wcpay_request:ok 支付成功<br>
|
||||
|
||||
@ -4,7 +4,7 @@ import com.foxinmy.weixin4j.mp.payment.JsPayNotify;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* Native支付回调时POST的信息
|
||||
* V2 Native支付回调时POST的信息
|
||||
*
|
||||
* @className PayNativeNotifyV2
|
||||
* @author jy
|
||||
|
||||
@ -4,7 +4,7 @@ import com.foxinmy.weixin4j.model.WeixinMpAccount;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* Native支付时的回调响应
|
||||
* V2 Native支付时的回调响应
|
||||
*
|
||||
* @className NativePayResponseV2
|
||||
* @author jy
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
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.mp.type.CurrencyType;
|
||||
import com.foxinmy.weixin4j.mp.type.TradeState;
|
||||
import com.foxinmy.weixin4j.util.DateUtil;
|
||||
|
||||
/**
|
||||
* 订单信息
|
||||
* V2订单信息
|
||||
*
|
||||
* @className Order
|
||||
* @author jy
|
||||
@ -16,27 +16,16 @@ import com.foxinmy.weixin4j.util.DateUtil;
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class Order extends JsonResult {
|
||||
public class Order extends ApiResult {
|
||||
|
||||
private static final long serialVersionUID = 4543552984506609920L;
|
||||
|
||||
// 是查询结果状态码,0 表明成功,其他表明错误;
|
||||
@JSONField(name = "ret_code")
|
||||
private int retCode;
|
||||
// 是查询结果出错信息;
|
||||
@JSONField(name = "ret_msg")
|
||||
private String retMsg;
|
||||
// 是返回信息中的编码方式;
|
||||
@JSONField(name = "input_charset")
|
||||
private String inputCharset;
|
||||
// 是订单状态,0 为成功,其他为失败;
|
||||
@JSONField(name = "trade_state")
|
||||
private int tradeState;
|
||||
// 是交易模式,1 为即时到帐,其他保留;
|
||||
@JSONField(name = "trade_mode")
|
||||
private int tradeMode;
|
||||
// 是财付通商户号,即前文的 partnerid;
|
||||
private String partner;
|
||||
// 是银行类型;
|
||||
@JSONField(name = "bank_type")
|
||||
private String bankType;
|
||||
@ -48,7 +37,7 @@ public class Order extends JsonResult {
|
||||
private int totalFee;
|
||||
// 是币种,1 为人民币;
|
||||
@JSONField(name = "fee_type")
|
||||
private String feeType;
|
||||
private int feeType;
|
||||
// 是财付通订单号;
|
||||
@JSONField(name = "transaction_id")
|
||||
private String transactionId;
|
||||
@ -76,39 +65,20 @@ public class Order extends JsonResult {
|
||||
private int discount;
|
||||
// 换算成人民币之后的总金额,单位为分,一般看 total_fee 即可。
|
||||
@JSONField(name = "rmb_total_fee")
|
||||
private int rmbTotalFee;
|
||||
|
||||
@JSONField(serialize = false)
|
||||
private Map<String, String> mapData;
|
||||
|
||||
public int getRetCode() {
|
||||
return retCode;
|
||||
}
|
||||
|
||||
public void setRetCode(int retCode) {
|
||||
this.retCode = retCode;
|
||||
}
|
||||
|
||||
public String getRetMsg() {
|
||||
return retMsg;
|
||||
}
|
||||
|
||||
public void setRetMsg(String retMsg) {
|
||||
this.retMsg = retMsg;
|
||||
}
|
||||
|
||||
public String getInputCharset() {
|
||||
return inputCharset;
|
||||
}
|
||||
|
||||
public void setInputCharset(String inputCharset) {
|
||||
this.inputCharset = inputCharset;
|
||||
}
|
||||
private Integer rmbTotalFee;
|
||||
|
||||
public int getTradeState() {
|
||||
return tradeState;
|
||||
}
|
||||
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public TradeState getFormatTradeState() {
|
||||
if (tradeState == 0) {
|
||||
return TradeState.SUCCESS;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setTradeState(int tradeState) {
|
||||
this.tradeState = tradeState;
|
||||
}
|
||||
@ -121,14 +91,6 @@ public class Order extends JsonResult {
|
||||
this.tradeMode = tradeMode;
|
||||
}
|
||||
|
||||
public String getPartner() {
|
||||
return partner;
|
||||
}
|
||||
|
||||
public void setPartner(String partner) {
|
||||
this.partner = partner;
|
||||
}
|
||||
|
||||
public String getBankType() {
|
||||
return bankType;
|
||||
}
|
||||
@ -145,12 +107,17 @@ public class Order extends JsonResult {
|
||||
this.bankBillno = bankBillno;
|
||||
}
|
||||
|
||||
public int getTotalFee() {
|
||||
return totalFee;
|
||||
}
|
||||
|
||||
/**
|
||||
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
|
||||
*
|
||||
* @return 元单位
|
||||
*/
|
||||
public double getTotalFee() {
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public double getFormatTotalFee() {
|
||||
return totalFee / 100d;
|
||||
}
|
||||
|
||||
@ -158,11 +125,19 @@ public class Order extends JsonResult {
|
||||
this.totalFee = totalFee;
|
||||
}
|
||||
|
||||
public String getFeeType() {
|
||||
public int getFeeType() {
|
||||
return feeType;
|
||||
}
|
||||
|
||||
public void setFeeType(String feeType) {
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public CurrencyType getFormatFeeType() {
|
||||
if (feeType == 1) {
|
||||
return CurrencyType.CNY;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setFeeType(int feeType) {
|
||||
this.feeType = feeType;
|
||||
}
|
||||
|
||||
@ -206,7 +181,12 @@ public class Order extends JsonResult {
|
||||
this.attach = attach;
|
||||
}
|
||||
|
||||
public Date getTimeEnd() {
|
||||
public String getTimeEnd() {
|
||||
return timeEnd;
|
||||
}
|
||||
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public Date getFormatTimeEnd() {
|
||||
return DateUtil.parse2yyyyMMddHHmmss(timeEnd);
|
||||
}
|
||||
|
||||
@ -214,12 +194,17 @@ public class Order extends JsonResult {
|
||||
this.timeEnd = timeEnd;
|
||||
}
|
||||
|
||||
public int getTransportFee() {
|
||||
return transportFee;
|
||||
}
|
||||
|
||||
/**
|
||||
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
|
||||
*
|
||||
* @return 元单位
|
||||
*/
|
||||
public double getTransportFee() {
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public double getFormatTransportFee() {
|
||||
return transportFee / 100d;
|
||||
}
|
||||
|
||||
@ -227,12 +212,17 @@ public class Order extends JsonResult {
|
||||
this.transportFee = transportFee;
|
||||
}
|
||||
|
||||
public int getProductFee() {
|
||||
return productFee;
|
||||
}
|
||||
|
||||
/**
|
||||
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
|
||||
*
|
||||
* @return 元单位
|
||||
*/
|
||||
public double getProductFee() {
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public double getFormatProductFee() {
|
||||
return productFee / 100d;
|
||||
}
|
||||
|
||||
@ -240,12 +230,17 @@ public class Order extends JsonResult {
|
||||
this.productFee = productFee;
|
||||
}
|
||||
|
||||
public int getDiscount() {
|
||||
return discount;
|
||||
}
|
||||
|
||||
/**
|
||||
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
|
||||
*
|
||||
* @return 元单位
|
||||
*/
|
||||
public double getDiscount() {
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public double getFormatDiscount() {
|
||||
return discount / 100d;
|
||||
}
|
||||
|
||||
@ -253,39 +248,42 @@ public class Order extends JsonResult {
|
||||
this.discount = discount;
|
||||
}
|
||||
|
||||
public Integer getRmbTotalFee() {
|
||||
return rmbTotalFee;
|
||||
}
|
||||
|
||||
/**
|
||||
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
|
||||
*
|
||||
* @return 元单位
|
||||
*/
|
||||
public double getRmbTotalFee() {
|
||||
return rmbTotalFee / 100d;
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public double getFormatRmbTotalFee() {
|
||||
return rmbTotalFee != null ? rmbTotalFee / 100d : 0d;
|
||||
}
|
||||
|
||||
public void setRmbTotalFee(int rmbTotalFee) {
|
||||
this.rmbTotalFee = rmbTotalFee;
|
||||
}
|
||||
|
||||
public Map<String, String> getMapData() {
|
||||
return mapData;
|
||||
}
|
||||
|
||||
public void setMapData(Map<String, String> mapData) {
|
||||
this.mapData = mapData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
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 + "]";
|
||||
return "Order [tradeState=" + tradeState + ", tradeMode=" + tradeMode
|
||||
+ ", 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 + ", getFormatTradeState()="
|
||||
+ getFormatTradeState() + ", getFormatTotalFee()="
|
||||
+ getFormatTotalFee() + ", getFormatFeeType()="
|
||||
+ getFormatFeeType() + ", getFormatTimeEnd()="
|
||||
+ getFormatTimeEnd() + ", getFormatTransportFee()="
|
||||
+ getFormatTransportFee() + ", getFormatProductFee()="
|
||||
+ getFormatProductFee() + ", getFormatDiscount()="
|
||||
+ getFormatDiscount() + ", getFormatRmbTotalFee()="
|
||||
+ getFormatRmbTotalFee() + ", " + super.toString() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import com.foxinmy.weixin4j.mp.payment.PayBaseInfo;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* 维权POST的数据
|
||||
* V2维权POST的数据
|
||||
*
|
||||
* @className PayFeedback
|
||||
* @author jy
|
||||
|
||||
@ -3,6 +3,14 @@ package com.foxinmy.weixin4j.mp.payment.v2;
|
||||
import com.foxinmy.weixin4j.mp.payment.PayBaseInfo;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* V2告警通知
|
||||
* @className PayWarn
|
||||
* @author jy
|
||||
* @date 2014年12月31日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
@XStreamAlias("xml")
|
||||
public class PayWarn extends PayBaseInfo {
|
||||
|
||||
|
||||
@ -0,0 +1,123 @@
|
||||
package com.foxinmy.weixin4j.mp.payment.v2;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.foxinmy.weixin4j.mp.type.RefundChannel;
|
||||
import com.foxinmy.weixin4j.mp.type.RefundStatus;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* V2退款详细
|
||||
*
|
||||
* @className RefundDetail
|
||||
* @author jy
|
||||
* @date 2014年11月6日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class RefundDetail extends ApiResult {
|
||||
|
||||
private static final long serialVersionUID = -3687863914168618620L;
|
||||
|
||||
@XStreamAlias("out_refund_no")
|
||||
@JSONField(name = "out_refund_no")
|
||||
private String outRefundNo; // 商户退款单号
|
||||
@XStreamAlias("refund_id")
|
||||
@JSONField(name = "refund_id")
|
||||
private String refundId; // 微信退款单号
|
||||
@XStreamAlias("refund_channel")
|
||||
@JSONField(name = "refund_channel")
|
||||
private int refundChannel; // 退款渠道 0:退到财付通、1:退到银行;
|
||||
@XStreamAlias("refund_fee")
|
||||
@JSONField(name = "refund_fee")
|
||||
private int refundFee; // 退款总金额,单位为分,可以做部分退款
|
||||
@XStreamAlias("refund_status")
|
||||
@JSONField(name = "refund_status")
|
||||
private int refundStatus; // 退款状态
|
||||
@XStreamAlias("recv_user_id")
|
||||
@JSONField(name = "recv_user_id")
|
||||
private String recvUserId;// 转账退款接收退款的财付通帐号
|
||||
@XStreamAlias("reccv_user_name")
|
||||
@JSONField(name = "reccv_user_name")
|
||||
private String reccvUserName;// 转账退款接收退款的姓名(需与接收退款的财 付通帐号绑定的姓名一致)
|
||||
|
||||
public String getOutRefundNo() {
|
||||
return outRefundNo;
|
||||
}
|
||||
|
||||
public String getRefundId() {
|
||||
return refundId;
|
||||
}
|
||||
|
||||
public int getRefundChannel() {
|
||||
return refundChannel;
|
||||
}
|
||||
|
||||
@JSONField(deserialize = false, serialize = false)
|
||||
public RefundChannel getFormatRefundChannel() {
|
||||
if (refundChannel == 0) {
|
||||
return RefundChannel.TENPAY;
|
||||
} else if (refundChannel == 1) {
|
||||
return RefundChannel.BANK;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getRefundFee() {
|
||||
return refundFee;
|
||||
}
|
||||
|
||||
/**
|
||||
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
|
||||
*
|
||||
* @return 元单位
|
||||
*/
|
||||
@JSONField(deserialize = false, serialize = false)
|
||||
public double getFormatRefundFee() {
|
||||
return refundFee / 100d;
|
||||
}
|
||||
|
||||
public int getRefundStatus() {
|
||||
return refundStatus;
|
||||
}
|
||||
|
||||
@JSONField(deserialize = false, serialize = false)
|
||||
public RefundStatus getFormatRefundStatus() {
|
||||
String refundStatus_ = String.format(",%d,", refundStatus);
|
||||
if (",4,10,".contains(refundStatus_)) {
|
||||
return RefundStatus.SUCCESS;
|
||||
} else if (",3,5,6,".contains(refundStatus_)) {
|
||||
return RefundStatus.FAIL;
|
||||
} else if (",8,9,11,".contains(refundStatus_)) {
|
||||
return RefundStatus.PROCESSING;
|
||||
} else if (",1,2,".contains(refundStatus_)) {
|
||||
return RefundStatus.NOTSURE;
|
||||
} else if (",7,".contains(refundStatus_)) {
|
||||
return RefundStatus.CHANGE;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getRecvUserId() {
|
||||
return StringUtils.isNotBlank(recvUserId) ? recvUserId : null;
|
||||
}
|
||||
|
||||
public String getReccvUserName() {
|
||||
return StringUtils.isNotBlank(reccvUserName) ? reccvUserName : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "outRefundNo=" + outRefundNo + ", refundId=" + refundId
|
||||
+ ", refundChannel=" + refundChannel + ", refundFee="
|
||||
+ refundFee + ", refundStatus=" + refundStatus
|
||||
+ ", recvUserId=" + recvUserId + ", reccvUserName="
|
||||
+ reccvUserName + ", getFormatRefundChannel()="
|
||||
+ getFormatRefundChannel() + ", getFormatRefundFee()="
|
||||
+ getFormatRefundFee() + ", getFormatRefundStatus()="
|
||||
+ getFormatRefundStatus();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package com.foxinmy.weixin4j.mp.payment.v2;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
import com.thoughtworks.xstream.annotations.XStreamOmitField;
|
||||
|
||||
/**
|
||||
* V2退款记录
|
||||
*
|
||||
* @className RefundRecord
|
||||
* @author jy
|
||||
* @date 2014年11月1日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v2.RefundDetail
|
||||
*/
|
||||
@XStreamAlias("xml")
|
||||
public class RefundRecord extends ApiResult {
|
||||
|
||||
private static final long serialVersionUID = -2971132874939642721L;
|
||||
|
||||
@XStreamAlias("transaction_id")
|
||||
@JSONField(name = "transaction_id")
|
||||
private String transactionId;// 微信订单号
|
||||
@XStreamAlias("out_trade_no")
|
||||
@JSONField(name = "out_trade_no")
|
||||
private String outTradeNo;// 商户订单号
|
||||
@XStreamAlias("refund_count")
|
||||
@JSONField(name = "refund_count")
|
||||
private int count;// 退款笔数
|
||||
@XStreamOmitField
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
private List<RefundDetail> details; // 退款详情
|
||||
|
||||
public String getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
public String getOutTradeNo() {
|
||||
return StringUtils.isNotBlank(outTradeNo) ? outTradeNo : null;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public List<RefundDetail> getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RefundRecord [transactionId=" + transactionId + ", outTradeNo="
|
||||
+ outTradeNo + ", count=" + count + ", details=" + details
|
||||
+ ", " + super.toString() + "]";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package com.foxinmy.weixin4j.mp.payment.v2;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* V2退款申请结果
|
||||
*
|
||||
* @className RefundResult
|
||||
* @author jy
|
||||
* @date 2014年11月6日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
@XStreamAlias("xml")
|
||||
public class RefundResult extends RefundDetail {
|
||||
|
||||
private static final long serialVersionUID = -3687863914168618620L;
|
||||
|
||||
@XStreamAlias("transaction_id")
|
||||
@JSONField(name = "transaction_id")
|
||||
private String transactionId; // 微信订单号
|
||||
@XStreamAlias("out_trade_no")
|
||||
@JSONField(name = "out_trade_no")
|
||||
private String outTradeNo;// 商户系统内部的订单号
|
||||
|
||||
public String getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
public String getOutTradeNo() {
|
||||
return outTradeNo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RefundResult [transactionId=" + transactionId + ", outTradeNo="
|
||||
+ outTradeNo + ", " + super.toString() + "]";
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,8 @@
|
||||
package com.foxinmy.weixin4j.mp.payment;
|
||||
package com.foxinmy.weixin4j.mp.payment.v3;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.foxinmy.weixin4j.http.XmlResult;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
@ -17,15 +20,20 @@ public class ApiResult extends XmlResult {
|
||||
private static final long serialVersionUID = -8430005768959715444L;
|
||||
|
||||
@XStreamAlias("appid")
|
||||
@JSONField(name = "appid")
|
||||
private String appId;// 微信分配的公众账号 ID商户号 非空
|
||||
@XStreamAlias("mch_id")
|
||||
@JSONField(name = "mch_id")
|
||||
private String mchId;// 微信支付分配的商户号 非空
|
||||
@XStreamAlias("sub_mch_id")
|
||||
@JSONField(name = "sub_mch_id")
|
||||
private String subMchId; // 未知 可能为空
|
||||
@XStreamAlias("nonce_str")
|
||||
@JSONField(name = "nonce_str")
|
||||
private String nonceStr;// 随机字符串 非空
|
||||
private String sign;// 签名 非空
|
||||
@XStreamAlias("device_info")
|
||||
@JSONField(name = "device_info")
|
||||
private String deviceInfo;// 微信支付分配的终端设备号 可能为空
|
||||
private String recall;// 是否需要继续调用接口 Y- 需要,N-不需要
|
||||
|
||||
@ -54,7 +62,7 @@ public class ApiResult extends XmlResult {
|
||||
}
|
||||
|
||||
public String getSubMchId() {
|
||||
return subMchId;
|
||||
return StringUtils.isNotBlank(subMchId) ? subMchId : null;
|
||||
}
|
||||
|
||||
public void setSubMchId(String subMchId) {
|
||||
@ -89,8 +97,9 @@ public class ApiResult extends XmlResult {
|
||||
return recall;
|
||||
}
|
||||
|
||||
public void setRecall(String recall) {
|
||||
this.recall = recall;
|
||||
@JSONField(deserialize = false, serialize = false)
|
||||
public boolean getFormatRecall() {
|
||||
return recall != null && recall.equalsIgnoreCase("y");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1,10 +1,9 @@
|
||||
package com.foxinmy.weixin4j.mp.payment.v3;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.payment.ApiResult;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* Native支付回调时POST的信息
|
||||
* V3 Native支付回调时POST的信息
|
||||
*
|
||||
* @className PayNativeNotifyV3
|
||||
* @author jy
|
||||
|
||||
@ -3,14 +3,13 @@ package com.foxinmy.weixin4j.mp.payment.v3;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.foxinmy.weixin4j.exception.PayException;
|
||||
import com.foxinmy.weixin4j.model.Consts;
|
||||
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;
|
||||
import com.thoughtworks.xstream.annotations.XStreamOmitField;
|
||||
|
||||
/**
|
||||
* Native支付时的回调响应
|
||||
* V3 Native支付时的回调响应
|
||||
*
|
||||
* @className NativePayResponseV3
|
||||
* @author jy
|
||||
|
||||
@ -2,7 +2,9 @@ package com.foxinmy.weixin4j.mp.payment.v3;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.payment.ApiResult;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.foxinmy.weixin4j.mp.type.CurrencyType;
|
||||
import com.foxinmy.weixin4j.mp.type.TradeState;
|
||||
import com.foxinmy.weixin4j.mp.type.TradeType;
|
||||
@ -10,7 +12,7 @@ import com.foxinmy.weixin4j.util.DateUtil;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* 订单信息
|
||||
* V3订单信息
|
||||
*
|
||||
* @className Order
|
||||
* @author jy
|
||||
@ -26,39 +28,52 @@ public class Order extends ApiResult {
|
||||
// USERPAYING--用户支付中 NOPAY--未支付(输入密码或 确认支付超时) PAYERROR--支付失败(其他 原因,如银行返回失败)
|
||||
// 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
|
||||
@XStreamAlias("trade_state")
|
||||
@JSONField(name = "trade_state")
|
||||
private TradeState tradeState;
|
||||
// 用户标识ID
|
||||
@XStreamAlias("openid")
|
||||
@JSONField(name = "openid")
|
||||
private String openId;
|
||||
// 用户是否关注公众账号,Y- 关注,N-未关注,仅在公众 账号类型支付有效
|
||||
@XStreamAlias("is_subscribe")
|
||||
@JSONField(name = "is_subscribe")
|
||||
private String isSubscribe;
|
||||
// 交易类型
|
||||
@XStreamAlias("trade_type")
|
||||
@JSONField(name = "trade_type")
|
||||
private TradeType tradeType;
|
||||
// 银行类型
|
||||
@XStreamAlias("bank_type")
|
||||
@JSONField(name = "bank_type")
|
||||
private String bankType;
|
||||
// 订单总金额,单位为分
|
||||
@XStreamAlias("total_fee")
|
||||
@JSONField(name = "total_fee")
|
||||
private int totalFee;
|
||||
// 现金券支付金额<=订单总金 额,订单总金额-现金券金额 为现金支付金额
|
||||
@XStreamAlias("coupon_fee")
|
||||
private int couponFee;
|
||||
@JSONField(name = "coupon_fee")
|
||||
private Integer couponFee;
|
||||
@XStreamAlias("cash_fee")
|
||||
@JSONField(name = "cash_fee")
|
||||
private int cashFee;
|
||||
// 货币类型,符合 ISO 4217 标准的三位字母代码,默认人民币:CNY
|
||||
@XStreamAlias("fee_type")
|
||||
@JSONField(name = "fee_type")
|
||||
private CurrencyType feeType;
|
||||
// 微信支付订单号
|
||||
@XStreamAlias("transaction_id")
|
||||
@JSONField(name = "transaction_id")
|
||||
private String transactionId;
|
||||
// 商户订单号
|
||||
@XStreamAlias("out_rade_no")
|
||||
@XStreamAlias("out_trade_no")
|
||||
@JSONField(name = "out_trade_no")
|
||||
private String outTradeNo;
|
||||
// 商家数据包
|
||||
@XStreamAlias("attach")
|
||||
private String attach;
|
||||
// 支付完成时间,格式为 yyyyMMddhhmmss
|
||||
@XStreamAlias("time_end")
|
||||
@JSONField(name = "time_end")
|
||||
private String timeEnd;
|
||||
|
||||
public TradeState getTradeState() {
|
||||
@ -81,13 +96,8 @@ public class Order extends ApiResult {
|
||||
return bankType;
|
||||
}
|
||||
|
||||
/**
|
||||
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
|
||||
*
|
||||
* @return 元单位
|
||||
*/
|
||||
public double getTotalFee() {
|
||||
return totalFee / 100d;
|
||||
public int getTotalFee() {
|
||||
return totalFee;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,8 +105,37 @@ public class Order extends ApiResult {
|
||||
*
|
||||
* @return 元单位
|
||||
*/
|
||||
public double getCouponFee() {
|
||||
return couponFee / 100d;
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public double getFormatTotalFee() {
|
||||
return totalFee / 100d;
|
||||
}
|
||||
|
||||
public Integer getCouponFee() {
|
||||
return couponFee;
|
||||
}
|
||||
|
||||
/**
|
||||
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
|
||||
*
|
||||
* @return 元单位
|
||||
*/
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public double getFormatCouponFee() {
|
||||
return couponFee != null ? couponFee / 100d : 0d;
|
||||
}
|
||||
|
||||
public int getCashFee() {
|
||||
return cashFee;
|
||||
}
|
||||
|
||||
/**
|
||||
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
|
||||
*
|
||||
* @return 元单位
|
||||
*/
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public double getFormatCashFee() {
|
||||
return cashFee / 100d;
|
||||
}
|
||||
|
||||
public CurrencyType getFeeType() {
|
||||
@ -112,10 +151,15 @@ public class Order extends ApiResult {
|
||||
}
|
||||
|
||||
public String getAttach() {
|
||||
return attach;
|
||||
return StringUtils.isBlank(attach) ? null : attach;
|
||||
}
|
||||
|
||||
public Date getTimeEnd() {
|
||||
public String getTimeEnd() {
|
||||
return timeEnd;
|
||||
}
|
||||
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public Date getFormatTimeEnd() {
|
||||
return DateUtil.parse2yyyyMMddHHmmss(timeEnd);
|
||||
}
|
||||
|
||||
@ -124,9 +168,13 @@ public class Order extends ApiResult {
|
||||
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
|
||||
+ ", " + super.toString() + "]";
|
||||
+ ", couponFee=" + couponFee + ", cashFee=" + cashFee
|
||||
+ ", feeType=" + feeType + ", transactionId=" + transactionId
|
||||
+ ", outTradeNo=" + outTradeNo + ", attach=" + attach
|
||||
+ ", timeEnd=" + timeEnd + ", getFormatTotalFee()="
|
||||
+ getFormatTotalFee() + ", getFormatCouponFee()="
|
||||
+ getFormatCouponFee() + ", getFormatCashFee()="
|
||||
+ getFormatCashFee() + ", getFormatTimeEnd()="
|
||||
+ getFormatTimeEnd() + ", " + super.toString() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import com.foxinmy.weixin4j.exception.PayException;
|
||||
import com.foxinmy.weixin4j.mp.payment.PayRequest;
|
||||
|
||||
/**
|
||||
* JS支付:get_brand_wcpay_request</br>
|
||||
* V3 JS支付:get_brand_wcpay_request</br>
|
||||
* <p>
|
||||
* get_brand_wcpay_request:ok 支付成功<br>
|
||||
* get_brand_wcpay_request:cancel 支付过程中用户取消<br>
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
package com.foxinmy.weixin4j.mp.payment.v3;
|
||||
|
||||
import com.foxinmy.weixin4j.mp.payment.ApiResult;
|
||||
import com.foxinmy.weixin4j.mp.type.TradeType;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* 预订单信息
|
||||
* V3预订单信息
|
||||
*
|
||||
* @className PrePay
|
||||
* @author jy
|
||||
|
||||
@ -0,0 +1,112 @@
|
||||
package com.foxinmy.weixin4j.mp.payment.v3;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.foxinmy.weixin4j.mp.type.RefundChannel;
|
||||
import com.foxinmy.weixin4j.mp.type.RefundStatus;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* V3退款详细
|
||||
*
|
||||
* @className RefundDetail
|
||||
* @author jy
|
||||
* @date 2014年11月6日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public class RefundDetail extends ApiResult {
|
||||
|
||||
private static final long serialVersionUID = -3687863914168618620L;
|
||||
|
||||
@XStreamAlias("out_refund_no")
|
||||
@JSONField(name = "out_refund_no")
|
||||
private String outRefundNo; // 商户退款单号
|
||||
@XStreamAlias("refund_id")
|
||||
@JSONField(name = "refund_id")
|
||||
private String refundId; // 微信退款单号
|
||||
@XStreamAlias("refund_channel")
|
||||
@JSONField(name = "refund_channel")
|
||||
private String refundChannel;// 退款渠道:ORIGINAL—原路退款,默认 BALANCE—退回到余额
|
||||
@XStreamAlias("refund_fee")
|
||||
@JSONField(name = "refund_fee")
|
||||
private int refundFee; // 退款总金额,单位为分,可以做部分退款
|
||||
@XStreamAlias("refund_status")
|
||||
@JSONField(name = "refund_status")
|
||||
private String refundStatus; // 退款状态
|
||||
@XStreamAlias("coupon_refund_fee")
|
||||
@JSONField(name = "coupon_refund_fee")
|
||||
private int couponRefundFee; // 现金券退款金额<=退款金额,退款金额-现金券退款金额为现金
|
||||
|
||||
public String getOutRefundNo() {
|
||||
return outRefundNo;
|
||||
}
|
||||
|
||||
public String getRefundId() {
|
||||
return refundId;
|
||||
}
|
||||
|
||||
public String getRefundChannel() {
|
||||
return refundChannel;
|
||||
}
|
||||
|
||||
@JSONField(deserialize = false, serialize = false)
|
||||
public RefundChannel getFormatRefundChannel() {
|
||||
if (StringUtils.isNotBlank(refundChannel)) {
|
||||
return RefundChannel.valueOf(refundChannel.toUpperCase());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getRefundFee() {
|
||||
return refundFee;
|
||||
}
|
||||
|
||||
/**
|
||||
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
|
||||
*
|
||||
* @return 元单位
|
||||
*/
|
||||
@JSONField(deserialize = false, serialize = false)
|
||||
public double getFormatRefundFee() {
|
||||
return refundFee / 100d;
|
||||
}
|
||||
|
||||
public String getRefundStatus() {
|
||||
return refundStatus;
|
||||
}
|
||||
|
||||
@JSONField(deserialize = false, serialize = false)
|
||||
public RefundStatus getFormatRefundStatus() {
|
||||
if (StringUtils.isNotBlank(refundStatus)) {
|
||||
return RefundStatus.valueOf(refundStatus);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getCouponRefundFee() {
|
||||
return couponRefundFee;
|
||||
}
|
||||
|
||||
/**
|
||||
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
|
||||
*
|
||||
* @return 元单位
|
||||
*/
|
||||
@JSONField(deserialize = false, serialize = false)
|
||||
public double getFormatCouponRefundFee() {
|
||||
return couponRefundFee / 100d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RefundDetail [outRefundNo=" + outRefundNo + ", refundId="
|
||||
+ refundId + ", refundChannel=" + refundChannel
|
||||
+ ", refundFee=" + refundFee + ", refundStatus=" + refundStatus
|
||||
+ ", couponRefundFee=" + couponRefundFee
|
||||
+ ", getFormatRefundChannel()=" + getFormatRefundChannel()
|
||||
+ ", getFormatRefundStatus()=" + getFormatRefundStatus()
|
||||
+ ", getFormatCouponRefundFee()=" + getFormatCouponRefundFee();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package com.foxinmy.weixin4j.mp.payment.v3;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
import com.thoughtworks.xstream.annotations.XStreamOmitField;
|
||||
|
||||
/**
|
||||
* V3退款记录
|
||||
*
|
||||
* @className RefundRecord
|
||||
* @author jy
|
||||
* @date 2014年11月1日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundDetail
|
||||
*/
|
||||
@XStreamAlias("xml")
|
||||
public class RefundRecord extends ApiResult {
|
||||
|
||||
private static final long serialVersionUID = -2971132874939642721L;
|
||||
|
||||
@XStreamAlias("transaction_id")
|
||||
@JSONField(name = "transaction_id")
|
||||
private String transactionId;// 微信订单号
|
||||
@XStreamAlias("out_trade_no")
|
||||
@JSONField(name = "out_trade_no")
|
||||
private String outTradeNo;// 商户订单号
|
||||
@XStreamAlias("cash_fee")
|
||||
@JSONField(name = "cash_fee")
|
||||
private int cashFee;
|
||||
@XStreamAlias("refund_count")
|
||||
@JSONField(name = "refund_count")
|
||||
private int count;// 退款笔数
|
||||
@XStreamOmitField
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
private List<RefundDetail> details; // 退款详情
|
||||
|
||||
public String getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
public String getOutTradeNo() {
|
||||
return outTradeNo;
|
||||
}
|
||||
|
||||
/**
|
||||
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
|
||||
*
|
||||
* @return 元单位
|
||||
*/
|
||||
@JSONField(serialize = false, deserialize = false)
|
||||
public double getFormatCashFee() {
|
||||
return cashFee / 100d;
|
||||
}
|
||||
|
||||
public int getCashFee() {
|
||||
return cashFee;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public List<RefundDetail> getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
public void setDetails(List<RefundDetail> details) {
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RefundRecord [transactionId=" + transactionId + ", outTradeNo="
|
||||
+ outTradeNo + ", cashFee=" + cashFee + ", count=" + count
|
||||
+ ", details=" + details + ", " + super.toString() + "]";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package com.foxinmy.weixin4j.mp.payment.v3;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* V3退款申请结果
|
||||
*
|
||||
* @className RefundResult
|
||||
* @author jy
|
||||
* @date 2014年11月6日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
@XStreamAlias("xml")
|
||||
public class RefundResult extends RefundDetail {
|
||||
|
||||
private static final long serialVersionUID = -3687863914168618620L;
|
||||
|
||||
@XStreamAlias("transaction_id")
|
||||
@JSONField(name = "transaction_id")
|
||||
private String transactionId;// 微信订单号
|
||||
@XStreamAlias("out_trade_no")
|
||||
@JSONField(name = "out_trade_no")
|
||||
private String outTradeNo;// 商户系统内部的订单号
|
||||
|
||||
public String getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
public String getOutTradeNo() {
|
||||
return outTradeNo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RefundResult [transactionId=" + transactionId + ", outTradeNo="
|
||||
+ outTradeNo + ", " + super.toString() + "]";
|
||||
}
|
||||
}
|
||||
@ -37,4 +37,10 @@ public class IdQuery implements Serializable {
|
||||
this.id = id;
|
||||
this.type = idType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s=%s", type.getName(), id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ package com.foxinmy.weixin4j.mp.type;
|
||||
* @see
|
||||
*/
|
||||
public enum RefundStatus {
|
||||
SUCCES, // 退款成功
|
||||
SUCCESS, // 退款成功
|
||||
FAIL, // 退款失败
|
||||
PROCESSING, // 退款处理中
|
||||
NOTSURE, // 未确定,需要商户 原退款单号重新发起
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
package com.foxinmy.weixin4j.mp.type;
|
||||
|
||||
/**
|
||||
* 退款类型
|
||||
*
|
||||
* @className RefundType
|
||||
* @author jy
|
||||
* @date 2014年12月31日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public enum RefundType {
|
||||
BALANCE(1), // 1:商户号余额退款;
|
||||
CASH(2), // 2:现金帐号 退款;
|
||||
BOTH(3);// 3:优先商户号退款,若商户号余额不足, 再做现金帐号退款。
|
||||
// 使用 2 或 3 时,需联系财 付通开通此功能
|
||||
private int val;
|
||||
|
||||
RefundType(int val) {
|
||||
this.val = val;
|
||||
}
|
||||
public int getVal() {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
@ -17,5 +17,7 @@ qr_path=/tmp/weixin/qr
|
||||
media_path=/tmp/weixin/media
|
||||
# \u5bf9\u8d26\u5355\u4fdd\u5b58\u8def\u5f84
|
||||
bill_path=/tmp/weixin/bill
|
||||
# ca\u8bc1\u4e66\u5b58\u653e\u7684\u5b8c\u6574\u8def\u5f84
|
||||
ca_file=/tmp/weixin/xxxxx.p12 | xxxxx.pfx
|
||||
# ca\u8bc1\u4e66\u5b58\u653e\u7684\u5b8c\u6574\u8def\u5f84 (V2\u7248\u672c\u540e\u7f00\u4e3a*.pfx,V3\u7248\u672c\u540e\u7f00\u4e3a*.p12)
|
||||
ca_file=/tmp/weixin/xxxxx.p12
|
||||
# classpath\u8def\u5f84\u4e0b\u53ef\u4ee5\u8fd9\u4e48\u5199
|
||||
ca_file=classpath:xxxxx.pfx
|
||||
@ -32,7 +32,7 @@ public class UserTest extends TokenTest {
|
||||
User user = userApi.getUser("owGBft_vbBbOaQOmpEUE4xDLeRSU");
|
||||
Assert.assertNotNull(user);
|
||||
System.out.println(user);
|
||||
following();
|
||||
// following();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -50,8 +50,8 @@ public class PayAction {
|
||||
JSONObject obj = new JSONObject();
|
||||
WeixinMpAccount weixinAccount = ConfigUtil.getWeixinMpAccount();
|
||||
// V3 支付
|
||||
PayPackage payPackage = new PayPackageV3(weixinAccount, "用户openid", "商品描述",
|
||||
"系统内部订单号", 1d, "IP地址", TradeType.JSAPI);
|
||||
PayPackage payPackage = new PayPackageV3(weixinAccount, "用户openid",
|
||||
"商品描述", "系统内部订单号", 1d, "IP地址", TradeType.JSAPI);
|
||||
// V2 支付
|
||||
payPackage = new PayPackageV2("商品描述", weixinAccount.getPartnerId(),
|
||||
"系统内部订单号", 1d, "回调地址", "IP地址");
|
||||
@ -163,7 +163,7 @@ public class PayAction {
|
||||
if (!sign.equals(valid_sign)) {
|
||||
return XmlStream.to(new XmlResult(Consts.FAIL, "签名错误"));
|
||||
}
|
||||
return XmlStream.to(new XmlResult());
|
||||
return XmlStream.to(new XmlResult(Consts.SUCCESS, ""));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -91,10 +91,14 @@ weixin4j-qy
|
||||
|
||||
* 2014-12-28
|
||||
|
||||
+ **weixin4j-qy**: 增加用户进入应用的callback事件
|
||||
+ **weixin4j-qy-api**: 增加用户进入应用的callback事件
|
||||
|
||||
+ **weixin4j-qy**: 增加批量获取用户详情的接口
|
||||
+ **weixin4j-qy-api**: 增加批量获取用户详情的接口
|
||||
|
||||
+ **weixin4j-qy**: 新增获取微信服务器IP接口
|
||||
+ **weixin4j-qy-api**: 新增获取微信服务器IP接口
|
||||
|
||||
+ **weixin4j-qy**: 调整回调模式下的首次验证的签名方式
|
||||
+ **weixin4j-qy-server**: 调整回调模式下的首次验证的签名方式
|
||||
|
||||
* 2015-01-04
|
||||
|
||||
+ **weixin4j-qy-api**: 新增批量删除员工接口
|
||||
|
||||
@ -63,3 +63,7 @@ weixin.properties说明
|
||||
+ 增加`批量获取用户详情`的接口
|
||||
|
||||
+ 新增`获取微信服务器IP`接口
|
||||
|
||||
* 2015-01-04
|
||||
|
||||
+ 新增批量删除员工接口
|
||||
|
||||
@ -231,6 +231,23 @@ public class WeixinProxy {
|
||||
return userApi.deleteUser(userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除成员
|
||||
*
|
||||
* @param userIds
|
||||
* 成员列表
|
||||
* @see <a href=
|
||||
* "http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98"
|
||||
* >批量删除成员说明</a
|
||||
* @see com.foxinmy.weixin4j.qy.api.UserApi
|
||||
* @return 处理结果
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public JsonResult batchDeleteUser(List<String> userIds)
|
||||
throws WeixinException {
|
||||
return userApi.batchDeleteUser(userIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建标签(创建的标签属于管理组;默认为未加锁状态)
|
||||
*
|
||||
|
||||
@ -200,6 +200,28 @@ public class UserApi extends QyApi {
|
||||
return response.getAsJsonResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除成员
|
||||
*
|
||||
* @param userIds
|
||||
* 成员列表
|
||||
* @see <a href=
|
||||
* "http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98"
|
||||
* >批量删除成员说明</a
|
||||
* @return 处理结果
|
||||
* @throws WeixinException
|
||||
*/
|
||||
public JsonResult batchDeleteUser(List<String> userIds)
|
||||
throws WeixinException {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("useridlist", userIds);
|
||||
String user_delete_uri = getRequestUri("user_batchdelete_uri");
|
||||
Token token = tokenHolder.getToken();
|
||||
Response response = request.post(String.format(user_delete_uri,
|
||||
token.getAccessToken(), obj.toJSONString()));
|
||||
return response.getAsJsonResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启二次验证成功时调用(管理员须拥有userid对应员工的管理权限)
|
||||
*
|
||||
|
||||
@ -27,6 +27,8 @@ user_slist_uri={api_base_url}/user/simplelist?access_token=%s&department_id=%d&f
|
||||
user_list_uri={api_base_url}/user/list?access_token=%s&department_id=%d&fetch_child=%d&status=%d
|
||||
# \u5220\u9664\u6210\u5458
|
||||
user_delete_uri={api_base_url}/user/delete?access_token=%s&userid=%s
|
||||
# \u6279\u91cf\u5220\u9664\u6210\u5458
|
||||
user_batchdelete_uri={api_base_url}/user/batchdelete?access_token=%s
|
||||
# \u6210\u5458\u4e8c\u6b21\u9a8c\u8bc1\u6210\u529f\u65f6\u8c03\u7528
|
||||
user_authsucc_uri={api_base_url}/user/authsucc?access_token=%s&userid=%s
|
||||
# \u521b\u5efa\u6807\u7b7e
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user