Browse Source

Merge remote-tracking branch 'remotes/origin/developerB-支付' into developer

Aslee 3 years ago
parent
commit
727098bda3
52 changed files with 3654 additions and 79 deletions
  1. 43 0
      pom.xml
  2. 90 0
      src/main/java/com/caimei/components/HeliPayUtil.java
  3. 212 0
      src/main/java/com/caimei/components/WeChatService.java
  4. 49 0
      src/main/java/com/caimei/constant/Constant.java
  5. 123 0
      src/main/java/com/caimei/controller/HeliPayApi.java
  6. 1 0
      src/main/java/com/caimei/controller/OrderSubmitApi.java
  7. 4 0
      src/main/java/com/caimei/mapper/OrderMapper.java
  8. 1 1
      src/main/java/com/caimei/mapper/OrderSubmitMapper.java
  9. 18 13
      src/main/java/com/caimei/mapper/PayOrderMapper.java
  10. 73 0
      src/main/java/com/caimei/model/dto/HeliDto.java
  11. 78 0
      src/main/java/com/caimei/model/enumerate/BizType.java
  12. 5 0
      src/main/java/com/caimei/model/po/CmHeHeProductPo.java
  13. 5 0
      src/main/java/com/caimei/model/po/CmOrderProductPo.java
  14. 5 0
      src/main/java/com/caimei/model/po/CmReceiptOrderRelationPo.java
  15. 25 0
      src/main/java/com/caimei/model/po/CmShopOrderPo.java
  16. 98 0
      src/main/java/com/caimei/model/po/PayParamBo.java
  17. 129 0
      src/main/java/com/caimei/model/po/PayShopPo.java
  18. 58 0
      src/main/java/com/caimei/model/po/PayShopRecordPo.java
  19. 79 0
      src/main/java/com/caimei/model/po/SplitAccountPo.java
  20. 26 0
      src/main/java/com/caimei/model/vo/AccountResVo.java
  21. 49 0
      src/main/java/com/caimei/model/vo/AppCreateOrderResponseVo.java
  22. 64 0
      src/main/java/com/caimei/model/vo/AppCreateOrderVo.java
  23. 69 0
      src/main/java/com/caimei/model/vo/AppPayPublicCreateOrderVo.java
  24. 42 0
      src/main/java/com/caimei/model/vo/AppPayPublicOrderResponseVo.java
  25. 22 0
      src/main/java/com/caimei/model/vo/BankCodeVo.java
  26. 42 0
      src/main/java/com/caimei/model/vo/HeliOnlineVo.java
  27. 38 0
      src/main/java/com/caimei/model/vo/NotifyResponseVo.java
  28. 8 3
      src/main/java/com/caimei/model/vo/OrderVo.java
  29. 16 0
      src/main/java/com/caimei/model/vo/PaySplitVo.java
  30. 50 0
      src/main/java/com/caimei/model/vo/QueryOrderResponseVo.java
  31. 27 0
      src/main/java/com/caimei/model/vo/QueryOrderVo.java
  32. 27 2
      src/main/java/com/caimei/model/vo/ShopOrderVo.java
  33. 5 0
      src/main/java/com/caimei/model/vo/ShopVo.java
  34. 34 0
      src/main/java/com/caimei/service/HeliPayService.java
  35. 529 0
      src/main/java/com/caimei/service/impl/HeliPayServiceImpl.java
  36. 102 17
      src/main/java/com/caimei/service/impl/OrderSubmitServiceImpl.java
  37. 29 29
      src/main/java/com/caimei/service/impl/PayOrderServiceImpl.java
  38. 476 0
      src/main/java/com/caimei/util/helipay/Base64.java
  39. 24 0
      src/main/java/com/caimei/util/helipay/ConvertUtils.java
  40. 63 0
      src/main/java/com/caimei/util/helipay/DesUtils.java
  41. 74 0
      src/main/java/com/caimei/util/helipay/Disguiser.java
  42. 127 0
      src/main/java/com/caimei/util/helipay/HttpClientService.java
  43. 113 0
      src/main/java/com/caimei/util/helipay/MyBeanUtils.java
  44. 25 0
      src/main/java/com/caimei/util/helipay/MyDateUtils.java
  45. 350 0
      src/main/java/com/caimei/util/helipay/RSA.java
  46. 3 1
      src/main/resources/config/beta/application-beta.yml
  47. 1 1
      src/main/resources/config/dev/application-dev.yml
  48. 1 1
      src/main/resources/config/prod/application-prod.yml
  49. 62 0
      src/main/resources/mapper/OrderMapper.xml
  50. 38 6
      src/main/resources/mapper/OrderSubmitMapper.xml
  51. 21 5
      src/main/resources/mapper/PayOrderMapper.xml
  52. 1 0
      src/main/resources/mapper/ProductMapper.xml

+ 43 - 0
pom.xml

@@ -118,6 +118,49 @@
             <artifactId>httpclient</artifactId>
             <version>4.5.7</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+            <version>3.8.1</version>
+        </dependency>
+
+        <!--freemarker 模板引擎-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-freemarker</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.3.2</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.6</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils</artifactId>
+            <version>1.8.3</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-httpclient</groupId>
+            <artifactId>commons-httpclient</artifactId>
+            <version>3.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcmail-jdk15</artifactId>
+            <version>1.46</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>29.0-jre</version>
+        </dependency>
     </dependencies>
 
     <profiles>

+ 90 - 0
src/main/java/com/caimei/components/HeliPayUtil.java

@@ -0,0 +1,90 @@
+package com.caimei.components;
+
+import com.caimei.constant.Constant;
+import com.caimei.mapper.OrderMapper;
+import com.caimei.mapper.PayOrderMapper;
+import com.caimei.model.dto.HeliDto;
+import com.caimei.model.enumerate.BizType;
+import com.caimei.model.vo.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+@Slf4j
+@Service
+public class HeliPayUtil {
+
+    @Resource
+    private PayOrderMapper payOrderMapper;
+    @Resource
+    private OrderMapper orderMapper;
+    /*@Value("${pay.second-notify-url}")
+    private String secondHandUrl;
+    @Value("${pay.vip-notify-url}")
+    private String superVipUrl;
+    @Value("${wx.mini-app-id}")
+    private String miniAppId;
+    @Value("${pay.notify-url}")
+    private String notifyUrl;
+    @Value("${pay.union-url}")
+    private String unionPay;
+    @Value("${pay.vip-union-url}")
+    private String vipUnionPay;
+    @Value("${pay.second-union-url}")
+    private String secondUnionPay;*/
+
+    @Value("${caimei.notifyUrl}")
+    private String notifyUrl;
+
+    @Value("${wx.AppId}")
+    private String appId;
+
+    @Value("${wx.AppSecret}")
+    private String appSecret;
+
+    private final String publicCode="wx91c4152b60ca91a3";
+
+    //小程序/公众号线上公用方法 p8 openid在自己方法传
+    public AppPayPublicCreateOrderVo setOnlineValue(AppPayPublicCreateOrderVo pay, HeliDto heliDto, String payFlag, HttpHeaders headers) {
+        String orderId = "";
+        // 时间戳
+        long time = System.currentTimeMillis() / 1000;
+        String environment = "";
+        if (notifyUrl.contains("18002")) {
+            environment = "DEV";
+        } else if (notifyUrl.contains("mall2c-b")) {
+            environment = "BETA";
+        }
+        if ("order".equals(payFlag)) {
+            //普通订单
+//            pay.setP3_customerNumber(Constant.CUSTOMERNUM2);
+            OrderVo order = orderMapper.findOrder(heliDto.getOrderId());
+            orderId = order.getOrderNo() + "T" + time + environment;
+            pay.setP9_orderAmount(heliDto.getPayAmount().toString());
+            pay.setP12_notifyUrl(notifyUrl);
+            String product = "采美订单" + order.getOrderNo();
+            pay.setP15_goodsName(product);
+            String attach = order.getOrderId() + "," + BizType.getNumByType(heliDto.getPayType())+","+heliDto.getShopOrderId();
+            pay.setP18_desc(attach);
+        }
+        pay.setP1_bizType(BizType.getP1ByType(heliDto.getPayType()));
+        pay.setP2_orderId(orderId);
+        pay.setP4_payType(BizType.getP4ByType(heliDto.getPayType()));
+        pay.setP10_currency("CNY");
+        pay.setP14_orderIp(headers.getFirst("X-CLIENT-IP"));
+        pay.setP11_appType("WXPAY");
+        //成功后跳转地址
+        pay.setP13_successToUrl(heliDto.getReturnUrl());
+        if ("GZH".equals(heliDto.getPayType())) {
+            //公众号appid
+            pay.setP5_appid(publicCode);
+        } else if ("XCX".equals(heliDto.getPayType())) {
+            //小程序appid
+            pay.setP5_appid(appId);
+        }
+        return pay;
+    }
+}

+ 212 - 0
src/main/java/com/caimei/components/WeChatService.java

