java版微信支付

前言:

由于最近公司项目需要接入网页版微信支付,我在官网上查询了很久,发现微信官网只提供了扫码支付等java demo,或者只有php等其它语言的微信支付,只提供了支付的一些API,所以这里需要自己根据官网提供的api自己写支付。

1,请先查看微信支付的流程图,了解下微信支付的过程。



1,在手机微信上打开一个h5页面(微信已经提供桌面版微信了,我们可以直接在桌面上面调试更加方便)

2,生成一个JAVA的订单接口,然后返回一个我们自己的订单号。

3,将订单号、商品信息、价格等信息拼接生成统一下单接口获得prepay_id

注:调用下单接口时需要先获取openid

     3.1)调用用户授权接口获得code值

     3.2)将得到的code的值去获取openid

     3.3)根据得到的openid获得prepay_id

4,用户点击支付,获取用户授权

5,授权成功进入微信端支付界面进行支付操作

6,支付成功后回调自己配置界面,微信端通知后端服务器

-----------------------------------------------------------------------------------------------------------------------------------------------------

进入正题

1,如何申请成为公众号,申请成为支付服务号这些这里就不提了

2,配置微信公众平台服务号(记住是进入服务号账号)

    2.1)进入微信公众平台,点击微信支付按钮(如图)

    2.2)添加你的支付授权测试目录跟正式授权支付目录、并且添加测试微信号

注:授权目录一定是支付页面的上级目录,如:我的支付页面为:http://www.baidu.com/test/pay/index.jsp,那么我的授权目录是http://www.baidu.com/test/pay/

注意是以/结束。

     2.3)进入微信开发者中心,找到网页授权获取用户基本信息添加网页授权域名(如www.baidu.com,不需要带http://,也不能是ip地址)

3,根据微信支付需要的配置去获取对应的信息

    3.1)appid公众号APPID、mch_id微信支付商户号在微信申请成功后发送到邮箱获取

    3.2)应用密钥AppSecret在开发者中心->配置项->开发者ID查看

   3.3)商户支付密钥Key以及证书路径 在微信支付商户平台,在【账户设置-密码安全-API安全】中下载以及设置

   3.4)配置支付页面已经支付回调页面(www.test.com/pay/testPay/index.jsp)






---------------------------------------------------------------------------------------------------------------

代码部分

1,支付配置页面

package com.weixin.config;


public class WeixinConfig {
//=======【基本信息设置】=====================================
//微信公众号身份的唯一标识。审核通过后,在微信发送的邮件中查看
public static final String APPID = "wxb69e254afdb3fae4";
//受理商ID,身份标识
public static final String MCHID = "1246043803";
//商户支付密钥Key。审核通过后,在微信发送的邮件中查看
public static final String KEY = "qwertyuioplkjhgfdsazxcvbnm125871";
//JSAPI接口中获取openid,审核后在公众平台开启开发模式后可查看
public static final String APPSECRET = "d896864455e000ea86e722e95e34d2d3";
//=======【JSAPI路径设置】===================================
//获取access_token过程中的跳转uri,通过跳转将code传入jsapi支付页面
public static final String JS_API_CALL_URL = "http://www.weixin.com/weixin/webApp/pay/index.jsp";
//=======【证书路径设置】=====================================
//证书路径,注意应该填写绝对路径
public static final String SSLCERT_PATH = "/home/wwwroot/weixin/webApp/WxPayPubHelper/cacert/apiclient_cert.pem";
public static final String SSLKEY_PATH = "/home/wwwroot/weixin/webApp/WxPayPubHelper/cacert/apiclient_key.pem";

//=======【异步通知url设置】===================================
//异步通知url,商户根据实际开发过程设定
public static final String NOTIFY_URL = "http://www.weixin.com/weixin/webApp/pay/notify_url.jsp";


//=======【http超时设置】===================================
//使用HTTP POST方法,此处可修改其超时时间,默认为30秒
public static final int POST_TIMEOUT = 30000;
//统一下单接口
public static final String UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
}


2,支付方法页面

package com.weixin.commons;


import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;


import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;


import com.golf.utils.MD5Utils;
import com.weixin.config.WeixinConfig;


