chao vor 3 Jahren
Ursprung
Commit
ee284d87b9
24 geänderte Dateien mit 3405 neuen und 11 gelöschten Zeilen
  1. 298 0
      src/main/java/com/caimei365/order/components/WeChatService.java
  2. 36 9
      src/main/java/com/caimei365/order/controller/PayOrderApi.java
  3. 72 0
      src/main/java/com/caimei365/order/model/bo/PayParamBo.java
  4. 41 0
      src/main/java/com/caimei365/order/model/dto/PayDto.java
  5. 10 0
      src/main/java/com/caimei365/order/model/enums/KeyFormat.java
  6. 15 1
      src/main/java/com/caimei365/order/service/PayOrderService.java
  7. 151 1
      src/main/java/com/caimei365/order/service/impl/PayOrderServiceImpl.java
  8. 103 0
      src/main/java/com/caimei365/order/utils/PayUtil.java
  9. 116 0
      src/main/java/com/caimei365/order/utils/RequestUtil.java
  10. 120 0
      src/main/java/com/caimei365/order/utils/pay/AES.java
  11. 605 0
      src/main/java/com/caimei365/order/utils/pay/Base64.java
  12. 36 0
      src/main/java/com/caimei365/order/utils/pay/CheckUtils.java
  13. 7 0
      src/main/java/com/caimei365/order/utils/pay/ConfigureEncryptAndDecrypt.java
  14. 373 0
      src/main/java/com/caimei365/order/utils/pay/ConvertUtils.java
  15. 164 0
      src/main/java/com/caimei365/order/utils/pay/Digest.java
  16. 86 0
      src/main/java/com/caimei365/order/utils/pay/HashUtil.java
  17. 167 0
      src/main/java/com/caimei365/order/utils/pay/HttpClient4Utils.java
  18. 240 0
      src/main/java/com/caimei365/order/utils/pay/HttpRequest.java
  19. 22 0
      src/main/java/com/caimei365/order/utils/pay/KeyGenerator.java
  20. 145 0
      src/main/java/com/caimei365/order/utils/pay/KeyPair.java
  21. 206 0
      src/main/java/com/caimei365/order/utils/pay/KeyWorker.java
  22. 102 0
      src/main/java/com/caimei365/order/utils/pay/RSAUtil.java
  23. 116 0
      src/main/java/com/caimei365/order/utils/pay/RandomCodeGenerator.java
  24. 174 0
      src/main/java/com/caimei365/order/utils/pay/XmlKeyBuilder.java

+ 298 - 0
src/main/java/com/caimei365/order/components/WeChatService.java

