Browse Source

线上支付

plf 4 years ago
parent
commit
ceede840a0
29 changed files with 3653 additions and 0 deletions
  1. 6 0
      pom.xml
  2. 56 0
      src/main/java/com/caimei/config/WebConfiguration.java
  3. 136 0
      src/main/java/com/caimei/controller/PayOrderApi.java
  4. 58 0
      src/main/java/com/caimei/mapper/PayOrderMapper.java
  5. 82 0
      src/main/java/com/caimei/model/dto/PaymentDto.java
  6. 10 0
      src/main/java/com/caimei/model/enumerate/KeyFormat.java
  7. 75 0
      src/main/java/com/caimei/model/enumerate/ReceivablesType.java
  8. 187 0
      src/main/java/com/caimei/model/po/CmDiscernReceiptPo.java
  9. 63 0
      src/main/java/com/caimei/model/po/CmReceiptOrderRelationPo.java
  10. 5 0
      src/main/java/com/caimei/model/vo/DiscernReceiptVo.java
  11. 5 0
      src/main/java/com/caimei/model/vo/OrderVo.java
  12. 44 0
      src/main/java/com/caimei/service/PayOrderService.java
  13. 1 0
      src/main/java/com/caimei/service/impl/OrderServiceImpl.java
  14. 303 0
      src/main/java/com/caimei/service/impl/PayOrderServiceImpl.java
  15. 128 0
      src/main/java/com/caimei/util/AES.java
  16. 601 0
      src/main/java/com/caimei/util/Base64.java
  17. 36 0
      src/main/java/com/caimei/util/CheckUtils.java
  18. 7 0
      src/main/java/com/caimei/util/ConfigureEncryptAndDecrypt.java
  19. 373 0
      src/main/java/com/caimei/util/ConvertUtils.java
  20. 164 0
      src/main/java/com/caimei/util/Digest.java
  21. 85 0
      src/main/java/com/caimei/util/HashUtil.java
  22. 167 0
      src/main/java/com/caimei/util/HttpClient4Utils.java
  23. 23 0
      src/main/java/com/caimei/util/KeyGenerator.java
  24. 133 0
      src/main/java/com/caimei/util/KeyPair.java
  25. 206 0
      src/main/java/com/caimei/util/KeyWorker.java
  26. 141 0
      src/main/java/com/caimei/util/PayUtils.java
  27. 106 0
      src/main/java/com/caimei/util/RSAUtil.java
  28. 174 0
      src/main/java/com/caimei/util/XmlKeyBuilder.java
  29. 278 0
      src/main/resources/mapper/PayOrderMapper.xml

+ 6 - 0
pom.xml

@@ -112,6 +112,12 @@
             <artifactId>knife4j-spring-boot-starter</artifactId>
             <version>3.0.2</version>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.7</version>
+        </dependency>
     </dependencies>
 
     <profiles>

+ 56 - 0
src/main/java/com/caimei/config/WebConfiguration.java

@@ -0,0 +1,56 @@
+package com.caimei.config;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.config.CorsRegistry;
+import org.springframework.web.reactive.config.WebFluxConfigurer;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+import java.net.InetSocketAddress;
+import java.util.Objects;
+
+@Configuration
+public class WebConfiguration implements WebFluxConfigurer {
+
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry
+                .addMapping("/**")
+                .allowedOrigins("*")
+                .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTION")
+                .allowedHeaders("header1", "header2", "header3")
+                .exposedHeaders("header1", "header2")
+                .allowCredentials(true)
+                .maxAge(3600);
+    }
+
+    /**
+     * https://stackoverflow.com/questions/51192630/how-do-you-get-clients-ip-address-spring-webflux-websocket?rq=1
+     * https://stackoverflow.com/questions/50981136/how-to-get-client-ip-in-webflux
+     * https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-filters
+     * 由于在低版本的 spring-webflux 中不支持直接获得请求 IP(https://jira.spring.io/browse/SPR-16681),因此写了一个补丁曲线救国,
+     * 从 org.springframework.web.server.ServerWebExchange 中获得 IP 后,在放到 header 里
+     */
+    @Component
+    public static class RetrieveClientIpWebFilter implements WebFilter {
+        @Override
+        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
+            ServerHttpRequest request = exchange.getRequest();
+            // 从我们的 nginx 重写请求获取的参数,X-Real-IP是在nginx中配置的,要对应上;
+            String clientIp = request.getHeaders().getFirst("X-Real-IP");
+            if (StringUtils.isEmpty(clientIp)) {
+                InetSocketAddress remoteAddress = request.getRemoteAddress();
+                clientIp = Objects.requireNonNull(remoteAddress).getAddress().getHostAddress();
+            }
+            ServerHttpRequest mutatedServerHttpRequest = request.mutate().header("X-CLIENT-IP", clientIp).build();
+            ServerWebExchange mutatedServerWebExchange = exchange.mutate().request(mutatedServerHttpRequest).build();
+            return chain.filter(mutatedServerWebExchange);
+        }
+    }
+}
+ 

+ 136 - 0
src/main/java/com/caimei/controller/PayOrderApi.java

@@ -0,0 +1,136 @@
+package com.caimei.controller;
+
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.caimei.model.ResponseJson;
+import com.caimei.model.dto.PaymentDto;
+import com.caimei.service.PayOrderService;
+import com.caimei.util.HttpRequest;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.reactive.function.server.ServerRequest;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2020/5/6
+ */
+@Api(tags = "订单支付")
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/PayOrder")
+public class PayOrderApi {
+    private final PayOrderService payOrderService;
+
+    @Value("${caimei.notifyUrl}")
+    private String notifyUrl;
+
+    @Value("${caimei.redirectLink}")
+    private String redirectLink;
+
+    @Value("${wx.AppId}")
+    private String appId;
+
+    @Value("${wx.AppSecret}")
+    private String appSecret;
+
+    /**
+     * 微信线上支付
+     */
+    @ApiOperation("微信线上支付")
+    @PostMapping("/miniWxPay")
+    public ResponseJson<JSONObject> miniWxPay(@RequestBody PaymentDto payment, @RequestHeader HttpHeaders headers) {
+        if (!"WEIXIN".equals(payment.getPayWay()) || payment.getPayAmount() == null || payment.getPayAmount() < 2) {
+            return ResponseJson.error("参数异常", null);
+        }
+        String clientIp = headers.getFirst("X-CLIENT-IP");
+        payment.setClientIp(clientIp);
+        //小程序微信快捷支付
+        payment.setPayType("MINIAPP_WEIXIN");
+        String infos;
+        try {
+            // 获取当前微信小程序的环境
+            String referer = headers.getFirst("Referer");
+            log.info("referer-is----:" + referer);
+            String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
+            Map<String, String> requestUrlParam = new HashMap<String, String>(4);
+            // 小程序appId
+            requestUrlParam.put("appid", appId);
+            // 小程序appSecret
+            requestUrlParam.put("secret", appSecret);
+            // 小程序端返回的code
+            requestUrlParam.put("js_code", payment.getCode());
+            // 默认参数
+            requestUrlParam.put("grant_type", "authorization_code");
+            // 发送post请求读取调用微信接口获取openid用户唯一标识
+            infos = HttpRequest.sendPost(requestUrl, requestUrlParam);
+        } catch (Exception e) {
+            log.error("错误信息", e);
+            return ResponseJson.error("wx小程序获取openid失败", null);
+        }
+        // 解析相应内容(转换成json对象)
+        JSONObject jsonObject = JSON.parseObject(infos);
+        String openId = jsonObject.getString("openid");
+        String errCode = jsonObject.getString("errcode");
+        String errMsg = jsonObject.getString("errmsg");
+        boolean errFlag = StringUtils.isNotEmpty(errCode) && ("-1".equals(errCode) || "40029".equals(errCode) || "45011".equals(errCode));
+        if (errFlag) {
+            return ResponseJson.error(-1, errMsg, null);
+        }
+        if (openId == null) {
+            return ResponseJson.error("wx获取openid失败", null);
+        }
+        payment.setOpenid(openId);
+        payment.setNotifyUrl(notifyUrl);
+        log.info("wx支付openid>>>>>" + openId);
+        return payOrderService.pay(payment);
+    }
+
+    /**
+     * 支付异步通知回调
+     */
+    @GetMapping("/paymentCallback")
+    public String paymentCallback(ServerRequest request) throws Exception {
+        log.info("异步回调通知>>>>>>>start");
+        String data = request.pathVariable("data");
+        if (StringUtils.isBlank(data)) {
+            return "回调参数失败";
+        }
+        return payOrderService.paymentCallback(data);
+    }
+
+    /**
+     * 判断此次支付是否完成
+     */
+    @ApiOperation("判断此次支付是否完成")
+    @GetMapping("/payWhetherSuccess")
+    public ResponseJson<String> payWhetherSuccess(Integer orderId, Integer paySuccessCounter) {
+        if (null == orderId || null == paySuccessCounter) {
+            return ResponseJson.error("参数异常", null);
+        }
+        return payOrderService.payWhetherSuccess(orderId, paySuccessCounter);
+    }
+
+    /**
+     * 查询本次支付订单是否完成
+     */
+    @GetMapping("/findOrderStatus")
+    public ResponseJson<JSONObject> findOrderStatus(String mbOrderId) {
+        if (null == mbOrderId) {
+            return ResponseJson.error("参数异常", null);
+        }
+        return payOrderService.findOrderStatus(mbOrderId);
+    }
+}

+ 58 - 0
src/main/java/com/caimei/mapper/PayOrderMapper.java

@@ -0,0 +1,58 @@
+package com.caimei.mapper;
+
+import com.caimei.model.po.CmDiscernReceiptPo;
+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 org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2021/3/26
+ */
+@Mapper
+public interface PayOrderMapper {
+    /**
+     * 查询支付记录
+     *
+     * @param order
+     * @return
+     */
+    List<DiscernReceiptVo> getDiscernReceipt(OrderVo order);
+
+    /**
+     * 更新订单信息
+     *
+     * @param order
+     */
+    void updateSelective(OrderVo order);
+
+    /**
+     * 保存收款记录
+     *
+     * @param discernReceipt
+     */
+    void insertDiscernReceipt(CmDiscernReceiptPo discernReceipt);
+
+    /**
+     * 保存订单收款关系
+     *
+     * @param relation
+     */
+    void insertOrderRelation(CmReceiptOrderRelationPo relation);
+
+    /**
+     * 查询订单商品
+     *
+     * @param orderId
+     * @return
+     */
+    List<OrderProductVo> findAllOrderProduct(Integer orderId);
+}

+ 82 - 0
src/main/java/com/caimei/model/dto/PaymentDto.java

@@ -0,0 +1,82 @@
+package com.caimei.model.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2020/5/7
+ */
+@Data
+public class PaymentDto implements Serializable {
+
+    /**
+     * 支付金额,单位分,必须大于2
+     */
+    private Integer payAmount;
+
+    /**
+     * 支付方式,
+     * 银联:UNIONPAY,
+     * 微信:WEIXIN,
+     * 支付宝:ALIPAY
+     */
+    private String payWay;
+
+    /**
+     * 支付类型(现阶段主要是区分微信支付)
+     * 微信小程序支付: MINIAPP_WEIXIN
+     * 微信公众号支付: JSAPI_WEIXIN
+     */
+    private String payType;
+
+    /**
+     * 银行编码(银联支付使用)
+     */
+    private String bankCode;
+
+    /**
+     * 用户类型(银联支付使用)
+     * 企业:ENTERPRISE
+     * 个人:USER
+     */
+    private String userType;
+
+    /**
+     * 页面回调地址
+     */
+    private String returnUrl;
+
+    /**
+     * 微信小程序code,微信小程序支付使用
+     */
+    private String code;
+
+    /**
+     * 主订单id
+     */
+    private Integer orderId;
+
+    /**
+     * 微信支付使用
+     */
+    private String openid;
+
+    /**
+     * 异步通知回调
+     */
+    private String notifyUrl;
+
+    /**
+     * 微信公众号state参数
+     */
+    private String state;
+
+    /**
+     * 用户ip地址
+     */
+    private String clientIp;
+}

+ 10 - 0
src/main/java/com/caimei/model/enumerate/KeyFormat.java

@@ -0,0 +1,10 @@
+package com.caimei.model.enumerate;
+
+/*
+2015-01-23
+*/
+public enum KeyFormat {
+    ASN,
+    XML,
+    PEM
+}

+ 75 - 0
src/main/java/com/caimei/model/enumerate/ReceivablesType.java

