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?"); Mapparams = 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()); + } }