Java微信支付总结

简介: Java微信支付总结

前段时间项目有涉及到微信支付一直没时间总结,今天有空就记录下(微信官方没提供示例代码太坑了= =)

 

    微信支付必须在在微信浏览器内,而且要申请微信支付的接口,才能进行

    微信支付的接口默认是关闭的要进行微信认证

 

 

 

 

 

 

认证地址:https://mp.weixin.qq.com/cgi-bin/settingpage?t=setting/index&action=index&token=1318876044&lang=zh_CN

 

微信提供的接口按调用方式大致可以分为三类:

1  获取票据接口,不需要获取票据就可以直接调用,为访问进一步访问其他接口提供凭据

   /** 获取Oauth的code */

   public final static String OAUTH_GET_CODE = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=STATE#wechat_redirect";

   /** 获取Oauth的openid */

   public final static String OAUTH_GET_OPENID = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";

 

2  外部接口,不需要获取票据就可以直接调用

    /** 统一支付接口 */

    public static final String UNIFIEDORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";

3 内部接口,必须要提供access_token才能继续访问

 微信接口多数都是这种接口

 

先上代码

 

WeixinUti.java

 

package cn.btkj.weixin.util;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import javax.net.ssl.SSLContext;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
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.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionPoolTimeoutException;
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 cn.btkj.weixin.wxpay.util.Configure;
import cn.btkj.weixin.wxpay.util.Util;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
public class WeixinUtil {
    private static int STATUS_CODE = 200;
    private static String APPID = Configure.appID;
    private static String SECRET = Configure.secretID;
    private static String access_token = null;
    private static boolean get_access_token = false;
    private static ArrayList<String> errcode = new ArrayList<String>();
    static {
        errcode.add("40001");
        errcode.add("40014");
        errcode.add("41001");
        errcode.add("42001");
    }
    public static void setAccessToken(String accessToken) {
        access_token = accessToken;
    }
    public static String getAccessToken() {
        while (get_access_token) {
            try {
                Thread.sleep(100);
            } catch (Exception e) {
            }
        }
        return access_token;
    }
    public static JSONObject weixinPostJson(String uri, Object model)
            throws Exception {
        return WeixinUtil.weixinPostJson(uri, model, true);
    }
    public static JSONObject weixinPostJson(String uri, Object model,
            boolean addToken) throws Exception {
        String postUri = uri;
        //请求的url后是否加入accesstoken参数
        if (addToken) {
            postUri = WeixinUtil.addToken(uri, getAccessToken());
        }
        CloseableHttpClient client = null;
        HttpPost request = null;
        CloseableHttpResponse response = null;
        JSONObject json = null;
        try {
            System.setProperty("https.protocols", "SSLv3,SSLv2Hello");
            client = HttpClients.createDefault();
            request = new HttpPost(postUri);
            if (model != null) {
                String params = WeixinUtil.buildEntity(model);
                StringEntity requestEntity = new StringEntity(params, "UTF-8");
                request.addHeader("Content-Type", "text/json");
                request.setEntity(requestEntity);
            }
            response = client.execute(request);
            if (response.getStatusLine().getStatusCode() == STATUS_CODE) {
                HttpEntity entity = response.getEntity();
                String result = EntityUtils.toString(entity, "UTF-8");
                json = JSONObject.fromObject(result);
                if (addToken) {
                    //返回错误代号码说明还获取Token或则获取的Token已过期,需要重新获取
                    if (json.containsKey("errcode")
                            && errcode.contains(json.getString("errcode"))) {
                        WeixinUtil.getToken();
                        json = WeixinUtil.weixinPostJson(uri, model);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            json = null;
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
                if (client != null) {
                    client.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return json;
    }
    public static byte[] weixinGetJson(String uri, boolean addToken)
            throws Exception {
        String getUri = uri;
        if (addToken) {
            getUri = WeixinUtil.addToken(uri, getAccessToken());
        }
        CloseableHttpClient client = null;
        HttpGet request = null;
        CloseableHttpResponse response = null;
        byte[] bytes = null;
        try {
            System.setProperty("https.protocols", "SSLv3,SSLv2Hello");
            client = HttpClients.createDefault();
            request = new HttpGet(getUri);
            response = client.execute(request);
            if (response.getStatusLine().getStatusCode() == STATUS_CODE) {
                HttpEntity entity = response.getEntity();
                bytes = EntityUtils.toByteArray(entity);
                try {
                    String result = new String(bytes);
                    JSONObject json = JSONObject.fromObject(result);
                    if (addToken) {
                        if (json.containsKey("errcode")
                                && errcode.contains(json.getString("errcode"))) {
                            WeixinUtil.getToken();
                            bytes = WeixinUtil.weixinGetJson(uri, addToken);
                        } else {
                        }
                    }
                } catch (Exception e) {
                    return bytes;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            bytes = null;
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
                if (client != null) {
                    client.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return bytes;
    }
    public static String weixinKeyPostXml(String uri, Object xmlObj)
            throws Exception {
        System.out.println("----wxpay key store post url----" + uri);
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        FileInputStream instream = new FileInputStream(new File(
                Configure.getCertLocalPath()));// 加载本地的证书进行https加密传输
        try {
            keyStore.load(instream, Configure.getCertPassword().toCharArray());// 设置证书密码
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } finally {
            instream.close();
        }
        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts
                .custom()
                .loadKeyMaterial(keyStore,
                        Configure.getCertPassword().toCharArray()).build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext, new String[] { "TLSv1" }, null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(sslsf).build();
        // 根据默认超时限制初始化requestConfig
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(10000).setConnectTimeout(30000).build();
        String result = null;
        HttpPost httpPost = new HttpPost(uri);
        // 解决XStream对出现双下划线的bug
        XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8",
                new XmlFriendlyNameCoder("-_", "_")));
        // 将要提交给API的数据对象转换成XML格式数据Post给API
        String postDataXML = xStreamForRequestPostData.toXML(xmlObj);
        Util.log("API,POST过去的数据是:");
        Util.log(postDataXML);
        // 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);
        // 设置请求器的配置
        httpPost.setConfig(requestConfig);
        Util.log("executing request" + httpPost.getRequestLine());
        try {
            HttpResponse response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity, "UTF-8");
        } catch (ConnectionPoolTimeoutException e) {
            e.printStackTrace();
        } catch (ConnectTimeoutException e) {
            e.printStackTrace();
        } catch (SocketTimeoutException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            httpPost.abort();
        }
        return result;
    }
    public static String weixinPostXml(String uri, Object xmlObj)
            throws Exception {
        System.out.println("----wxpay post url----" + uri);
        CloseableHttpClient client = null;
        HttpPost request = null;
        CloseableHttpResponse response = null;
        String result = null;
        try {
            client = HttpClients.createDefault();
            request = new HttpPost(uri);
            if (xmlObj != null) {
                XStream xStreamForRequestPostData = new XStream(new DomDriver(
                        "UTF-8", new XmlFriendlyNameCoder("-_", "_")));
                String postDataXML = xStreamForRequestPostData.toXML(xmlObj);
                System.out.println("----wxpay post data----");
                System.out.println(postDataXML);
                StringEntity requestEntity = new StringEntity(postDataXML,
                        "UTF-8");
                request.addHeader("Content-Type", "text/xml");
                request.setEntity(requestEntity);
            }
            response = client.execute(request);
            if (response.getStatusLine().getStatusCode() == STATUS_CODE) {
                HttpEntity entity = response.getEntity();
                result = EntityUtils.toString(entity, "UTF-8");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (response != null) {
                response.close();
            }
            if (client != null) {
                client.close();
            }
        }
        System.out.println(result);
        return result;
    }
    /**
     * access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。
     * 开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。
     * access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。 
     * @throws Exception
     */
    public static void getToken() throws Exception {
        if (get_access_token) {
            return;
        }
        CloseableHttpClient client = null;
        HttpPost request = null;
        CloseableHttpResponse response = null;
        try {
            get_access_token = true;
            StringBuffer uri = new StringBuffer();
            uri.append("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential");
            uri.append("&appid=").append(APPID);
            uri.append("&secret=").append(SECRET);
            client = HttpClients.createDefault();
            request = new HttpPost(uri.toString());
            request.addHeader("Content-Type", "text/json");
            response = client.execute(request);
            if (response.getStatusLine().getStatusCode() == STATUS_CODE) {
                HttpEntity entity = response.getEntity();
                String result = EntityUtils.toString(entity, "UTF-8");
                JSONObject json = JSONObject.fromObject(result);
                if (json != null && json.containsKey("access_token")) {
                    WeixinUtil.setAccessToken(json.getString("access_token"));
                    System.out.println("############ getToken ############  "
                            + access_token);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            get_access_token = false;
            if (response != null) {
                response.close();
            }
            if (client != null) {
                client.close();
            }
        }
    }
    private static String addToken(String uri, String token) {
        String s = uri;
        if (uri.indexOf("?") == -1) {
            s += "?";
        } else {
            s += "&";
        }
        s = s + "access_token=" + token;
        return s;
    }
    private static String buildEntity(Object model) {
        String s = "";
        if (model == null)
            return s;
        if (model instanceof String) {
            s = model.toString();
        } else if (model instanceof JSONObject) {
            s = model.toString();
        } else {
            s = JSONObject.fromObject(model).toString();
        }
        return s;
    }
    /**
     * 根据byte数组,生成文件
     */
    public static void getFile(byte[] bfile, String filePath, String fileName) {
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        File file = null;
        try {
            File dir = new File(filePath);
            if (!dir.exists() && dir.isDirectory()) {// 判断文件目录是否存在
                dir.mkdirs();
            }
            file = new File(filePath + fileName);
            if (!file.exists()) {
                file.createNewFile();
                file.setReadable(true, false);
                file.setWritable(true, false);
            }
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            bos.write(bfile);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }
    public static void main(String args[]) throws Exception {
        String url = "http://file.api.weixin.qq.com/cgi-bin/media/get?media_id=JbgA1hhyUoxqAALAIxrqDkwpqUZuJiEs3LKAQLs-Tx5AIryJUucHeZQjmrVrlssG";
        byte[] bytes = WeixinUtil.weixinGetJson(url, true);
        WeixinUtil.getFile(bytes, "D://", "2.jpg");
    }
}

 

Configure.java

package cn.btkj.weixin.util;
import cn.btkj.utils.PropertiesUtil;
/**
 * 
 * @author lzh
 * 放置各种配置数据
 */
public class Configure {
    /** 用户管理 */
    /** 获取用户基本信息 https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN */
    public final static String USER_INFO = "https://api.weixin.qq.com/cgi-bin/user/info?openid=%s&lang=%s";
    /** JS-SDK API */
    public final static String JSAPI = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi";
    /** 获取Oauth的code */
    public final static String OAUTH_GET_CODE = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=STATE#wechat_redirect";
    /** 获取Oauth的openid */
    public final static String OAUTH_GET_OPENID = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
    // 这个就是自己要保管好的私有Key了(切记只能放在自己的后台代码里,不能放在任何可能被看到源代码的客户端程序中)
    // 每次自己Post数据给API的时候都要用这个key来对所有字段进行签名,生成的签名会放在Sign这个字段,API收到Post数据的时候也会用同样的签名算法对Post过来的数据进行签名和验证
    // 收到API的返回的时候也要用这个key来对返回的数据算下签名,跟API的Sign数据进行比较,如果值不一致,有可能数据被第三方给篡改
    private static String key = "F00CCDADC245E17B2A663F78A7560097";
    //微信分配的公众号ID(开通公众号之后可以获取到)
    public static String appID =  PropertiesUtil.getConfigProperty().getProperty("weixin.appid");
    public static String secretID  = PropertiesUtil.getConfigProperty().getProperty("weixin.secret");
    //微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到)
    public static String mchID = PropertiesUtil.getConfigProperty().getProperty("weixin.mchID");
    //受理模式下给子商户分配的子商户号
    private static String subMchID = "";
    //HTTPS证书的本地(服务器)路径
    private static String certLocalPath = "/var/tomcat/tomcat6/cert/apiclient_cert.p12";
    //HTTPS证书密码,默认密码等于商户号MCHID
    private static String certPassword = "1238623202";
    //是否使用异步public线程的方式来上报API测速,默认为异步模式
    private static boolean useThreadToDoReport = true;
    //机器IP
    private static String ip = "";
    /** 统一支付接口 */
     public static final String UNIFIEDORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    public static boolean isUseThreadToDoReport() {
        return useThreadToDoReport;
    }
    public static void setUseThreadToDoReport(boolean useThreadToDoReport) {
        Configure.useThreadToDoReport = useThreadToDoReport;
    }
    public static String HttpsRequestClassName = "com.wxpay.util.HttpsRequest";
    public static void setKey(String key) {
        Configure.key = key;
    }
    public static void setAppID(String appID) {
        Configure.appID = appID;
    }
    public static void setMchID(String mchID) {
        Configure.mchID = mchID;
    }
    public static void setSubMchID(String subMchID) {
        Configure.subMchID = subMchID;
    }
    public static void setCertLocalPath(String certLocalPath) {
        Configure.certLocalPath = certLocalPath;
    }
    public static void setCertPassword(String certPassword) {
        Configure.certPassword = certPassword;
    }
    public static void setIp(String ip) {
        Configure.ip = ip;
    }
    public static String getKey(){
        return key;
    }
    public static String getAppid(){
        return appID;
    }
    public static String getMchid(){
        return mchID;
    }
    public static String getSubMchid(){
        return subMchID;
    }
    public static String getCertLocalPath(){
        return certLocalPath;
    }
    public static String getCertPassword(){
        return certPassword;
    }
    public static String getIP(){
        return ip;
    }
    public static void setHttpsRequestClassName(String name){
        HttpsRequestClassName = name;
    }
}


以上是访问微信接口所要用到的工具类

 

如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。

 

微信支付基本的流程是:

 

1获取code   (详见http://mp.weixin.qq.com/wiki/4/9ac2e7b1f1d22e9e57260f6553822520.html)

2通过code换取网页授权access_token和openid

 

3因为是通过微信JS-SDK发起微信支付所以登入前要获取jsapi_ticket

jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket

(详见 http://mp.weixin.qq.com/wiki/11/74ad127cc054f6b80759c40f77ec03db.html#.E5.88.A4.E6.96.AD.E5.BD.93.E5.89.8D.E5.AE.A2.E6.88.B7.E7.AB.AF.E7.89.88.E6.9C.AC.E6.98.AF.E5.90.A6.E6.94.AF.E6.8C.81.E6.8C.87.E5.AE.9AJS.E6.8E.A5.E5.8F.A3)

 


相关文章
|
6天前
|
人工智能 移动开发 小程序
Java医院智慧3D导诊系统源码 微信小程序源码
技术架构:springboot+redis+mybatis plus+mysql+RocketMQ
67 1
|
6天前
|
小程序 安全 Java
【Java】智慧校园云SaaS平台源码带微信小程序
【Java】智慧校园云SaaS平台源码带微信小程序
90 0
|
6天前
|
缓存 NoSQL Java
java中实现定时给微信群中发送每日简报
java中实现定时给微信群中发送每日简报
48 0
|
7月前
|
Java
Java实现微信支付
Java实现微信支付
512 2
|
6天前
|
编解码 小程序 Java
【Java】基于云计算-智慧校园电子班牌系统源码带原生微信小程序端
【Java】基于云计算-智慧校园电子班牌系统源码带原生微信小程序端
81 0
|
6天前
|
Java
java发送微信公众号模板消息
java发送微信公众号模板消息
|
6天前
|
JavaScript 小程序 Java
Java智慧校园系统源码springboot + vue智慧学校源码 微信小程序+电子班牌
智慧校园的建设逐渐被师生、家长认可接受,智慧校园通过对在校师生、教务等所有人员的信息以及各种信息搜集与储存,进行数据优化与管理,为师生们提供更加智能化的校园服务。未来智慧校园将不再是一个陌生词,而会真正地应用在更多的校园管理中,让我们的校园生活变得更加美好
34 2
|
6天前
|
人工智能 小程序 Java
Java智慧校园系统源码 微信小程序+电子班牌
通过设备管理对百纳智慧校园的智慧班牌以及百纳智慧屏(校牌)进行统一集中式管理,支持浏览所有设备的基本信息以及在离线状态,支持添加设备、设备一键开关机、一键重启、设置节假日开关机时间、设置日常开关机时间、远程班牌截屏、远程班牌升级等操作。
24 0
|
6天前
|
Java
java中实现定时给微信群中发送每日天气情况
java中实现定时给微信群中发送每日天气情况
23 0
|
6天前
|
安全 小程序 Java
java实现微信服务(公众)号用户关注时,获取openid,安全模式下的加密解密实现
java实现微信服务(公众)号用户关注时,获取openid,安全模式下的加密解密实现
32 0