src/main/java/cc/mrbird/febs/common/configure/WebMvcConfigure.java
@@ -29,5 +29,6 @@ registration.excludePathPatterns("/api/leader/noLoginLeaderTitle"); registration.excludePathPatterns("/api/xcxPay/wxpayCallback"); registration.excludePathPatterns("/api/xcxPay/rechargeCallBack"); registration.excludePathPatterns("/api/xcxPay/xcxCodePayCallBack"); } } src/main/java/cc/mrbird/febs/mall/controller/AdminSystemController.java
@@ -3,6 +3,8 @@ import cc.mrbird.febs.common.entity.FebsResponse; import cc.mrbird.febs.common.enumerates.DataDictionaryEnum; import cc.mrbird.febs.mall.dto.CashOutSettingDto; import cc.mrbird.febs.mall.dto.XcxSettingDto; import cc.mrbird.febs.mall.mapper.DataDictionaryCustomMapper; import cc.mrbird.febs.mall.service.ICommonService; import cc.mrbird.febs.mall.service.ISystemService; import lombok.RequiredArgsConstructor; @@ -27,6 +29,7 @@ private ISystemService systemService; private final ICommonService commonService; private final DataDictionaryCustomMapper dictionaryCustomMapper; @PostMapping(value = "/bonusSystemSetting") public FebsResponse bonusSystemSetting(@RequestBody Map<String, Object> map) { @@ -39,4 +42,29 @@ commonService.addDataDic(DataDictionaryEnum.CASHOUT_SETTING.getType(), DataDictionaryEnum.CASHOUT_SETTING.getCode(), cashOutSettingDto, "提现设置"); return new FebsResponse().success(); } @PostMapping(value = "/xcxSetting") public FebsResponse cashOutSetting(XcxSettingDto xcxSettingDto) { dictionaryCustomMapper.updateDicValueByTypeAndCode( DataDictionaryEnum.XCX_APP_ID.getType(), DataDictionaryEnum.XCX_APP_ID.getCode(), xcxSettingDto.getAppId()); dictionaryCustomMapper.updateDicValueByTypeAndCode( DataDictionaryEnum.XCX_MCH_ID.getType(), DataDictionaryEnum.XCX_MCH_ID.getCode(), xcxSettingDto.getMchId()); dictionaryCustomMapper.updateDicValueByTypeAndCode( DataDictionaryEnum.XCX_MCH_KEY.getType(), DataDictionaryEnum.XCX_MCH_KEY.getCode(), xcxSettingDto.getMchKey()); dictionaryCustomMapper.updateDicValueByTypeAndCode( DataDictionaryEnum.XCX_MCH_KEY_PATH_P12.getType(), DataDictionaryEnum.XCX_MCH_KEY_PATH_P12.getCode(), xcxSettingDto.getKeyPath()); dictionaryCustomMapper.updateDicValueByTypeAndCode( DataDictionaryEnum.XCX_NOTICE_URL.getType(), DataDictionaryEnum.XCX_NOTICE_URL.getCode(), xcxSettingDto.getNotifyUrl()); return new FebsResponse().success(); } } src/main/java/cc/mrbird/febs/mall/controller/ViewSystemController.java
@@ -5,6 +5,7 @@ import cc.mrbird.febs.common.utils.FebsUtil; import cc.mrbird.febs.mall.dto.CashOutSettingDto; import cc.mrbird.febs.mall.dto.ScoreSettingDto; import cc.mrbird.febs.mall.dto.XcxSettingDto; import cc.mrbird.febs.mall.entity.DataDictionaryCustom; import cc.mrbird.febs.mall.mapper.DataDictionaryCustomMapper; import com.alibaba.fastjson.JSONObject; @@ -40,7 +41,35 @@ @GetMapping("xcxSetting") public String xcxSetting() { @RequiresPermissions("xcxSetting:update") public String xcxSetting(Model model) { String appId = dataDictionaryCustomMapper.selectDicDataByTypeAndCode( DataDictionaryEnum.XCX_APP_ID.getType(), DataDictionaryEnum.XCX_APP_ID.getCode() ).getValue(); String mchId = dataDictionaryCustomMapper.selectDicDataByTypeAndCode( DataDictionaryEnum.XCX_MCH_ID.getType(), DataDictionaryEnum.XCX_MCH_ID.getCode() ).getValue(); String mchKey = dataDictionaryCustomMapper.selectDicDataByTypeAndCode( DataDictionaryEnum.XCX_MCH_KEY.getType(), DataDictionaryEnum.XCX_MCH_KEY.getCode() ).getValue(); String keyPath = dataDictionaryCustomMapper.selectDicDataByTypeAndCode( DataDictionaryEnum.XCX_MCH_KEY_PATH_P12.getType(), DataDictionaryEnum.XCX_MCH_KEY_PATH_P12.getCode() ).getValue(); String notifyUrl = dataDictionaryCustomMapper.selectDicDataByTypeAndCode( DataDictionaryEnum.XCX_NOTICE_URL.getType(), DataDictionaryEnum.XCX_NOTICE_URL.getCode() ).getValue(); XcxSettingDto xcxSettingDto = new XcxSettingDto(); xcxSettingDto.setAppId(appId); xcxSettingDto.setMchId(mchId); xcxSettingDto.setMchKey(mchKey); xcxSettingDto.setKeyPath(keyPath); xcxSettingDto.setNotifyUrl(notifyUrl); model.addAttribute("xcxSetting", xcxSettingDto); return FebsUtil.view("modules/system/xcxSetting"); } } src/main/java/cc/mrbird/febs/mall/dto/XcxSettingDto.java
New file @@ -0,0 +1,12 @@ package cc.mrbird.febs.mall.dto; import lombok.Data; @Data public class XcxSettingDto { private String appId; private String mchId; private String mchKey; private String notifyUrl; private String keyPath; } src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallAppServiceImpl.java
@@ -324,8 +324,17 @@ @Override public FebsResponse wxCodePay(ApiWxCodePayDto wxCodePayDto) { Long orderId = wxCodePayDto.getOrderId(); MallOrderInfo mallOrderInfo = mallOrderInfoMapper.selectById(orderId); String wxPayCode = wxCodePayDto.getWxPayCode(); /** * auth_code 扫码支付付款码 微信付款码规则:18位纯数字,前缀以10、11、12、13、14、15开头 */ String regex = "^(10|11|12|13|14|15)\\d{16}$"; boolean flag = wxPayCode.matches(regex); if(!flag){ throw new FebsException("请打开微信付款码!"); } MallOrderInfo mallOrderInfo = mallOrderInfoMapper.selectById(orderId); mallOrderInfo.setTakeCode(wxPayCode); List<ApiGetOrderOrderItemVo> appOrderItemByOrderId = mallOrderItemMapper.getAppOrderItemByOrderId(orderId); if(CollUtil.isEmpty(appOrderItemByOrderId)){ src/main/java/cc/mrbird/febs/pay/configure/WxPayConfiguration.java
@@ -1,7 +1,6 @@ package cc.mrbird.febs.pay.configure; import cc.mrbird.febs.common.enumerates.DataDictionaryEnum; import cc.mrbird.febs.mall.entity.DataDictionaryCustom; import cc.mrbird.febs.mall.mapper.DataDictionaryCustomMapper; import cn.hutool.core.util.StrUtil; import com.github.binarywang.wxpay.config.WxPayConfig; src/main/java/cc/mrbird/febs/pay/controller/XcxPayController.java
@@ -73,11 +73,11 @@ @Autowired private IApiMallMemberWalletService memberWalletService; @Autowired private WxPayService wxPayService; @Autowired private IXcxCodePayService xcxCodePayService; // @Autowired // private WxPayService wxPayService; // // @Autowired // private IXcxCodePayService xcxCodePayService; @Autowired private IXcxPayService iXcxPayService; @@ -299,13 +299,13 @@ } @PostMapping(value = "/xcxCodePayCallBack") public String xcxCodePayCallBack(@RequestBody String xmlData) throws WxPayException { log.info("=======微信支付码付款回调:{}========", xmlData); WxPayOrderNotifyResult notifyResult = this.wxPayService.parseOrderNotifyResult(xmlData); xcxCodePayService.payCallback(notifyResult.toMap()); return WxPayNotifyResponse.success("成功"); } // @PostMapping(value = "/xcxCodePayCallBack") // public String xcxCodePayCallBack(@RequestBody String xmlData) throws WxPayException { // log.info("=======微信支付码付款回调:{}========", xmlData); // WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData); // xcxCodePayService.payCallback(notifyResult.toMap()); // return WxPayNotifyResponse.success("成功"); // } } src/main/java/cc/mrbird/febs/pay/service/impl/xcxCodePayServiceImpl.java
@@ -1,33 +1,39 @@ package cc.mrbird.febs.pay.service.impl; import cc.mrbird.febs.common.enumerates.DataDictionaryEnum; import cc.mrbird.febs.common.enumerates.OrderStatusEnum; import cc.mrbird.febs.common.exception.FebsException; import cc.mrbird.febs.mall.entity.MallMember; import cc.mrbird.febs.mall.entity.MallOrderInfo; import cc.mrbird.febs.mall.mapper.DataDictionaryCustomMapper; import cc.mrbird.febs.mall.mapper.MallOrderInfoMapper; import cc.mrbird.febs.pay.model.BrandWCPayRequestData; import cc.mrbird.febs.pay.model.JsApiPayBusiness; import cc.mrbird.febs.pay.model.JsApiPayReqData; import cc.mrbird.febs.pay.model.JsApiPayResData; import cc.mrbird.febs.pay.service.IXcxCodePayService; import cc.mrbird.febs.pay.util.RandomStringGenerator; import cc.mrbird.febs.pay.util.Signature; import cc.mrbird.febs.pay.util.WebUtil; import cn.hutool.core.date.DateField; import cn.hutool.core.date.DatePattern; import cc.mrbird.febs.pay.util.WeixinServiceUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult; import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryRequest; import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult; import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult; import com.github.binarywang.wxpay.config.WxPayConfig; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.WxPayService; import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.HashMap; import java.util.List; import java.util.Map; @Slf4j @@ -36,37 +42,121 @@ public class xcxCodePayServiceImpl implements IXcxCodePayService { private final MallOrderInfoMapper mallOrderInfoMapper; private final WxPayService wxService; private final DataDictionaryCustomMapper dataDictionaryCustomMapper; private final WeixinServiceUtil weixinServiceUtil; private static final String PAY_SUCCESS = "SUCCESS"; private static final String PAY_USERPAYING = "USERPAYING"; @Override public String pay(MallOrderInfo mallOrderInfo) { WxPayMicropayRequest request = new WxPayMicropayRequest(); request request.setAppid(wxPayProperties.getAppId()); request.setMchId(wxPayProperties.getMchId()); request.setNotifyUrl(wxPayProperties.getNoticeUrl()); request.setBody(orderInfo.getName()); request.setOutTradeNo(orderInfo.getOrderNo()); request.setTimeExpire(DateUtil.format(DateUtil.offset(new Date(), DateField.MINUTE, 15), DatePattern.PURE_DATETIME_PATTERN)); request.setSpbillCreateIp("127.0.0.1"); request.setTradeType("APP"); // if ("dev".equals(active) || "test".equals(active)) { if ("dev".equals(active)) { request.setTotalFee(1); } else { request.setTotalFee(orderInfo.getAmount().add(orderInfo.getCarriage()).multiply(new BigDecimal(100)).intValue()); } String appId = dataDictionaryCustomMapper.selectDicDataByTypeAndCode( DataDictionaryEnum.XCX_APP_ID.getType(), DataDictionaryEnum.XCX_APP_ID.getCode() ).getValue(); String mchId = dataDictionaryCustomMapper.selectDicDataByTypeAndCode( DataDictionaryEnum.XCX_MCH_ID.getType(), DataDictionaryEnum.XCX_MCH_ID.getCode() ).getValue(); String paySecret = dataDictionaryCustomMapper.selectDicDataByTypeAndCode( DataDictionaryEnum.XCX_MCH_KEY.getType(), DataDictionaryEnum.XCX_MCH_KEY.getCode() ).getValue(); String keyPath = dataDictionaryCustomMapper.selectDicDataByTypeAndCode( DataDictionaryEnum.XCX_MCH_KEY_PATH_P12.getType(), DataDictionaryEnum.XCX_MCH_KEY_PATH_P12.getCode() ).getValue(); String notifyUrl = dataDictionaryCustomMapper.selectDicDataByTypeAndCode( DataDictionaryEnum.XCX_NOTICE_URL.getType(), DataDictionaryEnum.XCX_NOTICE_URL.getCode() ).getValue(); WxPayConfig payConfig = new WxPayConfig(); payConfig.setAppId(StrUtil.trim(appId)); payConfig.setMchId(StrUtil.trim(mchId)); payConfig.setMchKey(StrUtil.trim(paySecret)); payConfig.setKeyPath(StrUtil.trim(keyPath)); payConfig.setNotifyUrl(StrUtil.trim(notifyUrl)); // 可以指定是否使用沙箱环境 payConfig.setUseSandboxEnv(false); WxPayService wxPayService = new WxPayServiceImpl(); wxPayService.setConfig(payConfig); WxPayMicropayRequest request = new WxPayMicropayRequest(); /** * Map<String, String> map = new HashMap<>(16); map.put("attach", "订单额外描述"); map.put("auth_code", auth_code); map.put("body", "付款码支付测试"); map.put("device_info", "1000"); map.put("nonce_str", WXPayUtil.generateNonceStr()); map.put("out_trade_no", out_trade_no); map.put("spbill_create_ip", "14.17.22.52"); map.put("total_fee", "2"); //生成签名 String sign = WXPayUtil.generateSignature(map, config.getKey()); map.put("sign", sign); */ request.setAppid(appId); request.setMchId(mchId); request.setNonceStr(RandomStringGenerator.getRandomStringByLength(32)); request.setBody(mallOrderInfo.getName()); request.setOutTradeNo(mallOrderInfo.getOrderNo()); int totalFee = mallOrderInfo.getAmount().subtract(mallOrderInfo.getCarriage()).multiply(new BigDecimal(100)).intValue(); request.setTotalFee(totalFee); request.setSpbillCreateIp(weixinServiceUtil.getIpAddr(WebUtil.getRequest())); request.setAuthCode(mallOrderInfo.getTakeCode()); try { WxPayMicropayResult micropay = wxService.micropay(request); return JSONObject.toJSONString(micropay); request.setSign(Signature.getSign(request,paySecret)); } catch (IllegalAccessException e) { e.printStackTrace(); throw new FebsException("签名异常"); } try { WxPayMicropayResult micropay = wxPayService.micropay(request); String returnCode = micropay.getReturnCode(); String resultCode = micropay.getResultCode(); String errCode = micropay.getErrCode(); String outTradeNo = micropay.getOutTradeNo(); String errCodeDes = micropay.getErrCodeDes(); /** * 交易成功判断条件:return_code和result_code都为SUCCESS且trade_type为MICROPAY */ if(PAY_SUCCESS.equals(returnCode) && PAY_SUCCESS.equals(resultCode)){ log.info("微信免密支付成功"); return PAY_SUCCESS; } else if (PAY_USERPAYING.equals(errCode)){//等待用户输入密码 for(int i = 0; i < 4; i++){ try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } //调用微信的查询接口 /** * 交易成功判断条件: return_code、result_code和trade_state都为SUCCESS */ WxPayOrderQueryRequest wxPayOrderQueryRequest = new WxPayOrderQueryRequest(); wxPayOrderQueryRequest.setOutTradeNo(outTradeNo); WxPayOrderQueryResult wxPayOrderQueryResult = wxPayService.queryOrder(wxPayOrderQueryRequest); String returnCodePaying = wxPayOrderQueryResult.getReturnCode(); String resultCodePaying = wxPayOrderQueryResult.getResultCode(); String tradeTypePaying = wxPayOrderQueryResult.getTradeType(); if(PAY_SUCCESS.equals(tradeTypePaying)&& PAY_SUCCESS.equals(returnCodePaying)&& PAY_SUCCESS.equals(resultCodePaying)){ log.info("微信加密支付成功!"); return PAY_SUCCESS; } log.info("正在支付" + wxPayOrderQueryResult.getDetail()); } } log.error("微信支付失败!"); return errCodeDes; } catch (WxPayException e) { e.printStackTrace(); throw new FebsException("支付异常, 请检查"); throw new FebsException("支付异常"); } return null; } @Override src/main/resources/templates/febs/views/modules/system/xcxSetting.html
@@ -1,36 +1,40 @@ <div class="layui-fluid layui-anim febs-anim" id="xcx-setting" lay-title="小程序设置"> <div class="layui-fluid layui-anim febs-anim" id="xcx-setting" lay-title="微信收款码支付设置"> <div class="layui-row layui-col-space8 febs-container"> <form class="layui-form" action="" lay-filter="xcx-setting-form"> <div class="layui-card"> <div class="layui-card-body"> <div class="layui-form-item"> <label class="layui-form-label ">每次最小提现金额:</label> <label class="layui-form-label ">APPID:</label> <div class="layui-input-block"> <input type="text" name="minCashOut" data-th-id="${cashout.minCashOut}" lay-verify="number" autocomplete="off" class="layui-input" > <input type="text" name="appId" lay-verify="text" autocomplete="off" class="layui-input" > </div> </div> <div class="layui-form-item"> <label class="layui-form-label ">每日提现上限:</label> <label class="layui-form-label ">MCHID:</label> <div class="layui-input-block"> <input type="text" name="allCashOut" data-th-id="${cashout.allCashOut}" lay-verify="number" autocomplete="off" class="layui-input" > <input type="text" name="mchId" lay-verify="text" autocomplete="off" class="layui-input" > </div> <div class="layui-word-aux" style="margin-left: 150px;">商户号</div> </div> <div class="layui-form-item"> <label class="layui-form-label ">提现手续费:</label> <label class="layui-form-label ">MCHKEY:</label> <div class="layui-input-block"> <input type="text" name="serviceFee" data-th-id="${cashout.serviceFee}" lay-verify="text" autocomplete="off" class="layui-input" > <input type="text" name="mchKey" lay-verify="text" autocomplete="off" class="layui-input" > </div> <div class="layui-word-aux" style="margin-left: 150px;">提现手续费:直接输入百分比,例如25%,直接输入25</div> <div class="layui-word-aux" style="margin-left: 150px;">商户支付密钥</div> </div> <div class="layui-form-item"> <label class="layui-form-label">提现时间:</label> <label class="layui-form-label">证书地址:</label> <div class="layui-input-block"> <input type="radio" name="workingDays" value="1" title="工作日"> <input type="radio" name="workingDays" value="2" title="每一天"> <input type="text" name="keyPath" lay-verify="text" autocomplete="off" class="layui-input" > </div> <div class="layui-word-aux" style="margin-left: 150px;">证书地址p12,服务器路径</div> </div> <div class="layui-form-item"> <label class="layui-form-label">回调地址:</label> <div class="layui-input-block"> <input type="text" name="notifyUrl" lay-verify="text" autocomplete="off" class="layui-input" > </div> </div> </div> @@ -60,28 +64,29 @@ var $ = layui.jquery, febs = layui.febs, form = layui.form, cashout = [[${cashout}]], xcxSetting = [[${xcxSetting}]], validate = layui.validate, $view = $('#xcx-setting'); form.verify(validate); initCashoutValue(); xcxSettingValue(); form.render(); function initCashoutValue() { function xcxSettingValue() { form.val("xcx-setting-form", { "minCashOut": cashout.minCashOut, "allCashOut": cashout.allCashOut, "serviceFee": cashout.serviceFee, "workingDays": cashout.workingDays "appId": xcxSetting.appId, "mchId": xcxSetting.mchId, "mchKey": xcxSetting.mchKey, "keyPath": xcxSetting.keyPath, "notifyUrl": xcxSetting.notifyUrl }); } form.on('submit(xcx-setting-form-submit)', function (data) { console.log(data); febs.post(ctx + 'admin/system/cashOutSetting', data.field, function (res) { febs.post(ctx + 'admin/system/xcxSetting', data.field, function (res) { febs.alert.success('设置成功'); }); window.location.reload(); src/test/java/cc/mrbird/febs/PayTest.java
@@ -17,17 +17,17 @@ @SpringBootTest public class PayTest { @Test public void aliPay() throws AlipayApiException { AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); model.setBody("这是测试用"); model.setSubject("支付测试用的"); model.setOutTradeNo("2021092712215802974"); model.setTimeoutExpress("15m"); model.setTotalAmount("0.01"); model.setPassbackParams("callback params"); model.setProductCode("QUICK_MSECURITY_PAY"); AlipayTradeAppPayResponse resp = AliPayApi.appPayToResponse(model, ""); log.info("=====>>>{}", JSONObject.toJSONString(resp)); } // @Test // public void aliPay() throws AlipayApiException { // AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); // model.setBody("这是测试用"); // model.setSubject("支付测试用的"); // model.setOutTradeNo("2021092712215802974"); // model.setTimeoutExpress("15m"); // model.setTotalAmount("0.01"); // model.setPassbackParams("callback params"); // model.setProductCode("QUICK_MSECURITY_PAY"); // AlipayTradeAppPayResponse resp = AliPayApi.appPayToResponse(model, ""); // log.info("=====>>>{}", JSONObject.toJSONString(resp)); // } }