Pārlūkot izejas kodu

加入物业费开发功能

wuxw 6 gadi atpakaļ
vecāks
revīzija
fa3af86b30

+ 18 - 12
Api/src/main/java/com/java110/api/listener/fee/PayFeePreListener.java

@@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 
 import java.util.Calendar;
@@ -93,9 +94,14 @@ public class PayFeePreListener extends AbstractServiceApiDataFlowListener {
         super.freshHttpHeader(header, dataFlowContext.getRequestCurrentHeaders());
 
         ResponseEntity<String> responseEntity = this.callService(dataFlowContext, service.getServiceCode(), paramInObj);
+        if(responseEntity.getStatusCode() != HttpStatus.OK){
+            dataFlowContext.setResponseEntity(responseEntity);
+            return ;
+        }
 
+        JSONObject paramOut = JSONObject.parseObject(responseEntity.getBody());
+        paramOut.put("receivableAmount", paramObj.getString("receivableAmount"));
         dataFlowContext.setResponseEntity(responseEntity);
-
     }
 
     /**
@@ -105,10 +111,11 @@ public class PayFeePreListener extends AbstractServiceApiDataFlowListener {
      * @param headers 头部信息
      */
     protected void freshOrderProtocol(JSONObject orders, Map<String, String> headers) {
-        super.freshOrderProtocol(orders,headers);
+        super.freshOrderProtocol(orders, headers);
         orders.put("orderProcess", Orders.ORDER_PROCESS_ORDER_PRE_SUBMIT);
 
     }
+
     /**
      * 添加费用明细信息
      *
@@ -133,19 +140,19 @@ public class PayFeePreListener extends AbstractServiceApiDataFlowListener {
         feeDto.setCommunityId(paramInJson.getString("communityId"));
         List<FeeDto> feeDtos = feeInnerServiceSMOImpl.queryFees(feeDto);
 
-        if (feeDtos == null || feeDtos.size() != 1){
+        if (feeDtos == null || feeDtos.size() != 1) {
             throw new ListenerExecuteException(ResponseConstant.RESULT_CODE_ERROR, "查询费用信息失败,未查到数据或查到多条数据");
         }
 
         feeDto = feeDtos.get(0);
-        paramInJson.put("feeInfo",feeDto);
+        paramInJson.put("feeInfo", feeDto);
 
 
         FeeConfigDto feeConfigDto = new FeeConfigDto();
         feeConfigDto.setFeeTypeCd(feeDto.getFeeTypeCd());
         feeConfigDto.setCommunityId(feeDto.getCommunityId());
         List<FeeConfigDto> feeConfigDtos = feeConfigInnerServiceSMOImpl.queryFeeConfigs(feeConfigDto);
-        if (feeConfigDtos == null || feeConfigDtos.size() != 1){
+        if (feeConfigDtos == null || feeConfigDtos.size() != 1) {
             throw new ListenerExecuteException(ResponseConstant.RESULT_CODE_ERROR, "未查到费用配置信息,查询多条数据");
         }
 
@@ -153,7 +160,7 @@ public class PayFeePreListener extends AbstractServiceApiDataFlowListener {
         String builtUpArea = "0.00";
 
         //物业费时 需要建筑面积 但是停车费不需要建筑面积
-        if(FeeTypeConstant.FEE_TYPE_PROPERTY.equals(feeConfigDto.getFeeTypeCd())) {
+        if (FeeTypeConstant.FEE_TYPE_PROPERTY.equals(feeConfigDto.getFeeTypeCd())) {
 
             RoomDto roomDto = new RoomDto();
             roomDto.setRoomId(feeDto.getPayerObjId());
@@ -167,15 +174,14 @@ public class PayFeePreListener extends AbstractServiceApiDataFlowListener {
         }
 
 
-
         double receivableAmount = Double.parseDouble(feeConfigDto.getSquarePrice())
-                                    * Double.parseDouble(builtUpArea)
-                                    + Double.parseDouble(feeConfigDto.getAdditionalAmount());
+                * Double.parseDouble(builtUpArea)
+                + Double.parseDouble(feeConfigDto.getAdditionalAmount());
         receivableAmount = Double.parseDouble(paramInJson.getString("cycles")) * receivableAmount;
 
         businessFeeDetail.put("receivableAmount", receivableAmount);
         business.getJSONObject(CommonConstant.HTTP_BUSINESS_DATAS).put("businessFeeDetail", businessFeeDetail);
-
+        paramInJson.put("receivableAmount", receivableAmount);
         return business;
     }
 
@@ -192,10 +198,10 @@ public class PayFeePreListener extends AbstractServiceApiDataFlowListener {
 
         JSONObject business = JSONObject.parseObject("{\"datas\":{}}");
         business.put(CommonConstant.HTTP_BUSINESS_TYPE_CD, BusinessTypeConstant.BUSINESS_TYPE_UPDATE_FEE_INFO);
-        business.put(CommonConstant.HTTP_SEQ, DEFAULT_SEQ+1);
+        business.put(CommonConstant.HTTP_SEQ, DEFAULT_SEQ + 1);
         business.put(CommonConstant.HTTP_INVOKE_MODEL, CommonConstant.HTTP_INVOKE_MODEL_S);
         JSONObject businessFee = new JSONObject();
-        FeeDto feeInfo = (FeeDto)paramInJson.get("feeInfo");
+        FeeDto feeInfo = (FeeDto) paramInJson.get("feeInfo");
         Date endTime = feeInfo.getEndTime();
         Calendar endCalender = Calendar.getInstance();
         endCalender.setTime(endTime);

+ 21 - 2
AppFrontService/src/main/java/com/java110/app/controller/PaymentController.java

@@ -1,9 +1,13 @@
 package com.java110.app.controller;
 
 import com.java110.app.smo.complaint.ISaveComplaintSMO;
+import com.java110.app.smo.payment.IToNotifySMO;
+import com.java110.app.smo.payment.IToPaySMO;
 import com.java110.core.base.controller.BaseController;
 import com.java110.core.context.IPageData;
+import com.java110.core.context.PageData;
 import com.java110.utils.constant.CommonConstant;
+import com.java110.utils.util.PayUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -14,6 +18,9 @@ import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.servlet.http.HttpServletRequest;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.Map;
 
 /**
  * 支付 处理类
@@ -23,7 +30,11 @@ import javax.servlet.http.HttpServletRequest;
 public class PaymentController extends BaseController {
     private final static Logger logger = LoggerFactory.getLogger(PaymentController.class);
 
+    @Autowired
+    private IToPaySMO toPaySMOImpl;
 
+    @Autowired
+    private IToNotifySMO toNotifySMOImpl;
 
     /**
      * <p>统一下单入口</p>
@@ -33,7 +44,12 @@ public class PaymentController extends BaseController {
      */
     @RequestMapping(path = "/toPay", method = RequestMethod.POST)
     public ResponseEntity<String> toPay(@RequestBody String postInfo, HttpServletRequest request) {
-        return null;
+        IPageData pd = (IPageData) request.getAttribute(CommonConstant.CONTEXT_PAGE_DATA);
+        /*IPageData pd = (IPageData) request.getAttribute(CommonConstant.CONTEXT_PAGE_DATA);*/
+        IPageData newPd = PageData.newInstance().builder(pd.getUserId(), pd.getUserName(), pd.getToken(), postInfo,
+                "", "", "", pd.getSessionId(),
+                request.getHeader("APP_ID"));
+        return toPaySMOImpl.toPay(newPd);
     }
 
 
