您的位置:首页 > 移动开发 > 微信开发

java版微信支付

2015-07-23 16:12 603 查看
前言:

由于最近公司项目需要接入网页版微信支付,我在官网上查询了很久,发现微信官网只提供了扫码支付等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 onload="callpay()">

</body>

</html>

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

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

demo下载地址:http://download.csdn.net/detail/xuelinmei_happy/8926681
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: