实现刷卡支付提交API #101

This commit is contained in:
Binary Wang 2017-03-23 18:18:22 +08:00
parent f63e934945
commit 6945e7e0f9
6 changed files with 761 additions and 9 deletions

View File

@ -0,0 +1,375 @@
package com.github.binarywang.wxpay.bean.request;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import me.chanjar.weixin.common.annotation.Required;
/**
* <pre>
* 提交刷卡支付请求对象类
* Created by Binary Wang on 2017-3-23.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
@XStreamAlias("xml")
public class WxPayMicropayRequest extends WxPayBaseRequest {
/**
* <pre>
* 签名类型
* sign_type
*
* String(32)
* HMAC-SHA256
* 签名类型目前支持HMAC-SHA256和MD5默认为MD5
**/
@XStreamAlias("sign_type")
private String signType;
/**
* <pre>
* 商品描述
* body
*
* String(128)
* image形象店-深圳腾大- QQ公仔
* 商品简单描述该字段须严格按照规范传递具体请见参数规定
**/
@Required
@XStreamAlias("body")
private String body;
/**
* <pre>
* 商品详情
* detail
*
* String(6000)
*
* 单品优惠功能字段需要接入请见详细说明
**/
@XStreamAlias("detail")
private String detail;
/**
* <pre>
* 附加数据
* attach
*
* String(127)
* 说明
* 附加数据在查询API和支付通知中原样返回该字段主要用于商户携带订单的自定义数据
**/
@XStreamAlias("attach")
private String attach;
/**
* <pre>
* 商户订单号
* out_trade_no
*
* String(32)
* 1217752501201407033233368018
* 商户系统内部的订单号,32个字符内可包含字母,其他说明见商户订单号
**/
@Required
@XStreamAlias("out_trade_no")
private String outTradeNo;
/**
* <pre>
* 订单金额
* total_fee
*
* Int
* 888
* 订单总金额单位为分只能为整数详见支付金额
**/
@Required
@XStreamAlias("total_fee")
private Integer totalFee;
/**
* <pre>
* 货币类型
* fee_type
*
* String(16)
* CNY
* 符合ISO4217标准的三位字母代码默认人民币CNY其他值列表详见货币类型
**/
@XStreamAlias("fee_type")
private String feeType;
/**
* <pre>
* 终端IP
* spbill_create_ip
*
* String(16)
* 8.8.8.8
* 调用微信支付API的机器IP
**/
@Required
@XStreamAlias("spbill_create_ip")
private String spbillCreateIp;
/**
* <pre>
* 商品标记
* goods_tag
*
* String(32)
* 1234
* 商品标记代金券或立减优惠功能的参数说明详见代金券或立减优惠
**/
@XStreamAlias("goods_tag")
private String goodsTag;
/**
* <pre>
* 指定支付方式
* limit_pay
*
* String(32)
* no_credit
* no_credit--指定不能使用信用卡支付
**/
@XStreamAlias("limit_pay")
private String limitPay;
/**
* <pre>
* 授权码
* auth_code
*
* String(128)
* 120061098828009406
* 扫码支付授权码设备读取用户微信中的条码或者二维码信息注用户刷卡条形码规则18位纯数字以101112131415开头
**/
@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);
}
}
}

View File

@ -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文本
*/

View File

