优化接口,优化支付等。

This commit is contained in:
fengyapeng 2016-12-20 10:34:11 +08:00
parent a874973428
commit ddd1310ac9
27 changed files with 811 additions and 277 deletions

44
pom.xml
View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.foxinmy</groupId> <groupId>com.foxinmy</groupId>
<artifactId>weixin4j</artifactId> <artifactId>weixin4j</artifactId>
<version>1.7.3</version> <version>1.7.3-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>weixin4j</name> <name>weixin4j</name>
<url>https://github.com/foxinmy/weixin4j</url> <url>https://github.com/foxinmy/weixin4j</url>
@ -44,7 +44,6 @@
<module>weixin4j-mp</module> <module>weixin4j-mp</module>
<module>weixin4j-qy</module> <module>weixin4j-qy</module>
<module>weixin4j-server</module> <module>weixin4j-server</module>
<module>weixin4j-example</module>
</modules> </modules>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@ -54,6 +53,18 @@
</properties> </properties>
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
@ -211,8 +222,8 @@
<resource> <resource>
<directory>src/main/resources</directory> <directory>src/main/resources</directory>
<includes> <includes>
<include>**/*.xml</include> <include>*.xml</include>
<include>**/*.properties</include> <include>*.properties</include>
</includes> </includes>
</resource> </resource>
</resources> </resources>
@ -248,14 +259,27 @@
</dependency> </dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
<distributionManagement> <distributionManagement>
<snapshotRepository>
<id>oss-snapshot</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</snapshotRepository>
<repository> <repository>
<id>oss-release</id> <id>Project Releases</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url> <name>Project Releases</name>
<url>http://repo.wyying.com/nexus/content/repositories/releases/</url>
</repository> </repository>
<snapshotRepository>
<id>Project Snapshots</id>
<name>Project Snapshots</name>
<url>http://repo.wyying.com/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement> </distributionManagement>
<!--<distributionManagement>-->
<!--<snapshotRepository>-->
<!--<id>oss-snapshot</id>-->
<!--<url>https://oss.sonatype.org/content/repositories/snapshots/</url>-->
<!--</snapshotRepository>-->
<!--<repository>-->
<!--<id>oss-release</id>-->
<!--<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>-->
<!--</repository>-->
<!--</distributionManagement>-->
</project> </project>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.foxinmy</groupId> <groupId>com.foxinmy</groupId>
<artifactId>weixin4j</artifactId> <artifactId>weixin4j</artifactId>
<version>1.7.3</version> <version>1.7.3-SNAPSHOT</version>
</parent> </parent>
<artifactId>weixin4j-base</artifactId> <artifactId>weixin4j-base</artifactId>
<name>weixin4j-base</name> <name>weixin4j-base</name>

View File