@@ -0,0 +1,75 @@
+package com.caimei.model.enumerate;
+/**
+ *支付类型枚举类
+ *
+ * @author Aslee
+ */
+public enum ReceivablesType {
+    JIANSHE(1, "建设银行-7297"),
+    GUANGFA(2, "广发银行-0115"),
+    ZHONGXING_1(3, "中信银行-7172"),
+    ZHONGXING_2(4, "中信银行-0897"),
+    ZHONGXING_2_1(5, "中信银行-0897-财付通"),
+    ZHONGXING_2_2(6, "中信银行-0897-支付宝"),
+    XIANSHANG_ZFB(7, "线上-支付宝"),
+    XIANSHANG_WX(8, "线上-微信支付"),
+    XIANSHANG_KUAIQIAN(9, "线上-快钱支付"),
+    KOUTOUFANYONG(10, "口头返佣"),
+    GUANGFA_1(11, "广发银行-5461"),
+    WANGYIN(12, "企业网银"),
+    PCWX(13, "微信支付"),
+    ZHIFUBAO(14, "支付宝"),
+    MINIWX(15, "微信支付"),
+    YUEDIKO(16, "余额抵扣");
+
+
+    private int code;
+    private String desc;
+
+    public int getCode() {
+        return code;
+    }
+
+    public void setCode(int code) {
+        this.code = code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    ReceivablesType(int code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public static String getReceivablesType(int code) {
+        for (ReceivablesType type : ReceivablesType.values()) {
+            if (type.getCode() == code) {
+                return type.desc;
+            }
+        }
+        return "";
+    }
+
+    public static ReceivablesType getReceivablesType(String desc) {
+        for (ReceivablesType type : ReceivablesType.values()) {
+            if (type.getDesc().contains(desc)) {
+                return type;
+            }
+        }
+        return null;
+    }
+
+    public static Boolean havaHandlingFee(int code) {
+        boolean res = false;
+        if (ReceivablesType.ZHONGXING_2_1.getCode() == code || ReceivablesType.ZHONGXING_2_2.getCode() == code || ReceivablesType.ZHONGXING_2.getCode() == code || ReceivablesType.GUANGFA_1.getCode() == code) {
+            res = true;
+        }
+        return res;
+    }
+}

+ 187 - 0
src/main/java/com/caimei/model/po/CmDiscernReceiptPo.java

@@ -0,0 +1,187 @@
+package com.caimei.model.po;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * cm_discern_receipt
+ * @author 
+ */
+@Data
+public class CmDiscernReceiptPo implements Serializable {
+    private Long id;
+
+    /**
+     * 用户付款方式:1线上,2线下,3余额抵扣
+     */
+    private String payWay;
+
+    /**
+     * 付款类型:1建设银行7297、2广发银行0115、3中信银行7172、4中信银行0897、5中信银行0897-财付通、6中信银行0897-支付宝、7线上-支付宝、8线上-微信支付、9线上-快钱支付、10口头返佣、11广发银行5461、12PC-B2B网银、13PC-微信支付、14PC-支付宝、15小程序-微信支付、16余额抵扣、17PC-B2C网银
+     */
+    private String payType;
+
+    /**
+     * 收款款项类型:1订单款,2非订单款,3返佣款
+     */
+    private String receiptType;
+
+    /**
+     * 收款状态:1待确认、2已确认(待审核)、3审核通过、4审核未通过、5收款撤销【订单款项状态:12345】【非订单款项状态:125】【返佣款状态:125】【线上支付成功为审核通过】
+     */
+    private String receiptStatus;
+
+    /**
+     * 短信内容
+     */
+    private String smsContent;
+
+    /**
+     * 短信内容Md5加密串(适用于二次短信匹配查询)
+     */
+    private String smsMd5Code;
+
+    /**
+     * 短信订单标识内容(适用打款备注订单Id)
+     */
+    private String orderFlag;
+
+    /**
+     * 收款金额(线上一次性付款和支付金额一致)
+     */
+    private BigDecimal receiptAmount;
+
+    /**
+     * 手续费(个别银行存在手续费)
+     */
+    private BigDecimal handlingFee;
+
+    /**
+     * 确认订单类型:1小额抹平确认,2大额抹平确认,3大额退款余额,4确认关联(线上支付默认为4)
+     */
+    private String confirmType;
+
+    /**
+     * 收款人权限ID(对应receipt_user_permission表)
+     */
+    private Integer receiptUserPermissionID;
+
+    /**
+     * 确认人权限ID(对应receipt_user_permission表)
+     */
+    private Long confirmUserPermissionID;
+
+    /**
+     * 审核人权限ID(对应receipt_user_permission表)
+     */
+    private Long reviewUserPermissionID;
+
+    /**
+     * 撤销人ID(对应sys_user表)
+     */
+    private Long cancelUserPermissionID;
+
+    /**
+     * 交易号--[线上字段]
+     */
+    private String transactionNum;
+
+    /**
+     * 银行Id--[线上字段]
+     */
+    private Integer bankID;
+
+    /**
+     * 银行卡号--[线上字段]
+     */
+    private String bankCode;
+
+    /**
+     * 快钱支付类型ID--[线上字段]
+     */
+    private Integer kuaiQianPayTypeID;
+
+    /**
+     * 块钱支付ID--[线上字段]
+     */
+    private Integer kuaiQianPayerID;
+
+    /**
+     * 支付状态 1支付成功--[线上字段]
+     */
+    private String rePayFlag;
+
+    /**
+     * 支付金额(可能废弃如果只需要一个支付就使用receiptAmount)--[线上字段]
+     */
+    private Double actualAmount;
+
+    /**
+     * 支付回调返回数据--[线上字段]
+     */
+    private String formData;
+
+    /**
+     * 支付问题--[线上字段]
+     */
+    private String problem;
+
+    /**
+     * 非订单款说明(适用协销确认的时候区分订单和非订单款)
+     */
+    private String noOrderReason;
+
+    /**
+     * 抹平备注(目前适用后台直接抹平账户操作)
+     */
+    private String balanceAccountsRemark;
+
+    /**
+     * 返佣关联备注
+     */
+    private String rebateRemarks;
+
+    /**
+     * 审核不通过原因
+     */
+    private String reviewReason;
+
+    /**
+     * 撤销原因
+     */
+    private String cancelReason;
+
+    /**
+     * 收款时间
+     */
+    private String receiptDate;
+
+    /**
+     * 确认时间
+     */
+    private String confirmDate;
+
+    /**
+     * 审核时间
+     */
+    private String reviewDate;
+
+    /**
+     * 撤销时间
+     */
+    private String cancelDate;
+
+    /**
+     * 更新操作时间
+     */
+    private String updateDate;
+
+    /**
+     * 作废标记 0 否,其余是
+     */
+    private String delFlag;
+
+    private static final long serialVersionUID = 1L;
+}

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

@@ -0,0 +1,63 @@
+package com.caimei.model.po;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * cm_receipt_order_relation
+ *
+ * @author
+ */
+@Data
+public class CmReceiptOrderRelationPo implements Serializable {
+    private Long id;
+
+    /**
+     * 关系类型:1返佣订单(返佣款)、2非返佣订单(订单款或者非订单款)
+     */
+    private String relationType;
+
+    /**
+     * 识别款项Id(对应cm_discern_receipt表)
+     */
+    private Integer receiptID;
+
+    /**
+     * 订单Id(relationType值为1是为子订单ID,为2时为主订单ID)
+     */
+    private Integer orderID;
+
+    /**
+     * 关联方式: 1手动 2自动
+     */
+    private String associationType;
+
+    /**
+     * 米花科技平台唯一流水号
+     */
+    private String mbOrderId;
+
+    /**
+     * 商户唯一订单请求号(订单编号T随机时间戳)
+     */
+    private String orderRequestNo;
+
+    /**
+     * 分账状态:0待分账,1已分账(只针对线上支付)
+     */
+    private String splitStatus;
+
+    /**
+     * 关联金额:1普通收款(线下):收款具体对该应母订单的收金额、2线上支付:付款金额就等于该金额、3返佣收款:默认为0
+     */
+    private BigDecimal associateAmount;
+
+    /**
+     * 删除标记 0 否,其余是
+     */
+    private String delFlag;
+
+    private static final long serialVersionUID = 1L;
+}

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

@@ -23,4 +23,9 @@ public class DiscernReceiptVo implements Serializable {
      * 收款时间
      */
     private String receiptDate;
+
+    /**
+     * 用户付款方式:1线上,2线下,3余额抵扣
+     */
+    private String payWay;
 }

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

@@ -214,6 +214,11 @@ public class OrderVo implements Serializable {
      */
     private Integer actualCancelNum;
 
+    /**
+     * 支付时间
+     */
+    private String receiptDate;
+
     /**
      * 支付按钮是否消失,true消失
      */

+ 44 - 0
src/main/java/com/caimei/service/PayOrderService.java

@@ -0,0 +1,44 @@
+package com.caimei.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.caimei.model.ResponseJson;
+import com.caimei.model.dto.PaymentDto;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2020/5/9
+ */
+public interface PayOrderService {
+
+    /**
+     * 线上支付
+     *
+     * @param payment 支付参数对象
+     * @return
+     */
+    ResponseJson<JSONObject> pay(PaymentDto payment);
+
+    /**
+     * 异步回调通知
+     */
+    String paymentCallback(String data) throws Exception;
+
+    /**
+     * 判断此次支付是否完成
+     *
+     * @param orderId           订单id
+     * @param paySuccessCounter 付款次数
+     * @return
+     */
+    ResponseJson<String> payWhetherSuccess(Integer orderId, Integer paySuccessCounter);
+
+    /**
+     * 查询第三方订单状态
+     *
+     * @param mbOrderId 平台唯一流水号
+     * @return
+     */
+    ResponseJson<JSONObject> findOrderStatus(String mbOrderId);
+}

+ 1 - 0
src/main/java/com/caimei/service/impl/OrderServiceImpl.java

@@ -386,6 +386,7 @@ public class OrderServiceImpl implements OrderService {
             for (DiscernReceiptVo discernReceipt : discernReceiptList) {
                 receiptAmount = receiptAmount.add(discernReceipt.getAssociateAmount());
             }
+            order.setReceiptDate(discernReceiptList.get(0).getReceiptDate());
         }
         //待付总金额
         order.setPendingPayments(MathUtil.sub(order.getPayableAmount(), receiptAmount));

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

@@ -0,0 +1,303 @@
+package com.caimei.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.caimei.mapper.OrderMapper;
+import com.caimei.mapper.PayOrderMapper;
+import com.caimei.model.ResponseJson;
+import com.caimei.model.dto.PaymentDto;
+import com.caimei.model.po.CmDiscernReceiptPo;
+import com.caimei.model.po.CmReceiptOrderRelationPo;
+import com.caimei.model.vo.DiscernReceiptVo;
+import com.caimei.model.vo.OrderVo;
+import com.caimei.service.PayOrderService;
+import com.caimei.util.MathUtil;
+import com.caimei.util.PayUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Description
+ *
+ * @author : plf
+ * @date : 2020/5/9
+ */
+@Service
+@Slf4j
+public class PayOrderServiceImpl implements PayOrderService {
+    @Resource
+    private PayOrderMapper payOrderMapper;
+    @Resource
+    private OrderMapper orderMapper;
+
+    /**
+     * 商户标识
+     */
+    String merAccount = "aa9aee6a148843a6a9e4ea117df4454b";
+    /**
+     * 私钥,商户密钥
+     */
+    String merKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAM0qCTZFdi1I59/Jeis+8KVhzSNhsRrKiOlHazIVyxNLzUQvFpWN5PlfVKlnJToorURTStfjAv01HD0Z4ZvMauuHhDT0bptiDln928Ld6SzX889X2nsCxl0Q+WzrkFsnT6gotvGnTeQGUgbBV3SQD3IUOwRwxoWYbrZqxtqHFxqRAgMBAAECgYEAvsHx9MMbAToDVmEXtXP8/lh0Cwy/RgDA0d30voni2pslTNtXbVCUcIUBy8y6oVvG1nt3YEmTsuiZy/nvehPT6GV1Gqj8T6lqWQ8KQKsDGlubvh23tzNM90me2TLZEPdNqC5CPRrwYrjght4BXgzu7s2+5FpeYCob1gVNi+w0Jz0CQQD0dXb9Oez+Ybnxb3rCghGWM6cxi8fsqk6MuKaleg53qfXrkgAVH78faeWzRaeSbVOh5+Z9kX5HUeynfM7E/f4nAkEA1tmnvJp4JQaouO1Trzbnkhowjea5daK/tDE8K0hIMHUjAw+c1QTteGOVGBFBHWPkUwkSCd2HKmk4URkp/snMhwJAO32+qF+Jclq8EqqLmHxo5UHKxX7793d2yD5Dp++tR6fgBiUwyfNA4tc1pEwmPLdIbBVwfUyEC70/N39jHoOlbwI/dX6SPJI9IgKCQp+HJEriWQP5iaCjy7E1JVXHkeP1lop4mzPukJAhTbUn1AGbmncGZmKPetWrFYZ1ReR9EtlJAkEA2kBmARWyOTt8fbikImuHr65M+BDgD+2fkuDb5+tqJljNmSqAIRRwcXj1Je6vkzlDJuSw3wlG6iliFtwe0cq7+w==";
+    /**
+     * 公钥
+     */
+    String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNKgk2RXYtSOffyXorPvClYc0jYbEayojpR2syFcsTS81ELxaVjeT5X1SpZyU6KK1EU0rX4wL9NRw9GeGbzGrrh4Q09G6bYg5Z/dvC3eks1/PPV9p7AsZdEPls65BbJ0+oKLbxp03kBlIGwVd0kA9yFDsEcMaFmG62asbahxcakQIDAQAB";
+    /**
+     * 用户编号
+     */
+    String merNo = "10001720";
+
+    @Override
+    public synchronized ResponseJson<JSONObject> pay(PaymentDto payment) {
+        JSONObject result = null;
+        OrderVo order = orderMapper.findOrder(payment.getOrderId());
+        if (null == order) {
+            return ResponseJson.error("订单不存在", null);
+        }
+        if ("0".equals(order.getStatus()) || "33".equals(order.getStatus()) || "4".equals(order.getStatus()) || "5".equals(order.getStatus())
+                || "6".equals(order.getStatus()) || "7".equals(order.getStatus())) {
+            return ResponseJson.error("订单状态错误", null);
+        }
+        log.info("本次付款金额>>>>>" + payment.getPayAmount());
+        if (MathUtil.compare(MathUtil.mul(order.getPayableAmount(), 100), payment.getPayAmount()) < 0) {
+            return ResponseJson.error("付款金额错误", null);
+        }
+        try {
+            // 时间戳
+            long time = System.currentTimeMillis() / 1000;
+            JSONObject json = getPayParameter(payment, time);
+            // 商户订单号
+            String orderId = order.getOrderNo() + "T" + time;
+            json.put("orderId", orderId);
+            //商品名称
+            String product = "采美订单" + order.getOrderNo();
+            json.put("product", product);
+            //支付类型
+            String payType = getPayType(payment);
+            String attach = order.getOrderId() + "," + payType;
+            json.put("attach", attach);
+            String sign = PayUtils.buildSign(json, merKey);
+            json.put("sign", sign);
+            String data = PayUtils.buildDataPrivate(json, merKey);
+            result = PayUtils.httpGet("https://platform.mhxxkj.com/paygateway/mbpay/order/v1", merAccount, data);
+        } catch (Exception e) {
+            log.error("错误信息", e);
+            return ResponseJson.error("支付失败", null);
+        }
+        String code = result.getString("code");
+        if (!"000000".equals(code)) {
+            String msg = result.getString("msg");
+            log.info("第三方支付失败>>>>>>>msg:" + msg);
+            return ResponseJson.error(msg, null);
+        }
+        return ResponseJson.success(result);
+    }
+
+    /**
+     * 设置第三方支付参数
+     */
+    private JSONObject getPayParameter(PaymentDto payment, long time) throws Exception {
+        String userType = "ENTERPRISE";
+        JSONObject json = new JSONObject();
+        json.put("merAccount", merAccount);
+        json.put("merNo", merNo);
+        json.put("time", time);
+        //支付金额
+        json.put("amount", payment.getPayAmount());
+        json.put("payWay", payment.getPayWay());
+        json.put("payType", payment.getPayType());
+        json.put("userIp", payment.getClientIp());
+        json.put("returnUrl", payment.getReturnUrl());
+        json.put("notifyUrl", payment.getNotifyUrl());
+        if (null != payment.getBankCode()) {
+            json.put("bankCode", payment.getBankCode());
+        }
+        json.put("userType", userType);
+        if (null != payment.getOrderId()) {
+            json.put("openId", payment.getOpenid());
+        }
+        return json;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String paymentCallback(String data) throws Exception {
+        //公钥解密
+        JSONObject json = PayUtils.decryptDataPublic(data, publicKey);
+        log.info("公钥解密>>>>>>" + json);
+        //公钥验签
+        String signaa = json.getString("sign");
+        json.remove("sign");
+        String signbb = PayUtils.buildSign(json, publicKey);
+        if (!signaa.equals(signbb)) {
+            return "验签失败";
+        }
+        //订单状态
+        String orderStatus = json.getString("orderStatus");
+        //附加数据,下单时若有传输则原样返回,下单时为空,则不返回该数据
+        String attach = json.getString("attach");
+        //平台唯一流水号
+        String mbOrderId = json.getString("mbOrderId");
+        //商户唯一订单号
+        String orderRequestNo = json.getString("orderId");
+        //订单金额,以元为单位
+        BigDecimal amount = json.getBigDecimal("amount");
+        log.info("订单状态>>>>>>" + orderStatus);
+        if ("FAILED".equals(orderStatus)) {
+            return "支付失败";
+        }
+        String[] split = attach.split(",");
+        //订单id
+        Integer orderId = Integer.valueOf(split[0]);
+        //支付类型
+        String payType = split[1];
+        OrderVo order = orderMapper.findOrder(orderId);
+        //支付记录
+        List<DiscernReceiptVo> discernReceiptList = payOrderMapper.getDiscernReceipt(order);
+        BigDecimal receiptAmount = BigDecimal.ZERO;
+        if (null != discernReceiptList && discernReceiptList.size() > 0) {
+            for (DiscernReceiptVo discernReceipt : discernReceiptList) {
+                receiptAmount = receiptAmount.add(discernReceipt.getAssociateAmount());
+            }
+        }
+        //已付金额+本次支付金额
+        receiptAmount = MathUtil.add(receiptAmount, amount);
+        log.info("已付金额+本次支付金额>>>>>>>" + receiptAmount);
+        if (MathUtil.compare(order.getPayableAmount(), receiptAmount) == 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());
+        } 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");
+        }
+        //更新付款次数
+        order.setPaySuccessCounter(order.getPaySuccessCounter() + 1);
+        payOrderMapper.updateSelective(order);
+        /*//修改支付链接状态
+        OrderPayLinkVo orderPayLink = payOrderMapper.findOrderPayLink(order.getOrderId(), amount);
+        if (null != orderPayLink && ("12".equals(payType) || "17".equals(payType))) {
+            orderPayLink.setPayStatus("1");
+            payOrderMapper.updateOrderPayLink(orderPayLink);
+        }*/
+        //保存收款记录
+        String curDateStr = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date());
+        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(json.toJSONString());
+        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");
+        payOrderMapper.insertOrderRelation(relation);
+        log.info(">>>>>>>>>>>>>>>>>>>>>>>保存付款金额到收款记录," + amount);
+        return "SUCCESS";
+    }
+
+    /**
+     * 支付状态转换
+     */
+    private String getPayType(PaymentDto payment) {
+        String payType = payment.getPayType();
+        if ("ALIPAY_H5".equals(payType)) {
+            //支付宝H5
+            return "14";
+        } else if ("JSAPI_WEIXIN".equals(payType)) {
+            //微信公众号支付
+            return "13";
+        } else if ("MINIAPP_WEIXIN".equals(payType)) {
+            //微信小程序支付
+            return "15";
+        } else if ("GATEWAY_UNIONPAY".equals(payType) && "ENTERPRISE".equals(payment.getUserType())) {
+            //企业网银
+            return "12";
+        } else {
+            //个人网银
+            return "17";
+        }
+    }
+
+    @Override
+    public ResponseJson<String> payWhetherSuccess(Integer orderId, Integer paySuccessCounter) {
+        OrderVo order = orderMapper.findOrder(orderId);
+        if (order.getPaySuccessCounter().equals(paySuccessCounter)) {
+            return ResponseJson.error(-2, "支付失败", null);
+        } else if (order.getPaySuccessCounter() > paySuccessCounter) {
+            return ResponseJson.success("支付成功");
+        } else {
+            return ResponseJson.error("支付异常>>>>>>>" + order, null);
+        }
+    }
+
+    @Override
+    public ResponseJson<JSONObject> findOrderStatus(String mbOrderId) {
+        // 时间戳
+        long time = System.currentTimeMillis() / 1000;
+        JSONObject json = new JSONObject();
+        json.put("merAccount", merAccount);
+        json.put("mbOrderId", mbOrderId);
+        json.put("time", time);
+        String sign = PayUtils.buildSign(json, merKey);
+        json.put("sign", sign);
+        String data = "";
+        try {
+            data = PayUtils.buildDataPrivate(json, merKey);
+        } catch (Exception e) {
+            log.error("错误信息", e);
+        }
+        JSONObject result = PayUtils.httpGet("https://platform.mhxxkj.com/paygateway/mbpay/order/query/v1_1", merAccount, data);
+        String code = result.getString("code");
+        if (!"000000".equals(code)) {
+            String msg = result.getString("msg");
+            log.info("第三方查询订单失败>>>>>>>msg:" + msg);
+            return ResponseJson.error(msg, null);
+        }
+        return ResponseJson.success(result);
+    }
+}

+ 128 - 0
src/main/java/com/caimei/util/AES.java

@@ -0,0 +1,128 @@
+package com.caimei.util;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+
+public class AES {
+    /**
+     * 加密
+     *
+     * @param content  需要加密的内容
+     * @param password 加密密码
+     * @return
+     */
+    public static byte[] encrypt(byte[] data, byte[] key) {
+        CheckUtils.notEmpty(data, "data");
+        CheckUtils.notEmpty(key, "key");
+        if (key.length < 16) {
+            throw new RuntimeException("Invalid AES key length (must be 16 bytes)");
+        }
+        try {
+            SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
+            byte[] enCodeFormat = secretKey.getEncoded();
+            SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, "AES");
+            Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);// 创建密码器
+            cipher.init(Cipher.ENCRYPT_MODE, seckey);// 初始化
+            byte[] result = cipher.doFinal(data);
+            return result; // 加密
+        } catch (Exception e) {
+            throw new RuntimeException("encrypt fail!", e);
+        }
+    }
+
+    /**
+     * 解密
+     *
+     * @param content  待解密内容
+     * @param password 解密密钥
+     * @return
+     */
+    public static byte[] decrypt(byte[] data, byte[] key) {
+        CheckUtils.notEmpty(data, "data");
+        CheckUtils.notEmpty(key, "key");
+        System.out.println(key.length);
+        if (key.length < 16) {
+            throw new RuntimeException("Invalid AES key length (must be 16 bytes)");
+        }
+        try {
+            SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
+            byte[] enCodeFormat = secretKey.getEncoded();
+            SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, "AES");
+            Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);// 创建密码器
+            cipher.init(Cipher.DECRYPT_MODE, seckey);// 初始化
+            byte[] result = cipher.doFinal(data);
+            return result; // 加密
+        } catch (Exception e) {
+            throw new RuntimeException("decrypt fail!", e);
+        }
+    }
+
+    public static String encryptToBase64(String data, String key) {
+        try {
+            byte[] valueByte = encrypt(data.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING), key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING));
+            return new String(Base64.encode(valueByte));
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("encrypt fail!", e);
+        }
+
+    }
+
+    public static String decryptFromBase64(String data, String key) {
+        try {
+            byte[] originalData = Base64.decode(data.getBytes());
+            byte[] valueByte = decrypt(originalData, key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING));
+            return new String(valueByte, ConfigureEncryptAndDecrypt.CHAR_ENCODING);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("decrypt fail!", e);
+        }
+    }
+
+    public static String encryptWithKeyBase64(String data, String key) {
+        try {
+            byte[] valueByte = encrypt(data.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING), key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING));
+            return new String(Base64.encode(valueByte));
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("encrypt fail!", e);
+        }
+    }
+
+    public static String decryptWithKeyBase64(String data, String key) {
+        try {
+            byte[] originalData = Base64.decode(data.getBytes());
+            byte[] valueByte = decrypt(originalData, key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING));
+            return new String(valueByte, ConfigureEncryptAndDecrypt.CHAR_ENCODING);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("decrypt fail!", e);
+        }
+    }
+
+    public static byte[] genarateRandomKey() {
+        KeyGenerator keygen = null;
+        try {
+            keygen = KeyGenerator.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(" genarateRandomKey fail!", e);
+        }
+        SecureRandom random = new SecureRandom();
+        keygen.init(random);
+        Key key = keygen.generateKey();
+        return key.getEncoded();
+    }
+
+    public static String genarateRandomKeyWithBase64() {
+        return new String(Base64.encode(genarateRandomKey()));
+    }
+
+    public static void main(String[] args) {
+        String a = "123456";
+        String key = "1201jwe92123fdsd";
+        System.out.println(encryptToBase64(a, key));
+    }
+
+}

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

