diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java new file mode 100644 index 0000000..0bfd3ce --- /dev/null +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java @@ -0,0 +1,307 @@ + +package com.egzosn.pay.demo.controller; + + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.annotation.PostConstruct; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.egzosn.pay.common.bean.CertStoreType; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.demo.request.QueryOrder; +import com.egzosn.pay.demo.service.handler.WxV3CombinePayMessageHandler; +import com.egzosn.pay.web.support.HttpRequestNoticeParams; +import com.egzosn.pay.wx.v3.api.WxCombinePayService; +import com.egzosn.pay.wx.v3.api.WxPayConfigStorage; +import com.egzosn.pay.wx.v3.bean.WxTransactionType; +import com.egzosn.pay.wx.v3.bean.combine.CombineAmount; +import com.egzosn.pay.wx.v3.bean.combine.CombineCloseOrder; +import com.egzosn.pay.wx.v3.bean.combine.CombinePayOrder; +import com.egzosn.pay.wx.v3.bean.combine.CombineSubOrder; +import com.egzosn.pay.wx.v3.bean.order.H5Info; +import com.egzosn.pay.wx.v3.bean.order.SceneInfo; +import com.egzosn.pay.wx.v3.bean.order.SubOrder; +import com.egzosn.pay.wx.v3.bean.response.WxRefundResult; + +/** + * 微信V3合单发起支付入口 + * + * @author egan + * email egzosn@gmail.com + * date 2016/11/18 0:25 + */ +@RestController +@RequestMapping("wxV3combine") +public class WxV3CombinePayController { + + private WxCombinePayService service = null; + + @PostConstruct //没有证书的情况下注释掉,避免启动报错 + public void init() { + WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); + wxPayConfigStorage.setAppId("wxc7b993ff15a9f27c"); + wxPayConfigStorage.setMchId("1602947766"); + //V3密钥 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_2.shtml + wxPayConfigStorage.setV3ApiKey("9bd8f0e7af4841299d782406b7774f56"); + wxPayConfigStorage.setNotifyUrl("http://sailinmu.iok.la/wxV3/payBack.json"); + wxPayConfigStorage.setReturnUrl("http://sailinmu.iok.la/wxV3/payBack.json"); + wxPayConfigStorage.setInputCharset("utf-8"); + //使用证书时设置为true + wxPayConfigStorage.setCertSign(true); + //商户API证书 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_1.shtml + wxPayConfigStorage.setApiClientKeyP12("E:\\Documents\\gitee\\支付\\yifenli_mall.p12"); + wxPayConfigStorage.setCertStoreType(CertStoreType.PATH); + service = new WxCombinePayService(wxPayConfigStorage); + //设置回调消息处理 + //TODO {@link com.egzosn.pay.demo.controller.WxPayController#payBack} + service.setPayMessageHandler(new WxV3CombinePayMessageHandler()); + } + + + /** + * 跳到支付页面 + * 针对实时支付 + * + * @return 跳到支付页面 + */ + @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") + public String toPay() { + CombinePayOrder order = new CombinePayOrder(); + SceneInfo sceneInfo = new SceneInfo(); + sceneInfo.setPayerClientIp("用户终端IP "); + sceneInfo.setDeviceId("终端设备号(门店号或收银设备ID) 。为了方便问题定位,H5支付场景下,该字段必填"); + sceneInfo.setH5Info(new H5Info("场景类型,枚举值:\n" + + "iOS:IOS移动应用;\n" + + "Android:安卓移动应用;\n" + + "Wap:WAP网站应用;")); + order.setSceneInfo(sceneInfo); + order.setCombineOutTradeNo("合单商户订单号"); + //子单信息,最多50单. + List subOrders = new ArrayList<>(); + SubOrder subOrder = new SubOrder(); + subOrder.setMchid("子单商户号"); + subOrder.setAttach("附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 "); + //"子单金额,单位为分。 " + subOrder.setAmount(new CombineAmount(121)); + subOrder.setOutTradeNo("子单商户订单号 "); + subOrder.setDescription("商品描述"); + subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); + subOrders.add(subOrder); + order.setSubOrders(subOrders); + order.setTransactionType(WxTransactionType.COMBINE_H5); + return service.toPay(order); + } + + /** + * 公众号支付,小程序 + * + * @return 返回jsapi所需参数 + */ + @RequestMapping(value = "jsapi") + public Map jsapi() { + + CombinePayOrder order = new CombinePayOrder(); + order.setTransactionType(WxTransactionType.COMBINE_JSAPI); + order.setCombineOutTradeNo("合单商户订单号"); + order.setOpenid("使用合单appid获取的对应用户openid。是用户在商户appid下的唯一标识。 "); + //子单信息,最多50单. + List subOrders = new ArrayList<>(); + SubOrder subOrder = new SubOrder(); + subOrder.setMchid("子单商户号"); + subOrder.setAttach("附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 "); + //"子单金额,单位为分。 " + subOrder.setAmount(new CombineAmount(111)); + subOrder.setOutTradeNo("子单商户订单号 "); + subOrder.setDescription("商品描述"); + subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); + subOrders.add(subOrder); + order.setSubOrders(subOrders); + Map orderInfo = service.orderInfo(order); + orderInfo.put("code", 0); + return orderInfo; + } + + + /** + * 获取支付预订单信息 + * + * @return 支付预订单信息 + */ + @RequestMapping("app") + public Map app() { + + CombinePayOrder order = new CombinePayOrder(); + order.setTransactionType(WxTransactionType.COMBINE_APP); + order.setCombineOutTradeNo("合单商户订单号"); + //子单信息,最多50单. + List subOrders = new ArrayList<>(); + SubOrder subOrder = new SubOrder(); + subOrder.setMchid("子单商户号"); + subOrder.setAttach("附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 "); + //"子单金额,单位为分。 " + subOrder.setAmount(new CombineAmount(211)); + subOrder.setOutTradeNo("子单商户订单号 "); + subOrder.setDescription("商品描述"); + subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); + subOrders.add(subOrder); + order.setSubOrders(subOrders); + Map orderInfo = service.orderInfo(order); + orderInfo.put("code", 0); + return orderInfo; + } + + /** + * 获取二维码图像 + * 二维码支付 + * + * @return 二维码图像 + * @throws IOException IOException + */ + @RequestMapping(value = "toQrPay.jpg", produces = "image/jpeg;charset=UTF-8") + public byte[] toWxQrPay() throws IOException { + CombinePayOrder order = new CombinePayOrder(); + order.setTransactionType(WxTransactionType.COMBINE_NATIVE); + order.setCombineOutTradeNo("合单商户订单号"); + //子单信息,最多50单. + List subOrders = new ArrayList<>(); + SubOrder subOrder = new SubOrder(); + subOrder.setMchid("子单商户号"); + subOrder.setAttach("附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 "); + //"子单金额,单位为分。 " + subOrder.setAmount(new CombineAmount(131)); + subOrder.setOutTradeNo("子单商户订单号 "); + subOrder.setDescription("商品描述"); + subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); + subOrders.add(subOrder); + order.setSubOrders(subOrders); + + //获取对应的支付账户操作工具(可根据账户id) + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(service.genQrPay(order), "JPEG", baos); + return baos.toByteArray(); + } + + /** + * 获取二维码地址 + * 二维码支付 + * + * @return 二维码图像 + * @throws IOException IOException + */ + @RequestMapping(value = "getQrPay.json") + public String getQrPay() { + CombinePayOrder order = new CombinePayOrder(); + order.setTransactionType(WxTransactionType.COMBINE_NATIVE); + order.setCombineOutTradeNo("合单商户订单号"); + //子单信息,最多50单. + List subOrders = new ArrayList<>(); + SubOrder subOrder = new SubOrder(); + subOrder.setMchid("子单商户号"); + subOrder.setAttach("附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。1 "); + //"子单金额,单位为分。 " + subOrder.setAmount(new CombineAmount(115)); + subOrder.setOutTradeNo("子单商户订单号 "); + subOrder.setDescription("商品描述"); + subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); + subOrders.add(subOrder); + order.setSubOrders(subOrders); + //获取对应的支付账户操作工具(可根据账户id) + return service.getQrPay(order); + } + + + /** + * 支付回调地址 + * + * @param request 请求 + * @return 是否成功 + *

+ * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + * @throws IOException IOException + */ + @RequestMapping(value = "payBack.json") + public String payBack(HttpServletRequest request) { + //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + return service.payBack(new HttpRequestNoticeParams(request)).toMessage(); + } + + + /** + * 查询 + * + * @param order 订单的请求体 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @RequestMapping("query") + public Map query(QueryOrder order) { + return service.query(order.getTradeNo(), order.getOutTradeNo()); + } + + + /** + * 交易关闭接口 + * + * @return 返回支付方交易关闭后的结果 + */ + @RequestMapping("close") + public Map close() { + CombineCloseOrder order = new CombineCloseOrder(); + order.setOutTradeNo("合单商户订单号"); + //子单信息,最多50单. + List subOrders = new ArrayList<>(); + CombineSubOrder subOrder = new CombineSubOrder(); + subOrder.setMchid("子单商户号"); + subOrder.setOutTradeNo("子单商户订单号 "); + subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); + subOrders.add(subOrder); + order.setSubOrders(subOrders); + return service.close(order); + } + + /** + * 申请退款接口 + * + * @param order 订单的请求体 + * @return 返回支付方申请退款后的结果 + */ + @RequestMapping("refund") + public WxRefundResult refund(RefundOrder order) { + + return service.refund(order); + } + + /** + * 查询退款 + * + * @param order 订单的请求体 + * @return 返回支付方查询退款后的结果 + */ + @RequestMapping("refundquery") + public Map refundquery(RefundOrder order) { + return service.refundquery(order); + } + + /** + * 下载对账单 + * + * @param order 订单的请求体 + * @return 返回支付方下载对账单的结果 + */ + @RequestMapping("downloadbill") + public Object downloadBill(QueryOrder order) { + return service.downloadBill(order.getBillDate(), order.getBillType()); + } + + +} diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java index 8adc70e..5554c00 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -19,19 +19,16 @@ import org.springframework.web.bind.annotation.RestController; import com.egzosn.pay.common.bean.CertStoreType; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; -import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.demo.request.QueryOrder; import com.egzosn.pay.demo.service.handler.WxV3PayMessageHandler; import com.egzosn.pay.web.support.HttpRequestNoticeParams; -import com.egzosn.pay.wx.bean.WxBank; -import com.egzosn.pay.wx.bean.WxTransferType; import com.egzosn.pay.wx.v3.api.WxPayConfigStorage; import com.egzosn.pay.wx.v3.api.WxPayService; import com.egzosn.pay.wx.v3.bean.WxTransactionType; import com.egzosn.pay.wx.v3.bean.response.WxRefundResult; /** - * 发起支付入口 + * 微信V3发起支付入口 * * @author egan * email egzosn@gmail.com @@ -44,13 +41,8 @@ public class WxV3PayController { private WxPayService service = null; - - - - @PostConstruct //没有证书的情况下注释掉,避免启动报错 public void init() { - System.out.println("v3 init"); WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); wxPayConfigStorage.setAppId("wxc7b993ff15a9f27c"); wxPayConfigStorage.setMchId("1602947766"); @@ -161,8 +153,6 @@ public class WxV3PayController { } - - /** * 支付回调地址 * @@ -175,7 +165,7 @@ public class WxV3PayController { * @throws IOException IOException */ @RequestMapping(value = "payBack.json") - public String payBack(HttpServletRequest request) { + public String payBack(HttpServletRequest request) { //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() return service.payBack(new HttpRequestNoticeParams(request)).toMessage(); } @@ -239,68 +229,4 @@ public class WxV3PayController { } - /** - * 转账到余额 - * - * @param order 转账订单 - * @return 对应的转账结果 - */ - @RequestMapping("transfer") - public Map transfer(TransferOrder order) { - order.setOutNo("partner_trade_no 商户转账订单号"); - order.setPayeeAccount("用户openid"); - order.setPayeeName("收款用户姓名, 非必填,如果填写将强制验证收款人姓名"); - order.setRemark("转账备注, 非必填"); - order.setAmount(new BigDecimal(10)); - - //转账到余额,这里默认值是转账到银行卡 - order.setTransferType(WxTransferType.TRANSFERS); - - return service.transfer(order); - } - - - /** - * 转账到银行卡 - * - * @param order 转账订单 - * @return 对应的转账结果 - */ - @RequestMapping("transferPayBank") - public Map transferPayBank(TransferOrder order) { - order.setOutNo("partner_trade_no 商户转账订单号"); - //采用标准RSA算法,公钥由微信侧提供,将公钥信息配置在PayConfigStorage#setKeyPublic(String) - order.setPayeeAccount("enc_bank_no 收款方银行卡号"); - order.setPayeeName("收款方用户名"); - order.setBank(WxBank.ABC); - order.setRemark("转账备注, 非必填"); - order.setAmount(new BigDecimal(10)); - //转账到银行卡,这里默认值是转账到银行卡 - order.setTransferType(WxTransferType.PAY_BANK); - - return service.transfer(order); - } - - /** - * 转账查询 - * - * @param outNo 商户转账订单号 - * @param wxTransferType 微信转账类型, - * .....这里没办法了只能这样写(┬_┬),请见谅 - * {@link WxTransferType#QUERY_BANK} - * {@link WxTransferType#GETTRANSFERINFO} - * - *

- * 企业付款到零钱 - * 商户企业付款到银行卡 - *

- * @return 对应的转账订单 - */ - @RequestMapping("transferQuery") - public Map transferQuery(String outNo, String wxTransferType) { - //默认查询银行卡的记录 com.egzosn.pay.wx.v3.bean.WxTransferType#QUERY_BANK - return service.transferQuery(outNo, wxTransferType); - } - - } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3CombinePayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3CombinePayMessageHandler.java new file mode 100644 index 0000000..cdbcd0c --- /dev/null +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3CombinePayMessageHandler.java @@ -0,0 +1,29 @@ +package com.egzosn.pay.demo.service.handler; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.fastjson.JSON; +import com.egzosn.pay.common.api.DefaultPayMessageHandler; +import com.egzosn.pay.common.api.PayMessageHandler; +import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.wx.v3.bean.combine.CombinePayMessage; + +/** + * 微信合单支付回调处理器 + * Created by ZaoSheng on 2016/6/1. + */ +public class WxV3CombinePayMessageHandler implements PayMessageHandler { + + private final Logger LOG = LoggerFactory.getLogger(DefaultPayMessageHandler.class); + + @Override + public PayOutMessage handle(CombinePayMessage payMessage, Map context, PayService payService) throws PayErrorException { + LOG.info("回调支付消息处理器,回调消息:{}", JSON.toJSONString(payMessage)); + return payService.successPayOutMessage(payMessage); + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineAmount.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineAmount.java index f3d812b..8a677f6 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineAmount.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineAmount.java @@ -21,6 +21,12 @@ public class CombineAmount extends Amount { @JSONField(name = "total_amount") private Integer totalAmount; + public CombineAmount() { + } + + public CombineAmount(Integer totalAmount) { + this.totalAmount = totalAmount; + } public Integer getTotalAmount() { return totalAmount; diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineSubOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineSubOrder.java index ee66a24..3fd4f14 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineSubOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineSubOrder.java @@ -16,6 +16,15 @@ public class CombineSubOrder { * 子单发起方商户号,必填,必须与发起方appid有绑定关系。 */ private String mchid; + /** + * 二级商户商户号,由微信支付生成并下发。 + *

+ * 服务商子商户的商户号,被合单方。 + *

+ * 直连商户不用传二级商户号。 + */ + @JSONField(name = "sub_mchid") + private String subMchid; /** * 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。 @@ -32,6 +41,14 @@ public class CombineSubOrder { this.mchid = mchid; } + public String getSubMchid() { + return subMchid; + } + + public void setSubMchid(String subMchid) { + this.subMchid = subMchid; + } + public String getOutTradeNo() { return outTradeNo; } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java index 8574451..ae15d18 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java @@ -23,7 +23,7 @@ public class Amount { * 货币类型 CNY:人民币,境内商户号仅支持人民币。 * {@link com.egzosn.pay.common.bean.CurType} */ - private String currency; + private String currency = DefaultCurType.CNY.getType(); public Amount() { } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/H5Info.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/H5Info.java index 03e5f71..5568533 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/H5Info.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/H5Info.java @@ -41,10 +41,15 @@ public class H5Info { @JSONField(name = "package_name") private String packageName; + + public H5Info() { this.type = "Wap"; } + public H5Info(String type) { + this.type = type; + } public H5Info(String appName, String appUrl) { this(); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SubOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SubOrder.java index 0b0010d..5a579d8 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SubOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SubOrder.java @@ -32,15 +32,7 @@ public class SubOrder extends CombineSubOrder { private String description; - /** - * 二级商户商户号,由微信支付生成并下发。 - *

- * 服务商子商户的商户号,被合单方。 - *

- * 直连商户不用传二级商户号。 - */ - @JSONField(name = "sub_mchid") - private String subMchid; + /** * 结算信息,选填 @@ -113,13 +105,6 @@ public class SubOrder extends CombineSubOrder { } - public String getSubMchid() { - return subMchid; - } - - public void setSubMchid(String subMchid) { - this.subMchid = subMchid; - } public SettleInfo getSettleInfo() { return settleInfo;