diff --git a/CHANGE.md b/CHANGE.md index b8cd4882..2edf2625 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -309,4 +309,8 @@ * 2015-06-03 - + **去掉xstream依赖** \ No newline at end of file + + **去掉xstream依赖** + +* 2015-06-04 + + + **weixin4j-mp**: 新增查询红包接口 diff --git a/weixin4j-mp/CHANGE.md b/weixin4j-mp/CHANGE.md index a1b0e0ab..5224ad67 100644 --- a/weixin4j-mp/CHANGE.md +++ b/weixin4j-mp/CHANGE.md @@ -108,4 +108,8 @@ + 调整[客服接口](./src/main/java/com/foxinmy/weixin4j/mp/api/CustomApi.java)类的方法名 - + 在[二维码接口](./src/main/java/com/foxinmy/weixin4j/mp/api/QRApi.java)类新增获取二维码url方法 \ No newline at end of file + + 在[二维码接口](./src/main/java/com/foxinmy/weixin4j/mp/api/QRApi.java)类新增获取二维码url方法 + +* 2015-06-04 + + + 新增查询红包接口 \ No newline at end of file diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinPayProxy.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinPayProxy.java index ce7b2a02..7387c7d7 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinPayProxy.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinPayProxy.java @@ -20,6 +20,7 @@ import com.foxinmy.weixin4j.mp.payment.v3.ApiResult; import com.foxinmy.weixin4j.mp.payment.v3.MPPayment; import com.foxinmy.weixin4j.mp.payment.v3.MPPaymentResult; import com.foxinmy.weixin4j.mp.payment.v3.Redpacket; +import com.foxinmy.weixin4j.mp.payment.v3.RedpacketRecord; import com.foxinmy.weixin4j.mp.payment.v3.RedpacketSendResult; import com.foxinmy.weixin4j.mp.token.WeixinTokenCreator; import com.foxinmy.weixin4j.mp.type.BillType; @@ -591,6 +592,36 @@ public class WeixinPayProxy { return cashApi.sendRedpack(caFile, redpacket); } + /** + * 查询红包记录 + * + * @param caFile + * 证书文件(V3版本后缀为*.p12) + * @param outTradeNo + * 商户发放红包的商户订单号 + * @return 红包记录 + * @see com.foxinmy.weixin4j.mp.api.CashApi + * @see com.foxinmy.weixin4j.mp.payment.v3.RedpacketRecord + * @see 查询红包接口说明 + * @throws WeixinException + */ + public RedpacketRecord queryRedpack(File caFile, String outTradeNo) + throws WeixinException { + return cashApi.queryRedpack(caFile, outTradeNo); + } + + /** + * 查询红包采用properties中配置的ca文件 + * + * @see {@link com.foxinmy.weixin4j.mp.WeixinPayProxy#queryRedpack(File,String)} + */ + public RedpacketRecord queryRedpack(String outTradeNo) + throws WeixinException { + File caFile = new File(ConfigUtil.getClassPathValue("ca_file")); + return cashApi.queryRedpack(caFile, outTradeNo); + } + /** * 企业付款 实现企业向个人付款,针对部分有开发能力的商户, 提供通过API完成企业付款的功能。 比如目前的保险行业向客户退保、给付、理赔。 * diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CashApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CashApi.java index 41eab81f..e6de86f6 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CashApi.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CashApi.java @@ -4,6 +4,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; @@ -16,6 +18,7 @@ import com.foxinmy.weixin4j.mp.payment.PayUtil; import com.foxinmy.weixin4j.mp.payment.v3.MPPayment; import com.foxinmy.weixin4j.mp.payment.v3.MPPaymentResult; import com.foxinmy.weixin4j.mp.payment.v3.Redpacket; +import com.foxinmy.weixin4j.mp.payment.v3.RedpacketRecord; import com.foxinmy.weixin4j.mp.payment.v3.RedpacketSendResult; import com.foxinmy.weixin4j.util.RandomUtil; import com.foxinmy.weixin4j.xml.XmlStream; @@ -51,7 +54,7 @@ public class CashApi extends MpApi { * @see com.foxinmy.weixin4j.mp.payment.v3.Redpacket * @see com.foxinmy.weixin4j.mp.payment.v3.RedpacketSendResult * @see 红包接口说明 + * href="http://pay.weixin.qq.com/wiki/doc/api/cash_coupon.php?chapter=13_5">发放红包接口说明 * @throws WeixinException */ public RedpacketSendResult sendRedpack(File caFile, Redpacket redpacket) @@ -74,7 +77,7 @@ public class CashApi extends MpApi { response = request.post(redpack_send_uri, param); } catch (WeixinException e) { throw e; - } catch (Exception e) { + } catch (IOException e) { throw new WeixinException(e.getMessage()); } finally { if (ca != null) { @@ -89,6 +92,55 @@ public class CashApi extends MpApi { }); } + /** + * 查询红包记录 + * + * @param caFile + * 证书文件(V3版本后缀为*.p12) + * @param outTradeNo + * 商户发放红包的商户订单号 + * @return 红包记录 + * @see com.foxinmy.weixin4j.mp.payment.v3.RedpacketRecord + * @see 查询红包接口说明 + * @throws WeixinException + */ + public RedpacketRecord queryRedpack(File caFile, String outTradeNo) + throws WeixinException { + Map para = new HashMap(); + para.put("nonce_str", RandomUtil.generateString(16)); + para.put("mch_id", weixinAccount.getMchId()); + para.put("bill_type", "MCHT"); + para.put("appid", weixinAccount.getId()); + para.put("mch_billno", outTradeNo); + String sign = PayUtil.paysignMd5(para, weixinAccount.getPaySignKey()); + para.put("sign", sign); + String param = XmlStream.map2xml(para); + String redpack_query_uri = getRequestUri("redpack_query_uri"); + WeixinResponse response = null; + InputStream ca = null; + try { + ca = new FileInputStream(caFile); + SSLHttpClinet request = new SSLHttpClinet(weixinAccount.getMchId(), + ca); + response = request.post(redpack_query_uri, param); + } catch (WeixinException e) { + throw e; + } catch (IOException e) { + throw new WeixinException(e.getMessage()); + } finally { + if (ca != null) { + try { + ca.close(); + } catch (IOException e) { + ; + } + } + } + return response.getAsObject(new TypeReference() { + }); + } + /** * 企业付款 实现企业向个人付款,针对部分有开发能力的商户, 提供通过API完成企业付款的功能。 比如目前的保险行业向客户退保、给付、理赔。 * @@ -124,7 +176,7 @@ public class CashApi extends MpApi { response = request.post(mp_payment_uri, param); } catch (WeixinException e) { throw e; - } catch (Exception e) { + } catch (IOException e) { throw new WeixinException(e.getMessage()); } finally { if (ca != null) { diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CouponApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CouponApi.java index dd33e127..ccbf29e4 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CouponApi.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CouponApi.java @@ -85,7 +85,7 @@ public class CouponApi extends MpApi { response = request.post(coupon_send_uri, param); } catch (WeixinException e) { throw e; - } catch (Exception e) { + } catch (IOException e) { throw new WeixinException(e.getMessage()); } finally { if (ca != null) { diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/Pay3Api.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/Pay3Api.java index 14345d52..56f1b5f1 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/Pay3Api.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/Pay3Api.java @@ -146,7 +146,7 @@ public class Pay3Api extends PayApi { response = request.post(refund_uri, param); } catch (WeixinException e) { throw e; - } catch (Exception e) { + } catch (IOException e) { throw new WeixinException(e.getMessage()); } finally { if (ca != null) { diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/weixin.properties b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/weixin.properties index 05cede4c..8863efea 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/weixin.properties +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/weixin.properties @@ -175,7 +175,9 @@ coupon_send_uri={mch_base_url}/mmpaymkttransfers/send_coupon couponstock_query_uri={mch_base_url}/mmpaymkttransfers/query_coupon_stock # \u67e5\u8be2\u4ee3\u91d1\u5238\u8be6\u7ec6\u4fe1\u606f coupondetail_query_uri={mch_base_url}/promotion/query_coupon -# \u53d1\u653e\u73b0\u91d1\u7ea2\u5305 +# \u53d1\u9001\u73b0\u91d1\u7ea2\u5305 redpack_send_uri={mch_base_url}/mmpaymkttransfers/sendredpack +# \u67e5\u8be2\u73b0\u91d1\u7ea2\u5305 +redpack_query_uri={mch_base_url}/mmpaymkttransfers/gethbinfo # \u4f01\u4e1a\u5411\u4e2a\u4eba\u4ed8\u6b3e mp_payment_uri={mch_base_url}/mmpaymkttransfers/promotion/transfers \ No newline at end of file diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RedpacketRecord.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RedpacketRecord.java new file mode 100644 index 00000000..456d92cc --- /dev/null +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/payment/v3/RedpacketRecord.java @@ -0,0 +1,299 @@ +package com.foxinmy.weixin4j.mp.payment.v3; + +import java.util.Date; +import java.util.List; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; + +import com.alibaba.fastjson.annotation.JSONField; +import com.foxinmy.weixin4j.http.weixin.XmlResult; +import com.foxinmy.weixin4j.mp.type.RedpacketSendType; +import com.foxinmy.weixin4j.mp.type.RedpacketStatus; +import com.foxinmy.weixin4j.mp.type.RedpacketType; +import com.foxinmy.weixin4j.util.DateUtil; +import com.foxinmy.weixin4j.util.StringUtil; + +/** + * 红包记录 + * + * @className RedpacketRecord + * @author jy + * @date 2015年6月4日 + * @since JDK 1.7 + * @see + */ +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class RedpacketRecord extends XmlResult { + + private static final long serialVersionUID = 929959747323918458L; + + /** + * 商户订单号(每个订单号必须唯一) 组成: mch_id+yyyymmdd+10位一天内不能重复的数字。 + */ + @XmlElement(name = "mch_billno") + @JSONField(name = "mch_billno") + private String outTradeNo; + /** + * 微信支付分配的商户号 + */ + @XmlElement(name = "mch_id") + @JSONField(name = "mch_id") + private String mchId; + + /** + * 红包单号 + */ + @XmlElement(name = "detail_id") + @JSONField(name = "detail_id") + private String repacketId; + /** + * 红包状态 + */ + private RedpacketStatus status; + /** + * 发放类型 + */ + @XmlElement(name = "send_type") + @JSONField(name = "send_type") + private RedpacketSendType sendType; + /** + * 红包类型 + */ + @XmlElement(name = "hb_type") + @JSONField(name = "hb_type") + private RedpacketType type; + /** + * 红包个数 + */ + @XmlElement(name = "total_num") + @JSONField(name = "total_num") + private int totalNum; + /** + * 红包总金额(单位分) + */ + @XmlElement(name = "total_amount") + @JSONField(name = "total_amount") + private int totalAmount; + /** + * 发送失败原因 + */ + private String reason; + /** + * 发放时间 + */ + @XmlElement(name = "send_time") + @JSONField(name = "send_time") + private String sendTime; + /** + * 红包退款时间 + */ + @XmlElement(name = "refund_time") + @JSONField(name = "refund_time") + private String refundTime; + /** + * 红包退款金额 + */ + @XmlElement(name = "refund_amount") + @JSONField(name = "refund_amount") + private Integer refundAmount; + /** + * 祝福语 + */ + private String wishing; + /** + * 活动描述 + */ + private String remark; + /** + * 活动名称 + */ + @XmlElement(name = "act_name") + @JSONField(name = "act_name") + private String actName; + /** + * 裂变红包领取列表 + */ + @XmlElement(name = "hbinfo") + @XmlElementWrapper(name = "hblist") + @JSONField(name = "hblist") + private List receivers; + + public String getOutTradeNo() { + return outTradeNo; + } + + public String getMchId() { + return mchId; + } + + public String getRepacketId() { + return repacketId; + } + + public RedpacketStatus getStatus() { + return status; + } + + public RedpacketSendType getSendType() { + return sendType; + } + + public RedpacketType getType() { + return type; + } + + public int getTotalNum() { + return totalNum; + } + + public int getTotalAmount() { + return totalAmount; + } + + /** + * 调用接口获取单位为分,get方法转换为元方便使用 + * + * @return 元单位 + */ + @JSONField(serialize = false, deserialize = false) + public double getFormatTotalAmount() { + return totalAmount / 100d; + } + + public String getReason() { + return reason; + } + + public String getSendTime() { + return sendTime; + } + + @JSONField(serialize = false, deserialize = false) + public Date getFormatSendTime() { + return DateUtil.parse2yyyyMMddHHmmss(sendTime); + } + + public String getRefundTime() { + return refundTime; + } + + @JSONField(serialize = false, deserialize = false) + public Date getFormatRefundTime() { + if (StringUtil.isNotBlank(refundTime)) { + return DateUtil.parse2yyyyMMddHHmmss(refundTime); + } + return null; + } + + public Integer getRefundAmount() { + return refundAmount; + } + + /** + * 调用接口获取单位为分,get方法转换为元方便使用 + * + * @return 元单位 + */ + @JSONField(serialize = false, deserialize = false) + public double getFormatRefundAmount() { + if (refundAmount != null) { + return refundAmount.intValue() / 100d; + } + return 0d; + } + + public String getWishing() { + return wishing; + } + + public String getRemark() { + return remark; + } + + public String getActName() { + return actName; + } + + public List getReceivers() { + return receivers; + } + + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + public static class RedpacketReceiver { + /** + * 领取红包的Openid + */ + private String openid; + /** + * 领取状态 + */ + private RedpacketStatus status; + /** + * 领取金额 + */ + private int amount; + /** + * 领取时间 + */ + @XmlElement(name = "rcv_time") + @JSONField(name = "rcv_time") + private String receiveTime; + + public String getOpenid() { + return openid; + } + + public RedpacketStatus getStatus() { + return status; + } + + public int getAmount() { + return amount; + } + + public String getReceiveTime() { + return receiveTime; + } + + /** + * 调用接口获取单位为分,get方法转换为元方便使用 + * + * @return 元单位 + */ + @JSONField(serialize = false, deserialize = false) + public double getFormatAmount() { + return amount / 100d; + } + + @JSONField(serialize = false, deserialize = false) + public Date getFormatReceiveTime() { + return DateUtil.parse2yyyyMMddHHmmss(receiveTime); + } + + @Override + public String toString() { + return "RedpacketReceiver [openid=" + openid + ", status=" + status + + ", amount=" + getFormatAmount() + ", receiveTime=" + + receiveTime + "]"; + } + } + + @Override + public String toString() { + return "RedpacketRecord [outTradeNo=" + outTradeNo + ", mchId=" + mchId + + ", repacketId=" + repacketId + ", status=" + status + + ", sendType=" + sendType + ", type=" + type + ", totalNum=" + + totalNum + ", totalAmount=" + getFormatTotalAmount() + + ", reason=" + reason + ", sendTime=" + sendTime + + ", refundTime=" + refundTime + ", refundAmount=" + + getFormatRefundAmount() + ", wishing=" + wishing + + ", remark=" + remark + ", actName=" + actName + + ", receivers=" + receivers + "]"; + } +} diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/type/RedpacketSendType.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/type/RedpacketSendType.java new file mode 100644 index 00000000..f0b563e3 --- /dev/null +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/type/RedpacketSendType.java @@ -0,0 +1,25 @@ +package com.foxinmy.weixin4j.mp.type; + +/** + * 红包发放类型 + * + * @className RedpacketSendType + * @author jy + * @date 2015年6月4日 + * @since JDK 1.7 + * @see + */ +public enum RedpacketSendType { + /** + * 通过API接口发放 + */ + API, + /** + * 通过上传文件方式发放 + */ + UPLOAD, + /** + * 通过活动方式发放 + */ + ACTIVITY; +} diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/type/RedpacketStatus.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/type/RedpacketStatus.java new file mode 100644 index 00000000..dd839f4b --- /dev/null +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/type/RedpacketStatus.java @@ -0,0 +1,32 @@ +package com.foxinmy.weixin4j.mp.type; + +/** + * 红包状态 + * @className RedpacketStatus + * @author jy + * @date 2015年6月4日 + * @since JDK 1.7 + * @see + */ +public enum RedpacketStatus { + /** + * 发放中 + */ + SENDING, + /** + * 已发放待领取 + */ + SENT, + /** + * 发放失败 + */ + FAILED, + /** + * 已领取 + */ + RECEIVED, + /** + * 已退款 + */ + REFUND; +} diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/type/RedpacketType.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/type/RedpacketType.java new file mode 100644 index 00000000..fe76f1f3 --- /dev/null +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/type/RedpacketType.java @@ -0,0 +1,21 @@ +package com.foxinmy.weixin4j.mp.type; + +/** + * 红包类型 + * + * @className RedpacketType + * @author jy + * @date 2015年6月4日 + * @since JDK 1.7 + * @see + */ +public enum RedpacketType { + /** + * 裂变红包 + */ + GROUP, + /** + * 普通红包 + */ + NORMAL; +} diff --git a/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/CashTest.java b/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/CashTest.java index c5cc66cc..a908c046 100644 --- a/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/CashTest.java +++ b/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/CashTest.java @@ -6,6 +6,7 @@ import com.foxinmy.weixin4j.exception.WeixinException; import com.foxinmy.weixin4j.mp.payment.v3.MPPayment; import com.foxinmy.weixin4j.mp.payment.v3.MPPaymentResult; import com.foxinmy.weixin4j.mp.payment.v3.Redpacket; +import com.foxinmy.weixin4j.mp.payment.v3.RedpacketRecord; import com.foxinmy.weixin4j.mp.payment.v3.RedpacketSendResult; import com.foxinmy.weixin4j.mp.type.MPPaymentCheckNameType; @@ -35,6 +36,13 @@ public class CashTest extends CouponTest { System.err.println(result); } + @Test + public void queryRedpacket() throws WeixinException { + String outTradeNo = "HB001"; + RedpacketRecord record = WEIXINPAY.queryRedpack(caFile, outTradeNo); + System.err.println(record); + } + @Test public void mpPayment() throws WeixinException { MPPayment payment = new MPPayment("MP001", diff --git a/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/CouponTest.java b/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/CouponTest.java index 7f98084f..576ccb94 100644 --- a/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/CouponTest.java +++ b/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/CouponTest.java @@ -32,7 +32,7 @@ public class CouponTest { static { ACCOUNT = new WeixinMpAccount("appid", "appsecret", - "paysing", "mchid"); + "paysign", "mchid"); WEIXINPAY = new WeixinPayProxy(ACCOUNT, new FileTokenHolder( new WeixinTokenCreator(ACCOUNT.getId(), ACCOUNT.getSecret()))); } diff --git a/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/XmlstreamTest.java b/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/XmlstreamTest.java index 64fa1658..564039e5 100644 --- a/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/XmlstreamTest.java +++ b/weixin4j-mp/src/test/java/com/foxinmy/weixin4j/mp/test/XmlstreamTest.java @@ -95,13 +95,14 @@ public class XmlstreamTest { } System.err.println(ListsuffixResultConverter - .containRefundDetailConvert(sb.toString(), com.foxinmy.weixin4j.mp.payment.v3.RefundRecord.class)); + .containRefundDetailConvert(sb.toString(), + com.foxinmy.weixin4j.mp.payment.v3.RefundRecord.class)); } public static void main(String[] args) throws Exception { // map2xml(); // xml2map(); - xml2order(); + // xml2order(); // xml2refundRecordV2(); // xml2refundRecordV3(); // object2xmlWithoutRootElement();