JAVA微信支付

1,簡單說明

  現在好多項目上都需要用到微信支付接口,官方文檔上也是簡單的描述了下,技術不高深的真的難以理解(我自己看官方文檔就看不懂),還是需要自己收集,總結,

網上看了好多

有些照着弄最後還是沒法成功。接下來我分享下自己的微信支付。這個微信支付的微信公衆號或者小程序,都是需要微信認證的,不然無法申請微信支付,這個就不說了

,既然到了這一步,相信所有的前提都已經準備好了。直接上代碼吧~

2,java微信支付

一,首先了解下各個參數的意義

  https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1  這個是微信官方的說明 截取部分,詳細的請到這個鏈接查看。

二,微信支付代碼

首先創建service層插入如下代碼:

public interface WeiXinPayService {
 
    Object WeiXinPay(String outTradeNo,String openid, String body, int total_fee) throws UnsupportedEncodingException;
}
@Service
public class WeiXinPayServiceImpl implements WeiXinPayService {
    private static final Logger LOGGER = LoggerFactory
            .getLogger(WeiXinPayServiceImpl.class);
 
    @Override
    public Object WeiXinPay(String outTradeNo,String openid, String body, int total_fee) {
 
        String appid = "" // 公衆號--》“開發者ID”  微信小程序,或者公衆號的APPID
        String mch_id = "" // 商戶號,將該值賦值給partner
        String key =  "" // 微信支付商戶平臺登錄)--》“API安全”--》“API密鑰”--“設置密鑰”(設置之後的那個值就是partnerkey,32位)
        LOGGER.debug(appid);
        LOGGER.debug(mch_id);
       LOGGER.debug(key);
//        String body = body; // 描述           int total_fee = total_fee; // 支付金額
        String notify_url = ""; // 回調鏈接
//        String out_trade_no = IdUtils.genOrderName();//生成訂單號
//        LOGGER.debug("outTradeNo---:"+out_trade_no);
        LOGGER.debug("openid是----"+openid);
        LOGGER.debug("appid---"+appid);
        LOGGER.debug("mch_id---"+mch_id);
        Map<Object, Object> map = null;
        try {
            map = WeiXinAtcion.me.weixinPlay(mch_id, appid,
                    key, openid, total_fee, outTradeNo, notify_url, body);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return map;
    }
}

然後創建一個corcontroller層:

/**
 * <p>Title: WeiXinPayController</p>  
  * <p>Description: 微信支付</p>  
  * @author SpringRoot  
  * @date 2020/3/4-11:17
 */
@Controller
public class WeiXinPayController {
    private static final Logger LOGGER = LoggerFactory
            .getLogger(WeiXinPayController.class);
 
    @Autowired
    private WeiXinPayService weiXinPayService;
 
    /**
     * 支付接口
     *
     * @param openid
     * @param body      說明
     * @param total_fee 總價
     * @return
     */
    @RequestMapping("/WeiXinPay")
    public @ResponseBody
    Object WeiXinPay(String outTradeNo,String openid, String body, int total_fee) {
        LOGGER.debug("outTradeNo-------------"+outTradeNo);
        LOGGER.debug("openid-------------"+openid);
        LOGGER.debug("body-------------"+body);
        LOGGER.debug("total_fee-------------"+total_fee);
        try {
            return weiXinPayService.WeiXinPay(outTradeNo,openid, body, total_fee);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return TTResult.fail();
    }
}

以上代碼中使用到的工具類:首先WeiXinAtcion

/**
 * <p>Title: WeiXinAtcion</p>  
  * <p>Description: </p>  
  * @author SpringRoot
  * @date 2020/3/4-11:17
 */
@Component
public class WeiXinAtcion {
    //密鑰
    public static final WeiXinAtcion me = new WeiXinAtcion();
    private static final Logger LOGGER = LoggerFactory
            .getLogger(WeiXinAtcion.class);
 
    /**
     * 生成微信訂單
     *
     * @param mch_id
     * @param appid
     * @param key
     * @param openid
     * @param total_fee
     * @param out_trade_no
     * @param notify_url
     * @param body
     * @return
     * @throws UnsupportedEncodingException
     * @throws DocumentException
     */
    public SortedMap<Object, Object> weixinPlay(String mch_id, String appid, String key, String openid, int total_fee, String out_trade_no, String notify_url, String body) throws UnsupportedEncodingException, DocumentException, DocumentException {
 
        SortedMap<Object, Object> paymentPo = new TreeMap<Object, Object>();
        paymentPo.put("appid", appid);
        paymentPo.put("mch_id", mch_id);
        paymentPo.put("nonce_str", WXUtil.generate());
        paymentPo.put("body", body);
        paymentPo.put("out_trade_no", out_trade_no);
        paymentPo.put("total_fee", String.valueOf(total_fee));
        paymentPo.put("spbill_create_ip","服務器的ip地址");//此處是公網ip
        paymentPo.put("notify_url", notify_url);
        paymentPo.put("trade_type", "JSAPI");
        paymentPo.put("openid", openid);
        String sign = WXUtil.createSign_ChooseWXPay("UTF-8", paymentPo, key);
        paymentPo.put("sign", sign);
        String param = WXUtil.getRequestXml(paymentPo);
        //將參數通過post請求傳給微信端
        String request = WXUtil.httpRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", param);
        Map<String, String> map = new HashMap<String, String>();         // 將解析結果存儲在HashMap中
        InputStream in = new ByteArrayInputStream(request.getBytes());
        SAXReader reader = new SAXReader();                              // 讀取輸入流
        Document document = reader.read(in);
 
        Element root = document.getRootElement();                        // 得到xml根元素
        @SuppressWarnings("unchecked")                                   // 得到根元素的所有子節點
                List<Element> elementList = root.elements();
        for (Element element : elementList) {
            map.put(element.getName(), element.getText());
        }
        SortedMap<Object, Object> result = new TreeMap<Object, Object>();
 
        LOGGER.debug("第一次簽名返回碼" + map.get("return_code"));
        LOGGER.debug("第一次簽名返回結果" + map.get("return_msg"));
        //第一次簽名成功
        if (map.get("return_code").equals("SUCCESS")) {                  // 業務結果
            String nonceStr = WXUtil.generate();
            Long timeStamp = System.currentTimeMillis() / 1000;
            SortedMap<Object, Object> params = new TreeMap<Object, Object>();
 
            params.put("appId", appid);
            params.put("nonceStr", nonceStr);
            params.put("package", "prepay_id=" + map.get("prepay_id"));
            params.put("signType", "MD5");
            params.put("timeStamp", timeStamp);
 
            //第二次簽名成功
            LOGGER.debug("開始第二次簽名");
            String paySign = WXUtil.createSign_ChooseWXPay("UTF-8", params, key);
            result.put("paySign", paySign);
            result.put("timeStamp", timeStamp + "");
            result.put("nonceStr", nonceStr);
            result.put("out_trade_no", paymentPo.get("out_trade_no"));
            result.put("package", "prepay_id=" + map.get("prepay_id"));
            result.put("return_code", "SUCCESS");
        } else {
            result.put("return_code", "Fail");
            result.put("return_msg", map.get("return_msg"));
 
        }
        return result;
    }
}

工具類:WXUtil

public class WXUtil {
     
    private static final Logger LOGGER = LoggerFactory.getLogger(WXUtil.class);
    /**
     * 隨機字符串
     * @return
     */
    public static String generate() {
        return UUID.randomUUID().toString().trim().replaceAll("-", "");
    }
     
    /**
     * 解析xml,返回第一級元素鍵值對。如果第一級元素有子節點,則此節點的值是子節點的xml數據。
     * @param strxml
     * @return
     * @throws JDOMException
     * @throws IOException
     */
    public static Map doXMLParse(String strxml) throws JDOMException, IOException {
        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
 
        if(null == strxml || "".equals(strxml)) {
            return null;
        }
        Map m = new HashMap();
        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        Iterator it = list.iterator();
        while(it.hasNext()) {
            Element e = (Element) it.next();
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if(children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = WXUtil.getChildrenText(children);
            }
            m.put(k, v);
        }
         
        //關閉流
        in.close();
         
        return m;
    }
     
    /**
     * 獲取子結點的xml
     * @param children
     * @return String
     */
    public static String getChildrenText(List children) {
        StringBuffer sb = new StringBuffer();
        if(!children.isEmpty()) {
            Iterator it = children.iterator();
            while(it.hasNext()) {
                Element e = (Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<" + name + ">");
                if(!list.isEmpty()) {
                    sb.append(WXUtil.getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }
        return sb.toString();
    }
    /**
     * 將請求參數轉換爲xml格式的string字符串,微信服務器接收的是xml格式的字符串
     * @param parameters
     * @return
     */
    public static String getRequestXml(SortedMap<Object, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set<Entry<Object, Object>> es = parameters.entrySet();
        Iterator<Entry<Object, Object>> it = es.iterator();
        while (it.hasNext()) {
            Entry<Object, Object> entry = (Entry<Object, Object>) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
             
            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }
 
 
     
    /**
     * sign簽名,必須使用MD5簽名,且編碼爲UTF-8
     * @param characterEncoding
     * @param parameters
     * @return
     */
    public static String createSign_ChooseWXPay(String characterEncoding, SortedMap<Object, Object> parameters, String key) {
        StringBuffer sb = new StringBuffer();
        Set<Entry<Object, Object>> es = parameters.entrySet();
        Iterator<Entry<Object, Object>> it = es.iterator();
        while (it.hasNext()) {
            Entry<Object, Object> entry = (Entry<Object, Object>) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        /** 支付密鑰必須參與加密,放在字符串最後面 */
        sb.append("key=" + key);
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }
 
 
 
 
 
    public static String httpRequest(String requestUrl,String requestMethod,String outputStr){
        // 創建SSLContext
        StringBuffer buffer=null;
        try{
        URL url = new URL(requestUrl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod(requestMethod);
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.connect();
 
        //往服務器端寫內容
        if(null !=outputStr){
            OutputStream os=conn.getOutputStream();
            os.write(outputStr.getBytes("utf-8"));
            os.close();
        }
        // 讀取服務器端返回的內容
        InputStream is = conn.getInputStream();
        InputStreamReader isr = new InputStreamReader(is, "utf-8");
        BufferedReader br = new BufferedReader(isr);
        buffer = new StringBuffer();
        String line = null;
        while ((line = br.readLine()) != null) {
             buffer.append(line);
         }
        }catch(Exception e){
            e.printStackTrace();
        }
        return buffer.toString();
        }   
    public static String urlEncodeUTF8(String source){
        String result=source;
        try {
            result=java.net.URLEncoder.encode(source, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }
         
    /**
     * 退款和企業付款到銀行卡
     */
   public static Map<String, String> doRefund(HttpServletRequest request,String url,String data,String partner,String apiclient_certLocation) throws Exception {
    // p12證書的位置
            // 微信公衆平臺:“微信支付”--》“商戶信息”--》“交易數據”--》“詳情請登錄微信支付商戶平臺查看”(登錄)--》“API安全”--》“API證書”--》“下載證書”
            // 下載證書後將apiclient_cert.p12放在src目錄下面(出於安全考慮,請自行下載自己的證書)
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            String url2 = request.getSession().getServletContext().getRealPath("/")
                    + "cert/" + apiclient_certLocation;
            LOGGER.debug("url2--->"+url2);
            File file=new File(url2);
            FileInputStream instream = new FileInputStream(file);// P12文件目錄
        try {
            keyStore.load(instream, partner.toCharArray());
        } finally {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, partner.toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        try {
            HttpPost httpost = new HttpPost(url); // 設置響應頭信息
            httpost.addHeader("Connection", "keep-alive");
            httpost.addHeader("Accept", "*/*");
            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
//          httpost.addHeader("Host", "api.mch.weixin.qq.com");
            httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpost.addHeader("Cache-Control", "max-age=0");
            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            httpost.setEntity(new StringEntity(data, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(httpost);
            try {
                HttpEntity entity = response.getEntity();
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                EntityUtils.consume(entity);
                return WXUtil.doXMLParse(jsonStr);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }
 
 
    /**
     * 得到公鑰
     * @param request
     * @param url 請求微信端的鏈接
     * @param data//請求的數據
     * @param partner
     * @param apiclient_certLocation
     * @return
     * @throws Exception
     */
    public static Object getPublicKey(HttpServletRequest request,String url,String data,String partner,String apiclient_certLocation) throws Exception {
        // p12證書的位置
        // 微信公衆平臺:“微信支付”--》“商戶信息”--》“交易數據”--》“詳情請登錄微信支付商戶平臺查看”(登錄)--》“API安全”--》“API證書”--》“下載證書”
        // 下載證書後將apiclient_cert.p12放在src目錄下面(出於安全考慮,請自行下載自己的證書)
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        String url2 = request.getSession().getServletContext().getRealPath("/")
                + "cert/" + apiclient_certLocation;
        LOGGER.debug("url2--->"+url2);
        File file=new File(url2);
        FileInputStream instream = new FileInputStream(file);// P12文件目錄
        try {
            keyStore.load(instream, partner.toCharArray());
        } finally {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, partner.toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        try {
            HttpPost httpost = new HttpPost(url); // 設置響應頭信息
            httpost.addHeader("Connection", "keep-alive");
            httpost.addHeader("Accept", "*/*");
            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpost.addHeader("Cache-Control", "max-age=0");
            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            httpost.setEntity(new StringEntity(data, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(httpost);
            try {
                HttpEntity entity = response.getEntity();
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                EntityUtils.consume(entity);
                return jsonStr;
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }
 
    /** 
     * 是否簽名正確,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。 
     * @return boolean 
     */ 
    public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {  
        StringBuffer sb = new StringBuffer();  
        Set es = packageParams.entrySet();  
        Iterator it = es.iterator();  
        while(it.hasNext()) {  
            Entry entry = (Entry)it.next();
            String k = (String)entry.getKey();  
            String v = (String)entry.getValue();  
            if(!"sign".equals(k) && null != v && !"".equals(v)) {  
                sb.append(k + "=" + v + "&");  
            }  
        }  
        sb.append("key=" + API_KEY);  
        //算出摘要  
        String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();  
        String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();  
           
        return tenpaySign.equals(mysign);
    }
 
    public static Map<String, String> doRefund(String string, String xML, String mCH_ID, String cERT) {
        // TODO Auto-generated method stub
        return null;
    }
}

返回結果的工具類:TTResult

public class CommonResult {
    // 定義jackson對象
    private static final ObjectMapper MAPPER = new ObjectMapper();
 
    // 響應業務狀態
    private Integer status;  // 200 代表成功, 500 代表失敗
 
    // 響應消息
    private String msg;
 
    // 響應中的數據
    private Object data;
 
    public static CommonResult build(Integer status, String msg, Object data) {
        return new TTResult(status, msg, data);
    }
 
    public static CommonResult ok(Object data) {
        return new TTResult(data);
    }
     
     
    public static CommonResult ok() {
        return new TTResult(null);
    }
 
    public static CommonResult fail(){
        return new TTResult(500,"fail",null);
    }
     
    public static CommonResult fail(Object data){
        return new TTResult(500,"fail",data);
    }
     
    public TTResult() {
 
    }
 
    public static CommonResult build(Integer status, String msg) {
        return new TTResult(status, msg, null);
    }
     
    public TTResult(Integer status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }
 
    public TTResult(Object data) {
        this.status = 200;
        this.msg = "OK";
        this.data = data;
    }
     
 
    // public Boolean isOK() {
    // return this.status == 200;
    // }
 
    public Integer getStatus() {
        return status;
    }
 
    public void setStatus(Integer status) {
        this.status = status;
    }
 
    public String getMsg() {
        return msg;
    }
 
    public void setMsg(String msg) {
        this.msg = msg;
    }
 
    public Object getData() {
        return data;
    }
 
    public void setData(Object data) {
        this.data = data;
    }
 
    /**
     * 將json結果集轉化爲TTResult對象
     * 
     * @param jsonData
     *            json數據
     * @param clazz
     *            TTResult中的object類型
     * @return
     */
    public static CommonResult formatToPojo(String jsonData, Class<?> clazz) {
        try {
            if (clazz == null) {
                return MAPPER.readValue(jsonData, TTResult.class);
            }
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (clazz != null) {
                if (data.isObject()) {
                    obj = MAPPER.readValue(data.traverse(), clazz);
                } else if (data.isTextual() || data.isNumber()) {
                    obj = MAPPER.readValue(data.asText(), clazz);
                }
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg")
                    .asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }
 
    /**
     * 沒有object對象的轉化
     * 
     * @param json
     * @return
     */
    public static CommonResult format(String json) {
        try {
            return MAPPER.readValue(json, TTResult.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * Object是集合轉化
     * 
     * @param jsonData
     *            json數據
     * @param clazz
     *            集合中的類型
     * @return
     */
    public static CommonResult formatToList(String jsonData, Class<?> clazz) {
        try {
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (data.isArray() && data.size() > 0) {
                obj = MAPPER.readValue(data.traverse(), MAPPER.getTypeFactory()
                        .constructCollectionType(List.class, clazz));
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg")
                    .asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }
}

上面代碼中所涉及到的參數,要去微信官方文檔看說明,每個參數都有解釋的,按照我這個來,微信支付是肯定能跑通的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章