@@ -0,0 +1,298 @@
+package com.caimei365.order.components;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.caimei365.order.model.ResponseJson;
+import com.caimei365.order.utils.RequestUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.StringUtils;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.AlgorithmParameters;
+import java.security.Security;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 微信 服务工具类
+ *
+ * @author : Charles
+ * @date : 2021/3/9
+ */
+@Slf4j
+@Component
+public class WeChatService {
+
+    @Value("${wx.redirect-uri}")
+    private String redirectUri;
+    @Value("${wx.app-id}")
+    private String appId;
+    @Value("${wx.app-secret}")
+    private String appSecret;
+    @Value("${wx.mini-app-id}")
+    private String miniAppId;
+    @Value("${wx.mini-app-secret}")
+    private String miniAppSecret;
+    @Value("${wx.crm-app-id}")
+    private String crmAppId;
+    @Value("${wx.crm-app-secret}")
+    private String crmAppSecret;
+    @Value("${wx.hehe-app-id}")
+    private String heHeAppId;
+    @Value("${wx.hehe-app-secret}")
+    private String heHeAppSecret;
+
+    public void setRedirectUri(String redirectUri) {
+        redirectUri = redirectUri;
+    }
+
+    public void setAppId(String appId) {
+        appId = appId;
+    }
+
+    public void setAppSecret(String appSecret) {
+        appSecret = appSecret;
+    }
+
+    public void setMiniAppId(String miniAppId) {
+        miniAppId = miniAppId;
+    }
+
+    public void setMiniAppSecret(String miniAppSecret) {
+        miniAppSecret = miniAppSecret;
+    }
+
+    public void setCrmAppId(String crmAppId) {
+        crmAppId = crmAppId;
+    }
+
+    public void setCrmAppSecret(String crmAppSecret) {
+        crmAppSecret = crmAppSecret;
+    }
+
+    public String getRedirectUri() {
+        return redirectUri;
+    }
+
+    public String getAppId() {
+        return appId;
+    }
+
+    public String getAppSecret() {
+        return appSecret;
+    }
+
+    public String getMiniAppId() {
+        return miniAppId;
+    }
+
+    public String getMiniAppSecret() {
+        return miniAppSecret;
+    }
+
+    public String getCrmAppId() {
+        return crmAppId;
+    }
+
+    public String getCrmAppSecret() {
+        return crmAppSecret;
+    }
+
+    public static class Keys {
+        public static final String OPEN_ID = "openid";
+        public static final String UNION_ID = "unionid";
+        public static final String SESSION_KEY = "session_key";
+    }
+
+    static {
+        // BouncyCastle是一个开源的加解密解决方案
+        Security.addProvider(new BouncyCastleProvider());
+    }
+
+    /**
+     * AES解密
+     *
+     * @param data           密文,被加密的数据
+     * @param key            秘钥
+     * @param iv             偏移量
+     * @param encodingFormat 解密后的结果需要进行的编码
+     * @return 解密结果
+     */
+    public static String decrypt(String data, String key, String iv, String encodingFormat) throws Exception {
+        // 被加密的数据
+        byte[] dataByte = Base64.decodeBase64(data);
+        // 加密秘钥
+        byte[] keyByte = Base64.decodeBase64(key);
+        // 偏移量
+        byte[] ivByte = Base64.decodeBase64(iv);
+        try {
+            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
+            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
+            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
+            parameters.init(new IvParameterSpec(ivByte));
+            // 初始化
+            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
+            byte[] resultByte = cipher.doFinal(dataByte);
+            if (null != resultByte && resultByte.length > 0) {
+                return new String(resultByte, encodingFormat);
+            }
+            return null;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 小程序微信授权登录,获取openid
+     *
+     * @param code    微信凭证
+     * @param headers HttpHeaders
+     * @param mode    1:采美小程序,2:呵呵商城小程序+
+     * @return HashMap
+     */
+    public ResponseJson<Map<String, Object>> getInfoMapByApplets(String code, HttpHeaders headers, Integer mode) {
+        log.info("Start get SessionKey");
+        Map<String, Object> returnMap = new HashMap<>(4);
+        // 获取当前微信小程序的环境
+        String referer = headers.getFirst("Referer");
+        log.info("referer-is----:" + referer);
+        returnMap.put("referer", referer);
+        String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
+        Map<String, String> requestUrlParam = new HashMap<String, String>(4);
+        if (mode == 1) {
+            // 小程序appId
+            requestUrlParam.put("appid", miniAppId);
+            log.info("采美小程序appId: ---" + miniAppId);
+            // 小程序appSecret
+            requestUrlParam.put("secret", miniAppSecret);
+        } else {
+            // 呵呵商城小程序appId
+            requestUrlParam.put("appid", heHeAppId);
+            log.info("呵呵商城appId: ---" + heHeAppId);
+            // 呵呵商城小程序appSecret
+            requestUrlParam.put("secret", heHeAppSecret);
+        }
+        // 小程序端返回的code
+        requestUrlParam.put("js_code", code);
+        // 默认参数
+        requestUrlParam.put("grant_type", "authorization_code");
+        // 发送post请求读取调用微信接口获取openid用户唯一标识
+        String infos;
+        try {
+            infos = RequestUtil.sendPost(requestUrl, requestUrlParam);
+        } catch (Exception e) {
+            return ResponseJson.error("服务器内部异常", returnMap);
+        }
+        // 解析相应内容(转换成json对象)
+        JSONObject jsonObject = JSON.parseObject(infos);
+        String openId = jsonObject.getString(Keys.OPEN_ID);
+        String unionId = jsonObject.getString(Keys.UNION_ID);
+        String sessionKey = jsonObject.getString(Keys.SESSION_KEY);
+        String errCode = jsonObject.getString("errcode");
+        String errMsg = jsonObject.getString("errmsg");
+        log.info("openId----->" + openId + ", unionId------>" + unionId);
+        returnMap.put(Keys.OPEN_ID, openId);
+        returnMap.put(Keys.UNION_ID, unionId);
+        returnMap.put(Keys.SESSION_KEY, sessionKey);
+        boolean errFlag = StringUtils.isNotEmpty(errCode) && ("-1".equals(errCode) || "40029".equals(errCode) || "45011".equals(errCode));
+        if (errFlag) {
+            return ResponseJson.error(-1, errMsg, returnMap);
+        }
+        return ResponseJson.success(returnMap);
+    }
+
+    /**
+     * 微信公众号授权链接(www)
+     *
+     * @param redirectUri 用于微信授权的中间页面
+     * @param mode        授权方式:1静默授权,其他手动同意授权
+     * @return newRedirectUri
+     */
+    public String getAuthorizationLink(String redirectUri, Integer mode) {
+        String link = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
+        // 应用唯一标识
+        link = link.replace("APPID", crmAppId);
+        // 中间页连接
+        link = link.replace("REDIRECT_URI", redirectUri);
+        if (mode == 1) {
+            // 静默授权
+            link = link.replace("snsapi_userinfo", "snsapi_base");
+        }
+        return link;
+    }
+
+    /**
+     * 网页授权登录,通过code获取openid
+     *
+     * @param code   微信code
+     * @param source 来源
+     * @return
+     */
+    public Map<String, Object> getInfoMapByWeb(String code, String source) throws Exception {
+        String link = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
+        if ("pc".equals(source)) {
+            // 应用唯一标识
+            link = link.replace("APPID", appId);
+            // 应用密钥AppSecret,在微信开放平台提交应用审核通过后获得
+            link = link.replace("SECRET", appSecret);
+        } else {
+            //微信公众号
+            link = link.replace("APPID", crmAppId);
+            link = link.replace("SECRET", crmAppSecret);
+        }
+        // 获取的code参数
+        link = link.replace("CODE", code);
+        // 发送授权链接,得到微信用户信息
+        String result = RequestUtil.sendGet(link);
+        log.info(result);
+        Map<String, Object> map = JSONObject.parseObject(result, Map.class);
+        return map;
+    }
+
+    /**
+     * 微信公众号获取access_token
+     *
+     * @return access_token
+     */
+    public String getAccessToken() throws Exception {
+        String link = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
+        link = link.replace("APPID", crmAppId);
+        link = link.replace("APPSECRET", crmAppSecret);
+        String result = RequestUtil.sendGet(link);
+        log.info("微信公众号获取access_token>>>" + result);
+        Map<String, Object> map = JSONObject.parseObject(result, Map.class);
+        return (String) map.get("access_token");
+    }
+
+    /**
+     * 微信公众号获取用户信息
+     *
+     * @return
+     */
+    public Map<String, Object> getUserInfo(String accessToken, String openId) throws Exception {
+        String requestUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";
+        String userInfo = RequestUtil.sendGet(requestUrl);
+        log.info("微信公众号授权用户数据>>>>>>>>>>>" + userInfo);
+        Map<String, Object> map = JSONObject.parseObject(userInfo, Map.class);
+        return map;
+    }
+
+    /**
+     * 网页授权登录,获取用户信息(UnionID机制)
+     */
+    public Map<String, Object> getUserInfoByWeb(String access_token, String openId) throws Exception {
+        String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openId;
+        String userInfo = RequestUtil.sendGet(requestUrl);
+        Map<String, Object> map = JSONObject.parseObject(userInfo, Map.class);
+        return map;
+    }
+
+}

+ 36 - 9
src/main/java/com/caimei365/order/controller/PayOrderApi.java

@@ -1,14 +1,16 @@
 package com.caimei365.order.controller;
 
+import com.alibaba.fastjson.JSONObject;
 import com.caimei365.order.model.ResponseJson;
+import com.caimei365.order.model.dto.PayDto;
 import com.caimei365.order.service.PayOrderService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiOperation;
 import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.bind.annotation.*;
 
 import java.util.Map;
 
@@ -40,15 +42,17 @@ public class PayOrderApi {
 
     /**
      * 订单支付,余额抵扣
+     * @param payDto {
+     *                orderId      订单ID
+     * }
      */
     @ApiOperation("订单支付,余额抵扣(旧:/order/balanceDeduction)")
-    @ApiImplicitParam(required = false, name = "orderId", value = "订单Id")
-    @GetMapping("/balance/deduction")
-    public ResponseJson<Map<String, Object>> balanceDeduction(Integer orderId) {
-        if (null == orderId) {
+    @PostMapping("/balance/deduction")
+    public ResponseJson<Map<String, Object>> balanceDeduction(PayDto payDto){
+        if (null == payDto.getOrderId()) {
             return ResponseJson.error("订单Id不能为空!", null);
         }
-        return payOrderService.balanceDeduction(orderId);
+        return payOrderService.balanceDeduction(payDto.getOrderId());
     }
 
     /**
@@ -73,7 +77,30 @@ public class PayOrderApi {
         return payOrderService.getCheckoutCounter(orderId);
     }
 
-
+    /**
+     * 微信线上支付
+     * @param payDto {
+     *               orderId      订单ID
+     *               payAmount     支付金额,单位分,必须大于2
+     *               code          微信小程序code
+     *               state         微信公众号state参数
+     *               returnUrl     页面回调地址
+     * }
+     */
+    @ApiOperation("微信线上支付(旧:/PayOrder/miniWxPay)")
+    @PostMapping("/wechat")
+    public ResponseJson<JSONObject> weChatPay(PayDto payDto, @RequestHeader HttpHeaders headers){
+        if (null == payDto.getOrderId()) {
+            return ResponseJson.error("订单Id不能为空!", null);
+        }
+        if (null == payDto.getPayAmount() || payDto.getPayAmount() < 2) {
+            return ResponseJson.error("支付金额不正确!", null);
+        }
+        if (StringUtils.isEmpty(payDto.getCode())) {
+            return ResponseJson.error("微信code不能为空!", null);
+        }
+        return payOrderService.weChatPay(payDto, headers);
+    }
 
 
 

+ 72 - 0
src/main/java/com/caimei365/order/model/bo/PayParamBo.java

@@ -0,0 +1,72 @@
+package com.caimei365.order.model.bo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/7/30
+ */
+@Data
+public class PayParamBo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 订单Id
+     */
+    private Integer orderId;
+    /**
+     * 支付金额,单位分,必须大于2
+     */
+    private Integer payAmount;
+    /**
+     * 支付方式,
+     * 银联:UNIONPAY,
+     * 微信:WEIXIN,
+     * 支付宝:ALIPAY
+     */
+    private String payWay;
+    /**
+     * 支付类型(现阶段主要是区分微信支付)
+     * 微信小程序支付: MINIAPP_WEIXIN
+     * 微信公众号支付: JSAPI_WEIXIN
+     */
+    private String payType;
+    /**
+     * 微信小程序code,微信小程序支付使用
+     */
+    private String code;
+    /**
+     * 微信公众号state参数
+     */
+    private String state;
+    /**
+     * 页面回调地址
+     */
+    private String returnUrl;
+    /**
+     * 微信openId
+     */
+    private String openId;
+    /**
+     * 异步通知回调
+     */
+    private String notifyUrl;
+    /**
+     * 银行编码(银联支付使用)
+     */
+    private String bankCode;
+    /**
+     * 用户类型(银联支付使用)
+     * 企业:ENTERPRISE
+     * 个人:USER
+     */
+    private String userType;
+
+
+
+}

+ 41 - 0
src/main/java/com/caimei365/order/model/dto/PayDto.java

@@ -0,0 +1,41 @@
+package com.caimei365.order.model.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/7/30
+ */
+@Data
+public class PayDto {
+    private static final long serialVersionUID = 1L;
+    /**
+     * 订单Id
+     */
+    @ApiModelProperty("订单Id")
+    private Integer orderId;
+    /**
+     * 支付金额,单位分,必须大于2
+     */
+    @ApiModelProperty("支付金额,单位分,必须大于2")
+    private Integer payAmount;
+    /**
+     * 微信小程序code,微信小程序支付使用
+     */
+    @ApiModelProperty("微信小程序code")
+    private String code;
+    /**
+     * 微信公众号state参数
+     */
+    @ApiModelProperty("微信公众号state参数")
+    private String state;
+    /**
+     * 页面回调地址
+     */
+    @ApiModelProperty("页面回调地址")
+    private String returnUrl;
+
+}

+ 10 - 0
src/main/java/com/caimei365/order/model/enums/KeyFormat.java

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

+ 15 - 1
src/main/java/com/caimei365/order/service/PayOrderService.java

@@ -1,6 +1,9 @@
 package com.caimei365.order.service;
 
+import com.alibaba.fastjson.JSONObject;
 import com.caimei365.order.model.ResponseJson;
+import com.caimei365.order.model.dto.PayDto;
+import org.springframework.http.HttpHeaders;
 
 import java.util.Map;
 
@@ -30,5 +33,16 @@ public interface PayOrderService {
      * @param orderId 订单Id
      */
     ResponseJson<Map<String, Object>> getCheckoutCounter(Integer orderId);
-
+    /**
+     * 微信线上支付
+     * @param payDto {
+     *               orderId      订单ID
+     *               payAmount     支付金额,单位分,必须大于2
+     *               code          微信小程序code
+     *               state         微信公众号state参数
+     *               returnUrl     页面回调地址
+     * }
+     * @param headers HttpHeaders
+     */
+    ResponseJson<JSONObject> weChatPay(PayDto payDto, HttpHeaders headers);
 }

+ 151 - 1
src/main/java/com/caimei365/order/service/impl/PayOrderServiceImpl.java

@@ -1,19 +1,29 @@
 package com.caimei365.order.service.impl;
 
+import com.alibaba.fastjson.JSONObject;
 import com.caimei365.order.components.OrderCommonService;
 import com.caimei365.order.components.ProductService;
+import com.caimei365.order.components.WeChatService;
 import com.caimei365.order.mapper.BaseMapper;
 import com.caimei365.order.mapper.PayOrderMapper;
 import com.caimei365.order.mapper.OrderCommonMapper;
 import com.caimei365.order.model.ResponseJson;
+import com.caimei365.order.model.bo.PayParamBo;
+import com.caimei365.order.model.dto.PayDto;
 import com.caimei365.order.model.po.BalanceRecordPo;
 import com.caimei365.order.model.vo.DiscernReceiptVo;
 import com.caimei365.order.model.vo.OrderProductVo;
 import com.caimei365.order.model.vo.OrderVo;
 import com.caimei365.order.service.PayOrderService;
 import com.caimei365.order.utils.MathUtil;
+import com.caimei365.order.utils.PayUtil;
+import com.caimei365.order.utils.pay.RSAUtil;
 import com.google.common.util.concurrent.AtomicDouble;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
@@ -43,6 +53,10 @@ public class PayOrderServiceImpl implements PayOrderService {
     private OrderCommonService orderCommonService;
     @Resource
     private ProductService productService;
+    @Resource
+    private WeChatService weChatService;
+    @Value("${caimei.notifyUrl}")
+    private String notifyUrl;
 
     /**
      * 订单支付前效验付款规则
@@ -215,7 +229,7 @@ public class PayOrderServiceImpl implements PayOrderService {
                 }
             });
             if (offlineFlag.get()) {
-                return ResponseJson.error("已经线下支付过,只能支付支付!", null);
+                return ResponseJson.error("已经线下支付过,只能线下支付!", null);
             }
             order.setReceiptAmount(receiptAmount.get());
         }
@@ -241,4 +255,140 @@ public class PayOrderServiceImpl implements PayOrderService {
 
         return ResponseJson.success(map);
     }
+
+    /**
+     * 微信线上支付
+     *
+     * @param payDto  {
+     *                orderId      订单ID
+     *                payAmount     支付金额,单位分,必须大于2
+     *                code          微信小程序code
+     *                state         微信公众号state参数
+     *                productId     二手发布商品id
+     *                returnUrl     页面回调地址
+     *                }
+     * @param headers HttpHeaders
+     */
+    @Override
+    public ResponseJson<JSONObject> weChatPay(PayDto payDto, HttpHeaders headers) {
+        PayParamBo payParam = new PayParamBo();
+        // payDto -> payParam
+        BeanUtils.copyProperties(payDto, payParam);
+        String openId = null;
+        if (null == payParam.getState()) {
+            // 小程序微信授权获取登录信息
+            ResponseJson<Map<String, Object>> appletsInfo = weChatService.getInfoMapByApplets(payParam.getCode(), headers, 1);
+            if (appletsInfo.getCode() == -1) {
+                return ResponseJson.error(appletsInfo.getMsg(), null);
+            }
+            Map<String, Object> infoData = appletsInfo.getData();
+            openId = (String) infoData.get(WeChatService.Keys.OPEN_ID);
+            if (StringUtils.isEmpty(openId)) {
+                return ResponseJson.error("微信小程序获取openId失败!", null);
+            }
+            //小程序微信快捷支付
+            payParam.setPayType("MINIAPP_WEIXIN");
+        } else {
+            try {
+                // 微信公众号,通过code获取微信用户信息
+                Map<String, Object> map = weChatService.getInfoMapByWeb(payParam.getCode(), "crm");
+                openId = (String) map.get(WeChatService.Keys.OPEN_ID);
+            } catch (Exception e) {
+                log.error("try-catch:",e);
+            }
+            if (StringUtils.isEmpty(openId)) {
+                return ResponseJson.error("微信公众号获取openId失败!", null);
+            }
+            //pc微信扫码支付,微信公众号支付
+            payParam.setPayType("JSAPI_WEIXIN");
+        }
+        payParam.setOpenId(openId);
+        payParam.setNotifyUrl(notifyUrl);
+        log.info("【微信支付】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>获取openId:" + openId);
+        // 订单信息
+        OrderVo order = payOrderMapper.getOrderByOrderId(payParam.getOrderId());
+        if (null == order) {
+            return ResponseJson.error("订单不存在", null);
+        }
+        if (0 == order.getStatus() || 33 == order.getStatus() || 4 == order.getStatus() || 5 == order.getStatus() || 6 == order.getStatus() || 7 == order.getStatus()) {
+            return ResponseJson.error("订单状态错误", null);
+        }
+        // 支付记录
+        List<DiscernReceiptVo> discernReceiptList = orderCommonMapper.getDiscernReceipt(order.getOrderId());
+        if (!discernReceiptList.isEmpty()) {
+            AtomicReference<Boolean> offlineFlag = new AtomicReference<>(false);
+            discernReceiptList.forEach(discernReceipt -> {
+                if (2 == discernReceipt.getPayWay()) {
+                    offlineFlag.set(true);
+                }
+            });
+            if (offlineFlag.get()) {
+                return ResponseJson.error("已经线下支付过,只能线下支付!", null);
+            }
+        }
+        log.info("【微信支付】>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>本次付款金额:" + payParam.getPayAmount());
+        if (MathUtil.compare(MathUtil.mul(order.getPayableAmount(), 100), payParam.getPayAmount()) < 0) {
+            return ResponseJson.error("付款金额错误!", null);
+        }
+        // 定义支付请求结果
+        JSONObject result = null;
+        try {
+            // 时间戳
+            long time = System.currentTimeMillis() / 1000;
+            // 用户IP地址
+            String userIp = headers.getFirst("X-CLIENT-IP");
+            JSONObject json = new JSONObject();
+            json.put("merAccount", PayUtil.merAccount);
+            json.put("merNo", PayUtil.merNo);
+            json.put("time", time);
+            //支付金额
+            json.put("amount", payParam.getPayAmount());
+            json.put("payWay", payParam.getPayWay());
+            json.put("payType", payParam.getPayType());
+            json.put("userIp", userIp);
+            json.put("returnUrl", payParam.getReturnUrl());
+            json.put("notifyUrl", payParam.getNotifyUrl());
+            if (org.apache.commons.lang.StringUtils.isNotBlank(payParam.getBankCode())) {
+                json.put("bankCode", payParam.getBankCode());
+            }
+            if (org.apache.commons.lang.StringUtils.isNotBlank(payParam.getUserType())) {
+                json.put("userType", payParam.getUserType());
+            }
+            if (org.apache.commons.lang.StringUtils.isNotBlank(payParam.getOpenId())) {
+                json.put("openId", payParam.getOpenId());
+            }
+            String environment = "";
+            if (payParam.getNotifyUrl().contains("8008")) {
+                environment = "DEV";
+            } else if (payParam.getNotifyUrl().contains("spi-b")) {
+                environment = "BETA";
+            }
+            // 商户订单号
+            String orderId = order.getOrderNo() + "T" + time + environment;
+            json.put("orderId", orderId);
+            //商品名称
+            String product = "采美订单" + order.getOrderNo();
+            json.put("product", product);
+            //支付类型
+            String payType = PayUtil.getPayTypeId(payParam.getPayType(), payParam.getUserType());
+            String attach = order.getOrderId() + "," + payType;
+            json.put("attach", attach);
+            String sign = PayUtil.getPaySign(json, PayUtil.merKey);
+            json.put("sign", sign);
+            // 私钥加密
+            String data = RSAUtil.privateEncrypt(json.toJSONString(), PayUtil.merKey);
+            // 提交
+            result = PayUtil.httpGet("https://platform.mhxxkj.com/paygateway/mbpay/order/v1", PayUtil.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);
+    }
 }

+ 103 - 0
src/main/java/com/caimei365/order/utils/PayUtil.java

@@ -0,0 +1,103 @@
+package com.caimei365.order.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.caimei365.order.utils.pay.HashUtil;
+import com.caimei365.order.utils.pay.HttpClient4Utils;
+import com.caimei365.order.utils.pay.RSAUtil;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2021/7/30
+ */
+public class PayUtil {
+    /**
+     * 商户标识
+     */
+    public static final String merAccount = "aa9aee6a148843a6a9e4ea117df4454b";
+    /**
+     * 私钥,商户密钥
+     */
+    public static final 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==";
+    /**
+     * 公钥
+     */
+    public static final String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNKgk2RXYtSOffyXorPvClYc0jYbEayojpR2syFcsTS81ELxaVjeT5X1SpZyU6KK1EU0rX4wL9NRw9GeGbzGrrh4Q09G6bYg5Z/dvC3eks1/PPV9p7AsZdEPls65BbJ0+oKLbxp03kBlIGwVd0kA9yFDsEcMaFmG62asbahxcakQIDAQAB";
+    /**
+     * 用户编号
+     */
+    public static final String merNo = "10001720";
+    /**
+     * 公账-专票,子商户商编
+     */
+    public static final String publicAccountNo = "20001793";
+    /**
+     * 公账-普票,子商户商编
+     */
+    public static final String commonInvoiceNo = "20001754";
+    /**
+     * 私账-无票,子商户商编
+     */
+    public static final String privateAccountNo = "20001924";
+
+    /**
+     * 支付状态转换
+     */
+    public static String getPayTypeId(String payType, String userType) {
+        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(userType)) {
+            //企业网银
+            return "12";
+        } else {
+            //个人网银
+            return "17";
+        }
+    }
+
+    /**
+     * 生成RSA签名:sign
+     */
+    public static String getPaySign(JSONObject json, String merKey) {
+        StringBuffer buffer		= new StringBuffer();
+        TreeMap<String, Object> treeMap = new TreeMap<String, Object>();
+        for (Map.Entry<String, Object> entry : json.entrySet()) {
+            if (entry.getValue() != null) {
+                treeMap.put(entry.getKey(), entry.getValue());
+            }
+        }
+        for (Map.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();
+    }
+
+    //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, "UTF-8", false);
+        return JSON.parseObject(responseBody);
+    }
+}

+ 116 - 0
src/main/java/com/caimei365/order/utils/RequestUtil.java

@@ -0,0 +1,116 @@
+package com.caimei365.order.utils;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 请求工具类
+ *
+ * @author : Charles
+ * @date : 2021/3/8
+ */
+public class RequestUtil {
+
+    /**
+     * 向指定URL发送GET方法的请求
+     *
+     * @param url   发送请求的URL,请求参数应该是 name1=value1&name2=value2 的形式。
+     * @return 远程资源的响应结果
+     */
+    public static String sendGet(String url) throws Exception {
+        StringBuilder result = new StringBuilder();
+        BufferedReader in = null;
+        try {
+
+            URL realUrl = new URL(url);
+            // 打开和URL之间的连接
+            URLConnection connection = realUrl.openConnection();
+            // 设置通用的请求属性
+            connection.setRequestProperty("accept", "*/*");
+            connection.setRequestProperty("connection", "Keep-Alive");
+            connection.setRequestProperty("Accept-Charset", "utf-8");
+            connection.setRequestProperty("contentType", "utf-8");
+            connection.setConnectTimeout(5000);
+            // 建立实际的连接
+            connection.connect();
+            // 获取所有响应头字段
+            Map<String, List<String>> map = connection.getHeaderFields();
+            // 定义 BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(new InputStreamReader(
+                    connection.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+        }
+        // 使用finally块来关闭输入流
+        finally {
+            if (in != null) {
+                in.close();
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * 向指定 URL 发送POST方法的请求
+     *
+     * @param url 发送请求的 URL
+     * @param paramMap 请求参数
+     * @return 所代表远程资源的响应结果
+     */
+    public static String sendPost(String url, Map<String, ?> paramMap) throws Exception{
+        PrintWriter out = null;
+        BufferedReader in = null;
+        StringBuilder result = new StringBuilder();
+
+        StringBuilder param = new StringBuilder();
+
+        for (String key : paramMap.keySet()) {
+            param.append(key).append("=").append(paramMap.get(key)).append("&");
+        }
+
+        try {
+            URL realUrl = new URL(url);
+            // 打开和URL之间的连接
+            URLConnection conn = realUrl.openConnection();
+            // 设置通用的请求属性
+            conn.setRequestProperty("accept", "*/*");
+            conn.setRequestProperty("connection", "Keep-Alive");
+            conn.setRequestProperty("Accept-Charset", "utf-8");
+            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+            // 发送POST请求必须设置如下两行
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            // 获取URLConnection对象对应的输出流
+            out = new PrintWriter(conn.getOutputStream());
+            // 发送请求参数
+            out.print(param);
+            // flush输出流的缓冲
+            out.flush();
+            // 定义BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+        }
+        //使用finally块来关闭输出流、输入流
+        finally{
+            if(out!=null){
+                out.close();
+            }
+            if(in!=null){
+                in.close();
+            }
+        }
+        return result.toString();
+    }
+
+}

+ 120 - 0
src/main/java/com/caimei365/order/utils/pay/AES.java

@@ -0,0 +1,120 @@
+package com.caimei365.order.utils.pay;
+
+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 {
+	/**
+	 * 加密
+	 */
+	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);
+		}
+	}
+
+	/**
+	 * 解密
+	 */
+	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));
+	}
+	
+}

+ 605 - 0
src/main/java/com/caimei365/order/utils/pay/Base64.java

@@ -0,0 +1,605 @@
+package com.caimei365.order.utils.pay;
+
+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/caimei365/order/utils/pay/CheckUtils.java

@@ -0,0 +1,36 @@
+package com.caimei365.order.utils.pay;
+
+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/caimei365/order/utils/pay/ConfigureEncryptAndDecrypt.java

@@ -0,0 +1,7 @@
+package com.caimei365.order.utils.pay;
+
+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/caimei365/order/utils/pay/ConvertUtils.java

@@ -0,0 +1,373 @@
+package com.caimei365.order.utils.pay;
+
+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/caimei365/order/utils/pay/Digest.java

@@ -0,0 +1,164 @@
+package com.caimei365.order.utils.pay;
+
+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;
+		}
+	}
+
+}

