diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java new file mode 100644 index 00000000..10aac649 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java @@ -0,0 +1,375 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import me.chanjar.weixin.common.annotation.Required; + +/** + *
+ *  提交刷卡支付请求对象类
+ * Created by Binary Wang on 2017-3-23.
+ * @author binarywang(Binary Wang)
+ * 
+ */ +@XStreamAlias("xml") +public class WxPayMicropayRequest extends WxPayBaseRequest { + /** + *
+   * 签名类型
+   * sign_type
+   * 否
+   * String(32)
+   * HMAC-SHA256
+   * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
+   **/
+  @XStreamAlias("sign_type")
+  private String signType;
+
+  /**
+   * 
+   * 商品描述
+   * body
+   * 是
+   * String(128)
+   * image形象店-深圳腾大- QQ公仔
+   * 商品简单描述,该字段须严格按照规范传递,具体请见参数规定
+   **/
+  @Required
+  @XStreamAlias("body")
+  private String body;
+
+  /**
+   * 
+   * 商品详情
+   * detail
+   * 否
+   * String(6000)
+   *
+   * 单品优惠功能字段,需要接入请见详细说明
+   **/
+  @XStreamAlias("detail")
+  private String detail;
+
+  /**
+   * 
+   * 附加数据
+   * attach
+   * 否
+   * String(127)
+   * 说明
+   * 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
+   **/
+  @XStreamAlias("attach")
+  private String attach;
+
+  /**
+   * 
+   * 商户订单号
+   * out_trade_no
+   * 是
+   * String(32)
+   * 1217752501201407033233368018
+   * 商户系统内部的订单号,32个字符内、可包含字母,其他说明见商户订单号
+   **/
+  @Required
+  @XStreamAlias("out_trade_no")
+  private String outTradeNo;
+
+  /**
+   * 
+   * 订单金额
+   * total_fee
+   * 是
+   * Int
+   * 888
+   * 订单总金额,单位为分,只能为整数,详见支付金额
+   **/
+  @Required
+  @XStreamAlias("total_fee")
+  private Integer totalFee;
+
+  /**
+   * 
+   * 货币类型
+   * fee_type
+   * 否
+   * String(16)
+   * CNY
+   * 符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
+   **/
+  @XStreamAlias("fee_type")
+  private String feeType;
+
+  /**
+   * 
+   * 终端IP
+   * spbill_create_ip
+   * 是
+   * String(16)
+   * 8.8.8.8
+   * 调用微信支付API的机器IP
+   **/
+  @Required
+  @XStreamAlias("spbill_create_ip")
+  private String spbillCreateIp;
+
+  /**
+   * 
+   * 商品标记
+   * goods_tag
+   * 否
+   * String(32)
+   * 1234
+   * 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
+   **/
+  @XStreamAlias("goods_tag")
+  private String goodsTag;
+
+  /**
+   * 
+   * 指定支付方式
+   * limit_pay
+   * 否
+   * String(32)
+   * no_credit
+   * no_credit--指定不能使用信用卡支付
+   **/
+  @XStreamAlias("limit_pay")
+  private String limitPay;
+
+  /**
+   * 
+   * 授权码
+   * auth_code
+   * 是
+   * String(128)
+   * 120061098828009406
+   * 扫码支付授权码,设备读取用户微信中的条码或者二维码信息注:用户刷卡条形码规则:18位纯数字,以10、11、12、13、14、15开头)
+   **/
+  @Required
+  @XStreamAlias("auth_code")
+  private String authCode;
+
+  private WxPayMicropayRequest(Builder builder) {
+    setSignType(builder.signType);
+    setBody(builder.body);
+    setAppid(builder.appid);
+    setDetail(builder.detail);
+    setMchId(builder.mchId);
+    setAttach(builder.attach);
+    setSubAppId(builder.subAppId);
+    setOutTradeNo(builder.outTradeNo);
+    setSubMchId(builder.subMchId);
+    setTotalFee(builder.totalFee);
+    setNonceStr(builder.nonceStr);
+    setFeeType(builder.feeType);
+    setSign(builder.sign);
+    setSpbillCreateIp(builder.spbillCreateIp);
+    setGoodsTag(builder.goodsTag);
+    setLimitPay(builder.limitPay);
+    setAuthCode(builder.authCode);
+  }
+
+  public static Builder newBuilder() {
+    return new Builder();
+  }
+
+  public String getSignType() {
+    return this.signType;
+  }
+
+  public void setSignType(String signType) {
+    this.signType = signType;
+  }
+
+  public String getBody() {
+    return this.body;
+  }
+
+  public void setBody(String body) {
+    this.body = body;
+  }
+
+  public String getDetail() {
+    return this.detail;
+  }
+
+  public void setDetail(String detail) {
+    this.detail = detail;
+  }
+
+  public String getAttach() {
+    return this.attach;
+  }
+
+  public void setAttach(String attach) {
+    this.attach = attach;
+  }
+
+  public String getOutTradeNo() {
+    return this.outTradeNo;
+  }
+
+  public void setOutTradeNo(String outTradeNo) {
+    this.outTradeNo = outTradeNo;
+  }
+
+  public Integer getTotalFee() {
+    return this.totalFee;
+  }
+
+  public void setTotalFee(Integer totalFee) {
+    this.totalFee = totalFee;
+  }
+
+  public String getFeeType() {
+    return this.feeType;
+  }
+
+  public void setFeeType(String feeType) {
+    this.feeType = feeType;
+  }
+
+  public String getSpbillCreateIp() {
+    return this.spbillCreateIp;
+  }
+
+  public void setSpbillCreateIp(String spbillCreateIp) {
+    this.spbillCreateIp = spbillCreateIp;
+  }
+
+  public String getGoodsTag() {
+    return this.goodsTag;
+  }
+
+  public void setGoodsTag(String goodsTag) {
+    this.goodsTag = goodsTag;
+  }
+
+  public String getLimitPay() {
+    return this.limitPay;
+  }
+
+  public void setLimitPay(String limitPay) {
+    this.limitPay = limitPay;
+  }
+
+  public String getAuthCode() {
+    return this.authCode;
+  }
+
+  public void setAuthCode(String authCode) {
+    this.authCode = authCode;
+  }
+
+  public static final class Builder {
+    private String signType;
+    private String body;
+    private String appid;
+    private String detail;
+    private String mchId;
+    private String attach;
+    private String subAppId;
+    private String outTradeNo;
+    private String subMchId;
+    private Integer totalFee;
+    private String nonceStr;
+    private String feeType;
+    private String sign;
+    private String spbillCreateIp;
+    private String goodsTag;
+    private String limitPay;
+    private String authCode;
+
+    private Builder() {
+    }
+
+    public Builder signType(String signType) {
+      this.signType = signType;
+      return this;
+    }
+
+    public Builder body(String body) {
+      this.body = body;
+      return this;
+    }
+
+    public Builder appid(String appid) {
+      this.appid = appid;
+      return this;
+    }
+
+    public Builder detail(String detail) {
+      this.detail = detail;
+      return this;
+    }
+
+    public Builder mchId(String mchId) {
+      this.mchId = mchId;
+      return this;
+    }
+
+    public Builder attach(String attach) {
+      this.attach = attach;
+      return this;
+    }
+
+    public Builder subAppId(String subAppId) {
+      this.subAppId = subAppId;
+      return this;
+    }
+
+    public Builder outTradeNo(String outTradeNo) {
+      this.outTradeNo = outTradeNo;
+      return this;
+    }
+
+    public Builder subMchId(String subMchId) {
+      this.subMchId = subMchId;
+      return this;
+    }
+
+    public Builder totalFee(Integer totalFee) {
+      this.totalFee = totalFee;
+      return this;
+    }
+
+    public Builder nonceStr(String nonceStr) {
+      this.nonceStr = nonceStr;
+      return this;
+    }
+
+    public Builder feeType(String feeType) {
+      this.feeType = feeType;
+      return this;
+    }
+
+    public Builder sign(String sign) {
+      this.sign = sign;
+      return this;
+    }
+
+    public Builder spbillCreateIp(String spbillCreateIp) {
+      this.spbillCreateIp = spbillCreateIp;
+      return this;
+    }
+
+    public Builder goodsTag(String goodsTag) {
+      this.goodsTag = goodsTag;
+      return this;
+    }
+
+    public Builder limitPay(String limitPay) {
+      this.limitPay = limitPay;
+      return this;
+    }
+
+    public Builder authCode(String authCode) {
+      this.authCode = authCode;
+      return this;
+    }
+
+    public WxPayMicropayRequest build() {
+      return new WxPayMicropayRequest(this);
+    }
+  }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java
index a23a2604..fed9544c 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java
@@ -41,7 +41,9 @@ public abstract class WxPayBaseResult {
    */
   @XStreamAlias("return_msg")
   protected String returnMsg;
-  private String xmlString;
+
+  //当return_code为SUCCESS的时候,还会包括以下字段:
+
   /**
    * 业务结果
    */
@@ -70,7 +72,7 @@ public abstract class WxPayBaseResult {
   /**
    * 服务商模式下的子公众账号ID
    */
-  @XStreamAlias("appid")
+  @XStreamAlias("sub_appid")
   private String subAppId;
   /**
    * 服务商模式下的子商户号
@@ -88,6 +90,12 @@ public abstract class WxPayBaseResult {
   @XStreamAlias("sign")
   private String sign;
 
+  //以下为辅助属性
+  /**
+   * xml字符串
+   */
+  private String xmlString;
+
   /**
    * xml的Document对象,用于解析xml文本
    */
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java
new file mode 100644
index 00000000..2f8842af
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java
@@ -0,0 +1,328 @@
+package com.github.binarywang.wxpay.bean.result;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+
+/**
+ * 
+ * 提交刷卡支付接口响应结果对象类
+ * Created by Binary Wang on 2017-3-23.
+ * @author binarywang(Binary Wang)
+ * 
+ */ +@XStreamAlias("xml") +public class WxPayMicropayResult extends WxPayBaseResult { + /** + *
+   * 用户标识
+   * openid
+   * 是
+   * String(128)
+   * Y
+   * 用户在商户appid 下的唯一标识
+   * 
+ **/ + @XStreamAlias("openid") + private String openid; + + /** + *
+   * 是否关注公众账号
+   * is_subscribe
+   * 是
+   * String(1)
+   * Y
+   * 用户是否关注公众账号,仅在公众账号类型支付有效,取值范围:Y或N;Y-关注;N-未关注
+   * 
+ **/ + @XStreamAlias("is_subscribe") + private String isSubscribe; + + /** + *
+   * 交易类型
+   * trade_type
+   * 是
+   * String(16)
+   * MICROPAY
+   * 支付类型为MICROPAY(即扫码支付)
+   * 
+ **/ + @XStreamAlias("trade_type") + private String tradeType; + + /** + *
+   * 付款银行
+   * bank_type
+   * 是
+   * String(32)
+   * CMC
+   * 银行类型,采用字符串类型的银行标识,值列表详见银行类型
+   * 
+ **/ + @XStreamAlias("bank_type") + private String bankType; + + /** + *
+   * 货币类型
+   * fee_type
+   * 否
+   * String(16)
+   * CNY
+   * 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
+   * 
+ **/ + @XStreamAlias("fee_type") + private String feeType; + + /** + *
+   * 订单金额
+   * total_fee
+   * 是
+   * Int
+   * 888
+   * 订单总金额,单位为分,只能为整数,详见支付金额
+   * 
+ **/ + @XStreamAlias("total_fee") + private String totalFee; + + /** + *
+   * 应结订单金额
+   * settlement_total_fee
+   * 否
+   * Int
+   * 100
+   * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
+   * 
+ **/ + @XStreamAlias("settlement_total_fee") + private Integer settlementTotalFee; + + /** + *
+   * 代金券金额
+   * coupon_fee
+   * 否
+   * Int
+   * 100
+   * “代金券”金额<=订单金额,订单金额-“代金券”金额=现金支付金额,详见支付金额
+   * 
+ **/ + @XStreamAlias("coupon_fee") + private Integer couponFee; + + /** + *
+   * 现金支付货币类型
+   * cash_fee_type
+   * 否
+   * String(16)
+   * CNY
+   * 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
+   * 
+ **/ + @XStreamAlias("cash_fee_type") + private String cashFeeType; + + /** + *
+   * 现金支付金额
+   * cash_fee
+   * 是
+   * Int
+   * 100
+   * 订单现金支付金额,详见支付金额
+   * 
+ **/ + @XStreamAlias("cash_fee") + private Integer cashFee; + + /** + *
+   * 微信支付订单号
+   * transaction_id
+   * 是
+   * String(32)
+   * 1217752501201407033233368018
+   * 微信支付订单号
+   * 
+ **/ + @XStreamAlias("transaction_id") + private String transactionId; + + /** + *
+   * 商户订单号
+   * out_trade_no
+   * 是
+   * String(32)
+   * 1217752501201407033233368018
+   * 商户系统的订单号,与请求一致。
+   * 
+ **/ + @XStreamAlias("out_trade_no") + private String outTradeNo; + + /** + *
+   * 商家数据包
+   * attach
+   * 否
+   * String(128)
+   * 123456
+   * 商家数据包,原样返回
+   * 
+ **/ + @XStreamAlias("attach") + private String attach; + + /** + *
+   * 支付完成时间
+   * time_end
+   * 是
+   * String(14)
+   * 20141030133525
+   * 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。详见时间规则
+   * 
+ **/ + @XStreamAlias("time_end") + private String timeEnd; + + /** + *
+   * 营销详情
+   * promotion_detail
+   * 否
+   * String(6000)
+   * 示例见下文
+   * 新增返回,单品优惠功能字段,需要接入请见详细说明
+   * 
+ **/ + @XStreamAlias("promotion_detail") + private String promotionDetail; + + public String getOpenid() { + return this.openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getIsSubscribe() { + return this.isSubscribe; + } + + public void setIsSubscribe(String isSubscribe) { + this.isSubscribe = isSubscribe; + } + + public String getTradeType() { + return this.tradeType; + } + + public void setTradeType(String tradeType) { + this.tradeType = tradeType; + } + + public String getBankType() { + return this.bankType; + } + + public void setBankType(String bankType) { + this.bankType = bankType; + } + + public String getFeeType() { + return this.feeType; + } + + public void setFeeType(String feeType) { + this.feeType = feeType; + } + + public String getTotalFee() { + return this.totalFee; + } + + public void setTotalFee(String totalFee) { + this.totalFee = totalFee; + } + + public Integer getSettlementTotalFee() { + return this.settlementTotalFee; + } + + public void setSettlementTotalFee(Integer settlementTotalFee) { + this.settlementTotalFee = settlementTotalFee; + } + + public Integer getCouponFee() { + return this.couponFee; + } + + public void setCouponFee(Integer couponFee) { + this.couponFee = couponFee; + } + + public String getCashFeeType() { + return this.cashFeeType; + } + + public void setCashFeeType(String cashFeeType) { + this.cashFeeType = cashFeeType; + } + + public Integer getCashFee() { + return this.cashFee; + } + + public void setCashFee(Integer cashFee) { + this.cashFee = cashFee; + } + + public String getTransactionId() { + return this.transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public String getOutTradeNo() { + return this.outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getAttach() { + return this.attach; + } + + public void setAttach(String attach) { + this.attach = attach; + } + + public String getTimeEnd() { + return this.timeEnd; + } + + public void setTimeEnd(String timeEnd) { + this.timeEnd = timeEnd; + } + + public String getPromotionDetail() { + return this.promotionDetail; + } + + public void setPromotionDetail(String promotionDetail) { + this.promotionDetail = promotionDetail; + } +} 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 9097d8ab..0473d9ba 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 @@ -262,7 +262,7 @@ public interface WxPayService { * @return 生成的二维码的字节数组 */ byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength); - + /** *
    * 扫码支付模式一生成二维码的方法
@@ -324,4 +324,17 @@ public interface WxPayService {
    * @return 保存到本地的临时文件
    */
   File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException;
+
+  /**
+   * 
+   * 提交刷卡支付
+   * 应用场景:
+   * 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付。
+   * 提醒1:提交支付请求后微信会同步返回支付结果。当返回结果为“系统错误”时,商户系统等待5秒后调用【查询订单API】,查询支付实际交易结果;当返回结果为“USERPAYING”时,商户系统可设置间隔时间(建议10秒)重新查询支付结果,直到支付成功或超时(建议30秒);
+   * 提醒2:在调用查询接口返回后,如果交易状况不明晰,请调用【撤销订单API】,此时如果交易失败则关闭订单,该单不能再支付成功;如果交易成功,则将扣款退回到用户账户。当撤销无返回或错误时,请再次调用。注意:请勿扣款后立即调用【撤销订单API】,建议至少15秒后再调用。撤销订单API需要双向证书。
+   * 接口地址:   https://api.mch.weixin.qq.com/pay/micropay
+   * 是否需要证书:不需要。
+   * 
+ */ + WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java index ffa8127d..371b50c8 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java @@ -122,19 +122,19 @@ public class WxPayServiceImpl implements WxPayService { || !"SUCCESS".equalsIgnoreCase(result.getResultCode())) { StringBuilder errorMsg = new StringBuilder(); if (result.getReturnCode() != null) { - errorMsg.append("返回代码:" + result.getReturnCode()); + errorMsg.append("返回代码:").append(result.getReturnCode()); } if (result.getReturnMsg() != null) { - errorMsg.append(",返回信息:" + result.getReturnMsg()); + errorMsg.append(",返回信息:").append(result.getReturnMsg()); } if (result.getResultCode() != null) { - errorMsg.append(",结果代码:" + result.getResultCode()); + errorMsg.append(",结果代码:").append(result.getResultCode()); } if (result.getErrCode() != null) { - errorMsg.append(",错误代码:" + result.getErrCode()); + errorMsg.append(",错误代码:").append(result.getErrCode()); } if (result.getErrCodeDes() != null) { - errorMsg.append(",错误详情:" + result.getErrCodeDes()); + errorMsg.append(",错误详情:").append(result.getErrCodeDes()); } WxError error = WxError.newBuilder() @@ -389,7 +389,7 @@ public class WxPayServiceImpl implements WxPayService { @Override public String createScanPayQrcodeMode1(String productId){ - //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX + //weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX StringBuilder codeUrl = new StringBuilder("weixin://wxpay/bizpayurl?"); Map params = Maps.newHashMap(); params.put("appid", this.getConfig().getAppId()); @@ -455,6 +455,19 @@ public class WxPayServiceImpl implements WxPayService { return null; } + @Override + public WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException { + this.initRequest(request); + BeanUtils.checkRequiredFields(request); + request.setSign(this.createSign(request)); + + String url = this.getPayBaseUrl() + "/pay/micropay"; + String responseContent = this.post(url, request.toXML()); + WxPayMicropayResult result = WxPayBaseResult.fromXML(responseContent, WxPayMicropayResult.class); + this.checkResult(result); + return result; + } + private String post(String url, String xmlParam) { String requestString = null; try { 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/WxPayServiceImplTest.java index 7fa30ac2..6b4a0939 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/WxPayServiceImplTest.java @@ -195,4 +195,19 @@ public class WxPayServiceImplTest { assertEquals(QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()), qrcodeContent); } + @Test + public void testGetOrderNotifyResult() throws Exception { + } + + @Test + public void testMicropay() throws Exception { + WxPayMicropayResult result = this.payService.micropay(WxPayMicropayRequest.newBuilder() + .body("body") + .outTradeNo("aaaaa") + .totalFee(123) + .spbillCreateIp("127.0.0.1") + .authCode("aaa") + .build()); + this.logger.info(result.toString()); + } }