@@ -45,7 +61,10 @@ public class PaymentController extends BaseController {
      */
     @RequestMapping(path = "/notify", method = RequestMethod.POST)
     public ResponseEntity<String> notify(@RequestBody String postInfo, HttpServletRequest request) {
-        return null;
+
+        return toNotifySMOImpl.toNotify(request);
+
+
     }
 
 }

+ 46 - 0
AppFrontService/src/main/java/com/java110/app/properties/WechatAuthProperties.java

@@ -10,10 +10,24 @@ import org.springframework.stereotype.Component;
 @PropertySource("classpath:wechatAuth.properties")
 public class WechatAuthProperties {
 
+    //微信支付类型
+//NATIVE--原生支付
+//JSAPI--公众号支付-小程序支付
+//MWEB--H5支付
+//APP -- app支付
+    public static final String TRADE_TYPE_NATIVE = "NATIVE";
+    public static final String TRADE_TYPE_JSAPI = "JSAPI";
+    public static final String TRADE_TYPE_MWEB = "MWEB";
+    public static final String TRADE_TYPE_APP = "APP";
+
     private String sessionHost;
     private String appId;
     private String secret;
     private String grantType;
+    private String key;
+    private String mchId;
+    private String wxPayUnifiedOrder;
+    private String wxNotifyUrl;
 
     public String getSessionHost() {
         return sessionHost;
@@ -46,4 +60,36 @@ public class WechatAuthProperties {
     public void setGrantType(String grantType) {
         this.grantType = grantType;
     }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getMchId() {
+        return mchId;
+    }
+
+    public void setMchId(String mchId) {
+        this.mchId = mchId;
+    }
+
+    public String getWxPayUnifiedOrder() {
+        return wxPayUnifiedOrder;
+    }
+
+    public void setWxPayUnifiedOrder(String wxPayUnifiedOrder) {
+        this.wxPayUnifiedOrder = wxPayUnifiedOrder;
+    }
+
+    public String getWxNotifyUrl() {
+        return wxNotifyUrl;
+    }
+
+    public void setWxNotifyUrl(String wxNotifyUrl) {
+        this.wxNotifyUrl = wxNotifyUrl;
+    }
 }

+ 19 - 0
AppFrontService/src/main/java/com/java110/app/smo/payment/IToNotifySMO.java

@@ -0,0 +1,19 @@
+package com.java110.app.smo.payment;
+
+import com.java110.core.context.IPageData;
+import org.springframework.http.ResponseEntity;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 微信通知 支付完成
+ */
+public interface IToNotifySMO {
+
+    /**
+     * 支付完成
+     * @param request
+     * @return
+     */
+    public ResponseEntity<String> toNotify(HttpServletRequest request);
+}

+ 175 - 0
AppFrontService/src/main/java/com/java110/app/smo/payment/impl/ToNotifySMOImpl.java

@@ -0,0 +1,175 @@
+package com.java110.app.smo.payment.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.java110.app.properties.WechatAuthProperties;
+import com.java110.app.smo.AppAbstractComponentSMO;
+import com.java110.app.smo.payment.IToNotifySMO;
+import com.java110.app.smo.payment.IToPaySMO;
+import com.java110.core.base.smo.BaseServiceSMO;
+import com.java110.core.context.IPageData;
+import com.java110.utils.constant.CommonConstant;
+import com.java110.utils.constant.ServiceConstant;
+import com.java110.utils.util.Assert;
+import com.java110.utils.util.DateUtil;
+import com.java110.utils.util.PayUtil;
+import com.java110.utils.util.StringUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.*;
+import org.springframework.web.client.HttpStatusCodeException;
+import org.springframework.web.client.RestTemplate;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.*;
+
+public class ToNotifySMOImpl implements IToNotifySMO {
+    private static final Logger logger = LoggerFactory.getLogger(AppAbstractComponentSMO.class);
+
+    private static final String APP_ID = "";
+
+
+    @Autowired
+    private RestTemplate restTemplate;
+
+    @Autowired
+    private WechatAuthProperties wechatAuthProperties;
+
+    @Override
+    public ResponseEntity<String> toNotify(HttpServletRequest request) {
+        String resXml = "";
+        ResponseEntity responseEntity = null;
+        try {
+            InputStream inputStream = request.getInputStream();
+//获取请求输入流
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            byte[] buffer = new byte[1024];
+            int len = 0;
+            while ((len = inputStream.read(buffer)) != -1) {
+                outputStream.write(buffer, 0, len);
+            }
+            outputStream.close();
+            inputStream.close();
+            Map<String, Object> map = PayUtil.getMapFromXML(new String(outputStream.toByteArray(), "utf-8"));
+            logger.info("【小程序支付回调】 回调数据: \n" + map);
+            String returnCode = (String) map.get("return_code");
+            if ("SUCCESS".equalsIgnoreCase(returnCode)) {
+                String returnmsg = (String) map.get("result_code");
+                if ("SUCCESS".equals(returnmsg)) {
+//更新数据
+
+                    int result = confirmPayFee(map);
+
+                    if (result > 0) {
+//支付成功
+                        resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+                                + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml>";
+                    }
+                } else {
+                    resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+                            + "<return_msg><![CDATA[报文为空]></return_msg>" + "</xml>";
+                    logger.info("支付失败:" + resXml);
+                }
+            } else {
+                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+                        + "<return_msg><![CDATA[报文为空]></return_msg>" + "</xml>";
+                logger.info("【订单支付失败】");
+            }
+        } catch (Exception e) {
+            logger.error("通知失败", e);
+            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+                    + "<return_msg><![CDATA[鉴权失败]></return_msg>" + "</xml>";
+        }
+
+        logger.info("【小程序支付回调响应】 响应内容:\n" + resXml);
+        return new ResponseEntity<String>(resXml, HttpStatus.OK);
+    }
+
+    public int confirmPayFee(Map<String, Object> map) {
+        SortedMap<String, String> paramMap = new TreeMap<String, String>();
+        ResponseEntity<String> responseEntity = null;
+        for (String key : map.keySet()) {
+            paramMap.put(key, map.get(key).toString());
+        }
+        String sign = PayUtil.createSign(paramMap, wechatAuthProperties.getKey());
+
+        if (!sign.equals(map.get("sign"))) {
+            throw new IllegalArgumentException("鉴权失败");
+        }
+
+        String outTradeNo = map.get("out_trade_no").toString();
+        String openId = map.get("openid").toString();
+
+        responseEntity = getUserInfoByOpenId(restTemplate, openId);
+
+        logger.debug("查询用户信息返回报文:" + responseEntity);
+        if (responseEntity.getStatusCode() != HttpStatus.OK) {
+            throw new IllegalArgumentException("根绝openId 查询用户信息异常" + openId);
+        }
+
+        JSONObject userResult = JSONObject.parseObject(responseEntity.getBody());
+        JSONObject realUserInfo = userResult.getJSONArray("users").getJSONObject(0);
+        String useId = realUserInfo.getString("userId");
+
+        //查询用户ID
+        JSONObject paramIn = new JSONObject();
+        paramIn.put("oId", outTradeNo);
+        String url = ServiceConstant.SERVICE_API_URL + "/api/fee.payFeeConfirm";
+        responseEntity = this.callCenterService(restTemplate, useId, paramIn.toJSONString(), url, HttpMethod.POST);
+
+        if (responseEntity.getStatusCode() != HttpStatus.OK) {
+            return 0;
+        }
+        return 1;
+    }
+
+    /**
+     * 获取用户信息
+     *
+     * @param restTemplate
+     * @return
+     */
+    protected ResponseEntity<String> getUserInfoByOpenId(RestTemplate restTemplate, String openId) {
+        //Assert.hasLength(pd.getUserId(), "用户未登录请先登录");
+        ResponseEntity<String> responseEntity = null;
+        responseEntity = this.callCenterService(restTemplate, "-1", "",
+                ServiceConstant.SERVICE_API_URL + "/api/user.listUsers?openId=" + openId + "&page=1&row=1", HttpMethod.GET);
+        // 过滤返回报文中的字段,只返回name字段
+        //{"address":"","orderTypeCd":"Q","serviceCode":"","responseTime":"20190401194712","sex":"","localtionCd":"","userId":"302019033054910001","levelCd":"00","transactionId":"-1","dataFlowId":"-1","response":{"code":"0000","message":"成功"},"name":"996icu","tel":"18909780341","bId":"-1","businessType":"","email":""}
+
+        return responseEntity;
+
+    }
+
+    /**
+     * 调用中心服务
+     *
+     * @return
+     */
+    protected ResponseEntity<String> callCenterService(RestTemplate restTemplate, String userId, String param, String url, HttpMethod httpMethod) {
+
+        ResponseEntity<String> responseEntity = null;
+        HttpHeaders header = new HttpHeaders();
+        header.add(CommonConstant.HTTP_APP_ID.toLowerCase(), APP_ID);
+        header.add(CommonConstant.HTTP_USER_ID.toLowerCase(), userId);
+        header.add(CommonConstant.HTTP_TRANSACTION_ID.toLowerCase(), UUID.randomUUID().toString());
+        header.add(CommonConstant.HTTP_REQ_TIME.toLowerCase(), DateUtil.getDefaultFormateTimeString(new Date()));
+        header.add(CommonConstant.HTTP_SIGN.toLowerCase(), "");
+        HttpEntity<String> httpEntity = new HttpEntity<String>(param, header);
+        //logger.debug("请求中心服务信息,{}", httpEntity);
+        try {
+            responseEntity = restTemplate.exchange(url, httpMethod, httpEntity, String.class);
+        } catch (HttpStatusCodeException e) { //这里spring 框架 在4XX 或 5XX 时抛出 HttpServerErrorException 异常,需要重新封装一下
+            responseEntity = new ResponseEntity<String>("请求下游系统异常," + e.getResponseBodyAsString(), e.getStatusCode());
+        } catch (Exception e) {
+            responseEntity = new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
+        } finally {
+            logger.debug("请求地址为,{} 请求中心服务信息,{},中心服务返回信息,{}", url, httpEntity, responseEntity);
+            return responseEntity;
+        }
+
+    }
+
+}

+ 134 - 2
AppFrontService/src/main/java/com/java110/app/smo/payment/impl/ToPaySMOImpl.java

@@ -1,14 +1,39 @@
 package com.java110.app.smo.payment.impl;
 
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import com.java110.app.properties.WechatAuthProperties;
 import com.java110.app.smo.AppAbstractComponentSMO;
 import com.java110.app.smo.payment.IToPaySMO;
 import com.java110.core.context.IPageData;
+import com.java110.dto.order.WxOrderDto;
+import com.java110.utils.constant.ServiceConstant;
+import com.java110.utils.util.Assert;
+import com.java110.utils.util.PayUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestTemplate;
 
 import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
 
 public class ToPaySMOImpl extends AppAbstractComponentSMO implements IToPaySMO {
+    private static final Logger logger = LoggerFactory.getLogger(AppAbstractComponentSMO.class);
+
+
+    @Autowired
+    private RestTemplate restTemplate;
+
+    @Autowired
+    private WechatAuthProperties wechatAuthProperties;
+
     @Override
     public ResponseEntity<String> toPay(IPageData pd) {
         return super.businessProcess(pd);
@@ -17,10 +42,117 @@ public class ToPaySMOImpl extends AppAbstractComponentSMO implements IToPaySMO {
     @Override
     protected void validate(IPageData pd, JSONObject paramIn) {
 
+        Assert.jsonObjectHaveKey(paramIn, "communityId", "请求报文中未包含communityId节点");
+        Assert.jsonObjectHaveKey(paramIn, "cycles", "请求报文中未包含cycles节点");
+        Assert.jsonObjectHaveKey(paramIn, "receivedAmount", "请求报文中未包含receivedAmount节点");
+        Assert.jsonObjectHaveKey(paramIn, "feeId", "请求报文中未包含feeId节点");
+
     }
 
     @Override
-    protected ResponseEntity<String> doBusinessProcess(IPageData pd, JSONObject paramIn) throws IOException {
-        return null;
+    protected ResponseEntity<String> doBusinessProcess(IPageData pd, JSONObject paramIn) throws Exception {
+
+        ResponseEntity responseEntity = null;
+        //查询用户ID
+        paramIn.put("userId", pd.getUserId());
+        String url = ServiceConstant.SERVICE_API_URL + "/api/fee.payFeePre";
+        responseEntity = super.callCenterService(restTemplate, pd, paramIn.toJSONString(), url, HttpMethod.POST);
+
+        if (responseEntity.getStatusCode() != HttpStatus.OK) {
+            return responseEntity;
+        }
+        JSONObject orderInfo = JSONObject.parseObject(responseEntity.getBody().toString());
+        String orderId = orderInfo.getString("oId");
+        double money = Double.parseDouble(orderInfo.getString("receivableAmount"));
+        Map tmpParamIn = new HashMap();
+        tmpParamIn.put("userId", pd.getUserId());
+        responseEntity = super.getUserAndAttr(pd, restTemplate, tmpParamIn);
+        logger.debug("查询用户信息返回报文:" + responseEntity);
+        if (responseEntity.getStatusCode() != HttpStatus.OK) {
+            throw new IllegalArgumentException("未查询用户信息异常" + tmpParamIn);
+        }
+
+        JSONObject userResult = JSONObject.parseObject(responseEntity.getBody().toString());
+        int total = userResult.getIntValue("total");
+        if(total < 1){
+            //未查询到用户信息
+            throw new IllegalArgumentException("未查询微信用户");
+        }
+
+        JSONObject realUserInfo = userResult.getJSONArray("users").getJSONObject(0);
+
+        String openId = realUserInfo.getString("openId");
+
+        //微信下单PayUtil
+        Map result = java110Payment(orderId, money, openId);
+        responseEntity = new ResponseEntity(JSONObject.toJSONString(result), HttpStatus.OK);
+
+        return responseEntity;
+    }
+
+
+    /**
+     * 预下单
+     *
+     * @param orderNum
+     * @param money
+     * @param openId
+     * @return
+     * @throws Exception
+     */
+    private Map<String, String> java110Payment(String orderNum, double money, String openId) throws Exception {
+        logger.info("【小程序支付】 统一下单开始, 订单编号=" + orderNum);
+        SortedMap<String, String> resultMap = new TreeMap<String, String>();
+//生成支付金额,开发环境处理支付金额数到0.01、0.02、0.03元
+
+        double payAmount = PayUtil.getPayAmountByEnv("DEV", money);
+//添加或更新支付记录(参数跟进自己业务需求添加)
+
+        Map<String, String> resMap = this.java110UnifieldOrder(orderNum, wechatAuthProperties.TRADE_TYPE_JSAPI, payAmount, openId);
+        if ("SUCCESS".equals(resMap.get("return_code")) && "SUCCESS".equals(resMap.get("result_code"))) {
+            resultMap.put("appId", wechatAuthProperties.getAppId());
+            resultMap.put("timeStamp", PayUtil.getCurrentTimeStamp());
+            resultMap.put("nonceStr", PayUtil.makeUUID(32));
+            resultMap.put("package", "prepay_id=" + resMap.get("prepay_id"));
+            resultMap.put("signType", "MD5");
+            resultMap.put("sign", PayUtil.createSign(resultMap, wechatAuthProperties.getKey()));
+            resultMap.put("returnCode", "SUCCESS");
+            resultMap.put("returnMsg", "OK");
+            logger.info("【小程序支付】统一下单成功,返回参数:" + resultMap);
+        } else {
+            resultMap.put("returnCode", resMap.get("return_code"));
+            resultMap.put("returnMsg", resMap.get("return_msg"));
+            logger.info("【小程序支付】统一下单失败,失败原因:" + resMap.get("return_msg"));
+        }
+        return resultMap;
+    }
+
+    /**
+     * 小程序支付统一下单
+     */
+    private Map<String, String> java110UnifieldOrder(String orderNum, String tradeType, double payAmount, String openid) throws Exception {
+//封装参数
+        SortedMap<String, String> paramMap = new TreeMap<String, String>();
+        paramMap.put("appid", wechatAuthProperties.getAppId());
+        paramMap.put("mch_id", wechatAuthProperties.getMchId());
+        paramMap.put("nonce_str", PayUtil.makeUUID(32));
+        paramMap.put("body", "");
+        paramMap.put("out_trade_no", orderNum);
+        paramMap.put("total_fee", PayUtil.moneyToIntegerStr(payAmount));
+        paramMap.put("spbill_create_ip", PayUtil.getLocalIp());
+        paramMap.put("notify_url", wechatAuthProperties.getWxNotifyUrl());
+        paramMap.put("trade_type", tradeType);
+        paramMap.put("openid", openid);
+        paramMap.put("sign", PayUtil.createSign(paramMap, wechatAuthProperties.getKey()));
+//转换为xml
+        String xmlData = PayUtil.mapToXml(paramMap);
+
+        ResponseEntity<String> responseEntity = restTemplate.postForEntity(
+                wechatAuthProperties.getWxPayUnifiedOrder(), xmlData, String.class);
+//请求微信后台,获取预支付ID
+        if (responseEntity.getStatusCode() != HttpStatus.OK) {
+            throw new IllegalArgumentException("支付失败" + responseEntity.getBody());
+        }
+        return PayUtil.xmlStrToMap(responseEntity.getBody());
     }
 }

+ 4 - 0
AppFrontService/src/main/resources/wechatAuth.properties

@@ -2,5 +2,9 @@ java110.auth.wechat.sessionHost=https://api.weixin.qq.com/sns/jscode2session
 java110.auth.wechat.appId=wxf83d66b0e9f5964d
 java110.auth.wechat.secret=cc
 java110.auth.wechat.grantType=authorization_code
+java110.auth.wechat.key=key
+java110.auth.wechat.mchId=mchId
+java110.auth.wechat.wxPayUnifiedOrder=https://api.mch.weixin.qq.com/pay/unifiedorder
+java110.auth.wechat.wxNotifyUrl=https://app.demo.winqi.cn/app/payment/notify
 
 

+ 1 - 1
WebService/src/main/resources/components/appPackage/add-app/addApp.js

@@ -80,7 +80,7 @@
                     return;
                 }
 
-                vc.component.addAppInfo.communityId = vc.getCurrentCommunity().communityId;
+                //vc.component.addAppInfo.communityId = vc.getCurrentCommunity().communityId;
 
                 //不提交数据将数据 回调给侦听处理
                 if (vc.notNull($props.callBackListener)) {

+ 1 - 1
WebService/src/main/resources/components/appPackage/edit-app/editApp.js

@@ -20,7 +20,7 @@
                 vc.component.refreshEditAppInfo();
                 $('#editAppModel').modal('show');
                 vc.component.editAppInfo = _params;
-                vc.component.editAppInfo.communityId = vc.getCurrentCommunity().communityId;
+                //vc.component.editAppInfo.communityId = vc.getCurrentCommunity().communityId;
             });
         },
         methods: {

+ 118 - 0
java110-bean/src/main/java/com/java110/dto/order/WxOrderDto.java

@@ -0,0 +1,118 @@
+package com.java110.dto.order;
+
+import java.io.Serializable;
+
+/**
+ * 微信订单
+ */
+public class WxOrderDto implements Serializable {
+
+    private String appid;// 小程序ID
+    private String mch_id;// 商户号
+    private String nonce_str;// 随机字符串
+    private String sign_type;//签名类型
+    private String sign;// 签名
+    private String body;// 商品描述
+    private String out_trade_no;// 商户订单号
+    private String  total_fee;// 标价金额 ,单位为分
+    private String spbill_create_ip;// 终端IP
+    private String notify_url;// 通知地址
+    private String trade_type;// 交易类型
+    private String openid;//用户标识
+
+    public String getAppid() {
+        return appid;
+    }
+
+    public void setAppid(String appid) {
+        this.appid = appid;
+    }
+
+    public String getMch_id() {
+        return mch_id;
+    }
+
+    public void setMch_id(String mch_id) {
+        this.mch_id = mch_id;
+    }
+
+    public String getNonce_str() {
+        return nonce_str;
+    }
+
+    public void setNonce_str(String nonce_str) {
+        this.nonce_str = nonce_str;
+    }
+
+    public String getSign_type() {
+        return sign_type;
+    }
+
+    public void setSign_type(String sign_type) {
+        this.sign_type = sign_type;
+    }
+
+    public String getSign() {
+        return sign;
+    }
+
+    public void setSign(String sign) {
+        this.sign = sign;
+    }
+
+    public String getBody() {
+        return body;
+    }
+
+    public void setBody(String body) {
+        this.body = body;
+    }
+
+    public String getOut_trade_no() {
+        return out_trade_no;
+    }
+
+    public void setOut_trade_no(String out_trade_no) {
+        this.out_trade_no = out_trade_no;
+    }
+
+    public String getTotal_fee() {
+        return total_fee;
+    }
+
+    public void setTotal_fee(String total_fee) {
+        this.total_fee = total_fee;
+    }
+
+    public String getSpbill_create_ip() {
+        return spbill_create_ip;
+    }
+
+    public void setSpbill_create_ip(String spbill_create_ip) {
+        this.spbill_create_ip = spbill_create_ip;
+    }
+
+    public String getNotify_url() {
+        return notify_url;
+    }
+
+    public void setNotify_url(String notify_url) {
+        this.notify_url = notify_url;
+    }
+
+    public String getTrade_type() {
+        return trade_type;
+    }
+
+    public void setTrade_type(String trade_type) {
+        this.trade_type = trade_type;
+    }
+
+    public String getOpenid() {
+        return openid;
+    }
+
+    public void setOpenid(String openid) {
+        this.openid = openid;
+    }
+}

+ 10 - 0
java110-bean/src/main/java/com/java110/vo/api/user/ApiUserDataVo.java

@@ -20,6 +20,8 @@ public class ApiUserDataVo implements Serializable {
 
     private String locationCd;
 
+    private String openId;
+
     private int age;
 
     private String sex;
@@ -113,4 +115,12 @@ public class ApiUserDataVo implements Serializable {
     public void setLevelCd(String levelCd) {
         this.levelCd = levelCd;
     }
+
+    public String getOpenId() {
+        return openId;
+    }
+
+    public void setOpenId(String openId) {
+        this.openId = openId;
+    }
 }

+ 1 - 1
java110-core/src/main/java/com/java110/core/component/AbstractComponentSMO.java

@@ -60,5 +60,5 @@ public abstract class AbstractComponentSMO extends BaseComponentSMO {
      * @param pd      页面数据封装
      * @param paramIn 前台数据对象
      */
-    protected abstract ResponseEntity<String> doBusinessProcess(IPageData pd, JSONObject paramIn) throws IOException;
+    protected abstract ResponseEntity<String> doBusinessProcess(IPageData pd, JSONObject paramIn) throws Exception;
 }

+ 22 - 0
java110-core/src/main/java/com/java110/core/component/BaseComponentSMO.java

@@ -106,6 +106,28 @@ public class BaseComponentSMO extends BaseServiceSMO {
 
     }
 
+    /**
+     * 获取用户信息
+     *
+     * @param pd
+     * @param restTemplate
+     * @return
+     */
+    protected ResponseEntity<String> getUserAndAttr(IPageData pd, RestTemplate restTemplate, Map paramIn) {
+        //Assert.hasLength(pd.getUserId(), "用户未登录请先登录");
+        ResponseEntity<String> responseEntity = null;
+        if(paramIn !=null){
+            paramIn.put("page","1");
+            paramIn.put("row","1");
+        }
+        responseEntity = this.callCenterService(restTemplate, pd, "",
+                ServiceConstant.SERVICE_API_URL + "/api/user.listUsers" +mapToUrlParam(paramIn), HttpMethod.GET);
+        // 过滤返回报文中的字段,只返回name字段
+        //{"address":"","orderTypeCd":"Q","serviceCode":"","responseTime":"20190401194712","sex":"","localtionCd":"","userId":"302019033054910001","levelCd":"00","transactionId":"-1","dataFlowId":"-1","response":{"code":"0000","message":"成功"},"name":"996icu","tel":"18909780341","bId":"-1","businessType":"","email":""}
+        return responseEntity;
+
+    }
+
     /**
      * 查询商户信息
      *

+ 1 - 0
java110-front/src/main/java/com/java110/front/configuration/ServiceConfiguration.java

@@ -23,6 +23,7 @@ public class ServiceConfiguration {
         exclusions.append("/callComponent/advertVedioView/*,");//放开 广告页面是不需要登录的
         exclusions.append("/callComponent/download/getFile/file,");//放开 下载图片也不需要登录
         exclusions.append("/callComponent/download/getFile/fileByObjId,");//放开 下载图片也不需要登录
+        exclusions.append("/app/payment/notify,");//微信支付通知
         exclusions.append("/app/loginWx");// 登录跳过
         final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
         registrationBean.setFilter(new JwtFilter());

+ 348 - 0
java110-utils/src/main/java/com/java110/utils/util/PayUtil.java

@@ -0,0 +1,348 @@
+package com.java110.utils.util;
+
+import com.alibaba.fastjson.JSONObject;
+import com.java110.utils.constant.ResponseConstant;
+import com.java110.utils.exception.NoAuthorityException;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.math.BigDecimal;
+import java.net.InetAddress;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+public class PayUtil {
+
+    static Logger log = LogManager.getLogger(PayUtil.class.getName());
+
+    /**
+     * 获取当前机器的ip
+     *
+     * @return String
+     */
+    public static String getLocalIp() {
+        InetAddress ia = null;
+        String localip = null;
+        try {
+            ia = ia.getLocalHost();
+            localip = ia.getHostAddress();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return localip;
+
+    }
+
+    /**
+     * Map转换为 Xml
+     *
+     * @param map
+     * @return Xml
+     * @throws Exception
+     */
+    public static String mapToXml(SortedMap<String, String> map) throws Exception {
+        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+//防止XXE攻击
+        documentBuilderFactory.setXIncludeAware(false);
+        documentBuilderFactory.setExpandEntityReferences(false);
+        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+        org.w3c.dom.Document document = documentBuilder.newDocument();
+        org.w3c.dom.Element root = document.createElement("xml");
+        document.appendChild(root);
+        for (String key : map.keySet()) {
+            String value = map.get(key);
+            if (value == null) {
+                value = "";
+            }
+            value = value.trim();
+            org.w3c.dom.Element filed = document.createElement(key);
+            filed.appendChild(document.createTextNode(value));
+            root.appendChild(filed);
+        }
+        TransformerFactory tf = TransformerFactory.newInstance();
+        Transformer transformer = tf.newTransformer();
+        DOMSource source = new DOMSource(document);
+        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+        StringWriter writer = new StringWriter();
+        StreamResult result = new StreamResult(writer);
+        transformer.transform(source, result);
+        String output = writer.getBuffer().toString();
+        try {
+            writer.close();
+        } catch (Exception ex) {
+        }
+        return output;
+    }
+
+
+    /**
+     * 创建签名Sign
+     *
+     * @param key
+     * @param parameters
+     * @return
+     */
+    public static String createSign(SortedMap<String, String> parameters, String key) {
+        StringBuffer sb = new StringBuffer();
+        Set es = parameters.entrySet();
+        Iterator<?> it = es.iterator();
+        while (it.hasNext()) {
+            Map.Entry entry = (Map.Entry) it.next();
+            String k = (String) entry.getKey();
+            if (entry.getValue() != null || !"".equals(entry.getValue())) {
+                String v = String.valueOf(entry.getValue());
+                if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
+                    sb.append(k + "=" + v + "&");
+                }
+            }
+        }
+        sb.append("key=" + key);
+        String sign = md5(sb.toString()).toUpperCase();
+        return sign;
+    }
+
+    /**
+     * md5签名
+     *
+     * @param inStr
+     * @return
+     */
+    public static String md5(String inStr) throws NoAuthorityException {
+        try {
+            return DigestUtils.md5Hex(inStr.getBytes("UTF-8"));
+        } catch (UnsupportedEncodingException e) {
+            throw new NoAuthorityException(ResponseConstant.RESULT_CODE_NO_AUTHORITY_ERROR, "MD5签名过程中出现错误");
+        }
+    }
+
+
+    /**
+     * XML转换为Map
+     *
+     * @param strXML
+     * @return Map
+     * @throws Exception
+     */
+    public static Map<String, Object> getMapFromXML(String strXML) throws Exception {
+        try {
+            Map<String, Object> data = new HashMap<String, Object>();
+            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+//防止XXE攻击
+            documentBuilderFactory.setXIncludeAware(false);
+            documentBuilderFactory.setExpandEntityReferences(false);
+            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
+            org.w3c.dom.Document doc = documentBuilder.parse(stream);
+            doc.getDocumentElement().normalize();
+            NodeList nodeList = doc.getDocumentElement().getChildNodes();
+            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
+                Node node = nodeList.item(idx);
+                if (node.getNodeType() == Node.ELEMENT_NODE) {
+                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
+                    data.put(element.getNodeName(), element.getTextContent());
+                }
+            }
+            try {
+                stream.close();
+            } catch (Exception ex) {
+                ex.printStackTrace();
+            }
+            return data;
+        } catch (Exception ex) {
+            throw ex;
+        }
+    }
+
+    /**
+     * 生成随机数
+     *
+     * @return
+     */
+    public static String makeUUID(int len) {
+        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, len);
+    }
+
+    /**
+     * 获取当前的Timestamp
+     *
+     * @return
+     */
+    public static String getCurrentTimeStamp() {
+        return Long.toString(System.currentTimeMillis() / 1000);
+    }
+
+    /**
+     * 获取当前的时间
+     *
+     * @return
+     */
+    public static long getCurrentTimestampMs() {
+        return System.currentTimeMillis();
+    }
+
+    /**
+     * 生成订单号
+     *
+     * @return
+     */
+    public static String generateOrderNo() {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
+        return sdf.format(new Date()) + makeUUID(16);
+    }
+
+    /**
+     * 获取当前工程url
+     *
+     * @param request
+     * @return
+     */
+    public static String getCurrentUrl(HttpServletRequest request) {
+        return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
+    }
+
+    /**
+     * Xml字符串转换为Map
+     *
+     * @param xmlStr
+     * @return
+     */
+    public static Map<String, String> xmlStrToMap(String xmlStr) {
+        Map<String, String> map = new HashMap<String, String>();
+        Document doc;
+        try {
+            doc = DocumentHelper.parseText(xmlStr);
+            Element root = doc.getRootElement();
+            List children = root.elements();
+            if (children != null && children.size() > 0) {
+                for (int i = 0; i < children.size(); i++) {
+                    Element child = (Element) children.get(i);
+                    map.put(child.getName(), child.getTextTrim());
+                }
+            }
+        } catch (DocumentException e) {
+            e.printStackTrace();
+        }
+        return map;
+    }
+
+    public static String getSceneInfo(String wapUrl, String name) {
+        Map<String, Map<String, String>> map = new HashMap<String, Map<String, String>>();
+        if (!StringUtils.isEmpty(wapUrl) && !StringUtils.isEmpty(name)) {
+            /*{"h5_info": {"type":"Wap","wap_url": "https://pay.qq.com","wap_name": "腾讯充值"}}*/
+            Map<String, String> childmap = new TreeMap<String, String>();
+            childmap.put("type", "Wap");
+            childmap.put("wap_url", wapUrl);
+            childmap.put("wap_name", name);
+            map.put("h5_info", childmap);
+            return JSONObject.toJSONString(map);
+        }
+        return null;
+    }
+
+    /**
+     * 转换金额型到整型
+     *
+     * @param money
+     * @return
+     */
+    public static String moneyToIntegerStr(Double money) {
+        BigDecimal decimal = new BigDecimal(money);
+        int amount = decimal.multiply(new BigDecimal(100))
+                .setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
+        return String.valueOf(amount);
+    }
+
+    /**
+     * 除去数组中的空值和签名参数
+     *
+     * @param sArray 签名参数组
+     * @return 去掉空值与签名参数后的新签名参数组
+     */
+    public static Map<String, String> paraFilter(Map<String, String> sArray) {
+
+        Map<String, String> result = new HashMap<String, String>();
+
+        if (sArray == null || sArray.size() <= 0) {
+            return result;
+        }
+
+        for (String key : sArray.keySet()) {
+            String value = sArray.get(key);
+            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
+                    || key.equalsIgnoreCase("sign_type")) {
+                continue;
+            }
+            result.put(key, value);
+        }
+
+        return result;
+    }
+
+    /**
+     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
+     *
+     * @param params 需要排序并参与字符拼接的参数组
+     * @return 拼接后字符串
+     */
+    public static String createLinkString(Map<String, String> params) {
+        List<String> keys = new ArrayList<String>(params.keySet());
+        Collections.sort(keys);
+        String prestr = "";
+        for (int i = 0; i < keys.size(); i++) {
+            String key = keys.get(i);
+            String value = params.get(key);
+            if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
+                prestr = prestr + key + "=" + value;
+            } else {
+                prestr = prestr + key + "=" + value + "&";
+            }
+        }
+        return prestr;
+    }
+
+    /**
+     * 根据不同环境生成支付金额
+     *
+     * @param env
+     * @param money
+     * @return
+     */
+    public static double getPayAmountByEnv(String env, Double money) {
+        double pay_money = 0.01;
+//测试环境
+        if ("DEV".equals(env)) {
+            if (money > 10000) {
+                pay_money = 0.03;
+            } else if (money > 1000) {
+                pay_money = 0.02;
+            } else {
+                pay_money = 0.01;
+            }
+            return pay_money;
+        } else {
+//生成环境
+            return money;
+        }
+    }
+}