@@ -0,0 +1,601 @@
+package com.caimei.util;
+
+import java.io.UnsupportedEncodingException;
+
+public class Base64 {
+    /**
+     * 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 = Digest.digest(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 "";
+        }
+    }
+
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param encoded string containing Base64 data
+     * @return Array containind decoded data.
+     */
+    public static byte[] decode2(String encoded) {
+
+        if (encoded == null) {
+            return null;
+        }
+
+        char[] base64Data = encoded.toCharArray();
+        // remove white spaces
+        int len = removeWhiteSpace(base64Data);
+
+        if (len % FOURBYTE != 0) {
+            return null;//should be divisible by four
+        }
+
+        int numberQuadruple = (len / FOURBYTE);
+
+        if (numberQuadruple == 0) {
+            return new byte[0];
+        }
+
+        byte decodedData[] = null;
+        byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
+        char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
+
+        int i = 0;
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        decodedData = new byte[(numberQuadruple) * 3];
+
+        for (; i < numberQuadruple - 1; i++) {
+
+            if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
+                    || !isData((d3 = base64Data[dataIndex++]))
+                    || !isData((d4 = base64Data[dataIndex++]))) {
+                return null;
+            }//if found "no data" just return null
+
+            b1 = base64Alphabet[d1];
+            b2 = base64Alphabet[d2];
+            b3 = base64Alphabet[d3];
+            b4 = base64Alphabet[d4];
+
+            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
+        }
+
+        if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {
+            return null;//if found "no data" just return null
+        }
+
+        b1 = base64Alphabet[d1];
+        b2 = base64Alphabet[d2];
+
+        d3 = base64Data[dataIndex++];
+        d4 = base64Data[dataIndex++];
+        if (!isData((d3)) || !isData((d4))) {//Check if they are PAD characters
+            if (isPad(d3) && isPad(d4)) {
+                if ((b2 & 0xf) != 0)//last 4 bits should be zero
+                {
+                    return null;
+                }
+                byte[] tmp = new byte[i * 3 + 1];
+                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
+                tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+                return tmp;
+            } else if (!isPad(d3) && isPad(d4)) {
+                b3 = base64Alphabet[d3];
+                if ((b3 & 0x3) != 0)//last 2 bits should be zero
+                {
+                    return null;
+                }
+                byte[] tmp = new byte[i * 3 + 2];
+                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
+                tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+                tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+                return tmp;
+            } else {
+                return null;
+            }
+        } else { //No PAD e.g 3cQl
+            b3 = base64Alphabet[d3];
+            b4 = base64Alphabet[d4];
+            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
+
+        }
+
+        return decodedData;
+    }
+
+    private static boolean isWhiteSpace(char octect) {
+        return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
+    }
+
+    private static boolean isData(char octect) {
+        return (octect < BASELENGTH && base64Alphabet[octect] != -1);
+    }
+
+    private static boolean isPad(char octect) {
+        return (octect == PAD);
+    }
+
+    /**
+     * remove WhiteSpace from MIME containing encoded Base64 data.
+     *
+     * @param data the byte array of base64 data (with WS)
+     * @return the new length
+     */
+    private static int removeWhiteSpace(char[] data) {
+        if (data == null) {
+            return 0;
+        }
+
+        // count characters that's not whitespace
+        int newSize = 0;
+        int len = data.length;
+        for (int i = 0; i < len; i++) {
+            if (!isWhiteSpace(data[i])) {
+                data[newSize++] = data[i];
+            }
+        }
+        return newSize;
+    }
+}