+ 86 - 0
src/main/java/com/caimei365/order/utils/pay/HashUtil.java

@@ -0,0 +1,86 @@
+
+package com.caimei365.order.utils.pay;
+
+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/caimei365/order/utils/pay/HttpClient4Utils.java

@@ -0,0 +1,167 @@
+package com.caimei365.order.utils.pay;
+
+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());
+    }
+
+}

+ 240 - 0
src/main/java/com/caimei365/order/utils/pay/HttpRequest.java

@@ -0,0 +1,240 @@
+package com.caimei365.order.utils.pay;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * HTTP 请求工具
+ */
+public class HttpRequest {
+    /**
+     * 向指定URL发送GET方法的请求
+     *
+     * @param url   发送请求的URL
+     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+     * @return URL 所代表远程资源的响应结果
+     * @throws Exception
+     */
+    @SuppressWarnings("unused")
+    public static String sendGetWithParameter(String url, String param) throws Exception {
+        StringBuffer result=new StringBuffer();
+        BufferedReader in = null;
+        try {
+            String urlNameString = url + "?" + param;
+            URL realUrl = new URL(urlNameString);
+            // 打开和URL之间的连接
+            URLConnection connection = realUrl.openConnection();
+            // 设置通用的请求属性
+            connection.setRequestProperty("accept", "*/*");
+            connection.setRequestProperty("connection", "Keep-Alive");
+            connection.setRequestProperty("Accept-Charset", "utf-8");
+            connection.setRequestProperty("contentType", "utf-8");
+            connection.setConnectTimeout(5000);
+            // 建立实际的连接
+            connection.connect();
+            // 获取所有响应头字段
+            Map<String, List<String>> map = connection.getHeaderFields();
+            // 遍历所有的响应头字段
+            for (String key : map.keySet()) {
+                // System.out.println(key + "--->" + map.get(key)); //关闭响应头的输出
+            }
+            // 定义 BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(new InputStreamReader(
+                    connection.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+        } catch (Exception e) {
+            throw e;
+        }
+        // 使用finally块来关闭输入流
+        finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (Exception e2) {
+                throw e2;
+            }
+        }
+        return result.toString();
+    }
+
+
+    @SuppressWarnings("unused")
+    public static String sendGet(String url) throws Exception {
+        StringBuffer result=new StringBuffer();
+        BufferedReader in = null;
+        try {
+
+            URL realUrl = new URL(url);
+            // 打开和URL之间的连接
+            URLConnection connection = realUrl.openConnection();
+            // 设置通用的请求属性
+            connection.setRequestProperty("accept", "*/*");
+            connection.setRequestProperty("connection", "Keep-Alive");
+            connection.setRequestProperty("Accept-Charset", "utf-8");
+            connection.setRequestProperty("contentType", "utf-8");
+            connection.setConnectTimeout(5000);
+            // 建立实际的连接
+            connection.connect();
+            // 获取所有响应头字段
+            Map<String, List<String>> map = connection.getHeaderFields();
+            // 遍历所有的响应头字段
+            for (String key : map.keySet()) {
+                //  System.out.println(key + "--->" + map.get(key));
+            }
+            // 定义 BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(new InputStreamReader(
+                    connection.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+        } catch (Exception e) {
+            throw e;
+        }
+        // 使用finally块来关闭输入流
+        finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (Exception e2) {
+                throw e2;
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * 向指定 URL 发送POST方法的请求
+     *
+     * @param url   发送请求的 URL
+     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+     * @return 所代表远程资源的响应结果
+     * @throws Exception
+     */
+    public static String sendPost(String url, String param) throws Exception {
+        PrintWriter out = null;
+        BufferedReader in = null;
+        StringBuffer result=new StringBuffer();
+        try {
+            URL realUrl = new URL(url);
+            // 打开和URL之间的连接
+            URLConnection conn = realUrl.openConnection();
+            // 设置通用的请求属性
+            conn.setRequestProperty("accept", "*/*");
+            conn.setRequestProperty("connection", "Keep-Alive");
+            conn.setRequestProperty("Accept-Charset", "utf-8");
+            conn.setRequestProperty("contentType", "utf-8");
+            conn.setConnectTimeout(5000);
+            // 发送POST请求必须设置如下两行
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            // 获取URLConnection对象对应的输出流
+            out = new PrintWriter(conn.getOutputStream());
+            // 发送请求参数
+            out.print(param);
+            // flush输出流的缓冲
+            out.flush();
+            // 定义BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(
+                    new InputStreamReader(conn.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+        } catch (Exception e) {
+            throw e;
+        }
+        //使用finally块来关闭输出流、输入流
+        finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+                if (in != null) {
+                    in.close();
+                }
+            } catch (Exception ex) {
+                throw ex;
+            }
+        }
+        return result.toString();
+    }
+
+
+    /**
+     * 向指定 URL 发送POST方法的请求
+     *
+     * @param url 发送请求的 URL
+     * @param paramMap 请求参数
+     * @return 所代表远程资源的响应结果
+     */
+    public static String sendPost(String url, Map<String, ?> paramMap) throws Exception{
+        PrintWriter out = null;
+        BufferedReader in = null;
+        String result = "";
+
+        String param = "";
+        Iterator<String> it = paramMap.keySet().iterator();
+
+        while(it.hasNext()) {
+            String key = it.next();
+            param += key + "=" + paramMap.get(key) + "&";
+        }
+
+        try {
+            URL realUrl = new URL(url);
+            // 打开和URL之间的连接
+            URLConnection conn = realUrl.openConnection();
+            // 设置通用的请求属性
+            conn.setRequestProperty("accept", "*/*");
+            conn.setRequestProperty("connection", "Keep-Alive");
+            conn.setRequestProperty("Accept-Charset", "utf-8");
+            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+            // 发送POST请求必须设置如下两行
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            // 获取URLConnection对象对应的输出流
+            out = new PrintWriter(conn.getOutputStream());
+            // 发送请求参数
+            out.print(param);
+            // flush输出流的缓冲
+            out.flush();
+            // 定义BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result += line;
+            }
+        } catch (Exception e) {
+            throw e;
+        }
+        //使用finally块来关闭输出流、输入流
+        finally{
+            try{
+                if(out!=null){
+                    out.close();
+                }
+                if(in!=null){
+                    in.close();
+                }
+            }
+            catch(IOException ex){
+               throw ex;
+            }
+        }
+        return result;
+    }
+
+}