@ -36,7 +36,7 @@ public class MchApi extends BaseApi {
} }
protected final WeixinPayAccount weixinAccount; protected final WeixinPayAccount weixinAccount;
protected final WeixinSignature weixinSignature; protected final WeixinPaymentSignature weixinSignature;
private volatile WeixinRequestExecutor weixinSSLExecutor; private volatile WeixinRequestExecutor weixinSSLExecutor;
public MchApi(WeixinPayAccount weixinAccount) { public MchApi(WeixinPayAccount weixinAccount) {

View File

@ -16,6 +16,7 @@ import java.util.Map;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference; import com.alibaba.fastjson.TypeReference;
import com.foxinmy.weixin4j.exception.WeixinException; import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.exception.WeixinPayException;
import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.http.weixin.XmlResult; import com.foxinmy.weixin4j.http.weixin.XmlResult;
import com.foxinmy.weixin4j.model.WeixinPayAccount; import com.foxinmy.weixin4j.model.WeixinPayAccount;
@ -83,8 +84,14 @@ public class PayApi extends MchApi {
String payJsRequestXml = XmlStream.toXML(payPackage); String payJsRequestXml = XmlStream.toXML(payPackage);
WeixinResponse response = weixinExecutor.post( WeixinResponse response = weixinExecutor.post(
getRequestUri("order_create_uri"), payJsRequestXml); getRequestUri("order_create_uri"), payJsRequestXml);
return response.getAsObject(new TypeReference<PrePay>() { boolean validatePaySign = weixinSignature.validatePaySign(response);
}); if(validatePaySign) {
PrePay prePay = response.getAsObject(new TypeReference<PrePay>() {
});
prePay.setResponse(response.getAsString());
return prePay;
}
throw new WeixinPayException("验证签名信息失败,返回数据可能被篡改");
} }
/** /**
@ -122,19 +129,20 @@ public class PayApi extends MchApi {
MICROPayRequest microPayRequest = response MICROPayRequest microPayRequest = response
.getAsObject(new TypeReference<MICROPayRequest>() { .getAsObject(new TypeReference<MICROPayRequest>() {
}); });
microPayRequest.setResponse(response.getAsString());
microPayRequest.setPaymentAccount(weixinAccount); microPayRequest.setPaymentAccount(weixinAccount);
return microPayRequest; return microPayRequest;
} }
PrePay prePay = createPrePay(payPackage); PrePay prePay = createPrePay(payPackage);
if (TradeType.APP.name().equals(tradeType)) { if (TradeType.APP.name().equals(tradeType)) {
return new APPPayRequest(prePay.getPrepayId(), weixinAccount); return new APPPayRequest(prePay, weixinAccount);
} else if (TradeType.JSAPI.name().equals(tradeType)) { } else if (TradeType.JSAPI.name().equals(tradeType)) {
return new JSAPIPayRequest(prePay.getPrepayId(), weixinAccount); return new JSAPIPayRequest(prePay, weixinAccount);
} else if (TradeType.NATIVE.name().equals(tradeType)) { } else if (TradeType.NATIVE.name().equals(tradeType)) {
return new NATIVEPayRequest(prePay.getPrepayId(), return new NATIVEPayRequest(prePay,
prePay.getCodeUrl(), weixinAccount); prePay.getCodeUrl(), weixinAccount);
} else if (TradeType.WAP.name().equals(tradeType)) { } else if (TradeType.WAP.name().equals(tradeType)) {
return new WAPPayRequest(prePay.getPrepayId(), weixinAccount); return new WAPPayRequest(prePay, weixinAccount);
} else { } else {
throw new WeixinException("unknown tradeType:" + tradeType); throw new WeixinException("unknown tradeType:" + tradeType);
} }
@ -170,6 +178,36 @@ public class PayApi extends MchApi {
return createPayRequest(payPackage); return createPayRequest(payPackage);
} }
/**
* 创建JSAPI支付请求对象
*
* @param openId
* 用户ID
* @param body
* 订单描述
* @param outTradeNo
* 订单号
* @param totalFee
* 订单总额()
* @param notifyUrl
* 支付通知地址
* @param createIp
* ip地址
* @param attach
* 附加数据 非必填
* @see com.foxinmy.weixin4j.payment.mch.JSAPIPayRequest
* @return JSAPI支付对象
* @throws WeixinException
*/
public MchPayRequest createJSPayRequest(String openId, String body,
String outTradeNo, long totalFee, String notifyUrl,
String createIp, String attach) throws WeixinException {
MchPayPackage payPackage = new MchPayPackage(body, outTradeNo,
totalFee, notifyUrl, createIp, TradeType.JSAPI, openId, null,
null, attach);
return createPayRequest(payPackage);
}
/** /**
* <p> * <p>
* 生成编辑地址请求 * 生成编辑地址请求
@ -271,6 +309,43 @@ public class PayApi extends MchApi {
return new NativePayResponse(weixinAccount, prePay.getPrepayId()); return new NativePayResponse(weixinAccount, prePay.getPrepayId());
} }
/**
* 创建Native支付(扫码支付)回调对象模式一
*
* @param productId
* 商品ID
* @param body
* 商品描述
* @param outTradeNo
* 商户内部唯一订单号
* @param totalFee
* 商品总额 单位元
* @param notifyUrl
* 支付回调URL
* @param createIp
* 订单生成的机器 IP
* @param attach
* 附加数据 非必填
* @return Native回调对象
* @see com.foxinmy.weixin4j.payment.mch.NativePayResponse
* @see <a href=
* "https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1">扫码支付
* </a>
* @see <a href=
* "https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4">模式一
* </a>
* @throws WeixinException
*/
public NativePayResponse createNativePayResponse(String productId,
String body, String outTradeNo, long totalFee, String notifyUrl,
String createIp, String attach) throws WeixinException {
MchPayPackage payPackage = new MchPayPackage(body, outTradeNo,
totalFee, notifyUrl, createIp, TradeType.NATIVE, null, null,
productId, attach);
PrePay prePay = createPrePay(payPackage);
return new NativePayResponse(weixinAccount, prePay.getPrepayId());
}
/** /**
* 创建Native支付(扫码支付)链接模式二 * 创建Native支付(扫码支付)链接模式二
* *
@ -307,6 +382,42 @@ public class PayApi extends MchApi {
return createPayRequest(payPackage); return createPayRequest(payPackage);
} }
/**
* 创建Native支付(扫码支付)链接模式二
*
* @param productId
* 商品ID
* @param body
* 商品描述
* @param outTradeNo
* 商户内部唯一订单号
* @param totalFee
* 商品总额 单位元
* @param notifyUrl
* 支付回调URL
* @param createIp
* 订单生成的机器 IP
* @param attach
* 附加数据 非必填
* @return Native支付对象
* @see com.foxinmy.weixin4j.payment.mch.NATIVEPayRequest
* @see <a href=
* "https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1">扫码支付
* </a>
* @see <a href=
* "https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5">模式二
* </a>
* @throws WeixinException
*/
public MchPayRequest createNativePayRequest(String productId, String body,
String outTradeNo, long totalFee, String notifyUrl,
String createIp, String attach) throws WeixinException {
MchPayPackage payPackage = new MchPayPackage(body, outTradeNo,
totalFee, notifyUrl, createIp, TradeType.NATIVE, null, null,
productId, attach);
return createPayRequest(payPackage);
}
/** /**
* 创建APP支付请求对象 * 创建APP支付请求对象
* *
@ -338,6 +449,37 @@ public class PayApi extends MchApi {
return createPayRequest(payPackage); return createPayRequest(payPackage);
} }
/**
* 创建APP支付请求对象
*
* @param body
* 商品描述
* @param outTradeNo
* 商户内部唯一订单号
* @param totalFee
* 商品总额 单位元
* @param notifyUrl
* 支付回调URL
* @param createIp
* 订单生成的机器 IP
* @param attach
* 附加数据 非必填
* @return APP支付对象
* @see com.foxinmy.weixin4j.payment.mch.APPPayRequest
* @see <a href=
* "https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1">
* APP支付</a>
* @throws WeixinException
*/
public MchPayRequest createAppPayRequest(String body, String outTradeNo,
long totalFee, String notifyUrl, String createIp, String attach)
throws WeixinException {
MchPayPackage payPackage = new MchPayPackage(body, outTradeNo,
totalFee, notifyUrl, createIp, TradeType.APP, null, null, null,
attach);
return createPayRequest(payPackage);
}
/** /**
* 创建WAP支付请求对象 * 创建WAP支付请求对象
* *
@ -369,6 +511,37 @@ public class PayApi extends MchApi {
return createPayRequest(payPackage); return createPayRequest(payPackage);
} }
/**
* 创建WAP支付请求对象
*
* @param body
* 商品描述
* @param outTradeNo
* 商户内部唯一订单号
* @param totalFee
* 商品总额 单位元
* @param notifyUrl
* 支付回调URL
* @param createIp
* 订单生成的机器 IP
* @param attach
* 附加数据 非必填
* @return WAP支付对象
* @see com.foxinmy.weixin4j.payment.mch.WAPPayRequest
* @see <a href=
* "https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=15_1">WAP支付
* </a>
* @throws WeixinException
*/
public MchPayRequest createWapPayRequest(String body, String outTradeNo,
long totalFee, String notifyUrl, String createIp, String attach)
throws WeixinException {
MchPayPackage payPackage = new MchPayPackage(body, outTradeNo,
totalFee, notifyUrl, createIp, TradeType.WAP, null, null, null,
attach);
return createPayRequest(payPackage);
}
/** /**
* 提交被扫支付 * 提交被扫支付
* *
@ -393,11 +566,43 @@ public class PayApi extends MchApi {
* @throws WeixinException * @throws WeixinException
*/ */
public MchPayRequest createMicroPayRequest(String authCode, String body, public MchPayRequest createMicroPayRequest(String authCode, String body,
String outTradeNo, double totalFee, String createIp, String attach) String outTradeNo, double totalFee, String createIp, String attach)
throws WeixinException { throws WeixinException {
MchPayPackage payPackage = new MchPayPackage(body, outTradeNo, MchPayPackage payPackage = new MchPayPackage(body, outTradeNo,
totalFee, null, createIp, TradeType.MICROPAY, null, authCode, totalFee, null, createIp, TradeType.MICROPAY, null, authCode,
null, attach); null, attach);
return createPayRequest(payPackage);
}
/**
* 提交被扫支付
*
* @param authCode
* 扫码支付授权码 ,设备读取用户微信中的条码或者二维码信息
* @param body
* 商品描述
* @param outTradeNo
* 商户内部唯一订单号
* @param totalFee
* 商品总额 单位元
* @param createIp
* 订单生成的机器 IP
* @param attach
* 附加数据 非必填
* @return 支付的订单信息
* @see com.foxinmy.weixin4j.payment.mch.MICROPayRequest
* @see com.foxinmy.weixin4j.payment.mch.Order
* @see <a href=
* "http://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10">
* 提交被扫支付API</a>
* @throws WeixinException
*/
public MchPayRequest createMicroPayRequest(String authCode, String body,
String outTradeNo, long totalFee, String createIp, String attach)
throws WeixinException {
MchPayPackage payPackage = new MchPayPackage(body, outTradeNo,
totalFee, null, createIp, TradeType.MICROPAY, null, authCode,
null, attach);
return createPayRequest(payPackage); return createPayRequest(payPackage);
} }
@ -468,13 +673,51 @@ public class PayApi extends MchApi {
double totalFee, double refundFee, CurrencyType refundFeeType, double totalFee, double refundFee, CurrencyType refundFeeType,
String opUserId, RefundAccountType refundAccountType) String opUserId, RefundAccountType refundAccountType)
throws WeixinException { throws WeixinException {
return applyRefund(idQuery, outRefundNo, DateUtil.formatYuan2Fen(totalFee), DateUtil.formatYuan2Fen(refundFee), refundFeeType, opUserId,
refundAccountType);
}
/**
* 申请退款(请求需要双向证书)
* <p>
* 当交易发生之后一段时间内由于买家或者卖家的原因需要退款时卖家可以通过退款接口将支付款退还给买家微信支付将在收到退款请求并且验证成功之后
* 按照退款规则将支付款按原路退到买家帐号上
* </p>
* <p style="color:red">
* 1.交易时间超过半年的订单无法提交退款
* 2.微信支付退款支持单笔交易分多次退款多次退款需要提交原支付订单的商户订单号和设置不同的退款单号一笔退款失败后重新提交
* 要采用原来的退款单号总退款金额不能超过用户实际支付金额
* </p>
*
* @param idQuery
* 商户系统内部的订单号, transaction_id out_trade_no 二选一,如果同时存在优先级:
* transaction_id> out_trade_no
* @param outRefundNo
* 商户系统内部的退款单号, 户系统内部唯一,同一退款单号多次请求只退一笔
* @param totalFee
* 订单总金额,单位为元
* @param refundFee
* 退款总金额,单位为元,可以做部分退款
* @param refundFeeType
* 货币类型符合ISO 4217标准的三位字母代码默认人民币CNY
* @param opUserId
* 操作员帐号, 默认为商户号
* @param refundAccountType
* @return 退款申请结果
* @see com.foxinmy.weixin4j.payment.mch.RefundResult
* @see <a href=
* "http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4">
* 申请退款API</a>
* @since V3
* @throws WeixinException
*/
public RefundResult applyRefund(IdQuery idQuery, String outRefundNo, long totalFee, long refundFee, CurrencyType refundFeeType,
String opUserId, RefundAccountType refundAccountType) throws WeixinException {
WeixinResponse response = null; WeixinResponse response = null;
Map<String, String> map = createBaseRequestMap(idQuery); Map<String, String> map = createBaseRequestMap(idQuery);
map.put("out_refund_no", outRefundNo); map.put("out_refund_no", outRefundNo);
map.put("total_fee", map.put("total_fee", String.valueOf(totalFee));
Integer.toString(DateUtil.formatYuan2Fen(totalFee))); map.put("refund_fee", String.valueOf(refundFee));
map.put("refund_fee",
Integer.toString(DateUtil.formatYuan2Fen(refundFee)));
if (StringUtil.isBlank(opUserId)) { if (StringUtil.isBlank(opUserId)) {
opUserId = weixinAccount.getMchId(); opUserId = weixinAccount.getMchId();
} }
@ -505,7 +748,7 @@ public class PayApi extends MchApi {
* 商户系统内部的退款单号, 户系统内部唯一,同一退款单号多次请求只退一笔 * 商户系统内部的退款单号, 户系统内部唯一,同一退款单号多次请求只退一笔
* @param totalFee * @param totalFee
* 订单总金额,单位为元 * 订单总金额,单位为元
* @see {@link #applyRefund(IdQuery, String, double, double,CurrencyType, String)} * @see {@link #applyRefund(IdQuery, String, double, double,CurrencyType, String,RefundAccountType)}
*/ */
public RefundResult applyRefund(IdQuery idQuery, String outRefundNo, public RefundResult applyRefund(IdQuery idQuery, String outRefundNo,
double totalFee) throws WeixinException { double totalFee) throws WeixinException {
@ -513,6 +756,23 @@ public class PayApi extends MchApi {
null, null); null, null);
} }
/**
* 退款申请(全额退款)
*
* @param idQuery
* 商户系统内部的订单号, transaction_id out_trade_no 二选一,如果同时存在优先级:
* transaction_id> out_trade_no
* @param outRefundNo
* 商户系统内部的退款单号, 户系统内部唯一,同一退款单号多次请求只退一笔
* @param totalFee
* 订单总金额,单位为元
* @see {@link #applyRefund(IdQuery, String, double, double,CurrencyType, String,RefundAccountType)}
*/
public RefundResult applyRefund(IdQuery idQuery, String outRefundNo,
long totalFee) throws WeixinException {
return applyRefund(idQuery, outRefundNo, totalFee, totalFee, null, null, null);
}
/** /**
* 冲正订单(需要证书)</br> 当支付返回失败,或收银系统超时需要取消交易,可以调用该接口</br> 接口逻辑: * 冲正订单(需要证书)</br> 当支付返回失败,或收银系统超时需要取消交易,可以调用该接口</br> 接口逻辑:
* 付失败的关单,支付成功的撤销支付</br> <font color="red">7天以内的单可撤销,其他正常支付的单 * 付失败的关单,支付成功的撤销支付</br> <font color="red">7天以内的单可撤销,其他正常支付的单
@ -749,4 +1009,6 @@ public class PayApi extends MchApi {
return response.getAsObject(new TypeReference<OpenIdResult>() { return response.getAsObject(new TypeReference<OpenIdResult>() {
}); });
} }
} }

View File

@ -6,9 +6,11 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import com.foxinmy.weixin4j.http.ContentType; import com.foxinmy.weixin4j.http.ContentType;
import com.foxinmy.weixin4j.util.Consts;
public class StringEntity implements HttpEntity { public class StringEntity implements HttpEntity {
private final byte[] content;
private final byte[] content;
private final ContentType contentType; private final ContentType contentType;
public StringEntity(String body) { public StringEntity(String body) {
@ -40,4 +42,10 @@ public class StringEntity implements HttpEntity {
outstream.write(this.content); outstream.write(this.content);
outstream.flush(); outstream.flush();
} }
public String getContentString() {
return new String(this.content, Consts.UTF_8);
}
} }

View File

@ -55,6 +55,10 @@ public class Pagedata<T> implements Serializable, Iterable<T> {
return pageable == null ? null : pageable.getSort(); return pageable == null ? null : pageable.getSort();
} }
public List<T> getContent() {
return content;
}
@Override @Override
public Iterator<T> iterator() { public Iterator<T> iterator() {
return hasContent() ? content.iterator() : null; return hasContent() ? content.iterator() : null;

View File

@ -127,6 +127,47 @@ public class PayPackage extends MerchantResult {
this.goodsTag = goodsTag; this.goodsTag = goodsTag;
} }
/**
* 订单对象
*
* @param body
* 订单描述 必填
* @param detail
* 订单详情 非必填
* @param outTradeNo
* 商户内部ID 必填
* @param totalFee
* 订单总额 必填 <font color="red">单位为分</font>
* @param notifyUrl
* 回调地址 必填
* @param createIp
* 生成订单数据的机器IP 必填
* @param attach
* 附加数据 非必填
* @param timeStart
* 订单生成时间 非必填
* @param timeExpire
* 订单失效时间 非必填
* @param goodsTag
* 订单标记 非必填
*/
public PayPackage(String body, String detail, String outTradeNo,
long totalFee, String notifyUrl, String createIp, String attach,
Date timeStart, Date timeExpire, String goodsTag) {
this.body = body;
this.detail = detail;
this.outTradeNo = outTradeNo;
this.totalFee = Long.valueOf(totalFee).intValue();
this.notifyUrl = notifyUrl;
this.createIp = createIp;
this.attach = attach;
this.timeStart = timeStart != null ? DateUtil
.fortmat2yyyyMMddHHmmss(timeStart) : null;
this.timeExpire = timeExpire != null ? DateUtil
.fortmat2yyyyMMddHHmmss(timeExpire) : null;
this.goodsTag = goodsTag;
}
public String getBody() { public String getBody() {
return body; return body;
} }

View File

@ -36,6 +36,7 @@ public class PayRequest extends PayBaseInfo {
@JSONField(serialize = false) @JSONField(serialize = false)
private String partnerId; private String partnerId;
protected PayRequest() { protected PayRequest() {
// jaxb required // jaxb required
} }
@ -69,6 +70,7 @@ public class PayRequest extends PayBaseInfo {
this.partnerId = partnerId; this.partnerId = partnerId;
} }
@Override @Override
public String toString() { public String toString() {
return "package" + packageInfo + ", prepayId=" + prepayId return "package" + packageInfo + ", prepayId=" + prepayId

View File

@ -468,7 +468,7 @@ public class WeixinPayProxy {
* *
* @throws IOException * @throws IOException
* *
* @see {@link #applyRefund(IdQuery, String, double, double, String,CurrencyType)} * @see {@link #applyRefund(IdQuery, String, double, double,CurrencyType,String,RefundAccountType)}
*/ */
public RefundResult applyRefund(IdQuery idQuery, String outRefundNo, public RefundResult applyRefund(IdQuery idQuery, String outRefundNo,
double totalFee) throws WeixinException { double totalFee) throws WeixinException {

View File

@ -20,8 +20,8 @@ import com.foxinmy.weixin4j.util.MapUtil;
* href="https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1">APP支付</a> * href="https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1">APP支付</a>
*/ */
public class APPPayRequest extends AbstractPayRequest { public class APPPayRequest extends AbstractPayRequest {
public APPPayRequest(String prePayId, WeixinPayAccount payAccount) { public APPPayRequest(PrePay prePay, WeixinPayAccount payAccount) {
super(prePayId, payAccount); super(prePay.getPrepayId(),prePay.getResponse(), payAccount);
} }
@Override @Override

View File

@ -10,10 +10,14 @@ public abstract class AbstractPayRequest implements MchPayRequest {
private final WeixinPayAccount paymentAccount; private final WeixinPayAccount paymentAccount;
protected final WeixinSignature weixinSignature; protected final WeixinSignature weixinSignature;
public AbstractPayRequest(String prePayId, WeixinPayAccount paymentAccount) { protected final String payResponse;
public AbstractPayRequest(String prePayId, String payResponse, WeixinPayAccount paymentAccount) {
this.prePayId = prePayId; this.prePayId = prePayId;
this.payResponse = payResponse;
this.paymentAccount = paymentAccount; this.paymentAccount = paymentAccount;
this.weixinSignature = new WeixinPaymentSignature(paymentAccount.getPaySignKey()); this.weixinSignature = new WeixinPaymentSignature(paymentAccount.getPaySignKey());
} }
@Override @Override
@ -25,4 +29,10 @@ public abstract class AbstractPayRequest implements MchPayRequest {
public WeixinPayAccount getPaymentAccount() { public WeixinPayAccount getPaymentAccount() {
return this.paymentAccount; return this.paymentAccount;
} }
@Override
public String getResponseString() {
return payResponse;
}
} }

View File

@ -25,8 +25,8 @@ import com.foxinmy.weixin4j.type.TradeType;
*/ */
public class JSAPIPayRequest extends AbstractPayRequest { public class JSAPIPayRequest extends AbstractPayRequest {
public JSAPIPayRequest(String prePayId, WeixinPayAccount payAccount) { public JSAPIPayRequest(PrePay prePay, WeixinPayAccount payAccount) {
super(prePayId, payAccount); super(prePay.getPrepayId(), prePay.getResponse(), payAccount);
} }
@Override @Override

View File

@ -27,6 +27,8 @@ public class MICROPayRequest extends Order implements MchPayRequest {
@JSONField(serialize = false) @JSONField(serialize = false)
private WeixinPayAccount paymentAccount; private WeixinPayAccount paymentAccount;
private String response;
protected MICROPayRequest() { protected MICROPayRequest() {
// jaxb required // jaxb required
} }
@ -55,6 +57,16 @@ public class MICROPayRequest extends Order implements MchPayRequest {
return null; return null;
} }
public void setResponse(String response) {
this.response = response;
}
@Override
public String getResponseString() {
return response;
}
/** /**
* <font color="red">返回null,请不要尝试作为支付请求</font> * <font color="red">返回null,请不要尝试作为支付请求</font>
*/ */

View File

@ -108,6 +108,38 @@ public class MchPayPackage extends PayPackage {
null, null, null, null); null, null, null, null);
} }
/**
* 微信支付
*
* @param body
* 支付详情 必填
* @param outTradeNo
* 商户侧订单号 必填
* @param totalFee
* 支付金额(单位元) 必填
* @param notifyUrl
* 支付回调URL 必填
* @param createIp
* 发起支付的IP地址 必填
* @param tradeType
* 支付类型 必填
* @param openId
* 用户唯一标识 公众号JSAPI支付必填
* @param authCode
* 支付授权码 刷卡MICROPAY支付必填
* @param productId
* 商品ID 扫码NATIVE支付必填
* @param attach
* 支付时附加信息 非必填
*/
public MchPayPackage(String body, String outTradeNo, long totalFee,
String notifyUrl, String createIp, TradeType tradeType,
String openId, String authCode, String productId, String attach) {
this(body, null, outTradeNo, totalFee, notifyUrl, createIp, tradeType,
openId, authCode, productId, attach, null, null, null, null,
null);
}
/** /**
* 完整参数 * 完整参数
* *
@ -147,12 +179,12 @@ public class MchPayPackage extends PayPackage {
* 用户在子商户appid下的唯一标识 非必填 * 用户在子商户appid下的唯一标识 非必填
* openid和sub_openid可以选传其中之一如果选择传sub_openid ,则必须传sub_appid * openid和sub_openid可以选传其中之一如果选择传sub_openid ,则必须传sub_appid
*/ */
public MchPayPackage(String body, String detial, String outTradeNo, public MchPayPackage(String body, String detail, String outTradeNo,
double totalFee, CurrencyType feeType, String notifyUrl, double totalFee, CurrencyType feeType, String notifyUrl,
String createIp, TradeType tradeType, String openId, String createIp, TradeType tradeType, String openId,
String authCode, String productId, String attach, Date timeStart, String authCode, String productId, String attach, Date timeStart,
Date timeExpire, String goodsTag, String limitPay, String subOpenId) { Date timeExpire, String goodsTag, String limitPay, String subOpenId) {
super(body, detial, outTradeNo, totalFee, notifyUrl, createIp, attach, super(body, detail, outTradeNo, totalFee, notifyUrl, createIp, attach,
timeStart, timeExpire, goodsTag); timeStart, timeExpire, goodsTag);
this.tradeType = tradeType != null ? tradeType.name() : null; this.tradeType = tradeType != null ? tradeType.name() : null;
this.feeType = feeType == null ? CurrencyType.CNY.name() : feeType this.feeType = feeType == null ? CurrencyType.CNY.name() : feeType
@ -164,6 +196,58 @@ public class MchPayPackage extends PayPackage {
this.subOpenId = subOpenId; this.subOpenId = subOpenId;
} }
/**
* 完整参数
*
* @param body
* 商品描述 <font color="red">必填项</font>
* @param detail
* 商品名称明细列表 非必填项
* @param outTradeNo
* 商户内部唯一订单号 <font color="red">必填项</font>
* @param totalFee
* 商品总额 单位元 <font color="red">必填项</font>
* @param notifyUrl
* 支付回调URL <font color="red">必填项</font>
* @param createIp
* 订单生成的机器IP <font color="red">必填项</font>
* @param tradeType
* 交易类型 <font color="red">必填项</font>
* @param openId
* 用户ID <font color="red">tradeType=JSAPI时必填</font>
* @param authCode
* 刷卡支付授权码 <font color="red">tradeType=MICROPAY时必填</font>
* @param productId
* 产品ID <font color="red">tradeType=NATIVE时必填</font>
* @param attach
* 附加数据在查询API和支付通知中原样返回该字段主要用于商户携带订单的自定义数据 非必填项
* @param timeStart
* 订单生成时间格式为yyyyMMddHHmmss 非必填项
* @param timeExpire
* 订单失效时间格式为yyyyMMddHHmmss;注意最短失效时间间隔必须大于5分钟 非必填项
* @param goodsTag
* 商品标记代金券或立减优惠功能的参数 非必填项
* @param limitPay
* 指定支付方式:no_credit--指定不能使用信用卡支付 非必填项
* @param subOpenId
* 用户在子商户appid下的唯一标识 非必填
* openid和sub_openid可以选传其中之一如果选择传sub_openid ,则必须传sub_appid
*/
public MchPayPackage(String body, String detail, String outTradeNo,
long totalFee, String notifyUrl, String createIp,
TradeType tradeType, String openId, String authCode,
String productId, String attach, Date timeStart, Date timeExpire,
String goodsTag, String limitPay, String subOpenId) {
super(body, detail, outTradeNo, totalFee, notifyUrl, createIp, attach,
timeStart, timeExpire, goodsTag);
this.tradeType = tradeType.name();
this.openId = openId;
this.authCode = authCode;
this.productId = productId;
this.limitPay = limitPay;
this.subOpenId = subOpenId;
}
public String getTradeType() { public String getTradeType() {
return tradeType; return tradeType;
} }

View File

@ -52,4 +52,10 @@ public interface MchPayRequest {
* @return * @return
*/ */
public PayRequest toRequestObject(); public PayRequest toRequestObject();
/**
* 支付请求返回的结果
* @return
*/
public String getResponseString();
} }

View File

@ -20,9 +20,9 @@ public class NATIVEPayRequest extends AbstractPayRequest {
private final String codeUrl; private final String codeUrl;
public NATIVEPayRequest(String prePayId, String codeUrl, public NATIVEPayRequest(PrePay prePay, String codeUrl,
WeixinPayAccount payAccount) { WeixinPayAccount payAccount) {
super(prePayId, payAccount); super(prePay.getPrepayId(), prePay.getResponse(), payAccount);
this.codeUrl = codeUrl; this.codeUrl = codeUrl;
} }

View File

@ -69,7 +69,7 @@ public class Order extends MerchantTradeResult {
/** /**
* 现金支付货币类型,符合 ISO 4217 标准的三位字母代码,默认人民币:CNY * 现金支付货币类型,符合 ISO 4217 标准的三位字母代码,默认人民币:CNY
* *
* @see com.foxinmy.weixin4j.mp.type.CurrencyType * @see com.foxinmy.weixin4j.type.CurrencyType
*/ */
@XmlElement(name = "cash_fee_type") @XmlElement(name = "cash_fee_type")
@JSONField(name = "cash_fee_type") @JSONField(name = "cash_fee_type")

View File

@ -40,6 +40,8 @@ public class PrePay extends MerchantResult {
@XmlElement(name = "code_url") @XmlElement(name = "code_url")
private String codeUrl; private String codeUrl;
private String response;
protected PrePay() { protected PrePay() {
// jaxb required // jaxb required
} }
@ -72,6 +74,14 @@ public class PrePay extends MerchantResult {
this.codeUrl = codeUrl; this.codeUrl = codeUrl;
} }
public String getResponse() {
return response;
}
public void setResponse(String response) {
this.response = response;
}
@Override @Override
public String toString() { public String toString() {
return "PrePay [tradeType=" + tradeType + ", prepayId=" + prepayId return "PrePay [tradeType=" + tradeType + ", prepayId=" + prepayId

View File

@ -138,6 +138,43 @@ public class Redpacket extends MerchantResult {
this.amtType = totalNum > 1 ? "ALL_RAND" : null; this.amtType = totalNum > 1 ? "ALL_RAND" : null;
} }
/**
* 红包
*
* @param outTradeNo
* 商户侧一天内不可重复的订单号 接口根据商户订单号支持重入 如出现超时可再调用 必填
* @param sendName
* 红包发送者名称 必填
* @param openId
* 接受收红包的用户的openid 必填
* @param totalAmount
* 付款金额 <font color="red">单位为分,自动格式化为分</font> 必填
* @param totalNum
* 红包发放总人数 大于1视为裂变红包 必填
* @param wishing
* 红包祝福语 必填
* @param clientIp
* Ip地址 必填
* @param actName
* 活动名称 必填
* @param remark
* 备注 必填
*/
public Redpacket(String outTradeNo, String sendName, String openId,
int totalAmount, int totalNum, String wishing, String clientIp,
String actName, String remark) {
this.outTradeNo = outTradeNo;
this.sendName = sendName;
this.openId = openId;
this.totalNum = totalNum;
this.wishing = wishing;
this.clientIp = clientIp;
this.actName = actName;
this.remark = remark;
this.totalAmount = totalAmount;
this.amtType = totalNum > 1 ? "ALL_RAND" : null;
}
public String getOutTradeNo() { public String getOutTradeNo() {
return outTradeNo; return outTradeNo;
} }

View File

@ -22,8 +22,8 @@ import com.foxinmy.weixin4j.util.URLEncodingUtil;
*/ */
public class WAPPayRequest extends AbstractPayRequest { public class WAPPayRequest extends AbstractPayRequest {
public WAPPayRequest(String prePayId, WeixinPayAccount payAccount) { public WAPPayRequest(PrePay prePay, WeixinPayAccount payAccount) {
super(prePayId, payAccount); super(prePay.getPrepayId(),prePay.getResponse(), payAccount);
} }
@Override @Override

View File

@ -1,36 +1,60 @@
package com.foxinmy.weixin4j.sign; package com.foxinmy.weixin4j.sign;
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.type.SignType; import com.foxinmy.weixin4j.type.SignType;
import com.foxinmy.weixin4j.util.DigestUtil; import com.foxinmy.weixin4j.util.DigestUtil;
import com.foxinmy.weixin4j.xml.XmlStream;
import java.util.Map;
/** /**
* 微信支付签名实现 * 微信支付签名实现
* *
* @className WeixinPaymentSignature
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @className WeixinPaymentSignature
* @date 2016年3月26日 * @date 2016年3月26日
* @since JDK 1.6
* @see <a * @see <a
* href="https://pay.weixin.qq.com/wiki/doc/api/external/jsapi.php?chapter=4_3">支付签名说明</a> * href="https://pay.weixin.qq.com/wiki/doc/api/external/jsapi.php?chapter=4_3">支付签名说明</a>
* @since JDK 1.6
*/ */
public class WeixinPaymentSignature extends AbstractWeixinSignature { public class WeixinPaymentSignature extends AbstractWeixinSignature {
/**
* 支付密钥
*/
private final String paySignKey;
public WeixinPaymentSignature(String paySignKey) { /**
this.paySignKey = paySignKey; * 支付密钥
} */
private final String paySignKey;
public WeixinPaymentSignature(String paySignKey) {
this.paySignKey = paySignKey;
}
@Override
public SignType getSignType() {
return SignType.MD5;
}
@Override
public String sign(Object obj) {
StringBuilder sb = join(obj).append("&key=").append(paySignKey);
return DigestUtil.MD5(sb.toString()).toUpperCase();
}
public boolean validatePaySign(WeixinResponse weixinResponse) {
return this.validatePaySign(weixinResponse.getAsString());
}
public boolean validatePaySign(String xmlResult) {
return this.validatePaySign(XmlStream.xml2map(xmlResult));
}
public boolean validatePaySign(Map<String, String> map) {
String sign1 = map.get("sign");
map.remove("sign");
String sign2 = this.sign(map);
return sign1.equals(sign2);
}
@Override
public SignType getSignType() {
return SignType.MD5;
}
@Override
public String sign(Object obj) {
StringBuilder sb = join(obj).append("&key=").append(paySignKey);
return DigestUtil.MD5(sb.toString()).toUpperCase();
}
} }

View File

@ -11,152 +11,159 @@ import java.util.Map.Entry;
/** /**
* 文件工具类 * 文件工具类
* *
* @className FileUtil
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @className FileUtil
* @date 2014年11月21日 * @date 2014年11月21日
* @since JDK 1.6
* @see * @see
* @since JDK 1.6
*/ */
public class FileUtil { public class FileUtil {
private final static Map<String, String> FILE_TYPE_MAP = new HashMap<String, String>(); private final static Map<String, String> FILE_TYPE_MAP = new HashMap<String, String>();
static { static {
FILE_TYPE_MAP.put("ffd8ffe000104a464946", "jpg"); FILE_TYPE_MAP.put("ffd8ffe000104a464946", "jpg");
FILE_TYPE_MAP.put("89504e470d0a1a0a0000", "png"); FILE_TYPE_MAP.put("89504e470d0a1a0a0000", "png");
FILE_TYPE_MAP.put("47494638396126026f01", "gif"); FILE_TYPE_MAP.put("47494638396126026f01", "gif");
FILE_TYPE_MAP.put("49492a00227105008037", "tif"); FILE_TYPE_MAP.put("49492a00227105008037", "tif");
FILE_TYPE_MAP.put("424d228c010000000000", "bmp"); // 16色位图(bmp) FILE_TYPE_MAP.put("424d228c010000000000", "bmp"); // 16色位图(bmp)
FILE_TYPE_MAP.put("424d8240090000000000", "bmp"); // 24位位图(bmp) FILE_TYPE_MAP.put("424d8240090000000000", "bmp"); // 24位位图(bmp)
FILE_TYPE_MAP.put("424d8e1b030000000000", "bmp"); // 256色位图(bmp) FILE_TYPE_MAP.put("424d8e1b030000000000", "bmp"); // 256色位图(bmp)
FILE_TYPE_MAP.put("49443303000000002176", "mp3"); FILE_TYPE_MAP.put("49443303000000002176", "mp3");
FILE_TYPE_MAP.put("52494646", "wav"); FILE_TYPE_MAP.put("52494646", "wav");
FILE_TYPE_MAP.put("00005741", "wav"); FILE_TYPE_MAP.put("00005741", "wav");
FILE_TYPE_MAP.put("2321414d", "amr"); FILE_TYPE_MAP.put("2321414d", "amr");
FILE_TYPE_MAP.put("520a3c91", "amr"); FILE_TYPE_MAP.put("520a3c91", "amr");
FILE_TYPE_MAP.put("2e524d46000000120001", "rmvb"); // rmvbrm
FILE_TYPE_MAP.put("464c5601050000000900", "flv"); // flvf4v
FILE_TYPE_MAP.put("667479706d70", "mp4");
FILE_TYPE_MAP.put("667479706973", "mp4");
FILE_TYPE_MAP.put("000001ba210001000180", "mpg");
FILE_TYPE_MAP.put("3026b2758e66cf11a6d9", "wmv"); // wmvasf
FILE_TYPE_MAP.put("52494646d07d60074156", "avi");
FILE_TYPE_MAP.put("41433130313500000000", "dwg"); FILE_TYPE_MAP.put("2e524d46000000120001", "rmvb"); // rmvbrm
FILE_TYPE_MAP.put("3c21444f435459504520", "html"); FILE_TYPE_MAP.put("464c5601050000000900", "flv"); // flvf4v
FILE_TYPE_MAP.put("3c21646f637479706520", "htm"); FILE_TYPE_MAP.put("667479706d70", "mp4");
FILE_TYPE_MAP.put("48544d4c207b0d0a0942", "css"); FILE_TYPE_MAP.put("667479706973", "mp4");
FILE_TYPE_MAP.put("696b2e71623d696b2e71", "js"); FILE_TYPE_MAP.put("000001ba210001000180", "mpg");
FILE_TYPE_MAP.put("7b5c727466315c616e73", "rtf"); FILE_TYPE_MAP.put("3026b2758e66cf11a6d9", "wmv"); // wmvasf
FILE_TYPE_MAP.put("38425053000100000000", "psd"); FILE_TYPE_MAP.put("52494646d07d60074156", "avi");
FILE_TYPE_MAP.put("46726f6d3a203d3f6762", "eml");
FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "doc"); // MS ExcelWordMsi
FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "vsd");
FILE_TYPE_MAP.put("5374616E64617264204A", "mdb");
FILE_TYPE_MAP.put("255044462d312e350d0a", "pdf");
FILE_TYPE_MAP.put("4d546864000000060001", "mid");
FILE_TYPE_MAP.put("504b0304140000000800", "zip");
FILE_TYPE_MAP.put("526172211a0700cf9073", "rar");
FILE_TYPE_MAP.put("235468697320636f6e66", "ini");
FILE_TYPE_MAP.put("504b03040a0000000000", "jar");
FILE_TYPE_MAP.put("4d5a9000030000000400", "exe");
FILE_TYPE_MAP.put("3c25402070616765206c", "jsp");
FILE_TYPE_MAP.put("4d616e69666573742d56", "mf");
FILE_TYPE_MAP.put("3c3f786d6c2076657273", "xml");
FILE_TYPE_MAP.put("494e5345525420494e54", "sql");
FILE_TYPE_MAP.put("7061636b616765207765", "java");
FILE_TYPE_MAP.put("406563686f206f66660d", "bat");
FILE_TYPE_MAP.put("1f8b0800000000000000", "gz");
FILE_TYPE_MAP.put("6c6f67346a2e726f6f74", "properties");
FILE_TYPE_MAP.put("cafebabe0000002e0041", "class");
FILE_TYPE_MAP.put("49545346030000006000", "chm");
FILE_TYPE_MAP.put("04000000010000001300", "mxp");
FILE_TYPE_MAP.put("504b0304140006000800", "docx");
FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "wps");// WPS(wpsetdps)
FILE_TYPE_MAP.put("6431303a637265617465", "torrent");
FILE_TYPE_MAP.put("6D6F6F76", "mov");
FILE_TYPE_MAP.put("FF575043", "wpd");
FILE_TYPE_MAP.put("CFAD12FEC5FD746F", "dbx");
FILE_TYPE_MAP.put("2142444E", "pst");
FILE_TYPE_MAP.put("AC9EBD8F", "qdf");
FILE_TYPE_MAP.put("E3828596", "pwl");
FILE_TYPE_MAP.put("2E7261FD", "ram");
}
private static String bytesToHexString(byte[] src) { FILE_TYPE_MAP.put("41433130313500000000", "dwg");
StringBuilder stringBuilder = new StringBuilder(); FILE_TYPE_MAP.put("3c21444f435459504520", "html");
for (int i = 0; i < src.length; i++) { FILE_TYPE_MAP.put("3c21646f637479706520", "htm");
int v = src[i] & 0xFF; FILE_TYPE_MAP.put("48544d4c207b0d0a0942", "css");
String hv = Integer.toHexString(v); FILE_TYPE_MAP.put("696b2e71623d696b2e71", "js");
if (hv.length() < 2) { FILE_TYPE_MAP.put("7b5c727466315c616e73", "rtf");
stringBuilder.append(0); FILE_TYPE_MAP.put("38425053000100000000", "psd");
} FILE_TYPE_MAP.put("46726f6d3a203d3f6762", "eml");
stringBuilder.append(hv); FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "doc"); // MS ExcelWordMsi
} FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "vsd");
return stringBuilder.toString(); FILE_TYPE_MAP.put("5374616E64617264204A", "mdb");
} FILE_TYPE_MAP.put("255044462d312e350d0a", "pdf");
FILE_TYPE_MAP.put("4d546864000000060001", "mid");
FILE_TYPE_MAP.put("504b0304140000000800", "zip");
FILE_TYPE_MAP.put("526172211a0700cf9073", "rar");
FILE_TYPE_MAP.put("235468697320636f6e66", "ini");
FILE_TYPE_MAP.put("504b03040a0000000000", "jar");
FILE_TYPE_MAP.put("4d5a9000030000000400", "exe");
FILE_TYPE_MAP.put("3c25402070616765206c", "jsp");
FILE_TYPE_MAP.put("4d616e69666573742d56", "mf");
FILE_TYPE_MAP.put("3c3f786d6c2076657273", "xml");
FILE_TYPE_MAP.put("494e5345525420494e54", "sql");
FILE_TYPE_MAP.put("7061636b616765207765", "java");
FILE_TYPE_MAP.put("406563686f206f66660d", "bat");
FILE_TYPE_MAP.put("1f8b0800000000000000", "gz");
FILE_TYPE_MAP.put("6c6f67346a2e726f6f74", "properties");
FILE_TYPE_MAP.put("cafebabe0000002e0041", "class");
FILE_TYPE_MAP.put("49545346030000006000", "chm");
FILE_TYPE_MAP.put("04000000010000001300", "mxp");
FILE_TYPE_MAP.put("504b0304140006000800", "docx");
FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "wps");// WPS(wpsetdps)
FILE_TYPE_MAP.put("6431303a637265617465", "torrent");
FILE_TYPE_MAP.put("6D6F6F76", "mov");
FILE_TYPE_MAP.put("FF575043", "wpd");
FILE_TYPE_MAP.put("CFAD12FEC5FD746F", "dbx");
FILE_TYPE_MAP.put("2142444E", "pst");
FILE_TYPE_MAP.put("AC9EBD8F", "qdf");
FILE_TYPE_MAP.put("E3828596", "pwl");
FILE_TYPE_MAP.put("2E7261FD", "ram");
}
/** private static String bytesToHexString(byte[] src) {
* 获取文件类型 StringBuilder stringBuilder = new StringBuilder();
* for (int i = 0; i < src.length; i++) {
* @param is int v = src[i] & 0xFF;
* @return String hv = Integer.toHexString(v);
*/ if (hv.length() < 2) {
public static String getFileType(InputStream is) { stringBuilder.append(0);
String fileType = "file"; }
try { stringBuilder.append(hv);
byte[] b = new byte[10]; }
int t = is.read(b, 0, b.length); return stringBuilder.toString();
if (t > 0) { }
String fileCode = bytesToHexString(b).toLowerCase();
for (Entry<String, String> entry : FILE_TYPE_MAP.entrySet()) {
String key = entry.getKey().toLowerCase();
if (key.startsWith(fileCode) || fileCode.startsWith(key)
|| key.endsWith(fileCode) || fileCode.endsWith(key)) {
fileType = entry.getValue();
break;
}
}
}
} catch (IOException e) {
;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ignore) {
;
}
}
}
return fileType;
}
/** /**
* 获取文件后缀 * 获取文件类型
* */
* @param fileName public static String getFileType(InputStream is) {
* @return String fileType = "file";
*/ try {
public static String getFileExtension(String fileName) { byte[] b = new byte[10];
int extensionPos = fileName.lastIndexOf("."); int t = is.read(b, 0, b.length);
if (extensionPos < 0) { if (t > 0) {
return ""; String fileCode = bytesToHexString(b).toLowerCase();
} for (Entry<String, String> entry : FILE_TYPE_MAP.entrySet()) {
int lastUnixPos = fileName.lastIndexOf("/"); String key = entry.getKey().toLowerCase();
int lastWindowsPos = fileName.lastIndexOf("\\"); if (key.startsWith(fileCode) || fileCode.startsWith(key) || key.endsWith(fileCode) || fileCode.endsWith(key)) {
int lastSeparator = Math.max(lastUnixPos, lastWindowsPos); fileType = entry.getValue();
return lastSeparator > extensionPos ? "" : fileName break;
.substring(extensionPos + 1); }
} }
}
} catch (IOException e) {
;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ignore) {
;
}
}
}
return fileType;
}
public static void main(String[] args) throws IOException { /**
InputStream is = new FileInputStream(new File( * 获取文件后缀
"/Users/jy/Downloads/test.mp4")); */
System.err.println(getFileType(is)); public static String getFileExtension(String fileName) {
System.err.println(URLConnection.guessContentTypeFromStream(is)); int extensionPos = fileName.lastIndexOf(".");
} if (extensionPos < 0) {
return "";
}
int lastUnixPos = fileName.lastIndexOf("/");
int lastWindowsPos = fileName.lastIndexOf("\\");
int lastSeparator = Math.max(lastUnixPos, lastWindowsPos);
return lastSeparator > extensionPos ? "" : fileName.substring(extensionPos + 1);
}
/**
* 删除文件
* @param filePath
* @return
*/
public static boolean deleteFile(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
return true;
}
if (file.isFile()) {
return file.delete();
}
return false;
}
public static void main(String[] args) throws IOException {
InputStream is = new FileInputStream(new File("/Users/jy/Downloads/test.mp4"));
System.err.println(getFileType(is));
System.err.println(URLConnection.guessContentTypeFromStream(is));
}
} }

View File

@ -1,6 +1,7 @@
package com.foxinmy.weixin4j.util; package com.foxinmy.weixin4j.util;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -11,82 +12,84 @@ import java.nio.charset.Charset;
/** /**
* IOUtil * IOUtil
* *
* @className IOUtil
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @className IOUtil
* @date 2014年9月22日 * @date 2014年9月22日
* @since JDK 1.6
* @see org.apache.commons.io.IOUtils * @see org.apache.commons.io.IOUtils
* @since JDK 1.6
*/ */
public class IOUtil { public class IOUtil {
private static final int EOF = -1; private static final int EOF = -1;
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
public static byte[] toByteArray(Reader input) throws IOException { public static byte[] toByteArray(Reader input) throws IOException {
return toByteArray(input, Charset.defaultCharset()); return toByteArray(input, Charset.defaultCharset());
} }
public static byte[] toByteArray(Reader input, Charset encoding) public static byte[] toByteArray(Reader input, Charset encoding) throws IOException {
throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream();
ByteArrayOutputStream output = new ByteArrayOutputStream(); copy(input, output, encoding);
copy(input, output, encoding); return output.toByteArray();
return output.toByteArray(); }
}
public static void copy(Reader input, OutputStream output, Charset encoding) public static void copy(Reader input, OutputStream output, Charset encoding) throws IOException {
throws IOException { OutputStreamWriter out = new OutputStreamWriter(output, encoding);
OutputStreamWriter out = new OutputStreamWriter(output, encoding); copyLarge(input, out, new char[DEFAULT_BUFFER_SIZE]);
copyLarge(input, out, new char[DEFAULT_BUFFER_SIZE]); out.flush();
out.flush(); }
}
public static int copy(InputStream input, OutputStream output) public static int copy(InputStream input, OutputStream output) throws IOException {
throws IOException { long count = copyLarge(input, output);
long count = copyLarge(input, output); if (count > Integer.MAX_VALUE) {
if (count > Integer.MAX_VALUE) { return -1;
return -1; }
} return (int) count;
return (int) count; }
}
private static long copyLarge(InputStream input, OutputStream output) private static long copyLarge(InputStream input, OutputStream output) throws IOException {
throws IOException { byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; long count = 0;
long count = 0; int n = 0;
int n = 0; while (-1 != (n = input.read(buffer))) {
while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n);
output.write(buffer, 0, n); count += n;
count += n; }
} return count;
return count; }
}
public static byte[] toByteArray(InputStream input) throws IOException { public static byte[] toByteArray(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream(); ByteArrayOutputStream output = new ByteArrayOutputStream();
copyLarge(input, output, new byte[DEFAULT_BUFFER_SIZE]); copyLarge(input, output, new byte[DEFAULT_BUFFER_SIZE]);
return output.toByteArray(); return output.toByteArray();
} }
private static long copyLarge(InputStream input, OutputStream output, private static long copyLarge(InputStream input, OutputStream output, byte[] buffer) throws IOException {
byte[] buffer) throws IOException { long count = 0;
long count = 0; int n = 0;
int n = 0; while (EOF != (n = input.read(buffer))) {
while (EOF != (n = input.read(buffer))) { output.write(buffer, 0, n);
output.write(buffer, 0, n); count += n;
count += n; }
} return count;
return count; }
}
private static long copyLarge(Reader input, Writer output, char[] buffer) private static long copyLarge(Reader input, Writer output, char[] buffer) throws IOException {
throws IOException { long count = 0;
long count = 0; int n = 0;
int n = 0; while (EOF != (n = input.read(buffer))) {
while (EOF != (n = input.read(buffer))) { output.write(buffer, 0, n);
output.write(buffer, 0, n); count += n;
count += n; }
} return count;
return count; }
}
public static void close(Closeable stream) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} }