+ 36 - 0
src/main/java/com/caimei/util/CheckUtils.java

@@ -0,0 +1,36 @@
+package com.caimei.util;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+
+
+public class CheckUtils {
+
+	public static final String COMMON_FIELD = "flowID,initiator,";
+
+
+	/**
+	 * 验证对象是否为NULL,空字符串,空数组,空的Collection或Map(只有空格的字符串也认为是空串)
+	 * @param obj 被验证的对象
+	 * @param message 异常信息
+	 */
+	@SuppressWarnings("rawtypes")
+	public static void notEmpty(Object obj, String message) {
+		if (obj == null){
+			throw new IllegalArgumentException(message + " must be specified");
+		}
+		if (obj instanceof String && obj.toString().trim().length()==0){
+			throw new IllegalArgumentException(message + " must be specified");
+		}
+		if (obj.getClass().isArray() && Array.getLength(obj)==0){
+			throw new IllegalArgumentException(message + " must be specified");
+		}
+		if (obj instanceof Collection && ((Collection)obj).isEmpty()){
+			throw new IllegalArgumentException(message + " must be specified");
+		}
+		if (obj instanceof Map && ((Map)obj).isEmpty()){
+			throw new IllegalArgumentException(message + " must be specified");
+		}
+	}
+}

+ 7 - 0
src/main/java/com/caimei/util/ConfigureEncryptAndDecrypt.java

@@ -0,0 +1,7 @@
+package com.caimei.util;
+
+public class ConfigureEncryptAndDecrypt {
+	public static final String CHAR_ENCODING = "UTF-8";
+	public static final String AES_ALGORITHM = "AES/ECB/PKCS5Padding";
+	public static final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding";
+}

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

@@ -0,0 +1,373 @@
+package com.caimei.util;
+
+import sun.misc.BASE64Encoder;
+
+import java.io.UnsupportedEncodingException;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.text.DecimalFormat;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public abstract class ConvertUtils {
+
+    private static final DecimalFormat simpleFormat = new DecimalFormat("####");
+
+    public static final boolean objectToBoolean(Object o){
+        return o != null ? Boolean.valueOf(o.toString()).booleanValue() : false;
+    }
+
+    public static final int objectToInt(Object o){
+        if(o instanceof Number)
+            return ((Number)o).intValue();
+        try{
+            if(o == null)
+                return -1;
+            else
+                return Integer.parseInt(o.toString());
+        }catch(NumberFormatException e){
+            return -1;
+        }
+    }
+
+    public static final short objectToShort(Object o){
+        if(o instanceof Number)
+            return ((Number)o).shortValue();
+        try{
+            if(o == null)
+                return -1;
+            else
+                return Short.parseShort(o.toString());
+        }catch(NumberFormatException e){
+            return -1;
+        }
+    }
+
+    public static final double objectToDouble(Object o){
+        if(o instanceof Number)
+            return ((Number)o).doubleValue();
+        try{
+            if(o == null)
+                return -1D;
+            else
+                return Double.parseDouble(o.toString());
+        }catch(NumberFormatException e){
+            return -1D;
+        }
+    }
+
+    public static final long objectToLong(Object o)
+    {
+        if(o instanceof Number)
+            return ((Number)o).longValue();
+        try{
+            if(o == null)
+                return -1L;
+            else
+                return Long.parseLong(o.toString());
+        }catch(NumberFormatException e){
+            return -1L;
+        }
+    }
+
+    public static final String objectToString(Object obj, DecimalFormat fmt)
+    {
+        fmt.setDecimalSeparatorAlwaysShown(false);
+        if(obj instanceof Double)
+            return fmt.format(((Double)obj).doubleValue());
+        if(obj instanceof Long)
+            return fmt.format(((Long)obj).longValue());
+        else
+            return obj.toString();
+    }
+
+    public static final Object getObjectValue(String value)
+    {
+        try{
+            return Long.valueOf(value);
+        }catch(NumberFormatException e) {}
+
+        try{
+            return Double.valueOf(value);
+        }catch(NumberFormatException e){
+            return value;
+        }
+    }
+
+    public static String longToSimpleString(long value){
+        return simpleFormat.format(value);
+    }
+
+    public static String asHex(byte hash[]){
+        return toHex(hash);
+    }
+
+    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();
+    }
+
+    public static byte[] fromHex(String input){
+        if(input == null)
+            return null;
+        byte output[] = new byte[input.length() / 2];
+        for(int i = 0; i < output.length; i++)
+            output[i] = (byte)Integer.parseInt(input.substring(i * 2, (i + 1) * 2), 16);
+
+        return output;
+    }
+
+    public static String stringToHexString(String input, String encoding)
+        throws UnsupportedEncodingException{
+        return input != null ? toHex(input.getBytes(encoding)) : null;
+    }
+
+    public static String stringToHexString(String input){
+        try{
+            return stringToHexString(input, "UTF-8");
+        }catch(UnsupportedEncodingException e){
+            throw new IllegalStateException("UTF-8 encoding is not supported by JVM");
+        }
+    }
+
+    public static String hexStringToString(String input, String encoding)
+        throws UnsupportedEncodingException{
+        return input != null ? new String(fromHex(input), encoding) : null;
+    }
+
+    public static String hexStringToString(String input){
+        try{
+            return hexStringToString(input, "UTF-8");
+        }catch(UnsupportedEncodingException e){
+            throw new IllegalStateException("UTF-8 encoding is not supported by JVM");
+        }
+    }
+
+    public static String timeZoneToCode(TimeZone tz){
+
+        return timeZoneToString(tz);
+    }
+
+    public static TimeZone codeToTimeZone(String tzString){
+
+        return stringToTimeZone(tzString);
+    }
+
+    public static String timeZoneToString(TimeZone tz){
+
+        return tz != null ? tz.getID() : "";
+    }
+
+    public static TimeZone stringToTimeZone(String tzString){
+
+        return TimeZone.getTimeZone(tzString != null ? tzString : "");
+    }
+
+    public static String localeToCode(Locale aLocale){
+
+        return localeToString(aLocale);
+    }
+
+    public static Locale codeToLocale(String locString){
+
+        return stringToLocale(locString);
+    }
+
+    public static String localeToString(Locale loc){
+
+        return loc != null ? loc.toString() : "";
+    }
+
+    public static Locale stringToLocale(String locString){
+
+        locString = locString != null ? locString.trim() : "";
+        if(locString.equals(""))
+            return new Locale("", "", "");
+        int pos = locString.indexOf(95);
+        if(pos == -1)
+            return new Locale(locString, "", "");
+        String language = locString.substring(0, pos);
+        locString = locString.substring(pos + 1);
+        pos = locString.indexOf(95);
+        if(pos == -1){
+            return new Locale(language, locString, "");
+        }else{
+            String country = locString.substring(0, pos);
+            locString = locString.substring(pos + 1);
+            return new Locale(language, country, locString);
+        }
+    }
+
+    public static Date dateToSQLDate(java.util.Date d){
+
+        return d != null ? new Date(d.getTime()) : null;
+    }
+
+    public static Time dateToSQLTime(java.util.Date d){
+
+        return d != null ? new Time(d.getTime()) : null;
+    }
+
+    public static Timestamp dateToSQLTimestamp(java.util.Date d){
+
+        return d != null ? new Timestamp(d.getTime()) : null;
+    }
+
+    public static java.util.Date sqlTimestampToDate(Timestamp t){
+
+        return t != null ? new java.util.Date(Math.round((double)t.getTime() + (double)t.getNanos() / 1000000D)) : null;
+    }
+
+    public static Timestamp getCurrentDate(){
+
+        Calendar c = Calendar.getInstance();
+        c.set(c.get(1), c.get(2), c.get(5), 0, 0, 0);
+        Timestamp t = new Timestamp(c.getTime().getTime());
+        t.setNanos(0);
+        return t;
+    }
+
+    public static java.util.Date getDate(int y, int m, int d, boolean inclusive)
+    {
+        java.util.Date dt = null;
+        Calendar c = Calendar.getInstance();
+        c.clear();
+        if(c.getActualMinimum(1) <= y && y <= c.getActualMaximum(1))
+        {
+            c.set(1, y);
+            if(c.getActualMinimum(2) <= m && m <= c.getActualMaximum(2))
+            {
+                c.set(2, m);
+                if(c.getActualMinimum(5) <= d && d <= c.getActualMaximum(5))
+                    c.set(5, d);
+            }
+            if(inclusive)
+            {
+                c.add(5, 1);
+                c.add(14, -1);
+            }
+            dt = c.getTime();
+        }
+        return dt;
+    }
+
+    public static java.util.Date getDateStart(java.util.Date d)
+    {
+
+         Calendar c = new GregorianCalendar();
+         c.clear();
+         Calendar co = new GregorianCalendar();
+         co.setTime(d);
+         c.set(Calendar.DAY_OF_MONTH,co.get(Calendar.DAY_OF_MONTH));
+         c.set(Calendar.MONTH,co.get(Calendar.MONTH));
+         c.set(Calendar.YEAR,co.get(Calendar.YEAR));
+         //c.add(Calendar.DAY_OF_MONTH,1);
+         //c.add(Calendar.MILLISECOND,-1);
+         return c.getTime();
+    }
+
+    public static java.util.Date getDateEnd(java.util.Date d)
+    {
+         Calendar c = Calendar.getInstance();
+         c.clear();
+         Calendar co = Calendar.getInstance();
+         co.setTime(d);
+         c.set(Calendar.DAY_OF_MONTH,co.get(Calendar.DAY_OF_MONTH));
+         c.set(Calendar.MONTH,co.get(Calendar.MONTH));
+         c.set(Calendar.YEAR,co.get(Calendar.YEAR));
+         c.add(Calendar.DAY_OF_MONTH,1);
+         c.add(Calendar.MILLISECOND,-1);
+         return c.getTime();
+    }
+
+    public static double roundNumber(double rowNumber, int roundingPoint)
+    {
+        double base = Math.pow(10D, roundingPoint);
+        return (double)Math.round(rowNumber * base) / base;
+    }
+    public static Object getObject(String type,String value) throws Exception{
+
+    	type=type.toLowerCase();
+    	if("boolean".equals(type))
+            return Boolean.valueOf(value);
+        if("byte".equals(type))
+            return Byte.valueOf(value);
+        if("short".equals(type))
+            return Short.valueOf(value);
+        if("char".equals(type))
+            if(value.length() != 1)
+                throw new NumberFormatException("Argument is not a character!");
+            else
+                return Character.valueOf(value.toCharArray()[0]);
+        if("int".equals(type))
+            return Integer.valueOf(value);
+        if("long".equals(type))
+            return Long.valueOf(value);
+        if("float".equals(type))
+            return Float.valueOf(value);
+        if("double".equals(type))
+            return Double.valueOf(value);
+        if("string".equals(type))
+            return value;
+		else{
+	        Object objs[]=new String[]{value};
+			return Class.forName(type).getConstructor(new Class[] {
+				    String.class
+				}).newInstance(objs);
+		}
+    }
+    private ConvertUtils(){}
+
+    /**
+	 * 图片的十六进制字符串转为base64形式的字符串(去掉回车、换行字符)
+	 * @param src
+	 * @return
+	 */
+	public static String hexStr2Base64Str(String src){
+		byte[] bytes = hex2byte(src);
+		String strs = new BASE64Encoder().encodeBuffer(bytes);
+		StringBuilder sb = new StringBuilder(strs.length());
+		for(int i=0;i< strs.length();i++){
+			if(strs.charAt(i) != '\r' && strs.charAt(i) != '\n'){
+				sb.append(strs.charAt(i));
+			}
+		}
+		return sb.toString();
+	}
+	
+	/**
+	 * 将十六进制字符串转为字节数组
+	 * @param s
+	 * @return
+	 */
+	public static byte[] hex2byte(String s){
+		byte[] src = s.toLowerCase().getBytes();
+        byte[] ret = new byte[src.length / 2];
+        for (int i = 0; i < src.length; i += 2) {
+            byte hi = src[i];
+            byte low = src[i + 1];
+            hi = (byte) ((hi >= 'a' && hi <= 'f') ? 0x0a + (hi - 'a')
+                    : hi - '0');
+            low = (byte) ((low >= 'a' && low <= 'f') ? 0x0a + (low - 'a')
+                    : low - '0');
+            ret[i / 2] = (byte) (hi << 4 | low);
+        }
+        return ret;
+	}
+//    public static void main(String[] args)
+//    {
+//    	System.out.println(getDateStart(new java.util.Date()));
+//    }
+}