public class WeixinCommon {
private Logger logger = Logger.getLogger(WeixinCommon.class);
private String code;
private String prepay_id;
public static void main(String args[]){
WeixinCommon common = new WeixinCommon();
String url = common.createOauthUrlForCode(WeixinConfig.JS_API_CALL_URL);
System.out.println(url);
//common.httpRequest(url);
//System.out.println(common.createOauthUrlForCode("http://local/indx.html"));

}
/**
* 写日志
*/
public void logger(String msg){
logger.info(msg);
}
/**
* 作用:通过http向微信提交code,以获取openid
*   1、发送http请求
*   2、格式化json数据
*   3、获取openid
*/
public String getOpenid(){
String url = createOauthUrlForOpenid();
String open_id = "";
ObjectMapper mapper = new ObjectMapper();
try {
String response_msg = httpGetRequest(url);
logger("getOpenid method is response_msg: "+response_msg);
if(!"".equals(response_msg)){
JsonNode node = mapper.readTree(response_msg);
JsonNode child_node = node.get("openid");
open_id = child_node.getTextValue();
}
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return open_id;
}

public String httpGetRequest(String url) {
HttpClient client = new HttpClient();
String response_msg = "";
GetMethod get = new GetMethod(url);
client.getHttpConnectionManager().getParams().setConnectionTimeout(WeixinConfig.POST_TIMEOUT);
//get.setRequestHeader("Content-Type", "text/html; charset=utf-8");
try {
int success = client.executeMethod(get);
if(success > 0){
response_msg = get.getResponseBodyAsString();
logger("httpGetRequest method response_msg:"+response_msg);
}

} catch (HttpException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(get != null){
get.releaseConnection();
}
}


return response_msg;
}

public String httpPostRequest(String content,String url) {
HttpClient client = new HttpClient();
String response_msg = "";
PostMethod post = new PostMethod(url);
//post.setQueryString(content);
//post.setRequestBody(content);
RequestEntity requestEntity = new StringRequestEntity(content);
post.setRequestEntity(requestEntity);
post.setRequestHeader("Content-type", "text/xml; charset=utf-8");
client.getHttpConnectionManager().getParams().setConnectionTimeout(WeixinConfig.POST_TIMEOUT);  
try {
int success = client.executeMethod(post);
if(success > 0){
response_msg = post.getResponseBodyAsString();
logger("httpPostRequest method response_msg:"+response_msg);
}

} catch (HttpException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(post != null){
post.releaseConnection();
}
}

return response_msg;
}
/**
* 作用:生成可以获得openid的url
*/
public String createOauthUrlForOpenid()
{
SortedMap<String,Object> url_map = new TreeMap<String,Object>();
url_map.put("appid", WeixinConfig.APPID);
url_map.put("secret", WeixinConfig.APPSECRET);
url_map.put("code", this.code);
url_map.put("grant_type", "authorization_code");
String bizString = formatBizQueryParaMap(url_map);
return "https://api.weixin.qq.com/sns/oauth2/access_token?"+bizString;
}
/**
* 作用:生成可以获得code的url
*/
public String createOauthUrlForCode(String redirectUrl){
SortedMap<String,Object> url_map = new TreeMap<String,Object>();
url_map.put("redirect_uri", redirectUrl);
url_map.put("appid", WeixinConfig.APPID);
url_map.put("scope", "snsapi_base");
url_map.put("response_type", "code");

//url_map.put("scope", "snsapi_userinfo");
url_map.put("state", "STATE"+"#wechat_redirect");
String biz_string = formatBizQueryParaMap(url_map);
String code = "https://open.weixin.qq.com/connect/oauth2/authorize?"+biz_string;
logger("request code is :"+code);
return code;
}

/**
* 作用:格式化参数,签名过程需要使用
*  将参数按照ASCII字母顺序升序排序
*/
private String formatBizQueryParaMap(SortedMap<String,Object> url_map){
StringBuilder builder = new StringBuilder();
String str = "";
for(Map.Entry<String, Object> entry : url_map.entrySet()){
builder.append("&"+entry.getKey()+"="+entry.getValue());
}
if(builder.length() > 0){
str = builder.toString().substring(1);
}
return str;
}

/**
* 获取预支付prepayId
* @return
*/
public String getPrepayId(SortedMap<String,Object> sorted_map)
{
String response = postXml(sorted_map);
Map<String,Object> result = xmlToArray(response);
String prepay_id = String.valueOf(result.get("prepay_id"));
return prepay_id;
}
/**
* 作用:将xml转为array
*/
public Map<String,Object> xmlToArray(String xml)
{
Map<String,Object> map_value = new HashMap<String,Object>();
Document document;
try {
document = DocumentHelper.parseText(xml);
Element root_element = document.getRootElement();  
List<Element> child_element = root_element.elements();
for(Element ele : child_element){
map_value.put(ele.getName(), ele.getText());
}

} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return map_value;
}




/**
* 作用:post请求xml 调用统一下单接口
*/
private String postXml(SortedMap<String,Object> sorted_map)
{
   String xml = createXml(sorted_map);
return httpPostRequest(xml,WeixinConfig.UNIFIED_ORDER);
}

/**
* 作用:设置标配的请求参数,生成签名,生成接口参数xml
*/
private String createXml(SortedMap<String,Object> sorted_map)
{
   //$this->parameters["sign"] = $this->getSign($this->parameters);//签名
sorted_map.put("appid", WeixinConfig.APPID);
sorted_map.put("mch_id", WeixinConfig.MCHID);
sorted_map.put("nonce_str", createNoncestr());
sorted_map.put("sign", getSign(sorted_map));
String xml = arrayToXml(sorted_map);
logger("create prepay request parameters is :"+xml);
   return  xml;
}

/**
* 作用:产生随机字符串,不长于32位
*/
public String createNoncestr() 
{
String chars = "abcdefghijklmnopqrstuvwxyz0123456789";  
String str ="";
int current_str_length = 0;
Random random = new Random();
for ( int i = 0; i < 32; i++ )  {
current_str_length = random.nextInt(chars.length()-1);
str+=  chars.substring(current_str_length,current_str_length+1);
}
return str;
}

/**
* 作用:生成签名
*/
public String getSign(SortedMap<String,Object> value_map)
{
//将值按照key=value的形式格式化为字符串
String str= formatBizQueryParaMap(value_map);
//签名步骤二:在string后加入KEY
str = str+"&key="+WeixinConfig.KEY;
//签名步骤三:MD5加密
str = MD5Utils.encrypt(str);
//签名步骤四:所有字符转为大写
return str.toUpperCase();
}
/**
* 作用:array转xml
*/
private String arrayToXml(SortedMap<String,Object> map_value)
    {
        String xml = "<xml>";
        
        for(Map.Entry<String, Object> entry : map_value.entrySet()){
        if(entry.getValue() instanceof Integer){
        xml+="<"+entry.getKey()+">"+entry.getValue()+"</"+entry.getKey()+">"; 
        }else{
        xml+="<"+entry.getKey()+"><![CDATA["+entry.getValue()+"]]></"+entry.getKey()+">";  
        }
        }
        
        xml +="</xml>";
        return xml; 
    }

/**
* 作用:设置jsapi的参数
*/
public Object getParameters()
{
SortedMap<String,Object> order_map = new TreeMap<String,Object>();
order_map.put("appId", WeixinConfig.APPID);
order_map.put("timeStamp", System.currentTimeMillis()/1000);
order_map.put("nonceStr", createNoncestr());
order_map.put("package", "prepay_id="+prepay_id);
order_map.put("signType", "MD5");
order_map.put("paySign", getSign(order_map));
ObjectMapper mapper = new ObjectMapper();
String jsonObject =  "";
try {
jsonObject = mapper.writeValueAsString(order_map);
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
logger("order pay info is : "+jsonObject);
return jsonObject;
}

public void  setCode(String code)
{
this.code = code;
}
public void setPrepay_id(String prepay_id) {
this.prepay_id = prepay_id;
}


}

3,JSP支付页面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.weixin.config.*"%>
<%@ page import="com.weixin.commons.*"%>
<%@ page import="java.net.URLEncoder" %>
<%@ page import="java.util.TreeMap"%>
<%@ page import="java.util.SortedMap"%>
<%
WeixinCommon common = new WeixinCommon();
String parameters = request.getParameter("parameters");
String code = request.getParameter("code");
common.logger("page enter and parameters is :"+parameters +" and code is :"+code);
if(parameters == null || "".equals(parameters)){
return ;
}
String opendid = "";
if(code == null || "".equals(code)){
String parameter_path = "?parameters="+parameters;
common.logger("request code path is : "+parameter_path);
//触发微信返回code码
String url = common.createOauthUrlForCode(WeixinConfig.JS_API_CALL_URL+parameter_path);
response.sendRedirect(url);  
      return;
}else{
//获取code码,以获取openid
common.setCode(code);
opendid = common.getOpenid();
common.logger("code is exist and get opendid is :"+opendid);
}
//由于在我测试的时候如果传入多个参数会出现参数遗漏的情况,
//所以这里我把所有的参数按照一定的规则以逗号隔开处理,然后统一获取
//如果大家未遇到这种情况可以忽略

String[] param = parameters.split(",");
String out_trade_no = param[0];
    String out_trade_name = URLEncoder.encode(param[1],"UTF-8");
    String out_trade_fee = param[2];
    
    common.logger("out_trade_no is :"+out_trade_no+" and out_trade_name is:"+out_trade_name+" and out_trade_fee is :"+out_trade_fee);
    //=========步骤2:使用统一支付接口,获取prepay_id============
SortedMap<String,Object> order_map = new TreeMap<String,Object>();
order_map.put("openid",opendid);//商品描述
order_map.put("body",out_trade_name);//商品描述
order_map.put("out_trade_no",out_trade_no);//商户订单号 
order_map.put("total_fee",out_trade_fee);//总金额
order_map.put("notify_url",WeixinConfig.NOTIFY_URL);//通知地址 
order_map.put("trade_type","JSAPI");//交易类型
String prepay_id = common.getPrepayId(order_map);
common.logger("current prepay_id is : "+prepay_id);
common.setPrepay_id(prepay_id);

//=========步骤3:使用jsapi调起支付============

 %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">




<html>
<head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=2.0, user-scalable=yes" />
    <style type="text/css">
    body{
    margin:0px;
    padding:0px;
    }
    .title{
    margin-top: 12%;
text-align: center;
font-size: 1.5em;
font-weight: bold;
    }
    .price{
    margin-top: 5%;
text-align: center;
font-size: 3em;
font-weight: bold;
    }
    .line{
    margin-top: 10%;
    height:1px;
    background-color:#969696;
    margin-bottom: 10%;
    }
    .rec{
    float:left;
    font-weight: 600;
    color: #7c7c7c;
    margin-left:2%;
    }
    .com{
    float:right;
    font-weight: 600;
    margin-right:2%;
    }
    .clear{
    clear:both;
    }
    .submit{
    text-align: center;
    }
    .submit span{
    text-decoration: center;
    display:block;
    border-radius: 5px;
    width:90%;
    height:50px;
    line-height: 50px;
    margin-left:5%;
    background-color: #06be04;
    color:#fff;
    font-weight: bold;
    }
    </style>
    <title>微信安全支付</title>
<script type="text/javascript">


//调用微信JS api 支付
function jsApiCall()
{
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
<%out.println(common.getParameters());%>
,
function(res){
//WeixinJSBridge.log(res.err_msg);
alert(res.err_msg);
                    if(res.err_msg == "get_brand_wcpay_request:ok"){
                          window.location.href="call_back.html";
                     }
}
);
}


function callpay()
{
if (typeof WeixinJSBridge == "undefined"){
   if( document.addEventListener ){
       document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
   }else if (document.attachEvent){
       document.attachEvent('WeixinJSBridgeReady', jsApiCall); 
       document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
   }
}else{
   jsApiCall();
}
}
</script>
</head>
<body  οnlοad="callpay()">
</body>
</html>

-------------------------------------------------------------------------------------------------------------------

到这里,一个支付的接口就完成了,我这个只是简单的实现支付,里面的异常判断,一些逻辑优化就需要大家自己去优化了。因为微信提供的错误信息比较少,而且必须在微信客户端测试,所以大家在测试的时候最好是每走一步就打印一个日志,这样可以方便查询到底是哪里出错了,如果是走到最后一步出现get_brand_wcpay_request:fail 一般情况就是配置不对,需要仔细的查看每一个配置,或者是在调用支付接口的时候参数值不对。每获取一个微信提供的值成功后,则代表你这步已经成功了,不然微信不会返回正确的信息给你。

demo下载地址:http://download.csdn.net/detail/xuelinmei_happy/8926681

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值