@ -0,0 +1,328 @@
package com.github.binarywang.wxpay.bean.result;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* <pre>
* 提交刷卡支付接口响应结果对象类
* Created by Binary Wang on 2017-3-23.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
@XStreamAlias("xml")
public class WxPayMicropayResult extends WxPayBaseResult {
/**
* <pre>
* 用户标识
* openid
*
* String(128)
* Y
* 用户在商户appid 下的唯一标识
* </pre>
**/
@XStreamAlias("openid")
private String openid;
/**
* <pre>
* 是否关注公众账号
* is_subscribe
*
* String(1)
* Y
* 用户是否关注公众账号仅在公众账号类型支付有效取值范围Y或N;Y-关注;N-未关注
* </pre>
**/
@XStreamAlias("is_subscribe")
private String isSubscribe;
/**
* <pre>
* 交易类型
* trade_type
*
* String(16)
* MICROPAY
* 支付类型为MICROPAY(即扫码支付)
* </pre>
**/
@XStreamAlias("trade_type")
private String tradeType;
/**
* <pre>
* 付款银行
* bank_type
*
* String(32)
* CMC
* 银行类型采用字符串类型的银行标识值列表详见银行类型
* </pre>
**/
@XStreamAlias("bank_type")
private String bankType;
/**
* <pre>
* 货币类型
* fee_type
*
* String(16)
* CNY
* 符合ISO 4217标准的三位字母代码默认人民币CNY其他值列表详见货币类型
* </pre>
**/
@XStreamAlias("fee_type")
private String feeType;
/**
* <pre>
* 订单金额
* total_fee
*
* Int
* 888
* 订单总金额单位为分只能为整数详见支付金额
* </pre>
**/
@XStreamAlias("total_fee")
private String totalFee;
/**
* <pre>
* 应结订单金额
* settlement_total_fee
*
* Int
* 100
* 应结订单金额=订单金额-非充值代金券金额应结订单金额<=订单金额
* </pre>
**/
@XStreamAlias("settlement_total_fee")
private Integer settlementTotalFee;
/**
* <pre>
* 代金券金额
* coupon_fee
*
* Int
* 100
* 代金券金额<=订单金额订单金额-代金券金额=现金支付金额详见支付金额
* </pre>
**/
@XStreamAlias("coupon_fee")
private Integer couponFee;
/**
* <pre>
* 现金支付货币类型
* cash_fee_type
*
* String(16)
* CNY
* 符合ISO 4217标准的三位字母代码默认人民币CNY其他值列表详见货币类型
* </pre>
**/
@XStreamAlias("cash_fee_type")
private String cashFeeType;
/**
* <pre>
* 现金支付金额
* cash_fee
*
* Int
* 100
* 订单现金支付金额详见支付金额
* </pre>
**/
@XStreamAlias("cash_fee")
private Integer cashFee;
/**
* <pre>
* 微信支付订单号
* transaction_id
*
* String(32)
* 1217752501201407033233368018
* 微信支付订单号
* </pre>
**/
@XStreamAlias("transaction_id")
private String transactionId;
/**
* <pre>
* 商户订单号
* out_trade_no
*
* String(32)
* 1217752501201407033233368018
* 商户系统的订单号与请求一致
* </pre>
**/
@XStreamAlias("out_trade_no")
private String outTradeNo;
/**
* <pre>
* 商家数据包
* attach
*
* String(128)
* 123456
* 商家数据包原样返回
* </pre>
**/
@XStreamAlias("attach")
private String attach;
/**
* <pre>
* 支付完成时间
* time_end
*
* String(14)
* 20141030133525
* 订单生成时间格式为yyyyMMddHHmmss如2009年12月25日9点10分10秒表示为20091225091010详见时间规则
* </pre>
**/
@XStreamAlias("time_end")
private String timeEnd;
/**
* <pre>
* 营销详情
* promotion_detail
*
* String(6000)
* 示例见下文
* 新增返回单品优惠功能字段需要接入请见详细说明
* </pre>
**/
@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;
}
}

View File

@ -262,7 +262,7 @@ public interface WxPayService {
* @return 生成的二维码的字节数组
*/
byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength);
/**
* <pre>
* 扫码支付模式一生成二维码的方法
@ -324,4 +324,17 @@ public interface WxPayService {
* @return 保存到本地的临时文件
*/
File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException;
/**
* <pre>
* 提交刷卡支付
* 应用场景
* 收银员使用扫码设备读取微信用户刷卡授权码以后二维码或条码信息传送至商户收银台由商户收银台或者商户后台调用该接口发起支付
* 提醒1提交支付请求后微信会同步返回支付结果当返回结果为系统错误商户系统等待5秒后调用查询订单API查询支付实际交易结果当返回结果为USERPAYING商户系统可设置间隔时间(建议10秒)重新查询支付结果直到支付成功或超时(建议30秒)
* 提醒2在调用查询接口返回后如果交易状况不明晰请调用撤销订单API此时如果交易失败则关闭订单该单不能再支付成功如果交易成功则将扣款退回到用户账户当撤销无返回或错误时请再次调用注意请勿扣款后立即调用撤销订单API,建议至少15秒后再调用撤销订单API需要双向证书
* 接口地址 https://api.mch.weixin.qq.com/pay/micropay
* 是否需要证书不需要
* </pre>
*/
WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException;
}

View File

@ -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<String, String> 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 {

View File

@ -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());
}
}