+ 22 - 0
src/main/java/com/caimei365/order/utils/pay/KeyGenerator.java

@@ -0,0 +1,22 @@
+package com.caimei365.order.utils.pay;
+
+import com.caimei365.order.model.enums.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);
+	}
+}

+ 145 - 0
src/main/java/com/caimei365/order/utils/pay/KeyPair.java

@@ -0,0 +1,145 @@
+package com.caimei365.order.utils.pay;
+
+import com.caimei365.order.model.enums.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/caimei365/order/utils/pay/KeyWorker.java

@@ -0,0 +1,206 @@
+package com.caimei365.order.utils.pay;
+
+import com.caimei365.order.model.enums.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;
+	}
+
+}

+ 102 - 0
src/main/java/com/caimei365/order/utils/pay/RSAUtil.java

@@ -0,0 +1,102 @@
+package com.caimei365.order.utils.pay;
+
+
+import com.caimei365.order.model.enums.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);
+	}
+    
+}

+ 116 - 0
src/main/java/com/caimei365/order/utils/pay/RandomCodeGenerator.java

@@ -0,0 +1,116 @@
+package com.caimei365.order.utils.pay;
+import java.util.Random;
+
+public class RandomCodeGenerator {
+
+	// TODO 与WWW的RandomCodeGenerator是重复的,后续需要删除www的RandomCodeGenerator
+	private static char codeSequence[] = {
+			'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 
+			'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 
+			'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7', 
+			'8', '9'
+		};
+	
+	private static char intSequence[] = {
+		'2', '3', '4', '5', '6', '7', 
+		'8', '9'
+	};
+	
+	private static char stringSequence[] = {
+		'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 
+		'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 
+		'W', 'X', 'Y', 'Z'
+	};
+
+	private static char newStringSequence[] = {
+			'A','a', 'B','b', 'D','d', 'E' ,'e', 'F','f', 'G','g', 'H','h',
+			'L', 'N','n', 'Q','q', 'R','r', 'T','t', 'Y','y'
+	};
+//	C,i,j,k,M,O,P,S,U,V,W,X,Z,l
+
+	private static char[] shortLink = {
+		'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
+		'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'O',
+		'W', 'X', 'Y', 'Z',
+		'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
+		'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'o',
+		'w', 'x', 'y', 'z' ,
+		'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'
+	};
+
+	public static String generateShortLink(int length) {
+		StringBuffer sb = new StringBuffer();
+		Random random = new Random();
+		for (int i = 0; i < shortLink.length && i < length; ++i) {
+			sb.append(shortLink[random.nextInt(shortLink.length)]);
+		}
+		return sb.toString();
+	}
+
+	public static String generateCode(int length) {
+		StringBuffer sb = new StringBuffer();
+		Random random = new Random();
+		for (int i = 0; i < codeSequence.length && i < length; ++i) {
+			sb.append(codeSequence[random.nextInt(codeSequence.length)]);
+		}
+		
+		return sb.toString();
+	}
+	
+	public static String generateCodeInt(int length) {
+		StringBuffer sb = new StringBuffer();
+		Random random = new Random();
+		for (int i = 0; i < intSequence.length && i < length; ++i) {
+			sb.append(intSequence[random.nextInt(intSequence.length)]);
+		}
+		
+		return sb.toString();
+	}
+	
+	public static String generateCodeString(int length) {
+		StringBuffer sb = new StringBuffer();
+		Random random = new Random();
+		for (int i = 0; i < stringSequence.length && i < length; ++i) {
+			sb.append(stringSequence[random.nextInt(stringSequence.length)]);
+		}
+		
+		return sb.toString();
+	}
+	
+	/**
+	 * 随机生成指定位数字母与数字组成的字符串
+	 * @param length 长度
+	 * @return
+	 */
+	/*public static String generateRandomCode(int length) {
+		// 允许字母与数字的字符串
+		return RandomStringUtils.random(length, true, true);
+	}*/
+
+	public static String getRandomCharAndNumr(int length) {
+		String val = "";
+		Random random = new Random();
+		for (int i = 0; i < length; i++) {
+			// 输出字母还是数字
+			String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
+			// 字符串
+			if ("char".equalsIgnoreCase(charOrNum)) {
+				// 取得大写字母还是小写字母
+				int choice = random.nextInt(2) % 2 == 0 ? 65 : 97;
+				val += (char) (choice + random.nextInt(26));
+			} else if ("num".equalsIgnoreCase(charOrNum)) { // 数字
+				val += String.valueOf(random.nextInt(10));
+			}
+		}
+		return val;
+	}
+
+	public static String generateAccount(int length){
+		StringBuffer sb = new StringBuffer();
+		Random random = new Random();
+		for (int i = 0; i < newStringSequence.length && i < length; ++i) {
+			sb.append(newStringSequence[random.nextInt(newStringSequence.length)]);
+		}
+		return sb.toString();
+	}
+}

+ 174 - 0
src/main/java/com/caimei365/order/utils/pay/XmlKeyBuilder.java

@@ -0,0 +1,174 @@
+package com.caimei365.order.utils.pay;
+
+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;
+	}
+}