微信支付 Java后台接口

第一:首先有两个条件:

1.在微信开放平台成为微信开发者;https://open.weixin.qq.com/

2.申请微信支付,获取商户信息。https://pay.weixin.qq.com

第二:大致步骤:

获取金额->构造商户自己的订单->签名认证->服务端请求微信获取微信订单->构造移动端所需参数->移动端客户支付->微信回调->商户自己的业务逻辑实现

第三:具体实现:

  • 构造移动端所需参数
/**
 * 构造客户端所需的参数(这里是业务层代码实现,控制层不再罗列)
 * return 返回map的JSON串
 */
public String getOnlinePayInfo(HttpServletRequest request) throws Exception {
    String orderId = "orderId"; // 这里构造自己的订单
    String totalFee = request.getParameter("totalFee"); //金额由客户端获取还是服务端生成根据自己的需求而定
    String noncestr = WeChatUtil.getRandomString();
    String notify_url = Config.ReadStringPropertie("serverPath") + "/" + request.getContextPath()
                    + "/onlinePay/callBackWXpay";
    int fee = (int) (Float.parseFloat(totalFee + "F") * 100); // 微信支付的金额,微信支付金额的单位是分,这里需要自行转换
   SortedMap<String, Object> reqMap = new TreeMap<>();
            reqMap.put("appid", WeChatConfig.AppId); // APPID
            reqMap.put("mch_id", WeChatConfig.MchId); // 商户ID
            reqMap.put("nonce_str", noncestr); // 32位随机字符串
            reqMap.put("body","我要支付"); // 商品描述
            reqMap.put("out_trade_no", orderId); //商户系统内部的订单号,
            reqMap.put("total_fee", fee + ""); //订单总金额,微信支付的单位为分
            reqMap.put("spbill_create_ip", WeChatUtil.getHostIp()); //用户端实际ip
            reqMap.put("notify_url", notify_url); //回调通知地址
            reqMap.put("trade_type", "APP"); //交易类型
            reqMap.put("sign", WeChatUtil.createSign(WeChatConfig.input_charset, reqMap));// 签名
    // 请求微信,目的是获取prepay_id,必需先转换成xml格式;需要对xml进行转码,不然会报编码格式的问题
            String retStr = HttpClientUtil.postHttplient(WeChatConfig.PrepayUrl, new String(WeChatUtil.getRequestXml(reqMap).toString().getBytes("utf-8")));

            // 构造客户端需要的参数
            Map wxpayRet = WeChatUtil.doXMLParse(retStr);
            SortedMap<String, Object> retMap = new TreeMap<>();
            if (wxpayRet.get("return_code").equals("SUCCESS")) {
                retMap.put("appid", WeChatConfig.AppId);
                retMap.put("noncestr", noncestr);
                retMap.put("partnerid", WeChatConfig.MchId);
                retMap.put("prepayid", wxpayRet.get("prepay_id"));
                retMap.put("package", "Sign=WXPay");
                retMap.put("timestamp", System.currentTimeMillis() / 1000);  // 时间戳为10位
                retMap.put("sign", WeChatUtil.createSign(WeChatConfig.input_charset, retMap));
            }
            return JSONUtil.writeMapJSON(retMap);

}
  • 移动端支付后的回调
   /**
     * 微信支付的回调方法
     *
     * @param request  请求
     * @param response 响应
     */
    @RequestMapping(value = "/callBackWXpay", method = {RequestMethod.GET, RequestMethod.POST})
    public synchronized void callBackWXpay(HttpServletRequest request, HttpServletResponse response) {
        SortedMap<String, Object> ret = new TreeMap<>();
        try {
            if (service.callBackWXpay(request)) {
                ret.put("return_code", "SUCCESS");
                ret.put("return_msg", "OK");
                response.getWriter().write(WeChatUtil.getRequestXml(ret));
            } else {
                ret.put("return_code", "FALSE");
                ret.put("return_msg", "支付失败");
                response.getWriter().write(WeChatUtil.getRequestXml(ret));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // service中callBackWXpay的实现
    @Override
    public boolean callBackWXpay(HttpServletRequest request) throws Exception {
        String inputLine;
        String notityXml = "";
        Map<String, String> retMap = new HashMap();

        // 取出回调中的数据
        while (null != (inputLine = request.getReader().readLine())) {
            notityXml += inputLine;
        }
        request.getReader().close();
        Map<String, String> responseMap = WeChatUtil.doXMLParse(notityXml);
        String return_code = responseMap.get("return_code");
        String orderId = responseMap.get("out_trade_no"); // 商户订单号
        // 验证签名,判断支付是否成功
        if (WeChatUtil.checkSign(responseMap)) {
            if (StringUtils.isNotBlank(return_code) && return_code.equals("SUCCESS")) {
                // 这里处理支付成功的业务逻辑
                    return true;
                }
            }
        } else {
            // 这里处理支付失败的业务逻辑
        }
        return false;
    }
  • 用到的工具类
/**
 * 微信支付的配置信息
 * Created by zhangcg on 2017/4/5.
 */
public class WeChatConfig {

    /**
     * 预支付请求地址
     */
    public static final String  PrepayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    /**
     * 查询订单地址
     */
    public static final String  OrderUrl = "https://api.mch.weixin.qq.com/pay/orderquery";

    /**
     * 关闭订单地址
     */
    public static final String  CloseOrderUrl = "https://api.mch.weixin.qq.com/pay/closeorder";

    /**
     * 申请退款地址
     */
    public static final String  RefundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";

    /**
     * 查询退款地址
     */
    public static final String  RefundQueryUrl = "https://api.mch.weixin.qq.com/pay/refundquery";

    /**
     * 下载账单地址
     */
    public static final String  DownloadBillUrl = "https://api.mch.weixin.qq.com/pay/downloadbill";

    /**
     * 商户APPID
     */
    public static final String  AppId = "***";

    /**
     * 商户账户 获取支付能力后,从邮件中得到
     */
    public static final String  MchId = "***";

    /**
     * 商户秘钥  32位,在微信商户平台中设置
     */
    public static final String  AppSercret = "***";

    /**
     * 编码格式
     */
    public static final String input_charset = "UTF-8";

    /**
     * 商品描述
     */
    public static final String body ="***";

}
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

/**
 * 微信支付的工具类
 * Created by zhangcg on 2017/4/5.
 */
public class WeChatUtil {


    /**
     * 组装请求的xml
     *
     * @param parameters 集合参数
     * @return
     */
    public static String getRequestXml(SortedMap<String, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if ("sign".equalsIgnoreCase(k)) {

            } else if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k)) {
                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }
        }
        sb.append("<" + "sign" + ">" + "<![CDATA[" + parameters.get("sign") + "]]></" + "sign" + ">");
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * 得到随机字符串
     *
     * @return
     */
    public static String getRandomString() {
        int length = 32;
        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();

        for (int i = 0; i < length; ++i) {
            int number = random.nextInt(62);//[0,62)
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }

    /**
     * 定义签名,微信根据参数字段的ASCII码值进行排序 加密签名,故使用SortMap进行参数排序
     *
     * @param characterEncoding 编码格式
     * @param parameters        集合参数
     * @return 生成带签名的串
     */
    public static String createSign(String characterEncoding, SortedMap<String, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if (null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + WeChatConfig.AppSercret);//最后加密时添加商户密钥,由于key值放在最后,所以不用添加到SortMap里面去,单独处理,编码方式采用UTF-8
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }

    public static boolean checkSign(Map<String, String> map) {
        String charset = "UTF-8";
        String signFromAPIResponse = map.get("sign");
        if (null == signFromAPIResponse || "".equals(signFromAPIResponse)) {
            return false;
        }
        SortedMap<String, String> packageParams = new TreeMap<>();
        for (String parameter : map.keySet()) {
            String parameterValue = map.get(parameter);
            String v = "";
            if (null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }

        StringBuffer stringBuffer = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (!"sign".equals(k) && null != v && !"".equals(v)) {
                stringBuffer.append(k + "=" + v + "&");
            }
        }
        stringBuffer.append("key=" + WeChatConfig.AppSercret);
        String resultSign = "";
        String tobesign = stringBuffer.toString();
        if (null == charset || "".equals(charset)) {
            resultSign = MD5Util.MD5Encode(tobesign, WeChatConfig.input_charset).toUpperCase();
        } else {
            resultSign = MD5Util.MD5Encode(tobesign, WeChatConfig.input_charset).toUpperCase();
        }
        String tenpaySign = (packageParams.get("sign")).toUpperCase();
        return tenpaySign.equals(resultSign);
    }

    /**
     * 解析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 = getChildrenText(children);
            }

            m.put(k, v);
        }

        //关闭流
        in.close();

        return m;
    }

    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(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }
        return sb.toString();
    }

    /**
     * 得到本地机器的IP
     *
     * @return
     */
    public static String getHostIp() {
        String ip = "";
        try {
            ip = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return ip;
    }
}
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.security.KeyStore;
import java.util.Map;

/**
 * Created by zhangcg on 2017/4/5.
 */
public class HttpClientUtil {

    public static String post(String url, Map<String, String> headMap, Map<String, String> params) {
        try {
            HttpClient httpclient = new HttpClient();
            httpclient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8");
            PostMethod httpPost = new PostMethod(url);
            if (null != headMap) {
                for (String key : headMap.keySet()) {
                    httpPost.setRequestHeader(key, headMap.get(key));
                }
            }

            if (null != params) {
                for (String pkey : params.keySet()) {
                    httpPost.addParameter(pkey, params.get(pkey));
                }
            }
            httpclient.executeMethod(httpPost);

            BufferedReader reader = new BufferedReader(new InputStreamReader(httpPost.getResponseBodyAsStream()));
            StringBuffer stringBuffer = new StringBuffer();
            String str = "";
            while ((str = reader.readLine()) != null) {
                stringBuffer.append(str);
            }
            reader.close();
            return stringBuffer.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String postHttplient(String url, String xmlInfo) {
        try {
            HttpClient httpclient = new HttpClient();
            httpclient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8");
            PostMethod httpPost = new PostMethod(url);
            httpPost.setRequestEntity(new StringRequestEntity(xmlInfo));
            httpclient.executeMethod(httpPost);

            BufferedReader reader = new BufferedReader(new InputStreamReader(httpPost.getResponseBodyAsStream()));
            StringBuffer stringBuffer = new StringBuffer();
            String str = "";
            while ((str = reader.readLine()) != null) {
                stringBuffer.append(str);
            }
            reader.close();
            return stringBuffer.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String postHttplientNeedSSL(String url, String xmlInfo, String cretPath, String mrchId)
            throws Exception {
        //选择初始化密钥文件格式
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        //得到密钥文件流
        FileInputStream instream = new FileInputStream(new File(cretPath));
        try {
            //用商户的ID 来解读文件
            keyStore.load(instream, mrchId.toCharArray());
        } finally {
            instream.close();
        }
        //用商户的ID 来加载
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mrchId.toCharArray()).build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        //用最新的httpclient 加载密钥
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        StringBuffer ret = new StringBuffer();
        try {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(new StringEntity(xmlInfo));
            CloseableHttpResponse response = httpclient.execute(httpPost);
            try {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
                    String text;
                    while ((text = bufferedReader.readLine()) != null) {
                        ret.append(text);
                    }
                }
                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
        return ret.toString();
    }
}
import java.security.MessageDigest;

/**
 * 微信MD5加密工具
 * Created by zhangcg on 2017/4/5.
 */
public class MD5Util {

    private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++)
            resultSb.append(byteToHexString(b[i]));

        return resultSb.toString();
    }

    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0)
            n += 256;
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    public static String MD5Encode(String origin, String charsetname) {
        String resultString = null;
        try {
            resultString = new String(origin);
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charsetname == null || "".equals(charsetname))
                resultString = byteArrayToHexString(md.digest(resultString
                        .getBytes()));
            else
                resultString = byteArrayToHexString(md.digest(resultString
                        .getBytes(charsetname)));
        } catch (Exception exception) {
        }
        return resultString;
    }

    private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};

}

 

转载于:https://my.oschina.net/rampageangle/blog/877411

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值