@@ -0,0 +1,212 @@
+package com.caimei.components;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.caimei.model.ResponseJson;
+import com.caimei.util.HttpRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.StringUtils;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.AlgorithmParameters;
+import java.security.Security;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 微信 服务工具类
+ *
+ * @author : Charles
+ * @date : 2021/3/9
+ */
+@Slf4j
+@Component
+public class WeChatService {
+
+    @Value("${wx.AppId}")
+    private String heHeAppId;
+    @Value("${wx.AppSecret}")
+    private String heHeAppSecret;
+
+    /*private RedisService redisService;
+    @Autowired
+    public void setRedisService(RedisService redisService) {
+        this.redisService = redisService;
+    }*/
+
+    public static class Keys {
+        public static final String OPEN_ID = "openid";
+        public static final String UNION_ID = "unionid";
+        public static final String SESSION_KEY = "session_key";
+    }
+
+    static {
+        // BouncyCastle是一个开源的加解密解决方案
+        Security.addProvider(new BouncyCastleProvider());
+    }
+
+    /**
+     * AES解密
+     *
+     * @param data           密文,被加密的数据
+     * @param key            秘钥
+     * @param iv             偏移量
+     * @param encodingFormat 解密后的结果需要进行的编码
+     * @return 解密结果
+     */
+    public static String decrypt(String data, String key, String iv, String encodingFormat) throws Exception {
+        // 被加密的数据
+        byte[] dataByte = Base64.decodeBase64(data);
+        // 加密秘钥
+        byte[] keyByte = Base64.decodeBase64(key);
+        // 偏移量
+        byte[] ivByte = Base64.decodeBase64(iv);
+        try {
+            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
+            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
+            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
+            parameters.init(new IvParameterSpec(ivByte));
+            // 初始化
+            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
+            byte[] resultByte = cipher.doFinal(dataByte);
+            if (null != resultByte && resultByte.length > 0) {
+                return new String(resultByte, encodingFormat);
+            }
+            return null;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 小程序微信授权登录,获取openid
+     *
+     * @param code    微信凭证
+     * @param headers HttpHeaders
+     * @param mode    1:采美小程序,2:收款工具小程序appId
+     * @return HashMap
+     */
+    public ResponseJson<Map<String, Object>> getInfoMapByApplets(String code, HttpHeaders headers, Integer mode) {
+        log.info("Start get SessionKey");
+        Map<String, Object> returnMap = new HashMap<>(4);
+        // 获取当前微信小程序的环境
+        String referer = headers.getFirst("Referer");
+        log.info("referer-is----:" + referer);
+        returnMap.put("referer", referer);
+        String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
+        Map<String, String> requestUrlParam = new HashMap<String, String>(4);
+        // 呵呵商城小程序appId
+        requestUrlParam.put("appid", heHeAppId);
+        log.info("呵呵商城appId: ---" + heHeAppId);
+        // 呵呵商城小程序appSecret
+        requestUrlParam.put("secret", heHeAppSecret);
+        // 小程序端返回的code
+        requestUrlParam.put("js_code", code);
+        // 默认参数
+        requestUrlParam.put("grant_type", "authorization_code");
+        // 发送post请求读取调用微信接口获取openid用户唯一标识
+        String infos;
+        try {
+            infos = HttpRequest.sendPost(requestUrl, requestUrlParam);
+        } catch (Exception e) {
+            return ResponseJson.error("服务器内部异常", returnMap);
+        }
+        // 解析相应内容(转换成json对象)
+        JSONObject jsonObject = JSON.parseObject(infos);
+        String openId = jsonObject.getString(Keys.OPEN_ID);
+        String unionId = jsonObject.getString(Keys.UNION_ID);
+        String sessionKey = jsonObject.getString(Keys.SESSION_KEY);
+        String errCode = jsonObject.getString("errcode");
+        String errMsg = jsonObject.getString("errmsg");
+        log.info("openId----->" + openId + ", unionId------>" + unionId);
+        returnMap.put(Keys.OPEN_ID, openId);
+        returnMap.put(Keys.UNION_ID, unionId);
+        returnMap.put(Keys.SESSION_KEY, sessionKey);
+        boolean errFlag = StringUtils.isNotEmpty(errCode) && ("-1".equals(errCode) || "40029".equals(errCode) || "45011".equals(errCode));
+        if (errFlag) {
+            return ResponseJson.error(-1, errMsg, returnMap);
+        }
+        return ResponseJson.success(returnMap);
+    }
+
+
+    /**
+     * 网页授权登录,通过code获取openid
+     *
+     * @param code   微信code
+     * @param source 来源
+     * @return
+     */
+    public Map<String, Object> getInfoMapByWeb(String code, String source) throws Exception {
+        String link = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
+        // 应用唯一标识
+        link = link.replace("APPID", heHeAppId);
+        // 应用密钥AppSecret,在微信开放平台提交应用审核通过后获得
+        link = link.replace("SECRET", heHeAppSecret);
+
+        // 获取的code参数
+        link = link.replace("CODE", code);
+        // 发送授权链接,得到微信用户信息
+        String result = HttpRequest.sendGet(link);
+        log.info(result);
+        Map<String, Object> map = JSONObject.parseObject(result, Map.class);
+        return map;
+    }
+
+    /**
+     * 微信公众号获取access_token
+     *
+     * @return access_token
+     */
+    public String getAccessToken() throws Exception {
+//        String access_token = String.valueOf(redisService.get("access_token:"+crmAppId));
+//        // 过期后会得到"null"值,所以需判断字符串"null"
+//        if (access_token != null && access_token.length() != 0 && !"null".equals(access_token)) {
+//            return access_token;
+//        }
+        String link = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
+        link = link.replace("APPID", "wx0938e78f38bc203d");
+        link = link.replace("APPSECRET", "a563dd2c07c9c815a4e697c8b6cb73dc");
+        String result = HttpRequest.sendGet(link);
+        log.info("微信公众号获取access_token>>>" + result);
+        Map<String, Object> map = JSONObject.parseObject(result, Map.class);
+        String access_token = (String) map.get("access_token");
+//        // 将token存入redis, access_token的有效期目前为2个小时(redis存1.5小时)
+//        redisService.set("access_token:"+crmAppId, access_token, 5400L);
+        return access_token;
+    }
+
+    /**
+     * 微信公众号获取用户信息
+     *
+     * @return
+     */
+    public Map<String, Object> getUserInfo(String accessToken, String openId) throws Exception {
+        String requestUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";
+        String userInfo = HttpRequest.sendGet(requestUrl);
+        log.info("微信公众号授权用户数据>>>>>>>>>>>" + userInfo);
+        Map<String, Object> map = JSONObject.parseObject(userInfo, Map.class);
+        return map;
+    }
+
+    /**
+     * 网页授权登录,获取用户信息(UnionID机制)
+     */
+    public Map<String, Object> getUserInfoByWeb(String access_token, String openId) throws Exception {
+        String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openId;
+        String userInfo = HttpRequest.sendGet(requestUrl);
+        Map<String, Object> map = JSONObject.parseObject(userInfo, Map.class);
+        return map;
+    }
+
+
+}

+ 49 - 0
src/main/java/com/caimei/constant/Constant.java

@@ -0,0 +1,49 @@
+package com.caimei.constant;
+
+
+/**
+ * 常量类
+ * @author Administrator
+ */
+public class Constant {
+
+
+    //todo 子商户E商编
+    //专票 信息 测试佣金暂入信息  308785626@qq.com vip/二手暂入信息
+    public static final String CUSTOMERNUM = "E1807059160";
+    //信息的邮箱
+    public static final String XX_MAIL="308785626@qq.com";
+
+    //收款商编 网络   xun.zhang@caimei365.com
+    public static final String CUSTOMERNUM2 = "E1807062884";
+    //网络的邮箱
+    public static final String WL_MAIL="xun.zhang@caimei365.com";
+
+    //普票 奥泰 测试私账暂进奥泰   caimei365@yeah.net
+    public static final String CUSTOMERNUM3 = "E1807085606";
+
+    public static final String AT_MAIL="caimei365@yeah.net";
+
+    public static final String SPLIT = "&";
+
+    public static final String DOMAIN_NAME = "http://pay.trx.helipay.com/";
+    //网银地址http://pay.trx.helipay.com/trx/online/interface.action
+    public static final String YL="http://pay.trx.helipay.com/trx/online/interface.action";
+    //分账地址
+    public static final String FZ="http://pay.trx.helipay.com/trx/accountPay/interface.action";
+
+    //微信/支付宝扫码/小程序公众号支付
+    public static final String SAOMA="1iHnZaalUNAVcfcbKdh6n86Z0yUHtM6f";
+    //网银
+    public static final String WANGYIN="CZiCbGrgFYQMldVkQnzbFQeQkn6mp25w";
+    //虚拟账户支付
+    public static final String XUNI="8VmdRSXMIOfUo7aEq1iYs2XEWgGZpBQc";
+    //分账
+    public static final String FENZHANG="2hATS0A4IoxdudGxNkGRNOt6aFSdOd8Q";
+
+    /**
+     * 扫码接口地址
+     */
+    public static final String REQUEST_URL = DOMAIN_NAME + "trx/app/interface.action";
+
+}

+ 123 - 0
src/main/java/com/caimei/controller/HeliPayApi.java

@@ -0,0 +1,123 @@
+package com.caimei.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.caimei.model.ResponseJson;
+import com.caimei.model.dto.HeliDto;
+import com.caimei.model.vo.AccountResVo;
+import com.caimei.model.vo.NotifyResponseVo;
+import com.caimei.service.HeliPayService;
+import com.caimei.service.PayOrderService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.bind.annotation.*;
+
+import java.beans.IntrospectionException;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+
+
+/**
+ * Description
+ *
+ * @author : zzj
+ * @date : 2021/11/11
+ */
+@Api(tags = "合利宝支付API")
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/order/pay")
+public class HeliPayApi {
+
+    private final HeliPayService heliPayService;
+    private final PayOrderService payOrderService;
+
+
+    /**
+     * 获取线上支付全局开关状态
+     */
+    @ApiOperation("获取线上支付全局开关状态(旧:/PayOrder/onLineSwitch)")
+    @GetMapping("/online/switch")
+    public ResponseJson<Integer> getPayOnLineSwitch() {
+        return payOrderService.getPayOnLineSwitch();
+    }
+
+    /**
+     * 判断此次支付是否完成
+     *
+     * @param orderId           订单id
+     * @param paySuccessCounter 付款次数
+     */
+    @ApiOperation("判断此次支付是否完成(旧:/PayOrder/payWhetherSuccess)")
+    @ApiImplicitParams({
+            @ApiImplicitParam(required = false, name = "orderId", value = "订单id"),
+            @ApiImplicitParam(required = false, name = "paySuccessCounter", value = "付款次数")
+    })
+    @GetMapping("/result/check")
+    public ResponseJson<String> payWhetherSuccess(Integer orderId, Integer paySuccessCounter) {
+        if (null == orderId) {
+            return ResponseJson.error("订单Id不能为空!", null);
+        }
+        if (null == paySuccessCounter) {
+            return ResponseJson.error("付款次数不能为空!", null);
+        }
+        return payOrderService.payWhetherSuccess(orderId, paySuccessCounter);
+    }
+
+    /**
+     * 查询本次支付订单结果
+     *
+     * @param mbOrderId 平台唯一流水号
+     */
+    @ApiOperation("查询本次支付订单结果(旧:/PayOrder/findOrderStatus)")
+    @ApiImplicitParam(required = false, name = "mbOrderId", value = "平台唯一流水号")
+    @GetMapping("/result/json")
+    public ResponseJson<JSONObject> getPayOrderResult(String mbOrderId) {
+        if (null == mbOrderId) {
+            return ResponseJson.error("平台唯一流水号不能为空!", null);
+        }
+        return heliPayService.getPayOrderResult(mbOrderId);
+    }
+
+    /**
+     * 合利宝公众号/小程序支付
+     */
+    @ApiOperation("合利宝微信小程序/公众号支付")
+    @PostMapping("/online")
+    public ResponseJson<JSONObject> payOnline(@RequestBody HeliDto heliDto, @RequestHeader HttpHeaders headers) {
+        if (null == heliDto.getShopOrderId()) {
+            return ResponseJson.error("子订单Id不能为空!", null);
+        }
+        return heliPayService.payOnline(heliDto, headers);
+    }
+
+    /**
+     * 支付异步通知回调
+     */
+    @ApiOperation("支付异步通知回调(旧:/PayOrder/paymentCallback)")
+    @PostMapping("/callback")
+    public String paymentCallback(NotifyResponseVo res) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
+        if (res == null) {
+            return "回调参数失败";
+        }
+        return heliPayService.paymentCallback(res);
+    }
+
+    /**
+     * 延时分账异步通知回调
+     */
+    @ApiOperation("延时分账异步通知回调(旧:/PayOrder/delayedSplittingCallback)")
+    @PostMapping("/delay/split/callback")
+    public String delayedSplittingCallback(AccountResVo data) {
+        if (null == data) {
+            return "回调参数失败";
+        }
+        return heliPayService.delayedSplittingCallback(data);
+    }
+
+}

+ 1 - 0
src/main/java/com/caimei/controller/OrderSubmitApi.java

@@ -66,6 +66,7 @@ public class OrderSubmitApi {
      *               *   "orderInfo": [
      *               *                   { "shopId":1001,    // 供应商ID
      *               *                     "note":备注,
+     *               *                     "splitCode":分账号,
      *               *                     "productInfo":[   // 商品id,数量,分销者userId
      *               *                         {"productId": 2789, "productNum": 1,"heUserId":1010},
      *               *                         {"productId": 2789, "productNum": 1,"heUserId":0}

+ 4 - 0
src/main/java/com/caimei/mapper/OrderMapper.java

@@ -292,4 +292,8 @@ public interface OrderMapper {
      * @param receiveCouponId
      */
     void revertCoupon(Integer receiveCouponId);
+
+    ShopOrderVo getShopOrderByOrderId(Integer shopOrderId);
+
+    List<DiscernReceiptVo> getShopOrderDiscernReceipt(Integer shopOrderId);
 }

+ 1 - 1
src/main/java/com/caimei/mapper/OrderSubmitMapper.java

@@ -33,7 +33,7 @@ public interface OrderSubmitMapper {
      * @param cartIds
      * @return
      */
-    List<CartProductVo> findByShopCartProduct(@Param("shopId") Integer shopId, @Param("cartIds") String[] cartIds);
+    List<CartProductVo> findByShopCartProduct(@Param("shopId") Integer shopId, @Param("cartIds") String[] cartIds, @Param("splitCode") String splitCode);
 
     /**
      * 查询用户信息

+ 18 - 13
src/main/java/com/caimei/mapper/PayOrderMapper.java

@@ -1,13 +1,8 @@
 package com.caimei.mapper;
 
-import com.caimei.model.po.CmDiscernReceiptPo;
-import com.caimei.model.po.CmPayShopPo;
-import com.caimei.model.po.CmPayShopRecordPo;
-import com.caimei.model.po.CmReceiptOrderRelationPo;
-import com.caimei.model.vo.DiscernReceiptVo;
-import com.caimei.model.vo.OrderProductVo;
-import com.caimei.model.vo.OrderVo;
-import com.caimei.model.vo.SplitAccountVo;
+import com.caimei.model.po.*;
+import com.caimei.model.vo.*;
+import io.swagger.models.auth.In;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
@@ -127,7 +122,7 @@ public interface PayOrderMapper {
      * @param shopOrderId
      * @return
      */
-    BigDecimal findPaidShop(Integer shopOrderId);
+    Double findPaidShop(Integer shopOrderId);
 
     /**
      * 修改子订单付款状态及付款金额
@@ -136,21 +131,21 @@ public interface PayOrderMapper {
      * @param paidShop
      * @param payStatus
      */
-    void updateShopOrderByPayStatus(@Param("shopOrderId") Integer shopOrderId, @Param("paidShop") BigDecimal paidShop, @Param("payStatus") String payStatus);
+    void updateShopOrderByPayStatus(@Param("shopOrderId") Integer shopOrderId, @Param("paidShop") Double paidShop, @Param("payStatus") Integer payStatus);
 
     /**
      * 保存付款单
      *
      * @param payShop
      */
-    void insertPayShop(CmPayShopPo payShop);
+    void insertPayShop(PayShopPo payShop);
 
     /**
      * 保存付款记录
      *
      * @param shopRecord
      */
-    void insertPayShopRecord(CmPayShopRecordPo shopRecord);
+    void insertPayShopRecord(PayShopRecordPo shopRecord);
 
     /**
      * 修改主订单付款状态
@@ -158,5 +153,15 @@ public interface PayOrderMapper {
      * @param orderId
      * @param payStatus
      */
-    void updateOrderByPayStatus(@Param("orderId") Integer orderId, @Param("payStatus") String payStatus);
+    void updateOrderByPayStatus(@Param("orderId") Integer orderId, @Param("payStatus") Integer payStatus);
+
+    String findPayType(String mbOrderId);
+
+    String findShopOrderSplitCode(Integer shopOrderId);
+
+    /**
+     * 更新子订单收款金额和收款状态
+     * @param shopOrder
+     */
+    void updateShopOrderByReceiptStatus(ShopOrderVo shopOrder);
 }

+ 73 - 0
src/main/java/com/caimei/model/dto/HeliDto.java

@@ -0,0 +1,73 @@
+package com.caimei.model.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ */
+@Data
+public class HeliDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 订单Id
+     */
+    @ApiModelProperty("订单Id")
+    private Integer orderId;
+    /**
+     * 支付金额,单位分,必须大于2
+     */
+    @ApiModelProperty("支付金额,单位元,必须大于0.02")
+    private String payAmount;
+    /**
+     * 页面回调地址
+     */
+    @ApiModelProperty("支付成功跳转地址")
+    private String returnUrl;
+    /**
+     * 微信小程序code,微信小程序支付使用
+     */
+    @ApiModelProperty("微信小程序code")
+    private String code;
+    /**
+     * 微信公众号state参数
+     */
+    @ApiModelProperty("微信公众号state参数")
+    private String state;
+    /**
+     * 银行编码(银联支付使用)
+     */
+    @ApiModelProperty("银行编码(银联支付使用)")
+    private String bankCode;
+    /**
+     * 用户类型(银联支付使用)
+     * 企业:B2B
+     * 个人:B2C
+     */
+    @ApiModelProperty("用户类型(银联支付使用)企业:B2B,个人:B2C")
+    private String userType;
+
+
+    @ApiModelProperty("支付类型 微信:WXEWM  支付宝:ZFBEWM 公众号:GZH 小程序:XCX 银联:YL")
+    private String payType;
+
+    /**
+     * 会员购买记录Id
+     */
+    @ApiModelProperty("会员购买记录Id")
+    private Integer vipRecordId;
+    /**
+     * 二手发布商品id
+     */
+    @ApiModelProperty("二手发布商品id")
+    private Integer productId;
+    /**
+     * 子订单Id
+     */
+    @ApiModelProperty("子订单Id")
+    private Integer shopOrderId;
+
+}
+

+ 78 - 0
src/main/java/com/caimei/model/enumerate/BizType.java

@@ -0,0 +1,78 @@
+package com.caimei.model.enumerate;
+
+public enum BizType {
+
+    // 支付宝H5 return "14";
+    // 微信公众号支付 return "13";
+    // 微信小程序支付 return "15";
+    // 企业网银  return "12";
+    // 个人网银 "17";
+
+//    YL("YL", "AppPayPublic", "PUBLIC", "17"), //银联
+    B2B("B2B","","","12"),//企业网银
+    B2C("B2C","","","17"),//个人
+    XCX("XCX", "AppPayApplet", "APPLET", "15"),//小程序
+    GZH("GZH", "AppPayPublic", "PUBLIC", "13"),//公众号
+    ZFBEWM("ZFBEWM", "AppPay", "SCAN", "14"),//支付宝二维码
+    //pc二维码跟公众号一样13
+    WXEWM("WXEWM", "AppPay", "SCAN", "13"),//微信二维码
+    ;
+
+    BizType(String type, String p1, String p4, String num) {
+        this.type = type;
+        this.p1 = p1;
+        this.p4 = p4;
+        this.num = num;
+    }
+
+    public static String getP1ByType(String type) {
+        BizType[] BizType = values();
+        for (BizType bizType : BizType) {
+            if (type.equals(bizType.type)) {
+                return bizType.p1;
+            }
+        }
+        return null;
+    }
+
+    public static String getP4ByType(String type) {
+        BizType[] BizType = values();
+        for (BizType bizType : BizType) {
+            if (type.equals(bizType.type)) {
+                return bizType.p4;
+            }
+        }
+        return null;
+    }
+
+    public static String getNumByType(String type) {
+        BizType[] BizType = values();
+        for (BizType bizType : BizType) {
+            if (type.equals(bizType.type)) {
+                return bizType.num;
+            }
+        }
+        return null;
+    }
+
+    private String type;
+    private String p1;
+    private String p4;
+    private String num;
+
+    public String getP1() {
+        return p1;
+    }
+
+    public String getP4() {
+        return p4;
+    }
+
+    public String getNum() {
+        return num;
+    }
+
+    public String getType() {
+        return type;
+    }
+}

+ 5 - 0
src/main/java/com/caimei/model/po/CmHeHeProductPo.java

@@ -25,6 +25,11 @@ public class CmHeHeProductPo implements Serializable {
      */
     private BigDecimal price;
 
+    /**
+     * 分账号
+     */
+    private String splitCode;
+
     /**
      * 是否含税   0不含税,1含税,2未知
      */

+ 5 - 0
src/main/java/com/caimei/model/po/CmOrderProductPo.java

@@ -308,5 +308,10 @@ public class CmOrderProductPo implements Serializable {
      */
     private Integer heUserId;
 
+    /**
+     * 分账号
+     */
+    private String splitCode;
+
     private static final long serialVersionUID = 1L;
 }

+ 5 - 0
src/main/java/com/caimei/model/po/CmReceiptOrderRelationPo.java

@@ -29,6 +29,11 @@ public class CmReceiptOrderRelationPo implements Serializable {
      */
     private Integer orderID;
 
+    /**
+     * 子订单id
+     */
+    private Integer shopOrderId;
+
     /**
      * 关联方式: 1手动 2自动
      */

+ 25 - 0
src/main/java/com/caimei/model/po/CmShopOrderPo.java

@@ -340,5 +340,30 @@ public class CmShopOrderPo implements Serializable {
      */
     private Integer zeroCostFlag;
 
+    /**
+     * 均摊优惠
+     */
+    private Double eachDiscount;
+
+    /**
+     * 优惠分摊后子订单真实支付金额
+     */
+    private Double realPay;
+
+    /**
+     * 分账号
+     */
+    private String splitCode;
+
+    /**
+     * 收款金额
+     */
+    private Double receiptAmount;
+
+    /**
+     * 收款状态
+     */
+    private Integer receiptStatus;
+
     private static final long serialVersionUID = 1L;
 }

+ 98 - 0
src/main/java/com/caimei/model/po/PayParamBo.java

@@ -0,0 +1,98 @@
+package com.caimei.model.po;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/7/30
+ */
+@Data
+public class PayParamBo implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 用户Id
+     */
+    private Integer userId;
+    /**
+     * 订单Id
+     */
+    private Integer orderId;
+    /**
+     * 二手发布商品id
+     */
+    private Integer productId;
+    /**
+     * 会员购买记录Id
+     */
+    private Integer vipRecordId;
+    /**
+     * 优惠券购买记录id
+     */
+    private Integer couponRecordId;
+    /**
+     * 会员套餐id
+     */
+    private Integer vipId;
+    /**
+     * 支付金额,单位分,必须大于2
+     */
+    private Integer payAmount;
+    /**
+     * 支付方式,
+     * 银联:UNIONPAY,
+     * 微信:WEIXIN,
+     * 支付宝:ALIPAY,
+     * 银联转账:TRANSFER
+     */
+    private String payWay;
+    /**
+     * 支付类型(现阶段主要是区分微信支付)
+     * 微信小程序支付: MINIAPP_WEIXIN
+     * 微信公众号支付: JSAPI_WEIXIN
+     */
+    private String payType;
+    /**
+     * 微信小程序code,微信小程序支付使用
+     */
+    private String code;
+    /**
+     * 微信公众号state参数
+     */
+    private String state;
+    /**
+     * 页面回调地址
+     */
+    private String returnUrl;
+    /**
+     * 微信openId
+     */
+    private String openId;
+    /**
+     * 异步通知回调
+     */
+    private String notifyUrl;
+    /**
+     * 银行编码(银联支付使用)
+     */
+    private String bankCode;
+    /**
+     * 用户类型(银联支付使用)
+     * 企业:ENTERPRISE
+     * 个人:USER
+     */
+    private String userType;
+    /**
+     * 购买优惠券id
+     */
+    private Integer couponId;
+    /**
+     *  1小程序 2网站
+     */
+    @ApiModelProperty("领取渠道1小程序 2网站")
+    private Integer source;
+}

+ 129 - 0
src/main/java/com/caimei/model/po/PayShopPo.java

@@ -0,0 +1,129 @@
+package com.caimei.model.po;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/8/3
+ */
+@Data
+public class PayShopPo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Integer id;
+
+    /**
+     * 供应商Id
+     */
+    private Integer shopId;
+
+    /**
+     * 付款单名称
+     */
+    private String name;
+
+    /**
+     * 付款账号的户名
+     */
+    private String bankAccountName;
+
+    /**
+     * 付款账号
+     */
+    private String bankAccount;
+
+    /**
+     * 付款账号的开户行
+     */
+    private String bankName;
+
+    /**
+     *  付款账号的类型 0公账, 1私账
+     */
+    private Integer type;
+
+    /**
+     * 付供应商总金额
+     */
+    private Double totalAmount;
+
+    /**
+     * 余额支付
+     */
+    private Double balancePayFee;
+
+    /**
+     * 转账支付
+     */
+    private Double transferPayFee;
+
+    /**
+     * 付款方式 1建设银行7297, 2中信银行0897, 3中信银行7172, 4广发银行0115, 5广发银行5461,6线上分账
+     */
+    private Integer payType;
+
+    /**
+     * 付款抹平金额(总)
+     */
+    private Double wipePayment;
+
+    /**
+     * 付款抹平备注(文字)
+     */
+    private String wipeRemarks;
+
+    /**
+     * 付款抹平备注(图片),以"##"隔开
+     */
+    private String wipeRemarkImages;
+
+    /**
+     * 抹平申请时间
+     */
+    private String wipeTime;
+
+    /**
+     * 申请人ID
+     */
+    private Integer applicant;
+
+    /**
+     * 申请时间
+     */
+    private String applyTime;
+
+    /**
+     * 审核人ID
+     */
+    private Integer reviewer;
+
+    /**
+     * 审核时间
+     */
+    private String reviewTime;
+
+    /**
+     * 付款时间
+     */
+    private String payTime;
+
+    /**
+     * 审核状态  0待审核,  1审核通过  2审核不通过
+     */
+    private Integer status;
+
+    /**
+     * 审核不通过原因
+     */
+    private String reason;
+
+    /**
+     * 删除标记 0 否,其余是
+     */
+    private Integer delFlag;
+
+}

+ 58 - 0
src/main/java/com/caimei/model/po/PayShopRecordPo.java

@@ -0,0 +1,58 @@
+package com.caimei.model.po;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/8/3
+ */
+@Data
+public class PayShopRecordPo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Integer id;
+    /**
+     * 供应商Id
+     */
+    private Integer shopId;
+    /**
+     * 子订单ID
+     */
+    private Integer shopOrderId;
+    /**
+     * 子订单编号
+     */
+    private String shopOrderNo;
+    /**
+     * 付款金额
+     */
+    private Double payAmount;
+    /**
+     * 付款抹平金额
+     */
+    private Double wipePayment;
+    /**
+     * 付款方式 1建设银行7297, 2中信银行0897, 3中信银行7172, 4广发银行0115, 5广发银行5461,6线上分账
+     */
+    private Integer payType;
+    /**
+     * 付款时间
+     */
+    private String payTime;
+    /**
+     * 付款单表id
+     */
+    private Integer payShopId;
+    /**
+     * 0待审核, 1审核通过, 2审核不通过
+     */
+    private Integer status;
+    /**
+     * 删除标记 0 否,其余是
+     */
+    private Integer delFlag;
+}

+ 79 - 0
src/main/java/com/caimei/model/po/SplitAccountPo.java

@@ -0,0 +1,79 @@
+package com.caimei.model.po;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/8/3
+ */
+@Data
+public class SplitAccountPo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Integer id;
+    /**
+     * 主订单id
+     */
+    private Integer orderId;
+    /**
+     * 商品id,仅对二手发布商品
+     */
+    private Integer productId;
+    /**
+     * 订单商品id
+     */
+    private Integer orderProductId;
+    /**
+     * 供应商id
+     */
+    private Integer shopId;
+    /**
+     * 超级会员套餐id
+     */
+    private Integer vipRecordId;
+    /**
+     * 优惠券购买记录id
+     */
+    private Integer couponRecordId;
+    /**
+     * 认证通会员购买记录id
+     */
+    private Integer authVipRecordId;
+    /**
+     * 分账类型:1公账-专票,2私账-无票,3公账-普票,4供应商子商户,5佣金账户
+     */
+    private Integer type;
+    /**
+     * 子商户商编
+     */
+    private String subUserNo;
+    /**
+     * 分账金额
+     */
+    private Double splitAccount;
+    /**
+     * 米花科技平台唯一流水号
+     */
+    private String mbOrderId;
+    /**
+     * 商户唯一订单请求号(订单编号#随机时间戳)
+     */
+    private String orderRequestNo;
+    /**
+     * 付款状态,0待付,1付款成功
+     */
+    private Integer payStatus;
+    /**
+     * 商品类型:1商品成本,2供应商运费,3佣金,4二手发布
+     */
+    private Integer productType;
+    /**
+     * 分账时间
+     */
+    private Date splitTime;
+}

+ 26 - 0
src/main/java/com/caimei/model/vo/AccountResVo.java

@@ -0,0 +1,26 @@
+package com.caimei.model.vo;
+
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+@Setter
+@Getter
+@ToString
+public class AccountResVo {
+  private String  rt1_bizType;
+  private String  rt2_signType;
+  private String  rt3_timestamp;
+  private String  rt4_success;
+  private String  rt5_retCode;
+  private String  rt6_retMsg;
+  private String  rt7_orderId;
+  private String  rt8_customerNumber;
+  private String  rt9_serialNumber;
+  private String  rt10_orderStatus;
+  private String  rt11_amount;
+  private String  rt12_reason;
+  private String  rt13_ext;
+  private String  sign;
+}

+ 49 - 0
src/main/java/com/caimei/model/vo/AppCreateOrderResponseVo.java

@@ -0,0 +1,49 @@
+package com.caimei.model.vo;
+
+import com.google.common.collect.ImmutableSet;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.util.Set;
+
+@Setter
+@Getter
+@ToString
+public class AppCreateOrderResponseVo {
+    /**
+     * 扫码主被扫响应参数
+     */
+    private String rt1_bizType;
+    private String rt2_retCode;
+    private String rt3_retMsg;            //排除签名
+    private String rt4_customerNumber;
+    private String rt5_orderId;
+    private String rt6_serialNumber;
+    private String rt7_payType;
+    private String rt8_qrcode;
+    private String rt9_wapurl;
+    private String rt10_orderAmount;
+    private String rt11_currency;
+    private String rt12_openId;             //排除签名
+    private String rt13_orderStatus;        //排除签名
+    private String rt14_fundBillList;       //排除签名
+    private String rt15_channelRetCode;     //排除签名
+    private String rt16_outTransactionOrderId;  //排除签名
+    private String rt17_bankType;               //排除签名
+    private String rt18_subOpenId;              //排除签名
+    private String onlineCardType;              //排除签名
+    /**
+     * 排除签名
+     */
+    private String subMerchantNo;
+    private String sign;
+
+    /**
+     * 需要加签的属性参数,要求加签的参数空值也签名
+     * 看接口文档
+     */
+    public static final Set<String> NEED_SIGN_PARAMS = ImmutableSet.of("rt1_bizType", "rt2_retCode",
+            "rt4_customerNumber", "rt5_orderId", "rt6_serialNumber", "rt7_payType",
+            "rt8_qrcode", "rt9_wapurl", "rt10_orderAmount", "rt11_currency");
+}

+ 64 - 0
src/main/java/com/caimei/model/vo/AppCreateOrderVo.java

@@ -0,0 +1,64 @@
+package com.caimei.model.vo;
+
+import com.google.common.collect.ImmutableSet;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.util.Set;
+
+@Setter
+@Getter
+@ToString
+public class AppCreateOrderVo {
+
+
+    private String P1_bizType;
+    private String P2_orderId;
+    private String P3_customerNumber;
+    private String P4_payType;
+    private String P5_orderAmount;
+    private String P6_currency;
+    private String P7_authcode;
+    private String P8_appType;
+    private String P9_notifyUrl;
+    private String P10_successToUrl;
+    private String P11_orderIp;
+    private String P12_goodsName;
+    private String P13_goodsDetail;
+    private String P14_desc;
+    /**特殊参数,排除签名*/
+    private String P15_subMerchantId;
+    private String P16_appId;
+    private String P17_limitCreditPay;
+    private String P18_goodsTag;
+    private String P19_guid;
+    private String P20_marketingRule;
+    private String P21_identity;
+    private String hbfqNum;
+    private String deviceInfo;
+    private String timeExpire;
+    /**支付宝行业数据回流信息,排除签名*/
+    private String industryRefluxInfo;
+    /**排除签名*/
+    private String termInfo;
+    /**预授权场景参数,排除签名*/
+    private String openId;
+    /**预授权场景参数,排除签名*/
+    private String authConfirmMode;
+    /**商户自定义门店编码,排除签名*/
+    private String storeId;
+    private String sign;
+    /**分账类型FIXED_AMOUNT:固定金额*/
+    private String splitBillType;
+    /**json数组格式字符串*/
+    private String ruleJson;
+    /**
+     * 需要加签的属性参数,要求加签的参数空值也签名
+     * 看接口文档
+     */
+    public static final Set<String> NEED_SIGN_PARAMS = ImmutableSet.of("P1_bizType",
+            "P2_orderId", "P3_customerNumber", "P4_payType", "P5_orderAmount",
+            "P6_currency", "P7_authcode", "P8_appType", "P9_notifyUrl", "P10_successToUrl",
+            "P11_orderIp", "P12_goodsName", "P13_goodsDetail", "P14_desc");
+}

+ 69 - 0
src/main/java/com/caimei/model/vo/AppPayPublicCreateOrderVo.java

@@ -0,0 +1,69 @@
+package com.caimei.model.vo;
+
+import com.google.common.collect.ImmutableSet;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.util.Set;
+
+@Setter
+@Getter
+@ToString
+public class AppPayPublicCreateOrderVo {
+
+
+    private String P1_bizType;
+    private String P2_orderId;
+    private String P3_customerNumber;
+    private String P4_payType;
+    private String P5_appid;
+    private String P6_deviceInfo;
+    private String P7_isRaw;
+    private String P8_openid;
+    private String P9_orderAmount;
+    private String P10_currency;
+    private String P11_appType;
+    private String P12_notifyUrl;
+    private String P13_successToUrl;
+    private String P14_orderIp;
+    private String P15_goodsName;
+    private String P16_goodsDetail;
+    private String P17_limitCreditPay;
+    private String P18_desc;
+    private String P19_subscribeAppId;
+    /**特殊参数,排除签名*/
+    private String P20_subMerchantId;
+    private String P21_goodsTag;
+    private String P22_guid;
+    private String timeExpire;
+    /**支付宝行业数据回流信息,排除签名*/
+    private String industryRefluxInfo;
+    /**支付宝点餐码 排除签名*/
+    private String foodOrderType;
+    /**排除签名*/
+    private String termInfo;
+    /**分账类型FIXED_AMOUNT:固定金额*/
+    private String splitBillType;
+    /**json数组格式字符串*/
+    private String ruleJson;
+    /**
+     * 银联js用户id类型
+     * userAuthCode:表示为临时授权码;openId:表示为用户id
+     * 不送默认是userAuthCode
+     * 排除签名
+     */
+    private String openIdType;
+    /**商户自定义门店编码,排除签名*/
+    private String storeId;
+    private String sign;
+
+    /**
+     * 需要加签的属性参数,要求加签的参数空值也签名
+     * 看接口文档
+     */
+    public static final Set<String> NEED_SIGN_PARAMS = ImmutableSet.of("P1_bizType", "P2_orderId", "P3_customerNumber",
+            "P4_payType", "P5_appid", "P6_deviceInfo", "P7_isRaw", "P8_openid", "P9_orderAmount", "P10_currency",
+            "P11_appType", "P12_notifyUrl", "P13_successToUrl", "P14_orderIp", "P15_goodsName", "P16_goodsDetail", "P17_limitCreditPay",
+            "P18_desc");
+}

+ 42 - 0
src/main/java/com/caimei/model/vo/AppPayPublicOrderResponseVo.java

@@ -0,0 +1,42 @@
+package com.caimei.model.vo;
+
+import com.google.common.collect.ImmutableSet;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.util.Set;
+
+@Setter
+@Getter
+@ToString
+public class AppPayPublicOrderResponseVo {
+
+
+    private String rt1_bizType;
+    private String rt2_retCode;
+    private String rt3_retMsg;
+    private String rt4_customerNumber;
+    private String rt5_orderId;
+    private String rt6_serialNumber;
+    private String rt7_payType;
+    private String rt8_appid;
+    private String rt9_tokenId;
+    private String rt10_payInfo;
+    private String rt11_orderAmount;
+    private String rt12_currency;
+    private String rt13_channelRetCode;
+    /**
+     * 排除签名
+     */
+    private String subMerchantNo;
+    private String sign;
+
+    /**
+     * 需要加签的属性参数,要求加签的参数空值也签名
+     * 看接口文档
+     */
+    public static final Set<String> NEED_SIGN_PARAMS = ImmutableSet.of("rt1_bizType", "rt2_retCode", "rt4_customerNumber",
+            "rt5_orderId", "rt6_serialNumber", "rt7_payType", "rt8_appid", "rt9_tokenId", "rt10_payInfo", "rt11_orderAmount",
+            "rt12_currency");
+}

+ 22 - 0
src/main/java/com/caimei/model/vo/BankCodeVo.java

@@ -0,0 +1,22 @@
+package com.caimei.model.vo;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+@Setter
+@Getter
+@ToString
+@Data
+public class BankCodeVo {
+    private String id;
+    private String bankName;
+    //B2C通道编码
+    private String B2C;
+    //B2B通道编码
+    private String B2B;
+    //银行logo
+    private String bankLogo;
+    private String delFlag;
+}

+ 42 - 0
src/main/java/com/caimei/model/vo/HeliOnlineVo.java

@@ -0,0 +1,42 @@
+package com.caimei.model.vo;
+
+import com.google.common.collect.ImmutableSet;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.util.Set;
+
+@Setter
+@Getter
+@ToString
+public class HeliOnlineVo {
+    private String P1_bizType;
+    private String P2_orderId;
+    private String P3_customerNumber;
+    private String P4_orderAmount;
+    //银行编码
+    private String P5_bankId;
+    //业务类型 B2C:个人支付 B2B:企业支付
+    private String P6_business;
+    private String P7_timestamp;
+    private String P8_goodsName;
+    //有效期 Number(10)	1
+    private String P9_period;
+    //有效期单位 Day:天 Hour:时 Minute:分
+    private String P10_periodUnit;
+    //成功通知地址
+    private String P11_callbackUrl;
+    //异步回调地址
+    private String P12_serverCallback;
+    private String P13_orderIp;
+    private String P14_onlineCardType;
+    private String P15_desc;
+    //String(20)	MD5 不参与签名
+    private String P17_signatureType;
+    private String sign;
+
+    public static final Set<String> NEED_SIGN_PARAMS = ImmutableSet.of("P1_bizType", "P2_orderId", "P3_customerNumber",
+            "P4_orderAmount", "P5_bankId", "P6_business", "P7_timestamp", "P8_goodsName", "P9_period", "P10_periodUnit",
+            "P11_callbackUrl", "P12_serverCallback", "P13_orderIp", "P14_onlineCardType", "P15_desc");
+}

+ 38 - 0
src/main/java/com/caimei/model/vo/NotifyResponseVo.java

@@ -0,0 +1,38 @@
+package com.caimei.model.vo;
+
+
+import com.google.common.collect.ImmutableSet;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.util.Set;
+
+@Setter
+@Getter
+@ToString
+public class NotifyResponseVo {
+
+    private String rt1_customerNumber;
+    private String rt2_orderId;
+    private String rt3_systemSerial;
+    //INIT:已接收
+    //DOING:处理中
+    //SUCCESS:成功
+    //FAIL:失败
+    //CLOSE:关闭
+    //CANCEL:撤销
+    private String rt4_status;
+    private String rt5_orderAmount;
+    private String rt6_currency;
+    private String rt7_timestamp;
+    private String rt8_desc;
+    private String sign;
+
+    /**
+     * 需要加签的属性参数,要求加签的参数空值也签名
+     * 看接口文档
+     */
+    public static final Set<String> NEED_SIGN_PARAMS = ImmutableSet.of("rt1_customerNumber", "rt2_orderId",
+            "rt3_systemSerial", "rt4_status", "rt5_orderAmount", "rt6_currency", "rt7_timestamp", "rt8_desc");
+}

+ 8 - 3
src/main/java/com/caimei/model/vo/OrderVo.java

@@ -46,7 +46,7 @@ public class OrderVo implements Serializable {
     /**
      * (收款买家)收款状态:1待收款、2部分收款、3已收款
      */
-    private String receiptStatus;
+    private Integer receiptStatus;
 
     /**
      * (付款供应商)付款状态:1待付款、2部分付款、3已付款
@@ -71,12 +71,12 @@ public class OrderVo implements Serializable {
     /**
      * 是否已支付 未支付0 已支付1
      */
-    private String payFlag;
+    private Integer payFlag;
 
     /**
      * 是否能走线上支付 0可以 1不可以 只能线下
      */
-    private String onlinePayFlag;
+    private Integer onlinePayFlag;
 
     /**
      * 订单总额(小计金额减去经理折扣后,再加上运费)
@@ -265,5 +265,10 @@ public class OrderVo implements Serializable {
      */
     private BigDecimal reductionAmount;
 
+    /**
+     * 更新时间
+     */
+    private String updateDate;
+
     private static final long serialVersionUID = 1L;
 }

+ 16 - 0
src/main/java/com/caimei/model/vo/PaySplitVo.java

@@ -0,0 +1,16 @@
+package com.caimei.model.vo;
+
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+@Setter
+@Getter
+@ToString
+@Data
+public class PaySplitVo {
+    private String splitBillMerchantEmail;
+    private String splitBillAmount;
+}

+ 50 - 0
src/main/java/com/caimei/model/vo/QueryOrderResponseVo.java

@@ -0,0 +1,50 @@
+package com.caimei.model.vo;
+
+import com.google.common.collect.ImmutableSet;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.util.Set;
+
+@Setter
+@Getter
+@ToString
+public class QueryOrderResponseVo {
+
+    private String rt1_bizType;
+    private String rt2_retCode;
+    private String rt3_retMsg;
+    private String rt4_customerNumber;
+    private String rt5_orderId;
+    private String rt6_serialNumber;
+    private String rt7_orderStatus;
+    private String rt8_orderAmount;
+    private String rt9_currency;
+    private String rt10_desc;
+    private String rt11_openId;
+    private String rt12_channelOrderNum;
+    private String rt13_orderCompleteDate;
+    private String rt14_cashFee;
+    private String rt15_couponFee;
+    private String rt16_onlineCardType;
+    private String rt17_fundBillList;
+    private String rt18_outTransactionOrderId;
+    private String rt19_bankType;
+    private String rt20_subOpenId;
+    /**
+     * 排除签名
+     */
+    private String subMerchantNo;
+    /**渠道上游返回码 不签名*/
+    private String channelRetCode;
+    /**渠道上游返回信息 不签名*/
+    private String channelRetMsg;
+    private String sign;
+    /**
+     * 需要加签的属性参数
+     * 看接口文档
+     */
+    public static final Set<String> NEED_SIGN_PARAMS = ImmutableSet.of("rt1_bizType", "rt2_retCode", "rt4_customerNumber",
+            "rt5_orderId", "rt6_serialNumber", "rt7_orderStatus", "rt8_orderAmount", "rt9_currency");
+}

+ 27 - 0
src/main/java/com/caimei/model/vo/QueryOrderVo.java

@@ -0,0 +1,27 @@
+package com.caimei.model.vo;
+
+
+import com.google.common.collect.ImmutableSet;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.util.Set;
+
+@Setter
+@Getter
+@ToString
+public class QueryOrderVo {
+
+    private String P1_bizType;
+    private String P2_orderId;
+    private String P3_customerNumber;
+    private String P4_serialNumber;
+    private String sign;
+
+    /**
+     * 需要加签的属性参数
+     * 看接口文档
+     */
+    public static final Set<String> NEED_SIGN_PARAMS = ImmutableSet.of("P1_bizType", "P2_orderId", "P3_customerNumber");
+}

+ 27 - 2
src/main/java/com/caimei/model/vo/ShopOrderVo.java

@@ -96,6 +96,21 @@ public class ShopOrderVo implements Serializable {
      */
     private String payFlag;
 
+    /**
+     * 用户已付金额
+     */
+    private Double receiptAmount;
+
+    /**
+     * 优惠均摊后真实支付金额needpay
+     */
+    private Double realPay;
+
+    /**
+     * (收款买家)收款状态:1待收款、2部分收款、3已收款
+     */
+    private Integer receiptStatus;
+
     /**
      * 订单提交时间
      */
@@ -114,7 +129,7 @@ public class ShopOrderVo implements Serializable {
     /**
      * (付款供应商)付款状态:1待付款、2部分付款、3已付款
      */
-    private String payStatus;
+    private Integer payStatus;
 
     /**
      * 发货状态:1待发货、2部分发货、3已发货
@@ -222,7 +237,7 @@ public class ShopOrderVo implements Serializable {
     /**
      * 已付款金额
      */
-    private BigDecimal payedShopAmount;
+    private Double payedShopAmount;
 
     /**
      * 固定成本1,  比例成本2  为空就是还没有设置过
@@ -256,5 +271,15 @@ public class ShopOrderVo implements Serializable {
      */
     private List<LogisticsRecordVo> logisticsRecordList;
 
+    /**
+     * 均摊优惠
+     */
+    private Double eachDiscount;
+
+    /**
+     * 剩余应付金额
+     */
+    private Double restAmount;
+
     private static final long serialVersionUID = 1L;
 }

+ 5 - 0
src/main/java/com/caimei/model/vo/ShopVo.java

@@ -44,6 +44,11 @@ public class ShopVo implements Serializable {
      */
     private boolean checked = false;
 
+    /**
+     * 分账号
+     */
+    private String splitCode;
+
     /**
      * 供应商下商品信息
      */

+ 34 - 0
src/main/java/com/caimei/service/HeliPayService.java

@@ -0,0 +1,34 @@
+package com.caimei.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.caimei.model.ResponseJson;
+import com.caimei.model.dto.HeliDto;
+import com.caimei.model.vo.AccountResVo;
+import com.caimei.model.vo.NotifyResponseVo;
+import org.springframework.http.HttpHeaders;
+
+import java.beans.IntrospectionException;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Description
+ *
+ * @author : zzj
+ * @date : 2021/11/11
+ */
+public interface HeliPayService {
+
+    ResponseJson<JSONObject> payOnline(HeliDto heliDto, HttpHeaders headers);
+    /**
+     * 回调
+     */
+    String paymentCallback(NotifyResponseVo res) throws IntrospectionException, InvocationTargetException, IllegalAccessException;
+
+    /**
+     * 延时分账异步回调
+     */
+    String delayedSplittingCallback(AccountResVo data);
+
+    ResponseJson<JSONObject> getPayOrderResult(String mbOrderId);
+
+}

+ 529 - 0
src/main/java/com/caimei/service/impl/HeliPayServiceImpl.java

@@ -0,0 +1,529 @@
+package com.caimei.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.caimei.components.HeliPayUtil;
+import com.caimei.components.WeChatService;
+import com.caimei.constant.Constant;
+import com.caimei.mapper.CollageMapper;
+import com.caimei.mapper.CouponMapper;
+import com.caimei.mapper.OrderMapper;
+import com.caimei.mapper.PayOrderMapper;
+import com.caimei.model.ResponseJson;
+import com.caimei.model.dto.HeliDto;
+import com.caimei.model.po.*;
+import com.caimei.model.vo.*;
+import com.caimei.service.HeliPayService;
+import com.caimei.util.HttpRequest;
+import com.caimei.util.MathUtil;
+import com.caimei.util.helipay.Disguiser;
+import com.caimei.util.helipay.HttpClientService;
+import com.caimei.util.helipay.MyBeanUtils;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.OkHttpClient;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpStatus;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.beans.IntrospectionException;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Description
+ *
+ * @author : zzj
+ * @date : 2021/11/11
+ */
+@Slf4j
+@Service
+public class HeliPayServiceImpl implements HeliPayService {
+
+    public static OkHttpClient client = new OkHttpClient.Builder()
+            .connectTimeout(3, TimeUnit.SECONDS)
+            .readTimeout(20, TimeUnit.SECONDS)
+            .build();
+    @Resource
+    private OrderMapper orderMapper;
+    @Resource
+    private HeliPayUtil heliPayUtil;
+    @Resource
+    private PayOrderMapper payOrderMapper;
+    @Resource
+    private CouponMapper couponMapper;
+    @Resource
+    private CollageMapper collageMapper;
+    @Resource
+    private WeChatService weChatService;
+
+    @Value("${caimei.userUrl}")
+    private String userUrl;
+
+
+    @Override
+    public ResponseJson<JSONObject> payOnline(HeliDto heliDto, HttpHeaders headers) {
+        log.info("--------进入公众号/小程序预创建订单接口----------");
+        try {
+            AppPayPublicCreateOrderVo pay = new AppPayPublicCreateOrderVo();
+            String splitCode = payOrderMapper.findShopOrderSplitCode(heliDto.getShopOrderId());
+            pay.setP3_customerNumber(splitCode);
+            //合利宝主扫接口参数赋值
+            heliPayUtil.setOnlineValue(pay, heliDto, "order", headers);
+            if ("GZH".equals(heliDto.getPayType()) || "XCX".equals(heliDto.getPayType())) {
+                //公众号/小程序
+                String openId = getOpenId(heliDto, headers);
+                if (StringUtils.isEmpty(openId)) {
+                    return ResponseJson.error("微信openId获取失败", null);
+                }
+                pay.setP8_openid(openId);
+            }
+            Map<String, String> map = MyBeanUtils.convertBean(pay, new LinkedHashMap());
+            String oriMessage = MyBeanUtils.getSignedByPresetParameter(map, AppPayPublicCreateOrderVo.NEED_SIGN_PARAMS);
+            oriMessage += Constant.SPLIT + Constant.SAOMA;
+            log.info("签名原文串:" + oriMessage);
+            String sign = Disguiser.disguiseMD5(oriMessage.trim());
+            log.info("签名串:" + sign);
+            map.put("sign", sign);
+            log.info("发送参数:" + map);
+            Map<String, Object> resultMap = HttpClientService.getHttpResp(map, Constant.REQUEST_URL);
+            log.info("响应结果:" + resultMap);
+            if ((Integer) resultMap.get("statusCode") == HttpStatus.SC_OK) {
+                String resultMsg = (String) resultMap.get("response");
+                AppPayPublicOrderResponseVo orderResponseVo = JSONObject.parseObject(resultMsg, AppPayPublicOrderResponseVo.class);
+                String assemblyRespOriSign = MyBeanUtils.getSignedByPresetParameter(orderResponseVo, AppPayPublicOrderResponseVo.NEED_SIGN_PARAMS);
+                assemblyRespOriSign += Constant.SPLIT + Constant.SAOMA;
+                log.info("组装返回结果签名串:" + assemblyRespOriSign);
+                String responseSign = orderResponseVo.getSign();
+                log.info("响应签名:" + responseSign);
+                String checkSign = Disguiser.disguiseMD5(assemblyRespOriSign.trim());
+                if (checkSign.equals(responseSign) && "0000".equals(orderResponseVo.getRt2_retCode())) {
+                    JSONObject jsonObject = JSONObject.parseObject(resultMsg);
+                    return ResponseJson.success("请求成功", jsonObject);
+                } else {
+                    return ResponseJson.error("请求参数有误", JSONObject.parseObject(resultMsg));
+                }
+            } else {
+                return ResponseJson.error("请求失败", null);
+            }
+        } catch (Exception e) {
+            log.error("交易失败" + e);
+            return ResponseJson.error("交易失败", null);
+        }
+    }
+
+    @Override
+    public String paymentCallback(NotifyResponseVo res) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
+        log.info("******************** 支付异步回调 start *******************");
+        // 签名验证
+        String sign = res.getSign();
+        log.info("回调签名" + sign);
+        String oriMessage = MyBeanUtils.getSigned(res, null);
+        String oriMessage1 = oriMessage +Constant.SPLIT + Constant.SAOMA;
+        String oriMessage2 = oriMessage +Constant.SPLIT + Constant.FENZHANG;
+        String oriMessage3 = oriMessage +Constant.SPLIT + Constant.XUNI;
+        String oriMessage4 = oriMessage +Constant.SPLIT + Constant.WANGYIN;
+        String checkSign1 = Disguiser.disguiseMD5(oriMessage1.trim());
+        String checkSign2 = Disguiser.disguiseMD5(oriMessage2.trim());
+        String checkSign3 = Disguiser.disguiseMD5(oriMessage3.trim());
+        String checkSign4 = Disguiser.disguiseMD5(oriMessage4.trim());
+        boolean b = sign.equals(checkSign1) || sign.equals(checkSign2) || sign.equals(checkSign3) || sign.equals(checkSign4);
+        if (!b) {
+            return "验签名失败!";
+        }
+        // 订单状态 INIT:已接收 DOING:处理中 SUCCESS:成功 FAIL:失败 CLOSE:关闭 CANCEL:撤销
+        String orderStatus = res.getRt4_status();
+        // 平台唯一流水号
+        String mbOrderId = res.getRt3_systemSerial();
+        // 商户唯一订单号
+        String orderRequestNo = res.getRt2_orderId();
+        // 本次支付金额,以元为单位
+        BigDecimal amount = new BigDecimal(res.getRt5_orderAmount());
+        log.info("【支付异步回调】>>>>>>>>>>>>>>支付订单状态:" + orderStatus);
+        if (!"SUCCESS".equals(orderStatus)) {
+            return "支付失败";
+        }
+        String[] split = res.getRt8_desc().split(",");
+        //0位置订单id
+        Integer orderId = Integer.valueOf(split[0]);
+        //1位置支付类型
+        String payType = split[1];
+        //2位置子订单id
+        Integer shopOrderId = Integer.valueOf(split[2]);
+        // 订单信息
+        OrderVo order = orderMapper.findOrder(orderId);
+        ShopOrderVo shopOrder = orderMapper.getShopOrderByOrderId(shopOrderId);
+        if (null == order) {
+            return "订单不存在";
+        }
+        if(null == shopOrder){
+            return "子订单不存在";
+        }
+        // 支付记录
+        List<DiscernReceiptVo> discernReceiptList = payOrderMapper.getDiscernReceipt(order);
+        double receiptAmount = 0d;
+        if (null != discernReceiptList && discernReceiptList.size() > 0) {
+            for (DiscernReceiptVo discernReceipt : discernReceiptList) {
+                receiptAmount = MathUtil.add(receiptAmount, discernReceipt.getAssociateAmount()).doubleValue();
+            }
+        }
+        order.setReceiptAmount(MathUtil.add(receiptAmount, amount));
+        Date date = new Date();
+        String curDateStr = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(date);
+        if (MathUtil.compare(order.getPayableAmount(), order.getReceiptAmount()) == 0) {
+            /*
+             * 订单全部支付
+             * 0待确认,11待收待发,12待收部发,13待收全发,21部收待发,22部收部发,23部收全发,
+             * 31已收待发,32已收部发,33已收全发,4交易完成,5订单完成,6已关闭,7交易全退
+             */
+            if ("11".equals(order.getStatus()) || "21".equals(order.getStatus())) {
+                order.setStatus("31");
+            } else if ("12".equals(order.getStatus()) || "22".equals(order.getStatus())) {
+                order.setStatus("32");
+            } else {
+                order.setStatus("33");
+            }
+            order.setPayFlag(1);
+            order.setOnlinePayFlag(0);
+            //(收款买家)收款状态:1待收款、2部分收款、3已收款
+            order.setReceiptStatus(3);
+            log.info("【支付异步回调】>>>>>>>>>>>>>>订单(全部支付),修改订单状态:" + order.getStatus() + ",orderId:" + orderId);
+            /*
+             * 保存好友消费券赠送记录
+             */
+            CouponSharePo couponShare = couponMapper.getCouponShareId(order.getUserId());
+            if (null != couponShare) {
+                List<Integer> couponIds = couponMapper.getCurrentCouponIds(5);
+                couponIds.forEach(couponId->{
+                    couponShare.setShareCouponId(couponId);
+                    couponMapper.insertCouponShare(couponShare);
+                    BigDecimal couponAmount = couponMapper.getCouponAmount(couponId);
+                    HashMap<String, Object> map = new HashMap<>();
+                    String mobile = couponMapper.getUserMobile(couponShare.getShareUserId());
+                    String content = "恭喜您邀请的好友已成功消费一笔订单,现赠送" + couponAmount + "元优惠券到您的领券中心,请赶紧登录呵呵商城小程序领取下单吧。退订回T";
+                    map.put("type", 3);
+                    map.put("mobile", mobile);
+                    map.put("content", content);
+                    String url = userUrl + "/tools/sms/send";
+                    try {
+                        String result = HttpRequest.sendPost(url, map);
+                        log.info("【呵呵好友消费券派送】mobile:" + mobile + ",result:" + result);
+                        // 保存短信发送条数+count
+                        couponMapper.updateSmsSendCount(19, 1);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                });
+            }
+            // 拼团数据更新
+            CmHeheCollagePo collage = collageMapper.getCollageByOrderId(orderId);
+            if (null != collage) {
+                if (1 == collage.getLaunchFlag()) {
+                    // 拼团发起人支付订单,正式建立拼团
+                    collage.setStatus(1);
+                    Date startTime = new Date();
+                    collage.setStartTime(startTime);
+                    Calendar calendar = Calendar.getInstance();
+                    calendar.setTime(startTime);
+                    calendar.add(Calendar.DAY_OF_MONTH, 1);
+                    collage.setEndTime(calendar.getTime());
+                    collageMapper.createCollage(collage);
+                } else {
+                    // 已拼且已支付人数
+                    Integer memberNum = collageMapper.findCollageMemberNum(collage.getId());
+                    if (memberNum + 1 == collage.getMemberNum()) {
+                        // 拼团成功
+                        collage.setStatus(2);
+                        collage.setCompleteTime(new Date());
+                        collageMapper.completeCollage(collage);
+                        // 关闭其它未支付拼团订单
+                        List<Integer> orderIdList = collageMapper.findNoPayCollageOrderIds(collage.getId());
+                        orderIdList.forEach(noPayOrderId->{
+                            orderMapper.cancelOrder(noPayOrderId, "拼团完成关闭其它未支付拼团订单");
+                        });
+                        HashMap<String, Object> map = new HashMap<>();
+                        String mobile = collageMapper.getUserMobileByOrderId(orderId);
+                        List<String> mobileList = collageMapper.getMobilesByCollageId(collage.getId());
+                        mobileList.add(mobile);
+                        String content = "您的商品已拼团成功,请赶紧登录呵呵商城小程序查看订单吧。";
+                        map.put("type", 1);
+                        map.put("content", content);
+                        String url = userUrl + "/tools/sms/send";
+                        try {
+                            for (String userMobile : mobileList) {
+                                map.put("mobile", userMobile);
+                                String result = HttpRequest.sendPost(url, map);
+                                log.info("【呵呵拼团成功短信派送】mobile:" + mobile + ",result:" + result);
+                            }
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        }
+                    }
+                }
+            }
+        } else {
+            //部分支付
+            if ("11".equals(order.getStatus()) || "21".equals(order.getStatus())) {
+                order.setStatus("21");
+            } else if ("12".equals(order.getStatus()) || "22".equals(order.getStatus())) {
+                order.setStatus("22");
+            } else {
+                order.setStatus("23");
+            }
+            order.setOnlinePayFlag(0);
+            //(收款买家)收款状态:1待收款、2部分收款、3已收款
+            order.setReceiptStatus(2);
+            log.info("【支付异步回调】>>>>>>>>>>>>>>订单(部分支付),修改订单状态:" + order.getStatus() + ",orderId:" + orderId);
+        }
+        // 更新付款次数
+        order.setPaySuccessCounter(order.getPaySuccessCounter() + 1);
+        order.setUpdateDate(curDateStr);
+        payOrderMapper.updateSelective(order);
+        // 支付记录
+        // todo 子订单返佣????
+        List<DiscernReceiptVo> shopOrderDiscernReceiptList = orderMapper.getShopOrderDiscernReceipt(shopOrderId);
+        double shopOrderReceiptAmount = 0d;
+        if (null != shopOrderDiscernReceiptList && shopOrderDiscernReceiptList.size() > 0) {
+            for (DiscernReceiptVo discernReceipt : shopOrderDiscernReceiptList) {
+                shopOrderReceiptAmount = MathUtil.add(shopOrderReceiptAmount, discernReceipt.getAssociateAmount()).doubleValue();
+            }
+        }
+        shopOrder.setReceiptAmount(MathUtil.add(shopOrderReceiptAmount,amount).doubleValue());
+        //均摊后needpay:已支付
+        if (MathUtil.compare(shopOrder.getRealPay(), shopOrder.getReceiptAmount()) == 0) {
+            shopOrder.setReceiptStatus(3);
+        }else{
+            shopOrder.setReceiptStatus(2);
+        }
+        // 更新子订单收款金额和收款状态
+        payOrderMapper.updateShopOrderByReceiptStatus(shopOrder);
+        //保存收款记录
+        CmDiscernReceiptPo discernReceipt = new CmDiscernReceiptPo();
+        discernReceipt.setPayWay("1");
+        discernReceipt.setPayType(payType);
+        discernReceipt.setReceiptType("1");
+        discernReceipt.setReceiptStatus("3");
+        discernReceipt.setReceiptAmount(amount);
+        discernReceipt.setConfirmType("4");
+        discernReceipt.setRePayFlag("1");
+        discernReceipt.setFormData(JSONObject.toJSONString(res));
+        discernReceipt.setReceiptDate(curDateStr);
+        discernReceipt.setConfirmDate(curDateStr);
+        discernReceipt.setReviewDate(curDateStr);
+        discernReceipt.setUpdateDate(curDateStr);
+        discernReceipt.setDelFlag("0");
+        payOrderMapper.insertDiscernReceipt(discernReceipt);
+        CmReceiptOrderRelationPo relation = new CmReceiptOrderRelationPo();
+        relation.setReceiptID(discernReceipt.getId().intValue());
+        relation.setOrderID(order.getOrderId());
+        relation.setAssociateAmount(amount);
+        relation.setRelationType("2");
+        relation.setMbOrderId(mbOrderId);
+        relation.setOrderRequestNo(orderRequestNo);
+        relation.setDelFlag("0");
+        relation.setSplitStatus("0");
+        relation.setShopOrderId(shopOrderId);
+        payOrderMapper.insertOrderRelation(relation);
+        log.info(">>>>>>>>>>>>>>>>>>>>>>>保存付款金额到收款记录," + amount);
+        return "SUCCESS";
+    }
+    
+    private String getOpenId(HeliDto heliDto, HttpHeaders headers) {
+        PayParamBo payParam = new PayParamBo();
+        // payDto -> payParam
+        BeanUtils.copyProperties(heliDto, payParam);
+        // 微信线上支付
+        String openId = null;
+        if (null == payParam.getState()) {
+            // 小程序微信授权获取登录信息
+            ResponseJson<Map<String, Object>> appletsInfo = weChatService.getInfoMapByApplets(payParam.getCode(), headers, 1);
+            if (appletsInfo.getCode() == -1) {
+                return null;
+            }
+            Map<String, Object> infoData = appletsInfo.getData();
+            openId = (String) infoData.get(WeChatService.Keys.OPEN_ID);
+            if (StringUtils.isEmpty(openId)) {
+                return null;
+            }
+        } else {
+            try {
+                // 微信公众号,通过code获取微信用户信息
+                Map<String, Object> map = weChatService.getInfoMapByWeb(payParam.getCode(), "crm");
+                openId = (String) map.get(WeChatService.Keys.OPEN_ID);
+            } catch (Exception e) {
+                log.error("openId获取失败", e);
+            }
+            if (StringUtils.isEmpty(openId)) {
+                return null;
+            }
+        }
+        return openId;
+    }
+
+    @Override
+    public String delayedSplittingCallback(AccountResVo data) {
+        log.info("延时分账异步回调参数-------------------》 " + data.toString());
+        try {
+            String oriMessage = MyBeanUtils.getSigned(data, null);
+            oriMessage = oriMessage +Constant.SPLIT + Constant.XUNI;
+            String checkSign = Disguiser.disguiseMD5(oriMessage.trim());
+            log.info("回调签名 :" + data.getSign());
+            log.info("checkSign : " + checkSign);
+            if (!checkSign.equals(data.getSign())) {
+                log.info("延时分账异步回调验签失败------------------");
+                return "验签失败";
+            }
+            //分账的时候防止相同订单号,前面加了FZ,切割掉2位还原
+            String orderRequestNo = data.getRt7_orderId().substring(5);
+            //切2位是splict account表orderRequestNo
+            String substring = data.getRt7_orderId().substring(2);
+            String status = data.getRt10_orderStatus();
+            log.info("【延时分账回调】>>>>>>>>>>分账状态:" + status);
+            if (!"SUCCESS".equals(status)) {
+                return "分账失败";
+            }
+            // 修改收款分账状态为1
+            payOrderMapper.updateBySplitStatus(orderRequestNo);
+
+            List<SplitAccountVo> splitAccountList = payOrderMapper.findByMbOrderId(substring);
+            if (splitAccountList != null && splitAccountList.size() > 0) {
+                Integer orderId = splitAccountList.get(0).getOrderId();
+                List<ShopOrderVo> shopOrderList = orderMapper.findAllShopOrder(orderId);
+                Integer shopOrderId = null;
+                String shopOrderNo = "";
+                for (SplitAccountVo account : splitAccountList) {
+                    log.info("【延时分账回调】>>>>>>>>>>保存应付付供应商:" + account.getShopId());
+                    // 本次付供应商金额(分账金额)
+                    BigDecimal splitAmount = account.getSplitAccount();
+                    orderId = account.getOrderId();
+                    Integer shopId = account.getShopId();
+                    for (ShopOrderVo shopOrder : shopOrderList) {
+                        if (shopId.equals(shopOrder.getShopId())) {
+                            shopOrderId = shopOrder.getShopOrderId();
+                            shopOrderNo = shopOrder.getShopOrderNo();
+                            // 已付供应商金额
+                            Double paidAmount = payOrderMapper.findPaidShop(shopOrderId);
+                            Double paidShop = MathUtil.add(paidAmount, splitAmount).doubleValue();
+                            shopOrder.setPayedShopAmount(paidShop);
+                            if (MathUtil.compare(shopOrder.getShouldPayShopAmount(), paidShop) == 0) {
+                                shopOrder.setPayStatus(3);
+                            } else {
+                                shopOrder.setPayStatus(2);
+                            }
+                            // 修改子订单付款状态及付款金额
+                            payOrderMapper.updateShopOrderByPayStatus(shopOrderId, paidShop, shopOrder.getPayStatus());
+                        }
+                    }
+                    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                    String currentTime = format.format(new Date());
+                    // 保存付款单表
+                    PayShopPo payShop = new PayShopPo();
+                    payShop.setShopId(shopId);
+                    payShop.setName("线上支付分账");
+                    payShop.setTotalAmount(splitAmount.doubleValue());
+                    payShop.setWipePayment(0d);
+                    payShop.setPayType(6);
+                    payShop.setStatus(1);
+                    payShop.setDelFlag(0);
+                    payShop.setApplyTime(currentTime);
+                    payShop.setReviewTime(currentTime);
+                    payShop.setPayTime(currentTime);
+                    payOrderMapper.insertPayShop(payShop);
+                    // 保存 付供应商记录
+                    PayShopRecordPo shopRecord = new PayShopRecordPo();
+                    shopRecord.setShopId(shopId);
+                    shopRecord.setShopOrderId(shopOrderId);
+                    shopRecord.setShopOrderNo(shopOrderNo);
+                    shopRecord.setPayAmount(splitAmount.doubleValue());
+                    shopRecord.setWipePayment(0d);
+                    shopRecord.setPayType(6);
+                    shopRecord.setPayTime(currentTime);
+                    shopRecord.setPayShopId(payShop.getId());
+                    shopRecord.setStatus(1);
+                    shopRecord.setDelFlag(0);
+                    payOrderMapper.insertPayShopRecord(shopRecord);
+                }
+                // 子订单是否全部付款
+                boolean isPay = true;
+                for (ShopOrderVo shopOrder : shopOrderList) {
+                    if (3 != shopOrder.getPayStatus()) {
+                        isPay = false;
+                        break;
+                    }
+                }
+                // 修改主订单付款状态
+                if (isPay) {
+                    payOrderMapper.updateOrderByPayStatus(orderId, 3);
+                } else {
+                    payOrderMapper.updateOrderByPayStatus(orderId, 2);
+                }
+            }
+        } catch (Exception e) {
+            log.error("【延时分账回调】>>>>>>>>>>分账异步通知异常", e);
+            return "分账失败";
+        }
+        return "SUCCESS";
+    }
+
+    @Override
+    public ResponseJson<JSONObject> getPayOrderResult(String mbOrderId) {
+        log.info("--------进入交易订单查询接口----------");
+        QueryOrderVo queryOrderVo = new QueryOrderVo();
+        queryOrderVo.setP1_bizType("AppPayQuery");
+        queryOrderVo.setP2_orderId(mbOrderId);
+        //todo 换收款账户
+        queryOrderVo.setP3_customerNumber(Constant.CUSTOMERNUM2);
+        try {
+            Map<String, String> map = MyBeanUtils.convertBean(queryOrderVo, new LinkedHashMap());
+            String oriMessage = MyBeanUtils.getSignedByPresetParameter(map, QueryOrderVo.NEED_SIGN_PARAMS);
+            //区分网银和其他产品
+            String payType = payOrderMapper.findPayType(mbOrderId);
+            if ("12".equals(payType) || "17".equals(payType)) {
+                oriMessage += Constant.SPLIT + Constant.WANGYIN;
+            } else {
+                oriMessage += Constant.SPLIT + Constant.SAOMA;
+            }
+            log.info("签名原文串:" + oriMessage);
+            String sign = Disguiser.disguiseMD5(oriMessage.trim());
+            log.info("签名串:" + sign);
+            map.put("sign", sign);
+            log.info("发送参数:" + map);
+            Map<String, Object> resultMap = HttpClientService.getHttpResp(map, Constant.REQUEST_URL);
+            log.info("响应结果:" + resultMap);
+            if ((Integer) resultMap.get("statusCode") == HttpStatus.SC_OK) {
+                String resultMsg = (String) resultMap.get("response");
+                QueryOrderResponseVo queryOrderResponseVo = JSONObject.parseObject(resultMsg, QueryOrderResponseVo.class);
+                String assemblyRespOriSign = MyBeanUtils.getSignedByPresetParameter(queryOrderResponseVo, QueryOrderResponseVo.NEED_SIGN_PARAMS);
+                log.info("组装返回结果签名串:" + assemblyRespOriSign);
+                if ("12".equals(payType) || "17".equals(payType)) {
+                    assemblyRespOriSign += Constant.SPLIT + Constant.WANGYIN;
+                } else {
+                    assemblyRespOriSign += Constant.SPLIT + Constant.SAOMA;
+                }
+                String responseSign = queryOrderResponseVo.getSign();
+                log.info("响应签名:" + responseSign);
+                String checkSign = Disguiser.disguiseMD5(assemblyRespOriSign.trim());
+                if (checkSign.equals(responseSign)) {
+                    if ("0000".equals(queryOrderResponseVo.getRt2_retCode())) {
+                        JSONObject jsonObject = JSONObject.parseObject(resultMsg, JSONObject.class);
+                        return ResponseJson.success("查询成功", jsonObject);
+                    } else {
+                        return ResponseJson.error("验签失败", null);
+                    }
+                } else {
+                    return ResponseJson.error("验签失败", null);
+                }
+            } else {
+                return ResponseJson.error("请求失败", null);
+            }
+        } catch (Exception e) {
+            return ResponseJson.error("查询失败", null);
+        }
+    }
+
+}

+ 102 - 17
src/main/java/com/caimei/service/impl/OrderSubmitServiceImpl.java

@@ -12,6 +12,7 @@ import com.caimei.service.ShoppingCartService;
 import com.caimei.util.MathUtil;
 import com.caimei.util.OrderNoUtils;
 import com.caimei.util.ProductUtils;
+import com.google.common.util.concurrent.AtomicDouble;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Value;
@@ -24,6 +25,7 @@ import javax.annotation.Resource;
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -56,12 +58,24 @@ public class OrderSubmitServiceImpl implements OrderSubmitService {
         AtomicReference<BigDecimal> totalPrice = new AtomicReference<>(BigDecimal.ZERO);
         List<CartProductVo> totalProducts = new ArrayList<>();
         List<ShopVo> shopList = new ArrayList<>();
+        Integer onlinePay = 0;
         if (productId != null && productId > 0) {
             //立即购买
-            shopList.add(orderSubmitMapper.findShopByProductId(productId));
+            ShopVo shop = orderSubmitMapper.findShopByProductId(productId);
+            if (StringUtils.isEmpty(shop.getSplitCode())) {
+                // 商品未填写分账号,不支持线上支付
+                onlinePay = -1;
+            }
+            shopList.add(shop);
         } else {
             String[] cartId = cartIds.split(",");
             shopList = orderSubmitMapper.findShopByCartIds(cartId);
+            for (ShopVo shop : shopList) {
+                if (StringUtils.isEmpty(shop.getSplitCode())) {
+                    // 商品未填写分账号,不支持线上支付
+                    onlinePay = -2;
+                }
+            }
         }
         shopList.forEach(shop -> {
             shop.setLogo(ProductUtils.getImageURL("shopLogo", shop.getLogo(), 0, domain));
@@ -73,7 +87,7 @@ public class OrderSubmitServiceImpl implements OrderSubmitService {
                 shopProducts.add(cartProduct);
             } else {
                 String[] cartId = cartIds.split(",");
-                shopProducts = orderSubmitMapper.findByShopCartProduct(shop.getShopId(), cartId);
+                shopProducts = orderSubmitMapper.findByShopCartProduct(shop.getShopId(), cartId, shop.getSplitCode());
             }
             BigDecimal shopTotalPrice = BigDecimal.ZERO;
             for (CartProductVo product : shopProducts) {
@@ -137,6 +151,7 @@ public class OrderSubmitServiceImpl implements OrderSubmitService {
                 confirmData.put("reduction", currentReduction);
             }
         }
+        confirmData.put("onlinePay", onlinePay);
         confirmData.put("receiveCouponList", receiveCouponList);
         confirmData.put("totalPrice", totalPrice);
         confirmData.put("shopList", shopList);
@@ -165,6 +180,8 @@ public class OrderSubmitServiceImpl implements OrderSubmitService {
         BigDecimal payTotalFee = BigDecimal.ZERO;
         // 真实支付金额(订单总额减去抵扣的账户余额)
         BigDecimal payableAmount = BigDecimal.ZERO;
+        // 是否能线上支付
+        AtomicBoolean onlinePay = new AtomicBoolean(true);
 
         /*
          * 初始化主订单参数
@@ -432,16 +449,90 @@ public class OrderSubmitServiceImpl implements OrderSubmitService {
          */
         // 收集子订单供应商ID字符串
         String shopOrderIds = "";
+        List<CmShopOrderPo> shopOrderList = new ArrayList<>();
         for (Map<String, Object> shopOrderInfo : orderInfo) {
             Integer shopId = (Integer) shopOrderInfo.get("shopId");
             String shopNote = (String) shopOrderInfo.get("note");
+            String splitCode = (String) shopOrderInfo.get("splitCode");
             // 初始化子订单信息
-            CmShopOrderPo shopOrder = saveShopOrder(order, orderProductList, shopId, shopNote);
+            CmShopOrderPo shopOrder = initShopOrder(order, orderProductList, shopId, splitCode, shopNote);
+            shopOrderList.add(shopOrder);
+        }
+
+        //分摊优惠
+        BigDecimal allDiscount = MathUtil.add(couponAmount, reductionAmount);
+        AtomicReference<BigDecimal> totalBrokerage = new AtomicReference<>(BigDecimal.ZERO);
+        shopOrderList.forEach(shopOrder-> totalBrokerage.set(MathUtil.add(totalBrokerage, shopOrder.getBrokerage())));
+        shopOrderList.sort((o1, o2) -> o2.getBrokerage().compareTo(o1.getBrokerage()));
+        log.info(">>>>>>>>>>>>>>>分摊前总佣金:" + totalBrokerage);
+        // 前面子订单的分摊优惠总和
+        AtomicDouble allReduction = new AtomicDouble(0);
+        if (MathUtil.compare(allDiscount, BigDecimal.ZERO) > 0) {
+            for (int i = 0; i < shopOrderList.size(); i++) {
+                if (i < shopOrderList.size() - 1) {
+                    //最终支付金额=分摊前支付金额oldprice-折扣*(分摊前佣金/分摊前总佣金)
+                    //优惠分摊金额
+                    double discount = MathUtil.mul(allDiscount, MathUtil.div(shopOrderList.get(i).getBrokerage(), totalBrokerage.get()), 2).doubleValue();
+                    shopOrderList.get(i).setEachDiscount(discount);
+                    //子订单真实支付金额
+                    double realPay = MathUtil.round(MathUtil.sub(shopOrderList.get(i).getNeedPayAmount(), discount), 2).doubleValue();
+                    shopOrderList.get(i).setRealPay(realPay);
+                    allReduction.set(MathUtil.add(discount, allReduction.get()).doubleValue());
+                }
+                if (i == shopOrderList.size() - 1) {
+                    //最后一个子订单的分摊优惠金额=全优惠allDiscount-已分摊
+                    double discount = MathUtil.sub(allDiscount, allReduction.get()).doubleValue();
+                    shopOrderList.get(i).setEachDiscount(discount);
+                    double realPay = MathUtil.round(MathUtil.sub(shopOrderList.get(i).getNeedPayAmount(), discount), 2).doubleValue();
+                    shopOrderList.get(i).setRealPay(realPay);
+                }
+                //有优惠金额,最高手续费=支付金额*0.65%>10取0.65,不大于取10(合利宝最高0.65%)
+                double realPay = shopOrderList.get(i).getRealPay();
+                double charge = MathUtil.mul(realPay, 0.0065, 2).doubleValue();
+                //佣金>最高手续费可以线上,否则线下
+                double bro = MathUtil.sub(realPay, shopOrderList.get(i).getShouldPayShopAmount()).doubleValue();
+                if (bro < charge) {
+                    onlinePay.set(false);
+                }
+            }
+        } else {
+            //无优惠金额
+            shopOrderList.forEach(so -> {
+                BigDecimal realPay = so.getNeedPayAmount();
+                BigDecimal charge = MathUtil.mul(realPay, 0.0065, 2);
+                BigDecimal bro = so.getBrokerage();
+                so.setRealPay(realPay.doubleValue());
+                so.setEachDiscount(0d);
+                if (MathUtil.compare(bro,charge) < 0) {
+                    onlinePay.set(false);
+                }
+            });
+        }
+
+        if (!onlinePay.get()) {
+            //设置手动回滚事务
+            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+            String msg = "";
+            if (productCount > 1) {
+                msg = "抱歉,部分商品支付正在调整,暂不支持下单";
+            } else {
+                msg = "抱歉,该商品支付正在调整,暂不支持下单";
+            }
+            return ResponseJson.error(-1,msg, null);
+        }
+
+        for (int i = 0; i < shopOrderList.size(); i++) {
+            CmShopOrderPo shopOrder = shopOrderList.get(i);
+            /*
+             * 保存子订单信息到数据库
+             */
+            orderSubmitMapper.insertShopOrder(shopOrder);
+            log.info(">>>>>>>>>>>>>>>>>>>>>>>>>>新增子订单(insert[cm_shop_order])shopOrderId:" + shopOrder.getShopOrderID());
             // 保存子订单号
             shopOrderIds += (("".equals(shopOrderIds) ? "" : ",") + shopOrder.getShopOrderID());
             // 设置订单商品子订单号
             for (CmOrderProductPo orderProduct : orderProductList) {
-                if (shopId.longValue() == orderProduct.getShopID()) {
+                if (shopOrder.getShopID().longValue() == orderProduct.getShopID() && shopOrder.getSplitCode().equals(orderProduct.getSplitCode())) {
                     orderProduct.setShopOrderID(shopOrder.getShopOrderID());
                     orderProduct.setShopOrderNo(shopOrder.getShopOrderNo());
                 }
@@ -642,6 +733,7 @@ public class OrderSubmitServiceImpl implements OrderSubmitService {
         orderProduct.setShopID(product.getShopId().longValue());
         orderProduct.setProductID(product.getProductId());
         orderProduct.setOrganizeProductID(product.getId());
+        orderProduct.setSplitCode(product.getSplitCode());
         // 预留在保存保存子订单的时候添加
         orderProduct.setProductNo(null);
         orderProduct.setNum(productNum);
@@ -759,10 +851,11 @@ public class OrderSubmitServiceImpl implements OrderSubmitService {
      * @param order
      * @param orderProductList
      * @param shopId
+     * @param splitCode
      * @param shopNote
      * @return
      */
-    private CmShopOrderPo saveShopOrder(CmOrderPo order, List<CmOrderProductPo> orderProductList, Integer shopId, String shopNote) {
+    private CmShopOrderPo initShopOrder(CmOrderPo order, List<CmOrderProductPo> orderProductList, Integer shopId, String splitCode, String shopNote) {
         /*
          *  初始化子订单信息
          */
@@ -781,6 +874,9 @@ public class OrderSubmitServiceImpl implements OrderSubmitService {
         shopOrder.setOrderNo(order.getOrderNo());
         shopOrder.setUserID(order.getUserID().intValue());
         shopOrder.setOrganizeID(order.getOrganizeID());
+        shopOrder.setSplitCode(splitCode);
+        shopOrder.setReceiptAmount(0d);
+        shopOrder.setReceiptStatus(1);
         /*
          *  统计子订单金额信息
          */
@@ -802,7 +898,7 @@ public class OrderSubmitServiceImpl implements OrderSubmitService {
         Integer buyNum = 0;
         // 计算子订单信息
         for (CmOrderProductPo orderProduct : orderProductList) {
-            if (shopId.longValue() == orderProduct.getShopID()) {
+            if (shopId.longValue() == orderProduct.getShopID() && splitCode.equals(orderProduct.getSplitCode())) {
                 // 商品总金额
                 productAmount = MathUtil.add(productAmount, orderProduct.getTotalAmount());
                 // 订单总金额 包括税费
@@ -821,12 +917,6 @@ public class OrderSubmitServiceImpl implements OrderSubmitService {
                 }
             }
         }
-        shopOrder.setPromotionFullReduction(BigDecimal.ZERO);
-        if (MathUtil.compare(shopOrder.getPromotionFullReduction(), 0) > 0) {
-            totalAmount = MathUtil.sub(totalAmount, shopOrder.getPromotionFullReduction());
-            productAmount = MathUtil.sub(productAmount, shopOrder.getPromotionFullReduction());
-            needPayAmount = MathUtil.sub(needPayAmount, shopOrder.getPromotionFullReduction());
-        }
 
         // freePostFlag: 0包邮 -1到付 1 有运费
         shopOrder.setNote(shopNote);
@@ -877,11 +967,6 @@ public class OrderSubmitServiceImpl implements OrderSubmitService {
         } else {
             shopOrder.setSplitFlag("0");
         }
-        /*
-         * 保存子订单信息到数据库
-         */
-        orderSubmitMapper.insertShopOrder(shopOrder);
-        log.info(">>>>>>>>>>>>>>>>>>>>>>>>>>新增子订单(insert[cm_shop_order])shopOrderId:" + shopOrder.getShopOrderID());
         return shopOrder;
     }
 }

+ 29 - 29
src/main/java/com/caimei/service/impl/PayOrderServiceImpl.java

@@ -213,10 +213,10 @@ public class PayOrderServiceImpl implements PayOrderService {
             } else {
                 order.setStatus("33");
             }
-            order.setPayFlag("1");
-            order.setOnlinePayFlag("0");
+            order.setPayFlag(1);
+            order.setOnlinePayFlag(0);
             //(收款买家)收款状态:1待收款、2部分收款、3已收款
-            order.setReceiptStatus("3");
+            order.setReceiptStatus(3);
             log.info("订单全部支付,修改订单状态>>>>>>" + order.getStatus());
             /*
              * 保存好友消费券赠送记录
@@ -300,9 +300,9 @@ public class PayOrderServiceImpl implements PayOrderService {
             } else {
                 order.setStatus("23");
             }
-            order.setOnlinePayFlag("0");
+            order.setOnlinePayFlag(0);
             //(收款买家)收款状态:1待收款、2部分收款、3已收款
-            order.setReceiptStatus("2");
+            order.setReceiptStatus(2);
         }
         //更新付款次数
         order.setPaySuccessCounter(order.getPaySuccessCounter() + 1);
@@ -494,18 +494,18 @@ public class PayOrderServiceImpl implements PayOrderService {
                     orderId = account.getOrderId();
                     Integer shopId = account.getShopId();
                     List<ShopOrderVo> shopOrderList = orderMapper.findAllShopOrder(orderId);
-                    String payStatus = "";
+                    Integer payStatus = null;
                     for (ShopOrderVo shopOrder : shopOrderList) {
                         if (shopId.equals(shopOrder.getShopId())) {
                             shopOrderId = shopOrder.getShopOrderId();
                             shopOrderNo = shopOrder.getShopOrderNo();
                             //已付供应商金额
-                            BigDecimal paidAmount = payOrderMapper.findPaidShop(shopOrderId);
-                            BigDecimal paidShop = MathUtil.add(paidAmount, splitAmount);
+                            Double paidAmount = payOrderMapper.findPaidShop(shopOrderId);
+                            Double paidShop = MathUtil.add(paidAmount, splitAmount).doubleValue();
                             if (MathUtil.compare(shopOrder.getShouldPayShopAmount(), paidShop) == 0) {
-                                payStatus = "3";
+                                payStatus = 3;
                             } else {
-                                payStatus = "2";
+                                payStatus = 2;
                             }
                             //修改子订单付款状态及付款金额
                             payOrderMapper.updateShopOrderByPayStatus(shopOrderId, paidShop, payStatus);
@@ -515,30 +515,30 @@ public class PayOrderServiceImpl implements PayOrderService {
                     SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                     String currentTime = format.format(new Date());
                     //保存付供应商记录
-                    CmPayShopPo payShop = new CmPayShopPo();
-                    payShop.setShopID(shopId);
+                    PayShopPo payShop = new PayShopPo();
+                    payShop.setShopId(shopId);
                     payShop.setName("线上支付分账");
-                    payShop.setTotalAmount(splitAmount);
-                    payShop.setWipePayment(BigDecimal.ZERO);
-                    payShop.setPayType("6");
-                    payShop.setStatus("1");
-                    payShop.setDelFlag("0");
+                    payShop.setTotalAmount(splitAmount.doubleValue());
+                    payShop.setWipePayment(0d);
+                    payShop.setPayType(6);
+                    payShop.setStatus(1);
+                    payShop.setDelFlag(0);
                     payShop.setApplyTime(currentTime);
                     payShop.setReviewTime(currentTime);
                     payShop.setPayTime(currentTime);
                     payOrderMapper.insertPayShop(payShop);
 
-                    CmPayShopRecordPo shopRecord = new CmPayShopRecordPo();
-                    shopRecord.setShopID(shopId);
-                    shopRecord.setShopOrderID(shopOrderId);
+                    PayShopRecordPo shopRecord = new PayShopRecordPo();
+                    shopRecord.setShopId(shopId);
+                    shopRecord.setShopOrderId(shopOrderId);
                     shopRecord.setShopOrderNo(shopOrderNo);
-                    shopRecord.setPayAmount(splitAmount);
-                    shopRecord.setWipePayment(BigDecimal.ZERO);
-                    shopRecord.setPayType("6");
+                    shopRecord.setPayAmount(splitAmount.doubleValue());
+                    shopRecord.setWipePayment(0d);
+                    shopRecord.setPayType(6);
                     shopRecord.setPayTime(currentTime);
-                    shopRecord.setPayShopID(payShop.getId().intValue());
-                    shopRecord.setStatus("1");
-                    shopRecord.setDelFlag("0");
+                    shopRecord.setPayShopId(payShop.getId());
+                    shopRecord.setStatus(1);
+                    shopRecord.setDelFlag(0);
                     payOrderMapper.insertPayShopRecord(shopRecord);
                 }
 
@@ -547,14 +547,14 @@ public class PayOrderServiceImpl implements PayOrderService {
                 //子订单是否全部付款
                 boolean isPay = true;
                 for (ShopOrderVo shopOrder : shopOrderList) {
-                    if (!"3".equals(shopOrder.getPayStatus())) {
+                    if (3 != shopOrder.getPayStatus()) {
                         isPay = false;
                     }
                 }
                 if (isPay) {
-                    payOrderMapper.updateOrderByPayStatus(orderId, "3");
+                    payOrderMapper.updateOrderByPayStatus(orderId, 3);
                 } else {
-                    payOrderMapper.updateOrderByPayStatus(orderId, "2");
+                    payOrderMapper.updateOrderByPayStatus(orderId, 2);
                 }
             }
         } catch (Exception e) {

+ 476 - 0
src/main/java/com/caimei/util/helipay/Base64.java

@@ -0,0 +1,476 @@
+package com.caimei.util.helipay;
+
+import java.io.UnsupportedEncodingException;
+
+public class Base64 {
+	
+	public static void main(String[] args) {
+		
+	}
+    /**
+     * Chunk size per RFC 2045 section 6.8.
+     *
+     * <p>The {@value} character limit does not count the trailing CRLF, but counts
+     * all other characters, including any equal signs.</p>
+     *
+     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
+     */
+    static final int CHUNK_SIZE = 76;
+
+    /**
+     * Chunk separator per RFC 2045 section 2.1.
+     *
+     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
+     */
+    static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes();
+
+    /**
+     * The base length.
+     */
+    static final int BASELENGTH = 255;
+
+    /**
+     * Lookup length.
+     */
+    static final int LOOKUPLENGTH = 64;
+
+    /**
+     * Used to calculate the number of bits in a byte.
+     */
+    static final int EIGHTBIT = 8;
+
+    /**
+     * Used when encoding something which has fewer than 24 bits.
+     */
+    static final int SIXTEENBIT = 16;
+
+    /**
+     * Used to determine how many bits data contains.
+     */
+    static final int TWENTYFOURBITGROUP = 24;
+
+    /**
+     * Used to get the number of Quadruples.
+     */
+    static final int FOURBYTE = 4;
+
+    /**
+     * Used to test the sign of a byte.
+     */
+    static final int SIGN = -128;
+
+    /**
+     * Byte used to pad output.
+     */
+    static final byte PAD = (byte) '=';
+
+    // Create arrays to hold the base64 characters and a
+    // lookup for base64 chars
+    private static byte[] base64Alphabet = new byte[BASELENGTH];
+    private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
+
+    // Populating the lookup and character arrays
+    static {
+        for (int i = 0; i < BASELENGTH; i++) {
+            base64Alphabet[i] = (byte) -1;
+        }
+        for (int i = 'Z'; i >= 'A'; i--) {
+            base64Alphabet[i] = (byte) (i - 'A');
+        }
+        for (int i = 'z'; i >= 'a'; i--) {
+            base64Alphabet[i] = (byte) (i - 'a' + 26);
+        }
+        for (int i = '9'; i >= '0'; i--) {
+            base64Alphabet[i] = (byte) (i - '0' + 52);
+        }
+
+        base64Alphabet['+'] = 62;
+        base64Alphabet['/'] = 63;
+
+        for (int i = 0; i <= 25; i++) {
+            lookUpBase64Alphabet[i] = (byte) ('A' + i);
+        }
+
+        for (int i = 26, j = 0; i <= 51; i++, j++) {
+            lookUpBase64Alphabet[i] = (byte) ('a' + j);
+        }
+
+        for (int i = 52, j = 0; i <= 61; i++, j++) {
+            lookUpBase64Alphabet[i] = (byte) ('0' + j);
+        }
+
+        lookUpBase64Alphabet[62] = (byte) '+';
+        lookUpBase64Alphabet[63] = (byte) '/';
+    }
+
+    private static boolean isBase64(byte octect) {
+        if (octect == PAD) {
+            return true;
+        } else if (base64Alphabet[octect] == -1) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Tests a given byte array to see if it contains
+     * only valid characters within the Base64 alphabet.
+     *
+     * @param arrayOctect byte array to test
+     * @return true if all bytes are valid characters in the Base64
+     *         alphabet or if the byte array is empty; false, otherwise
+     */
+    public static boolean isArrayByteBase64(byte[] arrayOctect) {
+
+        arrayOctect = discardWhitespace(arrayOctect);
+
+        int length = arrayOctect.length;
+        if (length == 0) {
+            // shouldn't a 0 length array be valid base64 data?
+            // return false;
+            return true;
+        }
+        for (int i = 0; i < length; i++) {
+            if (!isBase64(arrayOctect[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm but
+     * does not chunk the output.
+     *
+     * @param binaryData binary data to encode
+     * @return Base64 characters
+     */
+    public static byte[] encodeBase64(byte[] binaryData) {
+        return encodeBase64(binaryData, false);
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm and chunks
+     * the encoded output into 76 character blocks
+     *
+     * @param binaryData binary data to encode
+     * @return Base64 characters chunked in 76 character blocks
+     */
+    public static byte[] encodeBase64Chunked(byte[] binaryData) {
+        return encodeBase64(binaryData, true);
+    }
+
+    /**
+     * Decodes a byte[] containing containing
+     * characters in the Base64 alphabet.
+     *
+     * @param pArray A byte array containing Base64 character data
+     * @return a byte array containing binary data
+     */
+    public static byte[] decode(byte[] pArray) {
+        return decodeBase64(pArray);
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm, optionally
+     * chunking the output into 76 character blocks.
+     *
+     * @param binaryData Array containing binary data to encode.
+     * @param isChunked if isChunked is true this encoder will chunk
+     *                  the base64 output into 76 character blocks
+     * @return Base64-encoded data.
+     */
+    public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
+        int lengthDataBits = binaryData.length * EIGHTBIT;
+        int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
+        int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
+        byte encodedData[] = null;
+        int encodedDataLength = 0;
+        int nbrChunks = 0;
+
+        if (fewerThan24bits != 0) {
+            //data not divisible by 24 bit
+            encodedDataLength = (numberTriplets + 1) * 4;
+        } else {
+            // 16 or 8 bit
+            encodedDataLength = numberTriplets * 4;
+        }
+
+        // If the output is to be "chunked" into 76 character sections,
+        // for compliance with RFC 2045 MIME, then it is important to
+        // allow for extra length to account for the separator(s)
+        if (isChunked) {
+
+            nbrChunks =
+                (CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math.ceil((float) encodedDataLength / CHUNK_SIZE));
+            encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
+        }
+
+        encodedData = new byte[encodedDataLength];
+
+        byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
+
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        int i = 0;
+        int nextSeparatorIndex = CHUNK_SIZE;
+        int chunksSoFar = 0;
+
+        //log.debug("number of triplets = " + numberTriplets);
+        for (i = 0; i < numberTriplets; i++) {
+            dataIndex = i * 3;
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            b3 = binaryData[dataIndex + 2];
+
+            //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
+
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+
+            byte val1 =
+                ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 =
+                ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+            byte val3 =
+                ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
+
+            encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+            //log.debug( "val2 = " + val2 );
+            //log.debug( "k4   = " + (k<<4) );
+            //log.debug(  "vak  = " + (val2 | (k<<4)) );
+            encodedData[encodedIndex + 1] =
+                lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex + 2] =
+                lookUpBase64Alphabet[(l << 2) | val3];
+            encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
+
+            encodedIndex += 4;
+
+            // If we are chunking, let's put a chunk separator down.
+            if (isChunked) {
+                // this assumes that CHUNK_SIZE % 4 == 0
+                if (encodedIndex == nextSeparatorIndex) {
+                    System.arraycopy(
+                        CHUNK_SEPARATOR,
+                        0,
+                        encodedData,
+                        encodedIndex,
+                        CHUNK_SEPARATOR.length);
+                    chunksSoFar++;
+                    nextSeparatorIndex =
+                        (CHUNK_SIZE * (chunksSoFar + 1)) +
+                        (chunksSoFar * CHUNK_SEPARATOR.length);
+                    encodedIndex += CHUNK_SEPARATOR.length;
+                }
+            }
+        }
+
+        // form integral number of 6-bit groups
+        dataIndex = i * 3;
+
+        if (fewerThan24bits == EIGHTBIT) {
+            b1 = binaryData[dataIndex];
+            k = (byte) (b1 & 0x03);
+            //log.debug("b1=" + b1);
+            //log.debug("b1<<2 = " + (b1>>2) );
+            byte val1 =
+                ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
+            encodedData[encodedIndex + 2] = PAD;
+            encodedData[encodedIndex + 3] = PAD;
+        } else if (fewerThan24bits == SIXTEENBIT) {
+
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+
+            byte val1 =
+                ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 =
+                ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+
+            encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex + 1] =
+                lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
+            encodedData[encodedIndex + 3] = PAD;
+        }
+
+        if (isChunked) {
+            // we also add a separator to the end of the final chunk.
+            if (chunksSoFar < nbrChunks) {
+                System.arraycopy(
+                    CHUNK_SEPARATOR,
+                    0,
+                    encodedData,
+                    encodedDataLength - CHUNK_SEPARATOR.length,
+                    CHUNK_SEPARATOR.length);
+            }
+        }
+
+        return encodedData;
+    }
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param base64Data Byte array containing Base64 data
+     * @return Array containing decoded data.
+     */
+    public static byte[] decodeBase64(byte[] base64Data) {
+        // RFC 2045 requires that we discard ALL non-Base64 characters
+        base64Data = discardNonBase64(base64Data);
+
+        // handle the edge case, so we don't have to worry about it later
+        if (base64Data.length == 0) {
+            return new byte[0];
+        }
+
+        int numberQuadruple = base64Data.length / FOURBYTE;
+        byte decodedData[] = null;
+        byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
+
+        // Throw away anything not in base64Data
+
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        {
+            // this sizes the output array properly - rlw
+            int lastData = base64Data.length;
+            // ignore the '=' padding
+            while (base64Data[lastData - 1] == PAD) {
+                if (--lastData == 0) {
+                    return new byte[0];
+                }
+            }
+            decodedData = new byte[lastData - numberQuadruple];
+        }
+
+        for (int i = 0; i < numberQuadruple; i++) {
+            dataIndex = i * 4;
+            marker0 = base64Data[dataIndex + 2];
+            marker1 = base64Data[dataIndex + 3];
+
+            b1 = base64Alphabet[base64Data[dataIndex]];
+            b2 = base64Alphabet[base64Data[dataIndex + 1]];
+
+            if (marker0 != PAD && marker1 != PAD) {
+                //No PAD e.g 3cQl
+                b3 = base64Alphabet[marker0];
+                b4 = base64Alphabet[marker1];
+
+                decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+                decodedData[encodedIndex + 1] =
+                    (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+                decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
+            } else if (marker0 == PAD) {
+                //Two PAD e.g. 3c[Pad][Pad]
+                decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+            } else if (marker1 == PAD) {
+                //One PAD e.g. 3cQ[Pad]
+                b3 = base64Alphabet[marker0];
+
+                decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+                decodedData[encodedIndex + 1] =
+                    (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            }
+            encodedIndex += 3;
+        }
+        return decodedData;
+    }
+
+    /**
+     * Discards any whitespace from a base-64 encoded block.
+     *
+     * @param data The base-64 encoded data to discard the whitespace
+     * from.
+     * @return The data, less whitespace (see RFC 2045).
+     */
+    static byte[] discardWhitespace(byte[] data) {
+        byte groomedData[] = new byte[data.length];
+        int bytesCopied = 0;
+
+        for (int i = 0; i < data.length; i++) {
+            switch (data[i]) {
+            case (byte) ' ' :
+            case (byte) '\n' :
+            case (byte) '\r' :
+            case (byte) '\t' :
+                    break;
+            default:
+                    groomedData[bytesCopied++] = data[i];
+            }
+        }
+
+        byte packedData[] = new byte[bytesCopied];
+
+        System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
+
+        return packedData;
+    }
+
+    /**
+     * Discards any characters outside of the base64 alphabet, per
+     * the requirements on page 25 of RFC 2045 - "Any characters
+     * outside of the base64 alphabet are to be ignored in base64
+     * encoded data."
+     *
+     * @param data The base-64 encoded data to groom
+     * @return The data, less non-base64 characters (see RFC 2045).
+     */
+    static byte[] discardNonBase64(byte[] data) {
+        byte groomedData[] = new byte[data.length];
+        int bytesCopied = 0;
+
+        for (int i = 0; i < data.length; i++) {
+            if (isBase64(data[i])) {
+                groomedData[bytesCopied++] = data[i];
+            }
+        }
+
+        byte packedData[] = new byte[bytesCopied];
+
+        System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
+
+        return packedData;
+    }
+
+    /**
+     * Encodes a byte[] containing binary data, into a byte[] containing
+     * characters in the Base64 alphabet.
+     *
+     * @param pArray a byte array containing binary data
+     * @return A byte array containing only Base64 character data
+     */
+    public static byte[] encode(byte[] pArray) {
+        return encodeBase64(pArray, false);
+    }
+
+    public static String encode(String str) throws UnsupportedEncodingException
+     {
+         String baseStr = new String(encode(str.getBytes("UTF-8")));
+         String tempStr = Disguiser.disguise(str).toUpperCase();
+         String result = tempStr+baseStr;
+         return new String(encode(result.getBytes("UTF-8")));
+     }
+
+     public static String decode(String cryptoStr) throws
+       UnsupportedEncodingException {
+       if(cryptoStr.length()<40)
+         return "";
+       try
+       {
+         String tempStr = new String(decode(cryptoStr.getBytes("UTF-8")));
+         String result = tempStr.substring(40, tempStr.length());
+         return new String(decode(result.getBytes("UTF-8")));
+       }
+       catch(ArrayIndexOutOfBoundsException ex)
+       {
+         return "";
+       }
+     }
+}

+ 24 - 0
src/main/java/com/caimei/util/helipay/ConvertUtils.java

@@ -0,0 +1,24 @@
+package com.caimei.util.helipay;
+
+/**
+ * 
+ * @ClassName: ConvertUtils
+ * @Description: 格式转化工具类
+ *
+ */
+public abstract class ConvertUtils {
+
+	public static String toHex(byte input[]) {
+		if (input == null)
+			return null;
+		StringBuffer output = new StringBuffer(input.length * 2);
+		for (int i = 0; i < input.length; i++) {
+			int current = input[i] & 0xff;
+			if (current < 16)
+				output.append("0");
+			output.append(Integer.toString(current, 16));
+		}
+
+		return output.toString();
+	}
+}

+ 63 - 0
src/main/java/com/caimei/util/helipay/DesUtils.java

@@ -0,0 +1,63 @@
+package com.caimei.util.helipay;
+
+import javax.crypto.*;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+public class DesUtils {
+
+    public static final String ENCODE = "UTF-8";
+
+    private DesUtils() {
+    }
+
+    public static String encode(String key, String data) throws NoSuchPaddingException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, InvalidKeyException {
+        try {
+            byte[] keyByte = key.getBytes(ENCODE);
+            byte[] dataByte = data.getBytes(ENCODE);
+            byte[] valueByte = des3Encryption(
+                    keyByte, dataByte);
+            String value = new String(Base64.encodeBase64(valueByte), ENCODE);
+            return value;
+        } catch (Exception e) {
+            System.err.println("3des加密失败:" + e.getMessage());
+            throw e;
+        }
+    }
+
+    public static String decode(String key, String value) throws NoSuchPaddingException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, InvalidKeyException {
+        try {
+            byte[] keyByte = key.getBytes(ENCODE);
+            byte[] bytes = value.getBytes(ENCODE);
+            byte[] dataByte = des3Decryption(keyByte,
+                    Base64.decode(bytes));
+            String data = new String(dataByte, ENCODE);
+            return data;
+        } catch (Exception e) {
+            System.err.println("3des解密失败:" + e.getMessage());
+            throw e;
+        }
+    }
+
+    private static byte[] des3Encryption(byte[] key, byte[] data) throws
+            NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
+            BadPaddingException, IllegalBlockSizeException, IllegalStateException {
+        final String Algorithm = "DESede";
+        SecretKey deskey = new SecretKeySpec(key, Algorithm);
+        Cipher c1 = Cipher.getInstance(Algorithm);
+        c1.init(Cipher.ENCRYPT_MODE, deskey);
+        return c1.doFinal(data);
+    }
+
+    private static byte[] des3Decryption(byte[] key, byte[] data) throws
+            NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
+            BadPaddingException, IllegalBlockSizeException, IllegalStateException {
+        final String Algorithm = "DESede";
+        SecretKey deskey = new SecretKeySpec(key, Algorithm);
+        Cipher c1 = Cipher.getInstance(Algorithm);
+        c1.init(Cipher.DECRYPT_MODE, deskey);
+        return c1.doFinal(data);
+    }
+}

+ 74 - 0
src/main/java/com/caimei/util/helipay/Disguiser.java

@@ -0,0 +1,74 @@
+package com.caimei.util.helipay;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+
+/**
+ * 
+ * @ClassName: Disguiser
+ * @Description: 哈希算法的工具类,提供SHA MD5 HMAC等算法
+ *
+ */
+public class Disguiser {
+
+	public static final String ENCODE = "UTF-8";
+	private static final String KEY = "8data998mnwepxugnk03-2zirb";
+
+	public static String disguiseMD5(String message) {
+
+		if (null == message) {
+			return null;
+		}
+		return disguiseMD5(message, ENCODE);
+	}
+	
+	public static String disguise(String message){
+    	return disguise(message+KEY,ENCODE);
+    	
+    }
+	public static String disguise(String message,String encoding){
+    	message = message.trim();
+        byte value[];
+        try{
+            value = message.getBytes(encoding);
+        }
+        catch(UnsupportedEncodingException e){
+            value = message.getBytes();
+        }
+        MessageDigest md = null;
+        try{
+            md = MessageDigest.getInstance("SHA");
+        }catch(NoSuchAlgorithmException e){
+        	e.printStackTrace();
+            return null;
+        }
+        return ConvertUtils.toHex(md.digest(value));
+    }
+
+	public static String disguiseMD5(String message, String encoding) {
+
+		if (null == message || null == encoding) {
+
+			return null;
+		}
+
+		message = message.trim();
+		byte value[];
+		try {
+			value = message.getBytes(encoding);
+		} catch (UnsupportedEncodingException e) {
+			value = message.getBytes();
+		}
+		MessageDigest md = null;
+		try {
+			md = MessageDigest.getInstance("MD5");
+		} catch (NoSuchAlgorithmException e) {
+			e.printStackTrace();
+			return null;
+		}
+		return ConvertUtils.toHex(md.digest(value));
+	}
+
+}

+ 127 - 0
src/main/java/com/caimei/util/helipay/HttpClientService.java

@@ -0,0 +1,127 @@
+package com.caimei.util.helipay;
+
+import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.NameValuePair;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.*;
+
+public class HttpClientService {
+
+    private static final Log LOG = LogFactory.getLog(HttpClientService.class);
+
+    public static Map<String, Object> getHttpResp(Map<String, String> reqMap, String httpUrl) {
+        HttpClient client = new HttpClient();
+        PostMethod method = new PostMethod(httpUrl);
+        method.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8");
+        method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
+        method.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, new Integer(300000));
+        String response = "";
+
+
+        Map<String, Object> mp = new HashMap<String, Object>();
+
+        try {
+            NameValuePair[] nvps = getNameValuePair(reqMap);
+            method.setRequestBody(nvps);
+            int rescode = client.executeMethod(method);
+            mp.put("statusCode", rescode);
+
+            LOG.info("http rescode:" + rescode);
+            if (rescode == HttpStatus.SC_OK) {
+                BufferedReader reader = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream(), "UTF-8"));
+                String curline = "";
+                while ((curline = reader.readLine()) != null) {
+                    response += curline;
+                }
+                LOG.info("http response:" + response);
+                mp.put("response", response);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            method.releaseConnection();
+        }
+        return mp;
+    }
+
+    /**
+     *
+     * HTTP协议POST请求方法
+     */
+    public static String httpMethodPost(String url, String params, String gb) {
+        if (null == gb || "".equals(gb)) {
+            gb = "UTF-8";
+        }
+        StringBuffer sb = new StringBuffer();
+        URL urls;
+        HttpURLConnection uc = null;
+        BufferedReader in = null;
+        DataOutputStream out = null;
+        try {
+            urls = new URL(url);
+            uc = (HttpURLConnection) urls.openConnection();
+            uc.setRequestMethod("POST");
+            uc.setDoOutput(true);
+            uc.setDoInput(true);
+            uc.setUseCaches(false);
+            uc.setRequestProperty("Connection", "keep-alive");
+            uc.setRequestProperty("Keep-Alive", "timeout=1, max=100");
+            uc.setRequestProperty("Content-Length", params.length() + "");
+            uc.setRequestProperty("Content-Type","application/json");
+            uc.setConnectTimeout(7000);
+            uc.setReadTimeout(10000);
+            uc.connect();
+            out = new DataOutputStream(uc.getOutputStream());
+            out.write(params.getBytes(gb));
+            out.flush();
+            out.close();
+            in = new BufferedReader(new InputStreamReader(uc.getInputStream(),
+                    gb));
+            String readLine = "";
+            while ((readLine = in.readLine()) != null) {
+                sb.append(readLine);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (out != null){
+                    out.close();
+                }
+                if (in != null){
+                    in.close();
+                }
+                if (uc != null) {
+                    uc.disconnect();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return sb.toString();
+    }
+
+    public static NameValuePair[] getNameValuePair(Map<String, String> bean) {
+        List<NameValuePair> x = new ArrayList<NameValuePair>();
+        for (Iterator<String> iterator = bean.keySet().iterator(); iterator.hasNext(); ) {
+            String type = (String) iterator.next();
+            x.add(new NameValuePair(type, String.valueOf(bean.get(type))));
+        }
+        Object[] y = x.toArray();
+        NameValuePair[] n = new NameValuePair[y.length];
+        System.arraycopy(y, 0, n, 0, y.length);
+        return n;
+    }
+}

+ 113 - 0
src/main/java/com/caimei/util/helipay/MyBeanUtils.java

@@ -0,0 +1,113 @@
+package com.caimei.util.helipay;
+
+
+import com.caimei.constant.Constant;
+import org.apache.commons.beanutils.BeanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.beans.IntrospectionException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ */
+public class MyBeanUtils extends BeanUtils {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(MyBeanUtils.class);
+
+    private MyBeanUtils(){ }
+
+    public static Map convertBean(Object bean, Map retMap)
+            throws IntrospectionException, IllegalAccessException, InvocationTargetException {
+        Class clazz = bean.getClass();
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field f : fields) {
+            f.setAccessible(true);
+        }
+        for (Field f : fields) {
+            String key = f.toString().substring(f.toString().lastIndexOf(".") + 1);
+            if (StringUtils.equalsIgnoreCase("NEED_SIGN_PARAMS", key)) {
+                continue;
+            }
+            Object value = f.get(bean);
+            if (value == null)
+                value = "";
+            retMap.put(key, value);
+        }
+        return retMap;
+    }
+
+    public static String getSigned(Map<String, String> map, String[] excludes){
+        StringBuffer sb = new StringBuffer();
+        Set<String> excludeSet = new HashSet<String>();
+        excludeSet.add("sign");
+        if(excludes != null){
+            for(String exclude : excludes){
+                excludeSet.add(exclude);
+            }
+        }
+        for(String key : map.keySet()){
+            if(!excludeSet.contains(key)){
+                String value = map.get(key);
+                value = (value == null ? "" : value);
+                sb.append(Constant.SPLIT);
+                sb.append(value);
+            }
+        }
+//        sb.append(Constant.SPLIT);
+        return sb.toString();
+    }
+
+    public static String getSigned(Object bean, String[] excludes) throws IllegalAccessException, IntrospectionException, InvocationTargetException {
+        Map map  = convertBean(bean, new LinkedHashMap());
+        String signedStr = getSigned(map, excludes);
+        return signedStr;
+    }
+
+    /**
+     * new style
+     *
+     * @param bean
+     * @param needSignParams
+     */
+    public static String getSignedByPresetParameter(Object bean, Set<String> needSignParams) throws IllegalAccessException,
+            IntrospectionException, InvocationTargetException {
+        Map map = convertBean(bean, new LinkedHashMap<>());
+        return getSignedByPresetParameter(map, needSignParams);
+    }
+
+    /**
+     * new style
+     *
+     * @param map
+     * @param needSignParams
+     * @return
+     */
+    public static String getSignedByPresetParameter(Map<String, String> map, Set<String> needSignParams) {
+        StringBuffer sb = new StringBuffer();
+        if (needSignParams == null || needSignParams.isEmpty()) {
+            throw new RuntimeException("needSignParams is required");
+        }
+        for (String key : map.keySet()) {
+            if (needSignParams.contains(key)) {
+                // do sign
+                String value = map.get(key);
+                value = (value == null ? "" : value);
+                sb.append(Constant.SPLIT);
+                sb.append(value);
+            }
+        }
+//        改为手动拼接密钥
+//        sb.append(Constant.SPLIT).append(Constant.SAOMA);
+        //LOGGER.info("sign result:{}", sb.toString());
+        return sb.toString();
+    }
+
+}

+ 25 - 0
src/main/java/com/caimei/util/helipay/MyDateUtils.java

@@ -0,0 +1,25 @@
+package com.caimei.util.helipay;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @author awu
+ */
+public class MyDateUtils {
+
+    private MyDateUtils(){
+
+    }
+
+    public static SimpleDateFormat format (){
+        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
+        return format;
+    }
+
+    public static String getDate2Str(Date date) {
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+        return format.format(date);
+    }
+
+}

+ 350 - 0
src/main/java/com/caimei/util/helipay/RSA.java

@@ -0,0 +1,350 @@
+package com.caimei.util.helipay;
+
+import org.apache.commons.lang.ArrayUtils;
+import sun.misc.BASE64Encoder;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.*;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+import java.util.Enumeration;
+
+/**
+ * 私钥签名,私钥签名(只有私钥能签),公钥验证签名,确认发起人是私钥持有人
+ * 公钥加密,公钥加密只有私钥能解密
+ *
+ * @author datou
+ */
+public class RSA {
+
+    /**
+     * String to hold name of the encryption padding.
+     */
+    public static final String NOPADDING = "RSA/NONE/NoPadding";
+
+    public static final String RSANONEPKCS1PADDING = "RSA/NONE/PKCS1Padding";
+
+    public static final String RSAECBPKCS1PADDING = "RSA/ECB/PKCS1Padding";
+
+    public static final String PROVIDER = "BC";
+
+    static {
+        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+    }
+
+    /**
+     * 验证签名
+     *
+     * @param data     数据
+     * @param sign     签名
+     * @param publicKey 公钥
+     * @return
+     */
+    public static boolean verifySign(byte[] data, byte[] sign,
+                                     PublicKey publicKey) {
+        try {
+            Signature signature = Signature
+                    .getInstance("MD5withRSA");
+            signature.initVerify(publicKey);
+            signature.update(data);
+            boolean result = signature.verify(sign);
+            return result;
+        } catch (Exception e) {
+
+            throw new RuntimeException("verifySign fail!", e);
+        }
+    }
+
+    /**
+     * 验证签名
+     *
+     * @param data     数据
+     * @param sign     签名
+     * @param pubicKey 公钥
+     * @return
+     */
+    public static boolean verifySign(String data, String sign,
+                                     PublicKey pubicKey) {
+        try {
+            byte[] dataByte = data
+                    .getBytes("UTF-8");
+            byte[] signByte = Base64.decode(sign
+                    .getBytes("UTF-8"));
+            return verifySign(dataByte, signByte, pubicKey);
+        } catch (UnsupportedEncodingException e) {
+
+            throw new RuntimeException("verifySign fail! data[" + data + "] sign[" + sign + "]", e);
+        }
+    }
+
+    /**
+     * 签名
+     *
+     * @param data
+     * @param key
+     * @return
+     */
+    public static byte[] sign(byte[] data, PrivateKey key) {
+        try {
+            Signature signature = Signature
+                    .getInstance("MD5withRSA");
+            signature.initSign(key);
+            signature.update(data);
+            return signature.sign();
+        } catch (Exception e) {
+            throw new RuntimeException("sign fail!", e);
+        }
+    }
+
+    /**
+     * 签名
+     *
+     * @param data
+     * @param key
+     * @return
+     */
+    public static String sign(String data, PrivateKey key) {
+        try {
+            byte[] dataByte = data.getBytes("UTF-8");
+            return new String(Base64.encode(sign(dataByte, key)));
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("sign fail!", e);
+        }
+    }
+
+    /**
+     * 加密
+     *
+     * @param data
+     * @param key
+     * @return
+     */
+    public static byte[] encrypt(byte[] data, Key key, String padding) {
+        try {
+            final Cipher cipher = Cipher.getInstance(padding, PROVIDER);
+            cipher.init(Cipher.ENCRYPT_MODE, key);
+            return cipher.doFinal(data);
+        } catch (Exception e) {
+
+            throw new RuntimeException("encrypt fail!", e);
+        }
+    }
+
+    /**
+     * 加密
+     *
+     * @param data
+     * @param key
+     * @return
+     */
+    public static String encryptToBase64(String data, Key key, String padding) {
+        try {
+            return new String(Base64.encode(encrypt(
+                    data.getBytes("UTF-8"),
+                    key, padding)));
+        } catch (Exception e) {
+            throw new RuntimeException("encrypt fail!", e);
+        }
+    }
+
+    /**
+     * 解密
+     *
+     * @param data
+     * @param key
+     * @return
+     */
+    public static byte[] decrypt(byte[] data, Key key, String padding) {
+        try {
+            final Cipher cipher = Cipher.getInstance(padding, PROVIDER);
+            cipher.init(Cipher.DECRYPT_MODE, key);
+            return cipher.doFinal(data);
+        } catch (Exception e) {
+            throw new RuntimeException("encrypt fail!", e);
+        }
+    }
+
+    /**
+     * 解密
+     *
+     * @param data
+     * @param key
+     * @return
+     */
+    public static String decryptFromBase64(String data, Key key, String padding) {
+        try {
+            return new String(decrypt(Base64.decode(data.getBytes()), key, padding),
+                    "UTF-8");
+        } catch (Exception e) {
+            throw new RuntimeException("encrypt fail!", e);
+        }
+    }
+
+    public static void createKeyPairs(int size) throws Exception {
+        // create the keys
+        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", PROVIDER);
+        generator.initialize(size, new SecureRandom());
+        KeyPair pair = generator.generateKeyPair();
+        PublicKey pubKey = pair.getPublic();
+        PrivateKey privKey = pair.getPrivate();
+        byte[] pk = pubKey.getEncoded();
+        byte[] privk = privKey.getEncoded();
+        String strpk = new String(Base64.encodeBase64(pk));
+        String strprivk = new String(Base64.encodeBase64(privk));
+        System.out.println("公钥:" + Arrays.toString(pk));
+        System.out.println("私钥:" + Arrays.toString(privk));
+        System.out.println("公钥Base64编码:" + strpk);
+        System.out.println("私钥Base64编码:" + strprivk);
+    }
+
+    public static PublicKey getPublicKey(String base64EncodePublicKey) throws Exception {
+        KeyFactory keyf = KeyFactory.getInstance("RSA", PROVIDER);
+        X509EncodedKeySpec pubX509 = new X509EncodedKeySpec(Base64.decodeBase64(base64EncodePublicKey.getBytes()));
+        PublicKey pubkey = keyf.generatePublic(pubX509);
+        return pubkey;
+    }
+
+    public static PrivateKey getPrivateKey(String base64EncodePrivateKey) throws Exception {
+        KeyFactory keyf = KeyFactory.getInstance("RSA", PROVIDER);
+        PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(base64EncodePrivateKey.getBytes()));
+        PrivateKey privkey = keyf.generatePrivate(priPKCS8);
+        return privkey;
+    }
+
+
+    public static byte[] encode(String encodeString, Key key, String padding) throws Exception {
+        final Cipher cipher = Cipher.getInstance(padding, PROVIDER);
+        cipher.init(Cipher.ENCRYPT_MODE, key);
+        byte[] bytes = encodeString.getBytes("UTF-8");
+        byte[] encodedByteArray = new byte[]{};
+        for (int i = 0; i < bytes.length; i += 117) {
+            byte[] subarray = ArrayUtils.subarray(bytes, i, i + 117);
+            byte[] doFinal = cipher.doFinal(subarray);
+            encodedByteArray = ArrayUtils.addAll(encodedByteArray, doFinal);
+        }
+        return encodedByteArray;
+    }
+
+    /**
+     * 加密
+     *
+     * @param data
+     * @param key
+     * @return
+     */
+    public static String encodeToBase64(String data, Key key, String padding) {
+        try {
+            return new String(Base64.encode(encode(data,
+                    key, padding)));
+        } catch (Exception e) {
+            throw new RuntimeException("encrypt fail!", e);
+        }
+    }
+
+    public static String decode(byte[] decodeByteArray, Key key, String padding) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException, NoSuchProviderException {
+        final Cipher cipher = Cipher.getInstance(padding, PROVIDER);
+        cipher.init(Cipher.DECRYPT_MODE, key);
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < decodeByteArray.length; i += 128) {
+            byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(decodeByteArray, i, i + 128));
+            sb.append(new String(doFinal));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 解密
+     *
+     * @param data
+     * @param key
+     * @return
+     */
+    public static String decodeFromBase64(String data, Key key, String padding) {
+        try {
+            return new String(decode(Base64.decode(data.getBytes()), key, padding).getBytes(),
+                    "UTF-8");
+        } catch (Exception e) {
+            throw new RuntimeException("encrypt fail!", e);
+        }
+    }
+
+    /**
+     * 得到密钥字符串(经过base64编码)
+     *
+     * @return
+     */
+    public static String getKeyString(Key key) throws Exception {
+        byte[] keyBytes = key.getEncoded();
+        String s = (new BASE64Encoder()).encode(keyBytes);
+        return s;
+    }
+
+    public static String getKeyStringByCer(String path) throws Exception {
+        CertificateFactory cff = CertificateFactory.getInstance("X.509");
+        FileInputStream fis1 = new FileInputStream(path);
+        Certificate cf = cff.generateCertificate(fis1);
+        PublicKey pk1 = cf.getPublicKey();
+        String key = getKeyString(pk1);
+        System.out.println("public:\n" + key);
+        return key;
+    }
+
+    public static String getKeyStringByPfx(String strPfx, String strPassword) {
+        try {
+            KeyStore ks = KeyStore.getInstance("PKCS12");
+            FileInputStream fis = new FileInputStream(strPfx);
+            // If the keystore password is empty(""), then we have to set
+            // to null, otherwise it won't work!!!
+            char[] nPassword = null;
+            if ((strPassword == null) || strPassword.trim().equals("")) {
+                nPassword = null;
+            } else {
+                nPassword = strPassword.toCharArray();
+            }
+            ks.load(fis, nPassword);
+            fis.close();
+            System.out.println("keystore type=" + ks.getType());
+            // Now we loop all the aliases, we need the alias to get keys.
+            // It seems that this value is the "Friendly name" field in the
+            // detals tab <-- Certificate window <-- view <-- Certificate
+            // Button <-- Content tab <-- Internet Options <-- Tools menu
+            // In MS IE 6.
+            Enumeration enumas = ks.aliases();
+            String keyAlias = null;
+            if (enumas.hasMoreElements())// we are readin just one certificate.
+            {
+                keyAlias = (String) enumas.nextElement();
+                System.out.println("alias=[" + keyAlias + "]");
+            }
+            // Now once we know the alias, we could get the keys.
+            System.out.println("is key entry=" + ks.isKeyEntry(keyAlias));
+            PrivateKey prikey = (PrivateKey) ks.getKey(keyAlias, nPassword);
+            Certificate cert = ks.getCertificate(keyAlias);
+            PublicKey pubkey = cert.getPublicKey();
+
+            String basePrikey = RSA.getKeyString(prikey);
+            System.out.println("cert class = " + cert.getClass().getName());
+            System.out.println("cert = " + cert);
+            System.out.println("public key = " + pubkey);
+            System.out.println("private key = " + prikey);
+            System.out.println("pubkey key = " + RSA.getKeyString(pubkey));
+            System.out.println("prikey key = " + RSA.getKeyString(prikey));
+            System.out.println("pubkey key length = " + RSA.getKeyString(pubkey).length());
+            System.out.println("prikey key length = " + RSA.getKeyString(prikey).length());
+            return basePrikey;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+}

+ 3 - 1
src/main/resources/config/beta/application-beta.yml

@@ -43,10 +43,12 @@ wx:
 caimei:
   oldapi: https://www-b.caimei365.com
   #支付异步回调地址
-  notifyUrl: https://mall2c-b.caimei365.com/PayOrder/paymentCallback
+  notifyUrl: https://mall2c-b.caimei365.com/order/pay/callback
   #支付链接重定向地址
   redirectLink: https://mall2c-b.caimei365.com/PayOrder/jumpPage
   #微服务网关地址
   userUrl: https://core-b.caimei365.com
   #延时分账异步回调地址
   delayedSplittingUrl: https://mall2c-b.caimei365.com/PayOrder/delayedSplittingCallback
+  #环境
+  environment: BETA

+ 1 - 1
src/main/resources/config/dev/application-dev.yml

@@ -42,7 +42,7 @@ wx:
 caimei:
   oldapi: http://localhost:8100
   #支付异步回调地址
-  notifyUrl: https://mall2c-b.caimei365.com/PayOrder/paymentCallback
+  notifyUrl: https://mall2c-b.caimei365.com/order/pay/callback
   #支付链接重定向地址
   redirectLink: https://mall2c-b.caimei365.com/PayOrder/jumpPage
   #微服务网关地址

+ 1 - 1
src/main/resources/config/prod/application-prod.yml

@@ -44,7 +44,7 @@ wx:
 caimei:
   oldapi: https://www.caimei365.com
   #支付异步回调地址
-  notifyUrl: https://mall2c.caimei365.com/PayOrder/paymentCallback
+  notifyUrl: https://mall2c.caimei365.com/order/pay/callback
   #支付链接重定向地址
   redirectLink: https://mall2c.caimei365.com/PayOrder/jumpPage
   #微服务网关地址

+ 62 - 0
src/main/resources/mapper/OrderMapper.xml

@@ -101,6 +101,12 @@
         cso.totalAmount,
         cso.note,
         cso.shopPostFee,
+        cso.needPayAmount,
+        cso.eachDiscount,
+        cso.realPay,
+        cso.receiptAmount,
+        cso.receiptStatus,
+        (ifnull(cso.realPay,0)-ifnull(cso.receiptAmount,0)) as restAmount,
         s.name AS shopName,
         s.logo AS shopLogo
         FROM
@@ -561,4 +567,60 @@
     <select id="findOrderCoupon" resultType="java.lang.Integer">
         select receiveCouponId from cm_hehe_coupon_order_record where orderId = #{orderId}
     </select>
+    <select id="getShopOrderByOrderId" resultType="com.caimei.model.vo.ShopOrderVo">
+        SELECT
+            shopOrderID AS shopOrderId,
+            shopOrderNo,
+            orderID AS orderId,
+            orderNo,
+            shopID AS shopId,
+            note,
+            userID AS userId,
+            clubID AS clubId,
+            spID AS spId,
+            orderPromotionsId,
+            promotionFullReduction,
+            brokerage,
+            canRefundAmount,
+            itemCount,
+            totalAmount,
+            productAmount,
+            needPayAmount,
+            shopProductAmount,
+            shopPostFee,
+            shopTaxFee,
+            shouldPayShopAmount,
+            orderTime,
+            orderSubmitType,
+            payStatus,
+            sendOutStatus,
+            splitFlag,
+            realPay,
+            receiptAmount,
+            receiptStatus
+        FROM cm_shop_order
+        WHERE delFlag = 0
+          AND shopOrderID = #{shopOrderId}
+    </select>
+    <select id="getShopOrderDiscernReceipt" resultType="com.caimei.model.vo.DiscernReceiptVo">
+        SELECT
+            cdr.id,
+            cdr.payWay,
+            cdr.payType,
+            cdr.receiptType,
+            cdr.receiptStatus,
+            cdr.receiptAmount,
+            cdr.confirmType,
+            cdr.receiptDate,
+            cdr.confirmDate,
+            cdr.reviewDate,
+            cdr.updateDate,
+            cdr.delFlag,
+            cror.associateAmount
+        FROM cm_receipt_order_relation cror
+                 LEFT JOIN cm_discern_receipt cdr ON cror.receiptID = cdr.id
+        WHERE cror.orderID = #{orderId} AND cror.relationType = '2'
+          AND cror.delFlag = '0' AND cdr.delFlag = '0' AND cdr.receiptStatus = '3' AND cdr.payType != '16' AND cdr.receiptStatus IN(2,3)
+        ORDER BY cdr.receiptDate DESC
+    </select>
 </mapper>

+ 38 - 6
src/main/resources/mapper/OrderSubmitMapper.xml

@@ -4,10 +4,11 @@
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.caimei.mapper.OrderSubmitMapper">
     <select id="findShopByCartIds" resultType="com.caimei.model.vo.ShopVo">
-        SELECT
+        SELECT DISTINCT
           s.shopID AS shopId,
           s.name,
-          s.logo
+          s.logo,
+          p.splitCode
         FROM
           cm_cart cc
           LEFT JOIN product p ON cc.productID = p.productID
@@ -17,10 +18,8 @@
         <foreach item="cartId" index="index" collection="cartIds" open="(" separator="," close=")">
             #{cartId}
         </foreach>
-        GROUP BY
-          s.shopID
         ORDER BY
-          MAX(cc.addTime) DESC
+          cc.addTime DESC
     </select>
 
     <select id="findByShopCartProduct" resultType="com.caimei.model.vo.CartProductVo">
@@ -44,6 +43,7 @@
         WHERE
           chp.validFlag = 1
           AND p.shopID = #{shopId}
+          and p.splitCode = #{splitCode}
           AND cc.cm_cartID IN
         <foreach item="cartId" index="index" collection="cartIds" open="(" separator="," close=")">
             #{cartId}
@@ -80,6 +80,7 @@
           p.normalPrice,
           p.name,
           p.mainImage,
+          p.splitCode,
           s.name AS shopName
         FROM
           cm_hehe_product chp
@@ -663,6 +664,21 @@
             <if test="zeroCostFlag != null">
                 zeroCostFlag,
             </if>
+            <if test="splitCode != null">
+                splitCode,
+            </if>
+            <if test="realPay != null">
+                realPay,
+            </if>
+            <if test="eachDiscount != null">
+                eachDiscount,
+            </if>
+            <if test="receiptAmount != null">
+                receiptAmount,
+            </if>
+            <if test="receiptStatus != null">
+                receiptStatus,
+            </if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="shopOrderNo != null">
@@ -875,6 +891,21 @@
             <if test="zeroCostFlag != null">
                 #{zeroCostFlag,jdbcType=INTEGER},
             </if>
+            <if test="splitCode != null">
+                #{splitCode},
+            </if>
+            <if test="realPay != null">
+                #{realPay},
+            </if>
+            <if test="eachDiscount != null">
+                #{eachDiscount},
+            </if>
+            <if test="receiptAmount != null">
+                #{receiptAmount},
+            </if>
+            <if test="receiptStatus != null">
+                #{receiptStatus},
+            </if>
         </trim>
     </insert>
 
@@ -1539,7 +1570,8 @@
         SELECT
           s.shopID AS shopId,
           s.name,
-          s.logo
+          s.logo,
+          p.splitCode
         FROM
           cm_hehe_product chp
           LEFT JOIN product p ON chp.productId = p.productID

+ 21 - 5
src/main/resources/mapper/PayOrderMapper.xml

@@ -57,6 +57,9 @@
             <if test="onlinePayFlag != null">
                 onlinePayFlag = #{onlinePayFlag},
             </if>
+            <if test="updateDate != null and updateDate != ''">
+                updateDate = #{updateDate},
+            </if>
         </set>
         where orderID = #{orderId}
     </update>
@@ -96,13 +99,13 @@
     <insert id="insertOrderRelation" keyColumn="id" keyProperty="id" useGeneratedKeys="true" parameterType="com.caimei.model.po.CmReceiptOrderRelationPo">
         INSERT INTO `cm_receipt_order_relation` (
           `relationType`, `receiptID`, `associateAmount`,
-          `orderID`, `delFlag`, mbOrderId, orderRequestNo,
+          `orderID`, `delFlag`, mbOrderId, shopOrderId, orderRequestNo,
           splitStatus
         )
         VALUES
           (
             #{relationType}, #{receiptID}, #{associateAmount},
-            #{orderID}, #{delFlag},#{mbOrderId},#{orderRequestNo},
+            #{orderID}, #{delFlag},#{mbOrderId}, #{shopOrderId}, #{orderRequestNo},
             #{splitStatus}
           )
     </insert>
@@ -245,7 +248,7 @@
           shopID
     </select>
 
-    <select id="findPaidShop" resultType="java.math.BigDecimal">
+    <select id="findPaidShop" resultType="java.lang.Double">
         SELECT
           SUM(payAmount)
         FROM
@@ -255,6 +258,14 @@
           AND delFlag = 0
           AND shopOrderID = #{shopOrderId}
     </select>
+    <select id="findPayType" resultType="java.lang.String">
+        SELECT payType FROM cm_discern_receipt cdr
+                                LEFT JOIN cm_receipt_order_relation cror ON cdr.id=cror.receiptID
+        WHERE cror.mbOrderId=#{mbOrderId}
+    </select>
+    <select id="findShopOrderSplitCode" resultType="java.lang.String">
+        select splitCode from cm_shop_order where shopOrderID = #{shopOrderId}
+    </select>
 
     <update id="updateShopOrderByPayStatus">
         UPDATE
@@ -279,7 +290,7 @@
         )
         VALUES
           (
-            #{shopID}, #{name}, #{bankAccountName},
+            #{shopId}, #{name}, #{bankAccountName},
             #{bankAccount}, #{bankName}, #{type},
             #{totalAmount}, #{balancePayFee},
             #{transferPayFee}, #{payType}, #{wipePayment},
@@ -299,7 +310,7 @@
         )
         VALUES
           (
-            #{shopID}, #{shopOrderID}, #{shopOrderNo},
+            #{shopId}, #{shopOrderId}, #{shopOrderNo},
             #{payAmount}, #{wipePayment}, #{payType},
             #{payTime}, #{payShopID},
             #{status}, #{delFlag}
@@ -314,4 +325,9 @@
         WHERE
           orderID = #{orderId}
     </update>
+    <update id="updateShopOrderByReceiptStatus">
+        UPDATE cm_shop_order SET
+        receiptStatus = #{receiptStatus}, receiptAmount = #{receiptAmount}
+        WHERE shopOrderID = #{shopOrderId}
+    </update>
 </mapper>

+ 1 - 0
src/main/resources/mapper/ProductMapper.xml

@@ -85,6 +85,7 @@
           AND NOW() BETWEEN cha.beginTime
             AND cha.endTime
           AND a.productId = #{productId}
+        limit 1
     </select>
 
     <select id="findActivityLadder" resultType="com.caimei.model.vo.ActivityLadderVo">