+ 164 - 0
src/main/java/com/caimei/util/Digest.java

@@ -0,0 +1,164 @@
+package com.caimei.util;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+public class Digest {
+    public static final String ENCODE = "UTF-8";
+
+    public static String signMD5(String aValue, String encoding) {
+        try {
+            byte[] input = aValue.getBytes(encoding);
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            return ConvertUtils.toHex(md.digest(input));
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static String hmacSign(String aValue) {
+        try {
+            byte[] input = aValue.getBytes();
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            return ConvertUtils.toHex(md.digest(input));
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static String hmacSign(String aValue, String aKey) {
+        return hmacSign(aValue, aKey, ENCODE);
+    }
+
+    public static String hmacSign(String aValue, String aKey, String encoding) {
+        byte k_ipad[] = new byte[64];
+        byte k_opad[] = new byte[64];
+        byte keyb[];
+        byte value[];
+        try {
+            keyb = aKey.getBytes(encoding);
+            value = aValue.getBytes(encoding);
+        } catch (UnsupportedEncodingException e) {
+            keyb = aKey.getBytes();
+            value = aValue.getBytes();
+        }
+        Arrays.fill(k_ipad, keyb.length, 64, (byte) 54);
+        Arrays.fill(k_opad, keyb.length, 64, (byte) 92);
+        for (int i = 0; i < keyb.length; i++) {
+            k_ipad[i] = (byte) (keyb[i] ^ 0x36);
+            k_opad[i] = (byte) (keyb[i] ^ 0x5c);
+        }
+
+        MessageDigest md = null;
+        try {
+            md = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        }
+        md.update(k_ipad);
+        md.update(value);
+        byte dg[] = md.digest();
+        md.reset();
+        md.update(k_opad);
+        md.update(dg, 0, 16);
+        dg = md.digest();
+        return ConvertUtils.toHex(dg);
+    }
+
+    public static String hmacSHASign(String aValue, String aKey, String encoding) {
+        byte k_ipad[] = new byte[64];
+        byte k_opad[] = new byte[64];
+        byte keyb[];
+        byte value[];
+        try {
+            keyb = aKey.getBytes(encoding);
+            value = aValue.getBytes(encoding);
+        } catch (UnsupportedEncodingException e) {
+            keyb = aKey.getBytes();
+            value = aValue.getBytes();
+        }
+        Arrays.fill(k_ipad, keyb.length, 64, (byte) 54);
+        Arrays.fill(k_opad, keyb.length, 64, (byte) 92);
+        for (int i = 0; i < keyb.length; i++) {
+            k_ipad[i] = (byte) (keyb[i] ^ 0x36);
+            k_opad[i] = (byte) (keyb[i] ^ 0x5c);
+        }
+
+        MessageDigest md = null;
+        try {
+            md = MessageDigest.getInstance("SHA");
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        }
+        md.update(k_ipad);
+        md.update(value);
+        byte dg[] = md.digest();
+        md.reset();
+        md.update(k_opad);
+        md.update(dg, 0, 20);
+        dg = md.digest();
+        return ConvertUtils.toHex(dg);
+    }
+
+    public static String digest(String aValue) {
+        return digest(aValue, ENCODE);
+
+    }
+
+    public static String digest(String aValue, String encoding) {
+        aValue = aValue.trim();
+        byte value[];
+        try {
+            value = aValue.getBytes(encoding);
+        } catch (UnsupportedEncodingException e) {
+            value = aValue.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 digest(String aValue, String alg, String encoding) {
+        aValue = aValue.trim();
+        byte value[];
+        try {
+            value = aValue.getBytes(encoding);
+        } catch (UnsupportedEncodingException e) {
+            value = aValue.getBytes();
+        }
+        MessageDigest md = null;
+        try {
+            md = MessageDigest.getInstance(alg);
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        }
+        return ConvertUtils.toHex(md.digest(value));
+    }
+
+    public static String udpSign(String aValue) {
+        try {
+            byte[] input = aValue.getBytes("UTF-8");
+            MessageDigest md = MessageDigest.getInstance("SHA1");
+            return new String(Base64.encode(md.digest(input)), ENCODE);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+}

+ 85 - 0
src/main/java/com/caimei/util/HashUtil.java

@@ -0,0 +1,85 @@
+
+package com.caimei.util;
+
+import java.security.MessageDigest;
+
+public class HashUtil {
+
+    private static final java.security.SecureRandom random = new java.security.SecureRandom();
+    private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
+    private static final char[] CHAR_ARRAY = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
+
+    public static String md5(String srcStr) {
+        return hash("MD5", srcStr);
+    }
+
+    public static String sha1(String srcStr) {
+        return hash("SHA-1", srcStr);
+    }
+
+    public static String sha256(String srcStr) {
+        return hash("SHA-256", srcStr);
+    }
+
+    public static String sha384(String srcStr) {
+        return hash("SHA-384", srcStr);
+    }
+
+    public static String sha512(String srcStr) {
+        return hash("SHA-512", srcStr);
+    }
+
+    public static String hash(String algorithm, String srcStr) {
+        try {
+            MessageDigest md = MessageDigest.getInstance(algorithm);
+            byte[] bytes = md.digest(srcStr.getBytes("utf-8"));
+            return toHex(bytes);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static String toHex(byte[] bytes) {
+        StringBuilder ret = new StringBuilder(bytes.length * 2);
+        for (int i = 0; i < bytes.length; i++) {
+            ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
+            ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
+        }
+        return ret.toString();
+    }
+
+    /**
+     * md5 128bit 16bytes
+     * sha1 160bit 20bytes
+     * sha256 256bit 32bytes
+     * sha384 384bit 48bytes
+     * sha512 512bit 64bytes
+     */
+    public static String generateSalt(int saltLength) {
+        StringBuilder salt = new StringBuilder(saltLength);
+        for (int i = 0; i < saltLength; i++) {
+            salt.append(CHAR_ARRAY[random.nextInt(CHAR_ARRAY.length)]);
+        }
+        return salt.toString();
+    }
+
+    public static String generateSaltForSha256() {
+        return generateSalt(32);
+    }
+
+    public static String generateSaltForSha512() {
+        return generateSalt(64);
+    }
+
+    public static boolean slowEquals(byte[] a, byte[] b) {
+        if (a == null || b == null) {
+            return false;
+        }
+
+        int diff = a.length ^ b.length;
+        for (int i = 0; i < a.length && i < b.length; i++) {
+            diff |= a[i] ^ b[i];
+        }
+        return diff == 0;
+    }
+}

+ 167 - 0
src/main/java/com/caimei/util/HttpClient4Utils.java

@@ -0,0 +1,167 @@
+package com.caimei.util;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * httpClient4 工具类
+ *
+ * @author: yingjie.wang
+ * @since : 2015-09-23 13:12
+ */
+
+public class HttpClient4Utils {
+
+    //设置默认超时时间为60s
+    public static final int DEFAULT_TIME_OUT = 60 * 1000;
+
+    //http请求
+    public static String sendHttpRequest(String url, Map<String, String> paramMap, String charset, boolean isPost) {
+        return sendHttpRequest(url, paramMap, charset, isPost, DEFAULT_TIME_OUT);
+    }
+
+    //http请求
+    public static String sendHttpRequest(String url, Map<String, String> paramMap, String charset, boolean isPost, int timeout) {
+        if (isPost) {
+            return httpPost(url, paramMap, charset, timeout);
+        }
+
+        return httpGet(url, paramMap, charset, timeout);
+    }
+
+    //post请求
+    public static String httpPost(String url, Map<String, String> params, String charset, int timeout) {
+
+        if (url == null || url.equals("")) {
+            return null;
+        }
+
+        String result = null;
+
+        //超时设置
+        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(timeout).setSocketTimeout(timeout).build();
+
+        //参数组装
+        List<NameValuePair> pairs = new ArrayList<NameValuePair>();
+        for (Entry<String, String> entry : params.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            pairs.add(new BasicNameValuePair(key, formatStr(value)));
+        }
+
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        HttpPost httpPost = null;
+        String responseBody = null;
+        CloseableHttpResponse response = null;
+
+        try {
+            httpPost = new HttpPost(url);
+            httpPost.setConfig(requestConfig);
+            httpPost.setEntity(new UrlEncodedFormEntity(pairs));
+            response = httpClient.execute(httpPost);
+
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode != 200) {
+                httpPost.abort();
+                throw new RuntimeException("HttpClient,error status code :" + statusCode);
+            }
+
+            HttpEntity entity = response.getEntity();
+            responseBody = EntityUtils.toString(entity, charset);
+            result = responseBody;
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                // 关闭连接,释放资源
+                if (response != null) {
+                    response.close();
+                }
+                if (httpClient != null) {
+                    httpClient.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return result;
+    }
+
+    //get请求
+    public static String httpGet(String url, Map<String, String> params, String charset, int timeout) {
+
+        if (url == null || url.equals("")) {
+            return null;
+        }
+
+        String result = null;
+
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        HttpGet httpGet = null;
+        String responseBody = null;
+        CloseableHttpResponse response = null;
+
+        try {
+
+            if (params != null && !params.isEmpty()) {
+                List<NameValuePair> pairs = new ArrayList<NameValuePair>();
+                for (Entry<String, String> entry : params.entrySet()) {
+                    String key = entry.getKey();
+                    String value = entry.getValue();
+                    pairs.add(new BasicNameValuePair(key, formatStr(value)));
+                }
+                url = url + "?" + EntityUtils.toString(new UrlEncodedFormEntity(pairs, charset));
+            }
+
+            httpGet = new HttpGet(url);
+            response = httpClient.execute(httpGet);
+
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode != 200) {
+                httpGet.abort();
+                throw new RuntimeException("HttpClient,error status code :" + statusCode);
+            }
+
+            HttpEntity entity = response.getEntity();
+            responseBody = EntityUtils.toString(entity, charset);
+            result = responseBody;
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                // 关闭连接,释放资源
+                if (response != null) {
+                    response.close();
+                }
+                if (httpClient != null) {
+                    httpClient.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return result;
+    }
+
+    public static String formatStr(String text) {
+        return (text == null ? "" : text.trim());
+    }
+
+}

+ 23 - 0
src/main/java/com/caimei/util/KeyGenerator.java

@@ -0,0 +1,23 @@
+package com.caimei.util;
+
+
+import com.caimei.model.enumerate.KeyFormat;
+
+import java.security.NoSuchAlgorithmException;
+
+/*
+2015-01-23
+*/
+public class KeyGenerator {
+
+    static public KeyPair generateKeyPair(KeyFormat format)
+            throws NoSuchAlgorithmException {
+        KeyPair keyPair = new KeyPair(format);
+
+        return keyPair;
+    }
+
+    static public KeyPair generateKeyPair() throws NoSuchAlgorithmException {
+        return generateKeyPair(KeyFormat.ASN);
+    }
+}

+ 133 - 0
src/main/java/com/caimei/util/KeyPair.java

@@ -0,0 +1,133 @@
+package com.caimei.util;
+
+import com.caimei.model.enumerate.KeyFormat;
+import sun.misc.BASE64Encoder;
+
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+
+/*
+2015-01-23
+*/
+public class KeyPair {
+    private java.security.KeyPair _rsa;
+    private KeyFormat _format;
+    private String _private;
+    private String _public;
+
+    public KeyFormat getFormat() {
+        return this._format;
+    }
+
+    KeyPair(KeyFormat format) throws NoSuchAlgorithmException {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+        kpg.initialize(1024);
+        this._rsa = kpg.genKeyPair();
+        ;
+        this._format = format;
+    }
+
+    private KeyPair(java.security.KeyPair rsa, KeyFormat format) {
+        this._rsa = rsa;
+        this._format = format;
+    }
+
+    public String getPrivateKey() {
+        if (this._private == null) {
+            switch (this._format) {
+                case ASN:
+                    this._private = this._toASNPrivateKey();
+                    break;
+                case XML:
+                    this._private = this._toXMLPrivateKey();
+                    break;
+                case PEM:
+                    this._private = this._toPEMPrivateKey();
+                    break;
+                default:
+                    this._private = this._toXMLPrivateKey();
+                    break;
+            }
+        }
+        return this._private;
+    }
+
+    public String getPublicKey() {
+        if (this._public == null) {
+            switch (this._format) {
+                case ASN:
+                    this._public = this._toASNPublicKey();
+                    break;
+                case XML:
+                    this._public = this._toXMLPublicKey();
+                    break;
+                case PEM:
+                    this._public = this._toPEMPublicKey();
+                    break;
+                default:
+                    this._public = this._toXMLPublicKey();
+                    break;
+            }
+        }
+        return this._public;
+    }
+
+    public KeyPair toASNKeyPair() {
+        return new KeyPair(this._rsa, KeyFormat.ASN);
+    }
+
+    public KeyPair toXMLKeyPair() {
+        return new KeyPair(this._rsa, KeyFormat.XML);
+    }
+
+    public KeyPair toPEMKeyPair() {
+        return new KeyPair(this._rsa, KeyFormat.PEM);
+    }
+
+
+    private String _toASNPublicKey() {
+        return (new BASE64Encoder()).encodeBuffer(this._rsa.getPublic().getEncoded());
+    }
+
+    private String _toASNPrivateKey() {
+        return (new BASE64Encoder()).encodeBuffer(this._rsa.getPrivate().getEncoded());
+    }
+
+    private String _toXMLPublicKey() {
+        return XmlKeyBuilder.publicKeyToXML(this._rsa.getPublic());
+    }
+
+    private String _toXMLPrivateKey() {
+        return XmlKeyBuilder.privateKeyToXML(this._rsa.getPrivate());
+    }
+
+    private String _toPEMPublicKey() {
+        String publicKey = this._toASNPublicKey();
+        StringBuilder sb = new StringBuilder();
+        sb.append("-----BEGIN PUBLIC KEY-----\r\n");
+        int i = 0;
+        while (i + 64 < publicKey.length()) {
+            sb.append(publicKey.substring(i, i + 64) + "\r\n");
+            i += 64;
+        }
+        sb.append(publicKey.substring(i, publicKey.length()) + "\r\n");
+        sb.append("-----END PUBLIC KEY-----\r\n");
+
+        return sb.toString();
+    }
+
+    private String _toPEMPrivateKey() {
+        String privateKey = this._toASNPrivateKey();
+        StringBuilder sb = new StringBuilder();
+        sb.append("-----BEGIN PRIVATE KEY-----\r\n");
+        int i = 0;
+        while (i + 64 < privateKey.length()) {
+            sb.append(privateKey.substring(i, i + 64) + "\r\n");
+            i += 64;
+        }
+        sb.append(privateKey.substring(i, privateKey.length()) + "\r\n");
+        sb.append("-----END PRIVATE KEY-----\r\n");
+
+        return sb.toString();
+    }
+}

+ 206 - 0
src/main/java/com/caimei/util/KeyWorker.java

@@ -0,0 +1,206 @@
+package com.caimei.util;
+
+
+import com.caimei.model.enumerate.KeyFormat;
+import org.xml.sax.SAXException;
+import sun.misc.BASE64Decoder;
+import sun.misc.BASE64Encoder;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.*;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+/*
+2015-01-23
+*/
+public class KeyWorker {
+
+    private String _key;
+    private KeyFormat _format;
+    private Cipher _decryptProvider;
+    private Cipher _encryptProvider;
+
+    private static final int MAX_ENCRYPT_BLOCK = 117;
+
+    private static final int MAX_DECRYPT_BLOCK = 128;
+
+    public KeyWorker(String key) {
+        this(key, KeyFormat.ASN);
+    }
+
+    public KeyWorker(String key, KeyFormat format) {
+        this._key = key;
+        this._format = format;
+    }
+
+    public String encrypt(String data) throws IllegalBlockSizeException,
+            BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
+            NoSuchPaddingException, InvalidKeySpecException, IOException, SAXException, ParserConfigurationException {
+        this._makesureEncryptProvider();
+        byte[] bytes = data.getBytes("UTF-8");
+        int inputLen = bytes.length;
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        int offSet = 0;
+        byte[] cache;
+        int i = 0;
+        while (inputLen - offSet > 0) {
+            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
+                cache = _encryptProvider.doFinal(bytes, offSet, MAX_ENCRYPT_BLOCK);
+            } else {
+                cache = _encryptProvider.doFinal(bytes, offSet, inputLen - offSet);
+            }
+            out.write(cache, 0, cache.length);
+            i++;
+            offSet = i * MAX_ENCRYPT_BLOCK;
+        }
+        bytes = out.toByteArray();
+        out.close();
+        return new BASE64Encoder().encode(bytes);
+    }
+
+    public String decrypt(String data) throws IOException,
+            IllegalBlockSizeException, BadPaddingException,
+            InvalidKeyException, NoSuchAlgorithmException,
+            NoSuchPaddingException, InvalidKeySpecException, SAXException, ParserConfigurationException {
+        this._makesureDecryptProvider();
+        byte[] bytes = new BASE64Decoder().decodeBuffer(data);
+        int inputLen = bytes.length;
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        int offSet = 0;
+        byte[] cache;
+        int i = 0;
+        while (inputLen - offSet > 0) {
+            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
+                cache = _decryptProvider.doFinal(bytes, offSet, MAX_DECRYPT_BLOCK);
+            } else {
+                cache = _decryptProvider.doFinal(bytes, offSet, inputLen - offSet);
+            }
+            out.write(cache, 0, cache.length);
+            i++;
+            offSet = i * MAX_DECRYPT_BLOCK;
+        }
+        bytes = out.toByteArray();
+        out.close();
+        return new String(bytes, "UTF-8");
+    }
+
+    private void _makesureDecryptProvider() throws NoSuchAlgorithmException,
+            NoSuchPaddingException, IOException, InvalidKeySpecException,
+            InvalidKeyException, SAXException, ParserConfigurationException {
+        if (this._decryptProvider != null)
+            return;
+
+        Cipher deCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+        switch (this._format) {
+            case XML: {
+                Boolean isPrivate = this._key.indexOf("<P>") > -1;
+                if (isPrivate) {
+                    PrivateKey privateKey = XmlKeyBuilder
+                            .xmlToPrivateKey(this._key);
+                    deCipher.init(Cipher.DECRYPT_MODE, privateKey);
+                } else {
+                    PublicKey publicKey = XmlKeyBuilder.xmlToPublicKey(this._key);
+                    deCipher.init(Cipher.DECRYPT_MODE, publicKey);
+                }
+            }
+            break;
+            case PEM: {
+                this._key = this._key.replace("-----BEGIN PUBLIC KEY-----", "")
+                        .replace("-----END PUBLIC KEY-----", "")
+                        .replace("-----BEGIN PRIVATE KEY-----", "")
+                        .replace("-----END PRIVATE KEY-----", "")
+                        .replaceAll("\r\n", "");
+            }
+            case ASN:
+            default: {
+                Boolean isPrivate = this._key.length() > 500;
+                if (isPrivate) {
+                    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(
+                            new BASE64Decoder().decodeBuffer(this._key));
+
+                    KeyFactory factory = KeyFactory.getInstance("RSA");
+                    RSAPrivateKey privateKey = (RSAPrivateKey) factory
+                            .generatePrivate(spec);
+                    deCipher.init(Cipher.DECRYPT_MODE, privateKey);
+                } else {
+                    X509EncodedKeySpec spec = new X509EncodedKeySpec(
+                            new BASE64Decoder().decodeBuffer(this._key));
+
+                    KeyFactory factory = KeyFactory.getInstance("RSA");
+                    RSAPublicKey publicKey = (RSAPublicKey) factory
+                            .generatePublic(spec);
+                    deCipher.init(Cipher.DECRYPT_MODE, publicKey);
+                }
+            }
+            break;
+        }
+
+        this._decryptProvider = deCipher;
+    }
+
+    private void _makesureEncryptProvider() throws NoSuchAlgorithmException,
+            NoSuchPaddingException, IOException, InvalidKeySpecException,
+            InvalidKeyException, SAXException, ParserConfigurationException {
+        if (this._encryptProvider != null)
+            return;
+
+        Cipher enCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+        switch (this._format) {
+            case XML: {
+                Boolean isPrivate = this._key.indexOf("<P>") > -1;
+                if (isPrivate) {
+                    PrivateKey privateKey = XmlKeyBuilder
+                            .xmlToPrivateKey(this._key);
+                    enCipher.init(Cipher.ENCRYPT_MODE, privateKey);
+                } else {
+                    PublicKey publicKey = XmlKeyBuilder.xmlToPublicKey(this._key);
+                    enCipher.init(Cipher.ENCRYPT_MODE, publicKey);
+                }
+            }
+            break;
+            case PEM: {
+                this._key = this._key.replace("-----BEGIN PUBLIC KEY-----", "")
+                        .replace("-----END PUBLIC KEY-----", "")
+                        .replace("-----BEGIN PRIVATE KEY-----", "")
+                        .replace("-----END PRIVATE KEY-----", "")
+                        .replaceAll("\r\n", "");
+            }
+            case ASN:
+            default: {
+                Boolean isPrivate = this._key.length() > 500;
+                if (isPrivate) {
+                    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(
+                            new BASE64Decoder().decodeBuffer(this._key));
+
+                    KeyFactory factory = KeyFactory.getInstance("RSA");
+                    RSAPrivateKey privateKey = (RSAPrivateKey) factory
+                            .generatePrivate(spec);
+                    enCipher.init(Cipher.ENCRYPT_MODE, privateKey);
+
+                } else {
+                    X509EncodedKeySpec spec = new X509EncodedKeySpec(
+                            new BASE64Decoder().decodeBuffer(this._key));
+
+                    KeyFactory factory = KeyFactory.getInstance("RSA");
+                    RSAPublicKey publicKey = (RSAPublicKey) factory
+                            .generatePublic(spec);
+                    enCipher.init(Cipher.ENCRYPT_MODE, publicKey);
+                }
+            }
+            break;
+        }
+
+        this._encryptProvider = enCipher;
+    }
+
+}

+ 141 - 0
src/main/java/com/caimei/util/PayUtils.java

@@ -0,0 +1,141 @@
+package com.caimei.util;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.lang3.StringUtils;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+/**
+ * 工具类
+ */
+
+public class PayUtils {
+    //编码格式UTF-8
+    public static final String CHARSET = "UTF-8";
+    private static JSONObject decrypt;
+
+
+    //生成RSA签名:sign
+    public static String buildSign(JSONObject json, String merKey) {
+        StringBuffer buffer = new StringBuffer();
+        TreeMap<String, Object> treeMap = new TreeMap<String, Object>();
+        for (Entry<String, Object> entry : json.entrySet()) {
+            if (entry.getValue() != null) {
+                treeMap.put(entry.getKey(), entry.getValue());
+            }
+        }
+        for (Entry<String, Object> entry : treeMap.entrySet()) {
+            if (entry.getValue() != null) {
+                buffer.append(entry.getValue());
+            }
+        }
+        buffer.append(merKey.replaceAll("(\r\n|\r|\n|\n\r)", ""));
+        System.out.println(buffer.toString());
+        return HashUtil.md5(buffer.toString()).toUpperCase();
+    }
+
+    public static String buildDataPrivate(JSONObject json, String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
+        return RSAUtil.privateEncrypt(json.toJSONString(), privateKey);
+    }
+
+    public static String buildDataPublic(JSONObject json, String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
+        return RSAUtil.publicEncrypt(json.toJSONString(), publicKey);
+    }
+
+    public static JSONObject decryptDataPrivate(String data, String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
+        JSONObject result = null;
+        String jsonStr = RSAUtil.privateDecrypt(data.trim(), privateKey);
+        result = JSON.parseObject(jsonStr);
+        return result;
+    }
+
+    public static JSONObject decryptDataPublic(String data, String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
+        JSONObject result = null;
+        String jsonStr = RSAUtil.publicDecrypt(data.trim(), publicKey);
+        result = JSON.parseObject(jsonStr);
+        return result;
+    }
+
+    //生成密文:data
+    public static String buildData(JSONObject json, String merKey) {
+        return AES.encryptWithKeyBase64(json.toJSONString(), merKey);
+    }
+
+    //解密data,获得明文参数
+    public static JSONObject decrypt(String data, String merKey) {
+        JSONObject result = null;
+        //2.使用AESKey解开data,取得明文参数;解密后格式为json
+        String jsonStr = AES.decryptWithKeyBase64(data.trim(), merKey.trim());
+        result = JSON.parseObject(jsonStr);
+        return result;
+    }
+
+    // sign验签
+    public static boolean checkSign(JSONObject json, String merKey) {
+        //获取明文参数中的sign。
+        String signPay = StringUtils.trimToEmpty(json.getString("sign"));
+        //将明文参数中sign之外的其他参数,拼接成字符串
+        StringBuffer buffer = new StringBuffer();
+        TreeMap<String, Object> treeMap = new TreeMap<String, Object>();
+        for (Entry<String, Object> entry : json.entrySet()) {
+            if (entry.getValue() != null) {
+                treeMap.put(entry.getKey(), entry.getValue());
+            }
+        }
+        for (Entry<String, Object> entry : treeMap.entrySet()) {
+            String key = formatStr(entry.getKey());
+            String value = formatStr(entry.getValue());
+            if ("sign".equals(key)) {
+                continue;
+            }
+            buffer.append(value);
+        }
+        buffer.append(merKey.replaceAll("(\r\n|\r|\n|\n\r)", ""));
+        //result为true时表明验签通过
+        String checkSign = HashUtil.md5(buffer.toString()).toUpperCase();
+        return signPay.equals(checkSign);
+    }
+
+    //一键支付post请求
+    public static JSONObject httpPost(String url, String merAccount, String data) {
+        //请求参数为如下:merAccount、data
+        Map<String, String> paramMap = new HashMap<String, String>();
+        paramMap.put("data", data);
+        paramMap.put("merAccount", merAccount);
+        String responseBody = HttpClient4Utils.sendHttpRequest(url, paramMap, CHARSET, true);
+        return JSON.parseObject(responseBody);
+    }
+
+    //get请求
+    public static JSONObject httpGet(String url, String merAccount, String data) {
+        //请求参数为如下:merAccount、data
+        Map<String, String> paramMap = new HashMap<String, String>();
+        paramMap.put("data", data);
+        paramMap.put("merAccount", merAccount);
+        String responseBody = HttpClient4Utils.sendHttpRequest(url, paramMap, CHARSET, false);
+        return JSON.parseObject(responseBody);
+    }
+
+    //get请求
+    public static JSONObject httpGet(String url, String merAccount, String appid, String data) {
+        //请求参数为如下:merAccount、data
+        Map<String, String> paramMap = new HashMap<String, String>();
+        paramMap.put("data", data);
+        paramMap.put("appid", appid);
+        paramMap.put("merAccount", merAccount);
+        String responseBody = HttpClient4Utils.sendHttpRequest(url, paramMap, CHARSET, false);
+        return JSON.parseObject(responseBody);
+    }
+
+    //字符串格式化
+    public static String formatStr(Object text) {
+        return (text == null) ? "" : (text + "").trim();
+    }
+
+}

+ 106 - 0
src/main/java/com/caimei/util/RSAUtil.java

@@ -0,0 +1,106 @@
+package com.caimei.util;
+
+
+import com.caimei.model.enumerate.KeyFormat;
+
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class RSAUtil {
+
+    public static Map<String, String> createKeys() throws NoSuchAlgorithmException {
+        //为RSA算法创建一个KeyPairGenerator对象
+        KeyPair keyPair = KeyGenerator.generateKeyPair();
+        KeyPair asnKeyPair = keyPair.toASNKeyPair();
+        //得到公钥
+        String publicKey = asnKeyPair.getPublicKey();
+        String privateKey = asnKeyPair.getPrivateKey();
+        Map<String, String> keyPairMap = new HashMap<String, String>();
+        keyPairMap.put("publicKey", publicKey);
+        keyPairMap.put("privateKey", privateKey);
+
+        return keyPairMap;
+    }
+
+    /**
+     * 公钥加密
+     *
+     * @param data
+     * @param publicKey
+     * @return
+     */
+    public static String publicEncrypt(String data, String publicKey) {
+        try {
+            KeyWorker publicWorker = new KeyWorker(publicKey, KeyFormat.ASN);
+            return publicWorker.encrypt(data);
+        } catch (Exception e) {
+            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
+        }
+    }
+
+    /**
+     * 私钥解密
+     *
+     * @param data
+     * @param privateKey
+     * @return
+     */
+
+    public static String privateDecrypt(String data, String privateKey) {
+        try {
+            KeyWorker privateWorker = new KeyWorker(privateKey, KeyFormat.ASN);
+            return privateWorker.decrypt(data);
+        } catch (Exception e) {
+            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
+        }
+    }
+
+    /**
+     * 私钥加密
+     *
+     * @param data
+     * @param privateKey
+     * @return
+     */
+
+    public static String privateEncrypt(String data, String privateKey) {
+        try {
+            KeyWorker privateWorker = new KeyWorker(privateKey, KeyFormat.ASN);
+            return privateWorker.encrypt(data);
+        } catch (Exception e) {
+            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
+        }
+    }
+
+    /**
+     * 公钥解密
+     *
+     * @param data
+     * @param publicKey
+     * @return
+     */
+
+    public static String publicDecrypt(String data, String publicKey) {
+        try {
+            KeyWorker publicWorker = new KeyWorker(publicKey, KeyFormat.ASN);
+            return publicWorker.decrypt(data);
+        } catch (Exception e) {
+            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
+        }
+    }
+
+    public static void main(String[] args) throws NoSuchAlgorithmException {
+        Map<String, String> keys = createKeys();
+        String publicKey = keys.get("publicKey");
+        String privateKey = keys.get("privateKey");
+        System.out.println("private:" + privateKey);
+        System.out.println("public:" + publicKey);
+        String conent = "sdsfalfasdl2对方是否";
+        String en = publicEncrypt(conent, publicKey);
+        System.out.println("公钥加密:" + en);
+        String de = privateDecrypt(en, privateKey);
+        System.out.println("私钥解密:" + de);
+    }
+
+}

+ 174 - 0
src/main/java/com/caimei/util/XmlKeyBuilder.java

@@ -0,0 +1,174 @@
+package com.caimei.util;
+
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import sun.misc.BASE64Decoder;
+import sun.misc.BASE64Encoder;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+
+/*
+ Jeffrey Walton
+ http://www.codeproject.com/Articles/25487/Cryptographic-Interoperability-Keys
+ */
+public class XmlKeyBuilder {
+    public static String publicKeyToXML(PublicKey key) {
+        if (!RSAPublicKey.class.isInstance(key)) {
+            return null;
+        }
+        RSAPublicKey pubKey = (RSAPublicKey) key;
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("<RSAKeyValue>");
+        sb.append("<Modulus>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(pubKey.getModulus()
+                        .toByteArray()))).append("</Modulus>");
+        sb.append("<Exponent>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(pubKey.getPublicExponent()
+                        .toByteArray()))).append("</Exponent>");
+        sb.append("</RSAKeyValue>");
+        return sb.toString();
+    }
+
+    public static String privateKeyToXML(PrivateKey key) {
+        if (!RSAPrivateCrtKey.class.isInstance(key)) {
+            return null;
+        }
+        RSAPrivateCrtKey priKey = (RSAPrivateCrtKey) key;
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("<RSAKeyValue>");
+        sb.append("<Modulus>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getModulus()
+                        .toByteArray()))).append("</Modulus>");
+        sb.append("<Exponent>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getPublicExponent()
+                        .toByteArray()))).append("</Exponent>");
+        sb.append("<P>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getPrimeP()
+                        .toByteArray()))).append("</P>");
+        sb.append("<Q>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getPrimeQ()
+                        .toByteArray()))).append("</Q>");
+        sb.append("<DP>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getPrimeExponentP()
+                        .toByteArray()))).append("</DP>");
+        sb.append("<DQ>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getPrimeExponentQ()
+                        .toByteArray()))).append("</DQ>");
+        sb.append("<InverseQ>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getCrtCoefficient()
+                        .toByteArray()))).append("</InverseQ>");
+        sb.append("<D>")
+                .append(new BASE64Encoder().encode(TrimLeadingZero(priKey.getPrivateExponent()
+                        .toByteArray()))).append("</D>");
+        sb.append("</RSAKeyValue>");
+        return sb.toString();
+    }
+
+    public static PublicKey xmlToPublicKey(String key)
+            throws ParserConfigurationException, SAXException, IOException {
+        key = key.replaceAll("\r", "").replaceAll("\n", "");
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder builder = factory.newDocumentBuilder();
+        Document doc = builder.parse(new InputSource(new ByteArrayInputStream(
+                key.getBytes("utf-8"))));
+        String n = doc.getDocumentElement().getElementsByTagName("Modulus")
+                .item(0).getNodeValue();
+        String e = doc.getDocumentElement().getElementsByTagName("Exponent")
+                .item(0).getNodeValue();
+        BigInteger modulus = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(n));
+        BigInteger publicExponent = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(e));
+
+        RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus,
+                publicExponent);
+
+        KeyFactory keyf;
+        try {
+            keyf = KeyFactory.getInstance("RSA");
+            return keyf.generatePublic(rsaPubKey);
+        } catch (Exception ex) {
+            return null;
+        }
+    }
+
+    public static PrivateKey xmlToPrivateKey(String key) throws IOException,
+            SAXException, ParserConfigurationException {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder builder = factory.newDocumentBuilder();
+        Document doc = builder.parse(new InputSource(new ByteArrayInputStream(
+                key.getBytes("utf-8"))));
+        String n = doc.getDocumentElement().getElementsByTagName("Modulus")
+                .item(0).getNodeValue();
+        String e = doc.getDocumentElement().getElementsByTagName("Exponent")
+                .item(0).getNodeValue();
+        String d = doc.getDocumentElement().getElementsByTagName("D").item(0)
+                .getNodeValue();
+        String p = doc.getDocumentElement().getElementsByTagName("P").item(0)
+                .getNodeValue();
+        String q = doc.getDocumentElement().getElementsByTagName("Q").item(0)
+                .getNodeValue();
+        String dp = doc.getDocumentElement().getElementsByTagName("DP").item(0)
+                .getNodeValue();
+        String dq = doc.getDocumentElement().getElementsByTagName("DQ").item(0)
+                .getNodeValue();
+        String inverseQ = doc.getDocumentElement()
+                .getElementsByTagName("InverseQ").item(0).getNodeValue();
+
+        key = key.replaceAll("\r", "").replaceAll("\n", "");
+        BigInteger modulus = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(n));
+        BigInteger publicExponent = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(e));
+        BigInteger privateExponent = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(d));
+        BigInteger primeP = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(p));
+        BigInteger primeQ = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(q));
+        BigInteger primeExponentP = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(dp));
+        BigInteger primeExponentQ = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(dq));
+        BigInteger crtCoefficient = new BigInteger(1,
+                new BASE64Decoder().decodeBuffer(inverseQ));
+
+        RSAPrivateCrtKeySpec rsaPriKey = new RSAPrivateCrtKeySpec(modulus,
+                publicExponent, privateExponent, primeP, primeQ,
+                primeExponentP, primeExponentQ, crtCoefficient);
+
+        KeyFactory keyf;
+        try {
+            keyf = KeyFactory.getInstance("RSA");
+            return keyf.generatePrivate(rsaPriKey);
+        } catch (Exception ex) {
+            return null;
+        }
+    }
+
+    static byte[] TrimLeadingZero(byte[] values) {
+        if ((0x00 == values[0]) && (values.length > 1)) {
+            byte[] r = null;
+            r = new byte[values.length - 1];
+            System.arraycopy(values, 1, r, 0, r.length);
+            return r;
+        }
+
+        return values;
+    }
+}