View File

@ -16,21 +16,23 @@ import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
/** /**
* 接口调用错误获取 * 接口调用错误获取
* *
* @author jy
* @className WeixinErrorUtil * @className WeixinErrorUtil
* @author jinyu(foxinmy@gmail.com) * @author jinyu(foxinmy@gmail.com)
* @date 2015年5月12日 * @date 2015年5月12日
* @since JDK 1.6
* @see * @see
* @since JDK 1.6
*/ */
public final class WeixinErrorUtil { public final class WeixinErrorUtil {
private static byte[] errorXmlByteArray;
private static byte[] errorXmlByteArray;
private final static Map<String, String> errorCacheMap; private final static Map<String, String> errorCacheMap;
static { static {
errorCacheMap = new ConcurrentHashMap<String, String>(); errorCacheMap = new ConcurrentHashMap<String, String>();
try { try {
errorXmlByteArray = IOUtil.toByteArray(WeixinResponse.class errorXmlByteArray = IOUtil.toByteArray(WeixinResponse.class.getResourceAsStream("error.xml"));
.getResourceAsStream("error.xml"));
} catch (IOException e) { } catch (IOException e) {
; ;
} }
@ -44,26 +46,23 @@ public final class WeixinErrorUtil {
this.code = code; this.code = code;
} }
private String text; private String text;
private boolean codeElement; private boolean codeElement;
private boolean textElement; private boolean textElement;
private boolean findElement; private boolean findElement;
@Override @Override
public void startElement(String uri, String localName, String qName, public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
Attributes attributes) throws SAXException {
codeElement = qName.equalsIgnoreCase("code"); codeElement = qName.equalsIgnoreCase("code");
textElement = qName.equalsIgnoreCase("text"); textElement = qName.equalsIgnoreCase("text");
} }
@Override @Override
public void endElement(String uri, String localName, String qName) public void endElement(String uri, String localName, String qName) throws SAXException {
throws SAXException {
} }
@Override @Override
public void characters(char[] ch, int start, int length) public void characters(char[] ch, int start, int length) throws SAXException {
throws SAXException {
String _text = new String(ch, start, length); String _text = new String(ch, start, length);
if (codeElement && _text.equalsIgnoreCase(code)) { if (codeElement && _text.equalsIgnoreCase(code)) {
findElement = true; findElement = true;
@ -88,8 +87,7 @@ public final class WeixinErrorUtil {
try { try {
XMLReader xmlReader = XMLReaderFactory.createXMLReader(); XMLReader xmlReader = XMLReaderFactory.createXMLReader();
xmlReader.setContentHandler(textHandler); xmlReader.setContentHandler(textHandler);
xmlReader.parse(new InputSource(new ByteArrayInputStream( xmlReader.parse(new InputSource(new ByteArrayInputStream(errorXmlByteArray)));
errorXmlByteArray)));
text = textHandler.getText(); text = textHandler.getText();
errorCacheMap.put(code, text); errorCacheMap.put(code, text);
} catch (IOException e) { } catch (IOException e) {
@ -102,9 +100,10 @@ public final class WeixinErrorUtil {
return text; return text;
} }
public static void main(String[] args) { public static void main(String[] args) {
System.out.println(getText("40001")); System.out.println(getText("30002"));
System.out.println(getText("40001")); System.out.println(getText("30002"));
System.out.println(getText("1234")); System.out.println(getText("1234"));
} }
} }

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.foxinmy</groupId> <groupId>com.foxinmy</groupId>
<artifactId>weixin4j</artifactId> <artifactId>weixin4j</artifactId>
<version>1.7.3</version> <version>1.7.3-SNAPSHOT</version>
</parent> </parent>
<artifactId>weixin4j-mp</artifactId> <artifactId>weixin4j-mp</artifactId>
<name>weixin4j-mp</name> <name>weixin4j-mp</name>

View File

@ -306,7 +306,8 @@ public class MassApi extends MpApi {
* @see <a * @see <a
* href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">删除群发</a> * href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN">删除群发</a>
* @see {@link #massByTagId(Tuple, int)} * @see {@link #massByTagId(Tuple, int)}
* @see {@link #massByOpenIds(Tuple, String...) * @see {@link #massByOpenIds(Tuple, String...)
*/ */
public ApiResult deleteMassNews(String msgid) throws WeixinException { public ApiResult deleteMassNews(String msgid) throws WeixinException {
JSONObject obj = new JSONObject(); JSONObject obj = new JSONObject();

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.foxinmy</groupId> <groupId>com.foxinmy</groupId>
<artifactId>weixin4j</artifactId> <artifactId>weixin4j</artifactId>
<version>1.7.3</version> <version>1.7.3-SNAPSHOT</version>
</parent> </parent>
<artifactId>weixin4j-qy</artifactId> <artifactId>weixin4j-qy</artifactId>
<name>weixin4j-qy</name> <name>weixin4j-qy</name>