diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendRequest.java new file mode 100644 index 00000000..9a98fd64 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendRequest.java @@ -0,0 +1,311 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.request.WxPayBaseRequest; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import me.chanjar.weixin.common.annotation.Required; + +/** + *
+ * 发送代金券请求对象类
+ * Created by Binary Wang on 2017-7-15.
+ * 
+ * + * @author Binary Wang + */ +@XStreamAlias("xml") +public class WxPayCouponSendRequest extends WxPayBaseRequest { + /** + *
+   * 字段名:代金券批次id
+   * 变量名:coupon_stock_id
+   * 是否必填:是
+   * 示例值:1757
+   * 类型:String
+   * 说明:代金券批次id
+   * 
+ */ + @Required + @XStreamAlias("coupon_stock_id") + private String couponStockId; + + /** + *
+   * 字段名:openid记录数
+   * 变量名:openid_count
+   * 是否必填:是
+   * 示例值:1
+   * 类型:int
+   * 说明:openid记录数(目前支持num=1)
+   * 
+ */ + @Required + @XStreamAlias("openid_count") + private Integer openidCount; + + /** + *
+   * 字段名:商户单据号
+   * 变量名:partner_trade_no
+   * 是否必填:是
+   * 示例值:1000009820141203515766
+   * 类型:String
+   * 说明:商户此次发放凭据号(格式:商户id+日期+流水号),商户侧需保持唯一性
+   * 
+ */ + @Required + @XStreamAlias("partner_trade_no") + private String partnerTradeNo; + + /** + *
+   * 字段名:用户openid
+   * 变量名:openid
+   * 是否必填:是
+   * 示例值:onqOjjrXT-776SpHnfexGm1_P7iE
+   * 类型:String
+   * 说明:Openid信息,用户在appid下的openid。
+   * 
+ */ + @Required + @XStreamAlias("openid") + private String openid; + + /** + *
+   * 字段名:操作员
+   * 变量名:op_user_id
+   * 是否必填:否
+   * 示例值:10000098
+   * 类型:String(32)
+   * 说明:操作员帐号, 默认为商户号,可在商户平台配置操作员对应的api权限
+   * 
+ */ + @XStreamAlias("op_user_id") + private String opUserId; + + /** + *
+   * 字段名:设备号
+   * 变量名:device_info
+   * 是否必填:否
+   * 示例值:
+   * 类型:String(32)
+   * 说明:微信支付分配的终端设备号
+   * 
+ */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
+   * 字段名:协议版本
+   * 变量名:version
+   * 是否必填:否
+   * 示例值:1.0
+   * 类型:String(32)
+   * 说明:默认1.0
+   * 
+ */ + @XStreamAlias("version") + private String version; + + /** + *
+   * 字段名:协议类型
+   * 变量名:type
+   * 是否必填:否
+   * 示例值:XML
+   * 类型:String(32)
+   * 说明:XML【目前仅支持默认XML】
+   * 
+ */ + @XStreamAlias("type") + private String type; + + public WxPayCouponSendRequest() { + } + + private WxPayCouponSendRequest(Builder builder) { + setAppid(builder.appid); + setMchId(builder.mchId); + setSubAppId(builder.subAppId); + setSubMchId(builder.subMchId); + setNonceStr(builder.nonceStr); + setSign(builder.sign); + setCouponStockId(builder.couponStockId); + setOpenidCount(builder.openidCount); + setPartnerTradeNo(builder.partnerTradeNo); + setOpenid(builder.openid); + setOpUserId(builder.opUserId); + setDeviceInfo(builder.deviceInfo); + setVersion(builder.version); + setType(builder.type); + } + + public static Builder newBuilder() { + return new Builder(); + } + + public String getCouponStockId() { + return this.couponStockId; + } + + public void setCouponStockId(String couponStockId) { + this.couponStockId = couponStockId; + } + + public Integer getOpenidCount() { + return this.openidCount; + } + + public void setOpenidCount(Integer openidCount) { + this.openidCount = openidCount; + } + + public String getPartnerTradeNo() { + return this.partnerTradeNo; + } + + public void setPartnerTradeNo(String partnerTradeNo) { + this.partnerTradeNo = partnerTradeNo; + } + + public String getOpenid() { + return this.openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getOpUserId() { + return this.opUserId; + } + + public void setOpUserId(String opUserId) { + this.opUserId = opUserId; + } + + public String getDeviceInfo() { + return this.deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getVersion() { + return this.version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + protected void checkConstraints() { + //do nothing + } + + public static final class Builder { + private String appid; + private String mchId; + private String subAppId; + private String subMchId; + private String nonceStr; + private String sign; + private String couponStockId; + private Integer openidCount; + private String partnerTradeNo; + private String openid; + private String opUserId; + private String deviceInfo; + private String version; + private String type; + + private Builder() { + } + + public Builder appid(String appid) { + this.appid = appid; + return this; + } + + public Builder mchId(String mchId) { + this.mchId = mchId; + return this; + } + + public Builder subAppId(String subAppId) { + this.subAppId = subAppId; + return this; + } + + public Builder subMchId(String subMchId) { + this.subMchId = subMchId; + return this; + } + + public Builder nonceStr(String nonceStr) { + this.nonceStr = nonceStr; + return this; + } + + public Builder sign(String sign) { + this.sign = sign; + return this; + } + + public Builder couponStockId(String couponStockId) { + this.couponStockId = couponStockId; + return this; + } + + public Builder openidCount(Integer openidCount) { + this.openidCount = openidCount; + return this; + } + + public Builder partnerTradeNo(String partnerTradeNo) { + this.partnerTradeNo = partnerTradeNo; + return this; + } + + public Builder openid(String openid) { + this.openid = openid; + return this; + } + + public Builder opUserId(String opUserId) { + this.opUserId = opUserId; + return this; + } + + public Builder deviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + return this; + } + + public Builder version(String version) { + this.version = version; + return this; + } + + public Builder type(String type) { + this.type = type; + return this; + } + + public WxPayCouponSendRequest build() { + return new WxPayCouponSendRequest(this); + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendResult.java new file mode 100644 index 00000000..1c562ada --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponSendResult.java @@ -0,0 +1,204 @@ +package com.github.binarywang.wxpay.bean.coupon; + +import com.github.binarywang.wxpay.bean.result.WxPayBaseResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + *
+ * 发送代金券响应结果类
+ * Created by Binary Wang on 2017-7-15.
+ * 
+ * + * @author Binary Wang + */ +@XStreamAlias("xml") +public class WxPayCouponSendResult extends WxPayBaseResult { + /** + *
+   * 字段名:设备号
+   * 变量名:device_info
+   * 是否必填:否
+   * 示例值:123456sb
+   * 类型:String(32)
+   * 描述:微信支付分配的终端设备号,
+   * 
+ */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
+   * 字段名:代金券批次id
+   * 变量名:coupon_stock_id
+   * 是否必填:是
+   * 示例值:1757
+   * 类型:String
+   * 描述:用户在商户appid下的唯一标识
+   * 
+ */ + @XStreamAlias("coupon_stock_id") + private String couponStockId; + + /** + *
+   * 字段名:返回记录数
+   * 变量名:resp_count
+   * 是否必填:是
+   * 示例值:1
+   * 类型:Int
+   * 描述:返回记录数
+   * 
+ */ + @XStreamAlias("resp_count") + private Integer respCount; + + /** + *
+   * 字段名:成功记录数
+   * 变量名:success_count
+   * 是否必填:是
+   * 示例值:1或者0
+   * 类型:Int
+   * 描述:成功记录数
+   * 
+ */ + @XStreamAlias("success_count") + private Integer successCount; + + /** + *
+   * 字段名:失败记录数
+   * 变量名:failed_count
+   * 是否必填:是
+   * 示例值:1或者0
+   * 类型:Int
+   * 描述:失败记录数
+   * 
+ */ + @XStreamAlias("failed_count") + private Integer failedCount; + + /** + *
+   * 字段名:用户标识
+   * 变量名:openid
+   * 是否必填:是
+   * 示例值:onqOjjrXT-776SpHnfexGm1_P7iE
+   * 类型:String
+   * 描述:用户在商户appid下的唯一标识
+   * 
+ */ + @XStreamAlias("openid") + private String openid; + + /** + *
+   * 字段名:返回码
+   * 变量名:ret_code
+   * 是否必填:是
+   * 示例值:SUCCESS或者FAILED
+   * 类型:String
+   * 描述:返回码,SUCCESS/FAILED
+   * 
+ */ + @XStreamAlias("ret_code") + private String retCode; + + /** + *
+   * 字段名:代金券id
+   * 变量名:coupon_id
+   * 是否必填:是
+   * 示例值:1870
+   * 类型:String
+   * 描述:对一个用户成功发放代金券则返回代金券id,即ret_code为SUCCESS的时候;如果ret_code为FAILED则填写空串""
+   * 
+ */ + @XStreamAlias("coupon_id") + private String couponId; + + /** + *
+   * 字段名:返回信息
+   * 变量名:ret_msg
+   * 是否必填:是
+   * 示例值:失败描述信息,例如:“用户已达领用上限”
+   * 类型:String
+   * 描述:返回信息,当返回码是FAILED的时候填写,否则填空串“”
+   * 
+ */ + @XStreamAlias("ret_msg") + private String retMsg; + + public String getDeviceInfo() { + return this.deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getCouponStockId() { + return this.couponStockId; + } + + public void setCouponStockId(String couponStockId) { + this.couponStockId = couponStockId; + } + + public Integer getRespCount() { + return this.respCount; + } + + public void setRespCount(Integer respCount) { + this.respCount = respCount; + } + + public Integer getSuccessCount() { + return this.successCount; + } + + public void setSuccessCount(Integer successCount) { + this.successCount = successCount; + } + + public Integer getFailedCount() { + return this.failedCount; + } + + public void setFailedCount(Integer failedCount) { + this.failedCount = failedCount; + } + + public String getOpenid() { + return this.openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getRetCode() { + return this.retCode; + } + + public void setRetCode(String retCode) { + this.retCode = retCode; + } + + public String getCouponId() { + return this.couponId; + } + + public void setCouponId(String couponId) { + this.couponId = couponId; + } + + public String getRetMsg() { + return this.retMsg; + } + + public void setRetMsg(String retMsg) { + this.retMsg = retMsg; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java index 0d4fc08b..b0c2e85d 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java @@ -17,13 +17,6 @@ import java.math.BigDecimal; *
  * Created by Binary Wang on 2016-10-24.
  *  微信支付请求对象共用的参数存放类
- * 注释中各行每个字段描述对应如下:
- * 
  • 字段名 - *
  • 变量名 - *
  • 是否必填 - *
  • 类型 - *
  • 示例值 - *
  • 描述 *
  • * * @author binarywang(Binary Wang) @@ -31,72 +24,72 @@ import java.math.BigDecimal; public abstract class WxPayBaseRequest { /** *
    -   * 公众账号ID
    -   * appid
    -   * 是
    -   * String(32)
    -   * wxd678efh567hg6787
    -   * 微信分配的公众账号ID(企业号corpid即为此appId)
    +   * 字段名:公众账号ID
    +   * 变量名:appid
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:wxd678efh567hg6787
    +   * 描述:微信分配的公众账号ID(企业号corpid即为此appId)
        * 
    */ @XStreamAlias("appid") protected String appid; /** *
    -   * 商户号
    -   * mch_id
    -   * 是
    -   * String(32)
    -   * 1230000109
    -   * 微信支付分配的商户号
    +   * 字段名:商户号
    +   * 变量名:mch_id
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1230000109
    +   * 描述:微信支付分配的商户号
        * 
    */ @XStreamAlias("mch_id") protected String mchId; /** *
    -   * 服务商模式下的子商户公众账号ID
    -   * sub_appid
    -   * 是
    -   * String(32)
    -   * wxd678efh567hg6787
    -   * 微信分配的子商户公众账号ID
    +   * 字段名:服务商模式下的子商户公众账号ID
    +   * 变量名:sub_appid
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:wxd678efh567hg6787
    +   * 描述:微信分配的子商户公众账号ID
        * 
    */ @XStreamAlias("sub_appid") protected String subAppId; /** *
    -   * 服务商模式下的子商户号
    -   * sub_mch_id
    -   * 是
    -   * String(32)
    -   * 1230000109
    -   * 微信支付分配的子商户号,开发者模式下必填
    +   * 字段名:服务商模式下的子商户号
    +   * 变量名:sub_mch_id
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:1230000109
    +   * 描述:微信支付分配的子商户号,开发者模式下必填
        * 
    */ @XStreamAlias("sub_mch_id") protected String subMchId; /** *
    -   * 随机字符串
    -   * nonce_str
    -   * 是
    -   * String(32)
    -   * 5K8264ILTKCH16CQ2502SI8ZNMTM67VS
    -   * 随机字符串,不长于32位。推荐随机数生成算法
    +   * 字段名:随机字符串
    +   * 变量名:nonce_str
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:5K8264ILTKCH16CQ2502SI8ZNMTM67VS
    +   * 描述:随机字符串,不长于32位。推荐随机数生成算法
        * 
    */ @XStreamAlias("nonce_str") protected String nonceStr; /** *
    -   * 签名
    -   * sign
    -   * 是
    -   * String(32)
    -   * C380BEC2BFD727A4B6845133519F3AD6
    -   * 签名,详见签名生成算法
    +   * 字段名:签名
    +   * 变量名:sign
    +   * 是否必填:是
    +   * 类型:String(32)
    +   * 示例值:C380BEC2BFD727A4B6845133519F3AD6
    +   * 描述:签名,详见签名生成算法
        * 
    */ @XStreamAlias("sign") diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index 03640eb0..f2106c72 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java @@ -1,5 +1,7 @@ package com.github.binarywang.wxpay.service; +import com.github.binarywang.wxpay.bean.coupon.WxPayCouponSendRequest; +import com.github.binarywang.wxpay.bean.coupon.WxPayCouponSendResult; import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.result.*; import com.github.binarywang.wxpay.config.WxPayConfig; @@ -339,4 +341,14 @@ public interface WxPayService { * */ String getSandboxSignKey() throws WxPayException; + + /** + *
    +   * 发放代金券
    +   * 接口请求链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/send_coupon
    +   * 是否需要证书:请求需要双向证书。
    +   * 文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/sp_coupon.php?chapter=12_3
    +   * 
    + */ + WxPayCouponSendResult sendCoupon(WxPayCouponSendRequest request) throws WxPayException; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java index aa929176..b1c31b86 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImpl.java @@ -1,6 +1,8 @@ package com.github.binarywang.wxpay.service.impl; import com.github.binarywang.utils.qrcode.QrcodeUtils; +import com.github.binarywang.wxpay.bean.coupon.WxPayCouponSendRequest; +import com.github.binarywang.wxpay.bean.coupon.WxPayCouponSendResult; import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.result.*; import com.github.binarywang.wxpay.config.WxPayConfig; @@ -463,4 +465,15 @@ public abstract class WxPayServiceAbstractImpl implements WxPayService { result.checkResult(this); return result.getSandboxSignKey(); } + + @Override + public WxPayCouponSendResult sendCoupon(WxPayCouponSendRequest request) throws WxPayException { + request.checkAndSign(this.getConfig()); + + String url = this.getPayBaseUrl() + "/mmpaymkttransfers/send_coupon"; + String responseContent = this.post(url, request.toXML(), true); + WxPayCouponSendResult result = WxPayBaseResult.fromXML(responseContent, WxPayCouponSendResult.class); + result.checkResult(this); + return result; + } } diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java similarity index 94% rename from weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImplTest.java rename to weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java index 1cba44c5..cc6ab5a0 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImplTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceAbstractImplTest.java @@ -1,6 +1,8 @@ package com.github.binarywang.wxpay.service.impl; import com.github.binarywang.utils.qrcode.QrcodeUtils; +import com.github.binarywang.wxpay.bean.coupon.WxPayCouponSendRequest; +import com.github.binarywang.wxpay.bean.coupon.WxPayCouponSendResult; import com.github.binarywang.wxpay.bean.request.*; import com.github.binarywang.wxpay.bean.result.*; import com.github.binarywang.wxpay.exception.WxPayException; @@ -26,7 +28,7 @@ import static org.testng.Assert.*; */ @Test @Guice(modules = ApiTestModule.class) -public class WxPayServiceImplTest { +public class WxPayServiceAbstractImplTest { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Inject @@ -271,4 +273,14 @@ public class WxPayServiceImplTest { this.logger.info(signKey); } + @Test + public void testSendCoupon() throws Exception { + WxPayCouponSendResult result = this.payService.sendCoupon(WxPayCouponSendRequest.newBuilder() + .couponStockId("123") + .openid("122") + .partnerTradeNo("1212") + .openidCount(1) + .build()); + this.logger.info(result.toString()); + } }