+ 278 - 0
src/main/resources/mapper/PayOrderMapper.xml

@@ -0,0 +1,278 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.caimei.mapper.PayOrderMapper">
+    <select id="getDiscernReceipt" resultType="com.caimei.model.vo.DiscernReceiptVo">
+        SELECT
+          cdr.*,
+          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'
+          OR
+            cror.orderID = #{shopOrderIds}
+            AND cror.relationType = '1'
+          )
+          AND cror.delFlag = '0'
+          AND cdr.delFlag = '0'
+          AND cdr.payType != '16'
+          AND cdr.receiptStatus IN(2,3)
+        ORDER BY
+          cdr.receiptDate DESC
+    </select>
+
+    <update id="updateSelective">
+        update cm_order
+        <set>
+            <if test="orderNo != null">
+                orderNo = #{orderNo,jdbcType=VARCHAR},
+            </if>
+            <if test="userID != null">
+                userID = #{userID,jdbcType=BIGINT},
+            </if>
+            <if test="buyUserID != null">
+                buyUserID = #{buyUserID,jdbcType=INTEGER},
+            </if>
+            <if test="shopOrderIDs != null">
+                shopOrderIDs = #{shopOrderIDs,jdbcType=VARCHAR},
+            </if>
+            <if test="orderSubmitType != null">
+                orderSubmitType = #{orderSubmitType,jdbcType=INTEGER},
+            </if>
+            <if test="orderType != null">
+                orderType = #{orderType,jdbcType=INTEGER},
+            </if>
+            <if test="hasActProduct != null">
+                hasActProduct = #{hasActProduct,jdbcType=CHAR},
+            </if>
+            <if test="autoCloseTimeMills != null">
+                autoCloseTimeMills = #{autoCloseTimeMills,jdbcType=DECIMAL},
+            </if>
+            <if test="status != null">
+                `status` = #{status,jdbcType=CHAR},
+            </if>
+            <if test="receiptStatus != null">
+                receiptStatus = #{receiptStatus,jdbcType=CHAR},
+            </if>
+            <if test="payStatus != null">
+                payStatus = #{payStatus,jdbcType=CHAR},
+            </if>
+            <if test="sendOutStatus != null">
+                sendOutStatus = #{sendOutStatus,jdbcType=CHAR},
+            </if>
+            <if test="refundType != null">
+                refundType = #{refundType,jdbcType=CHAR},
+            </if>
+            <if test="paySuccessCounter != null">
+                paySuccessCounter = #{paySuccessCounter},
+            </if>
+            <if test="payFlag != null">
+                payFlag = #{payFlag,jdbcType=CHAR},
+            </if>
+            <if test="onlinePayFlag != null">
+                onlinePayFlag = #{onlinePayFlag,jdbcType=CHAR},
+            </if>
+            <if test="productTotalFee != null">
+                productTotalFee = #{productTotalFee,jdbcType=DECIMAL},
+            </if>
+            <if test="orderTotalFee != null">
+                orderTotalFee = #{orderTotalFee,jdbcType=DECIMAL},
+            </if>
+            <if test="payTotalFee != null">
+                payTotalFee = #{payTotalFee,jdbcType=DECIMAL},
+            </if>
+            <if test="payableAmount != null">
+                payableAmount = #{payableAmount,jdbcType=DECIMAL},
+            </if>
+            <if test="balancePayFee != null">
+                balancePayFee = #{balancePayFee,jdbcType=DECIMAL},
+            </if>
+            <if test="preferential != null">
+                preferential = #{preferential,jdbcType=DECIMAL},
+            </if>
+            <if test="discountFee != null">
+                discountFee = #{discountFee,jdbcType=DECIMAL},
+            </if>
+            <if test="spID != null">
+                spID = #{spID,jdbcType=BIGINT},
+            </if>
+            <if test="mainSpID != null">
+                mainSpID = #{mainSpID,jdbcType=BIGINT},
+            </if>
+            <if test="note != null">
+                note = #{note,jdbcType=VARCHAR},
+            </if>
+            <if test="clubID != null">
+                clubID = #{clubID,jdbcType=BIGINT},
+            </if>
+            <if test="clubScanTime != null">
+                clubScanTime = #{clubScanTime,jdbcType=VARCHAR},
+            </if>
+            <if test="payWay != null">
+                payWay = #{payWay,jdbcType=VARCHAR},
+            </if>
+            <if test="orderSource != null">
+                orderSource = #{orderSource,jdbcType=CHAR},
+            </if>
+            <if test="closeTime != null">
+                closeTime = #{closeTime,jdbcType=VARCHAR},
+            </if>
+            <if test="confirmTime != null">
+                confirmTime = #{confirmTime,jdbcType=VARCHAR},
+            </if>
+            <if test="payTime != null">
+                payTime = #{payTime,jdbcType=VARCHAR},
+            </if>
+            <if test="orderTime != null">
+                orderTime = #{orderTime,jdbcType=VARCHAR},
+            </if>
+            <if test="productCount != null">
+                productCount = #{productCount,jdbcType=INTEGER},
+            </if>
+            <if test="presentCount != null">
+                presentCount = #{presentCount,jdbcType=INTEGER},
+            </if>
+            <if test="cooFreeFlag != null">
+                cooFreeFlag = #{cooFreeFlag,jdbcType=CHAR},
+            </if>
+            <if test="cooFreeRate != null">
+                cooFreeRate = #{cooFreeRate,jdbcType=INTEGER},
+            </if>
+            <if test="cooFreeAmount != null">
+                cooFreeAmount = #{cooFreeAmount,jdbcType=DECIMAL},
+            </if>
+            <if test="invoiceFlag != null">
+                invoiceFlag = #{invoiceFlag,jdbcType=CHAR},
+            </if>
+            <if test="confirmFlag != null">
+                confirmFlag = #{confirmFlag,jdbcType=CHAR},
+            </if>
+            <if test="clauseID != null">
+                clauseID = #{clauseID,jdbcType=BIGINT},
+            </if>
+            <if test="clauseContent != null">
+                clauseContent = #{clauseContent,jdbcType=VARCHAR},
+            </if>
+            <if test="clauseName != null">
+                clauseName = #{clauseName,jdbcType=VARCHAR},
+            </if>
+            <if test="updateDate != null">
+                updateDate = #{updateDate,jdbcType=VARCHAR},
+            </if>
+            <if test="freePostFlag != null">
+                freePostFlag = #{freePostFlag,jdbcType=CHAR},
+            </if>
+            <if test="freight != null">
+                freight = #{freight,jdbcType=DECIMAL},
+            </if>
+            <if test="delFlag != null">
+                delFlag = #{delFlag,jdbcType=CHAR},
+            </if>
+            <if test="freePostageTicketID != null">
+                freePostageTicketID = #{freePostageTicketID,jdbcType=INTEGER},
+            </if>
+            <if test="splitFlag != null">
+                splitFlag = #{splitFlag,jdbcType=CHAR},
+            </if>
+            <if test="closeReason != null">
+                closeReason = #{closeReason,jdbcType=VARCHAR},
+            </if>
+            <if test="postageOrderFlag != null">
+                postageOrderFlag = #{postageOrderFlag,jdbcType=CHAR},
+            </if>
+        </set>
+        where orderID = #{orderID,jdbcType=BIGINT}
+    </update>
+
+    <insert id="insertDiscernReceipt" keyColumn="id" keyProperty="id" useGeneratedKeys="true" parameterType="com.caimei.model.po.CmDiscernReceiptPo">
+        INSERT INTO `cm_discern_receipt` (
+          `payWay`, `payType`, `receiptType`,
+          `receiptStatus`, `smsContent`, `smsMd5Code`,
+          `orderFlag`, `receiptAmount`, `handlingFee`,
+          `confirmType`, `confirmUserPermissionID`,
+          `reviewUserPermissionID`, `cancelUserPermissionID`,
+          `transactionNum`, `bankID`, `bankCode`,
+          `kuaiQianPayTypeID`, `kuaiQianPayerID`,
+          `rePayFlag`, `actualAmount`, `formData`,
+          `problem`, `noOrderReason`, `reviewReason`,
+          `cancelReason`, `receiptDate`, `confirmDate`,
+          `reviewDate`, `cancelDate`, `updateDate`,
+          `delFlag`
+        )
+        VALUES
+          (
+            #{payWay}, #{payType}, #{receiptType},
+            #{receiptStatus}, #{smsContent}, #{smsMd5Code},
+            #{orderFlag}, #{receiptAmount}, #{handlingFee},
+            #{confirmType}, #{confirmUserPermissionID},
+            #{reviewUserPermissionID}, #{cancelUserPermissionID},
+            #{transactionNum}, #{bankID}, #{bankCode},
+            #{kuaiQianPayTypeID}, #{kuaiQianPayerID},
+            #{rePayFlag}, #{actualAmount}, #{formData},
+            #{problem}, #{noOrderReason}, #{reviewReason},
+            #{cancelReason}, #{receiptDate}, #{confirmDate},
+            #{reviewDate}, #{cancelDate}, #{updateDate},
+            #{delFlag}
+          )
+    </insert>
+
+    <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,
+          splitStatus
+        )
+        VALUES
+          (
+            #{relationType}, #{receiptID}, #{associateAmount},
+            #{orderID}, #{delFlag},#{mbOrderId},#{orderRequestNo},
+            #{splitStatus}
+          )
+    </insert>
+
+    <select id="findAllOrderProduct" resultType="com.caimei.model.vo.OrderProductVo">
+        SELECT
+          orderProductID AS orderProductId,
+          orderID AS orderId,
+          shopOrderID AS shopOrderId,
+          shopID AS shopId,
+          productID AS productId,
+          organizeProductID AS organizeProductId,
+          organizeID AS organizeId,
+          num,
+          presentNum,
+          outStoreType,
+          productNo,
+          price,
+          normalPrice,
+          costPrice,
+          price1,
+          totalAmount,
+          totalFee,
+          shouldPayFee,
+          discount,
+          discountPrice,
+          includedTax,
+          invoiceType,
+          taxRate,
+          totalAddedValueTax,
+          notOutStore,
+          name,
+          productUnit,
+          productImage,
+          productType,
+          orderPromotionsId
+        FROM
+          cm_order_product
+        WHERE
+          orderID = #{orderId}
+        ORDER BY
+          shopID ASC,
+          productID ASC
+    </select>
+</mapper>