微信公众号支付
一、设置支付目录
请确保实际支付时的请求目录与后台配置的目录一致,否则将无法成功唤起微信支付。
在微信商户平台(pay.weixin.qq.com)设置您的公众号支付支付目录,设置路径:商户平台–>产品中心–>开发配置,如图7.7所示。公众号支付在请求支付的时候会校验请求来源是否有在商户平台做了配置,所以必须确保支付目录已经正确的被配置,否则将验证失败,请求支付不成功。
二、 发起统一支付
public class PayConfig {
//微信支付商户开通后 微信会提供appid和appsecret和商户号partner
private static String appid = "..."; //公众号唯一id标识符
private static String appsecret ="...";
private static String partner = "..."; //微信支付分配的商户号
//这个参数partnerkey是在商户后台配置的一个32位的key,微信商户平台-账户设置-安全设置-api安全
private static String partnerkey = "...";
//统一下单api
private static String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
/*微信支付成功的回调地址*/
//微信拼团订单返回 地址
private static String wxPtBackUrl="https://www.ulin5.com/vip/buy-group-manage!wxPtReturnBackUrl.action";
}
2.接受前端的action:
public String openPt(){
//微信支付 参数
String payUrl="https://www.ulin5.com/youlin/pay/pay.jsp";
String desc=esZcPro.getPtTitle();//商品标题
String notifyUrl=PayConfig.getWxPtBackUrl();
payUrl=WxPayUtil.getPayUrl(payUrl, desc, notifyUrl, openId, esJy.getId(), totalPay, request, response);
//自定义 支付页面参数,例如 取消支付 返回页面 支付成功返回页面
payUrl+="&wxPayType=wxpt&zcProId="+esZcPro.getId();
response.sendRedirect(payUrl);
}
}
3.微信支付帮助类:
/**
*
*@date2016-11-8
* @param payUrl 自己支付页面的地址
* @param notifyUrl 支付成功 回调地址
* @param openId 微信openId
* @param desc 交易描述
* @param esJyId 交易单号
* @param totalPay 交易总金额
* @param request
* @param response
* @return 返回支付页面 拼接的参数的 地址
*/
public static String getPayUrl(String payUrl,String desc,String notifyUrl,String openId,String esJyId,Double totalPay,HttpServletRequest request,HttpServletResponse response){
//商户相关资料
String appid = PayConfig.getAppid();
String appsecret = PayConfig.getAppsecret();
String partner = PayConfig.getPartner();
String partnerkey = PayConfig.getPartnerkey();
RequestHandler reqHandler = new RequestHandler(request, response);
reqHandler.init(appid, appsecret, partnerkey);
String currTime = TenpayUtil.getCurrTime();
//8位日期
String strTime = currTime.substring(8, currTime.length());
//四位随机数
String strRandom = TenpayUtil.buildRandom(4) + "";
//10位序列号,可以自行调整。
String strReq = strTime + strRandom;
//随机数
String nonce_str = strReq;
//商品描述根据情况修改
String body = desc;
//商户号
String mch_id = partner;
//子商户号 非必输
//String sub_mch_id="";
//设备号 非必输
String device_info="";
//商品描述
//String body = describe;
//金额转化为分为单位
float sessionmoney =new Float(totalPay);
String finalmoney = String.format("%.2f", sessionmoney);
finalmoney = finalmoney.replace(".", "");
//附加数据
String attach = "";
//商户订单号
String out_trade_no = esJyId; //esJy 表中的id 交易表id
int intMoney = Integer.parseInt(finalmoney);
//总金额以分为单位,不带小数点
int total_fee = intMoney;
System.out.println("total_fee "+total_fee);
//total_fee=1; //测试环境一分钱
//订单生成的机器 IP
String spbill_create_ip = request.getRemoteAddr();
//订 单 生 成 时 间 非必输
// String time_start ="";
//订单失效时间 非必输
// String time_expire = "";
//商品标记 非必输
// String goods_tag = "";
//这里notify_url是 支付完成后微信发给该链接信息,可以判断会员是否支付成功,改变订单状态等。
String notify_url =notifyUrl;
String trade_type = "JSAPI";
String openid = openId;
//非必输
// String product_id = "";
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", appid);
packageParams.put("mch_id", mch_id);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", body);
packageParams.put("attach", attach);
packageParams.put("out_trade_no", out_trade_no);
//total_fee=1; //**************************************************************测试使用
//这里写的金额为1 分到时修改
packageParams.put("total_fee",total_fee+"");
// packageParams.put("total_fee", "finalmoney");
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", trade_type);
packageParams.put("openid", openid);
String sign = reqHandler.createSign(packageParams);
String xml="<xml>"+
"<appid>"+appid+"</appid>"+
"<mch_id>"+mch_id+"</mch_id>"+
"<nonce_str>"+nonce_str+"</nonce_str>"+
"<sign>"+sign+"</sign>"+
"<body><![CDATA["+body+"]]></body>"+
"<attach>"+attach+"</attach>"+
"<out_trade_no>"+out_trade_no+"</out_trade_no>"+
//金额,这里写的1 分到时修改
"<total_fee>"+total_fee+"</total_fee>"+
//"<total_fee>"+finalmoney+"</total_fee>"+
"<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+
"<notify_url>"+notify_url+"</notify_url>"+
"<trade_type>"+trade_type+"</trade_type>"+
"<openid>"+openid+"</openid>"+
"</xml>";
String allParameters = "";
try {
allParameters = reqHandler.genPackage(packageParams);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String prepay_id="";
try {
System.out.println(PayConfig.getCreateOrderURL());
GetWxOrderno derno=new GetWxOrderno();
System.out.println(derno);
prepay_id =GetWxOrderno.getPayNo(PayConfig.getCreateOrderURL(), xml);
// HttpPost httpost= HttpClientConnectionManager.getPostMethod(PayConfig.getCreateOrderURL());
if(prepay_id.equals("")){
request.setAttribute("message", "统一支付接口获取预支付订单出错");
request.setAttribute("tipTitle", "支付失败");
return "message";
}
} catch (Exception e1) {
// TODO Auto-generated catch block
System.out.println("=-=-");
e1.printStackTrace();
}
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
String appid2 = appid;
String timestamp = Sha1Util.getTimeStamp();
String nonceStr2 = nonce_str;
String prepay_id2 = "prepay_id="+prepay_id;
String packages = prepay_id2;
finalpackage.put("appId", appid2);
finalpackage.put("timeStamp", timestamp);
finalpackage.put("nonceStr", nonceStr2);
finalpackage.put("package", packages); //预支付订单号
finalpackage.put("signType", "MD5");
String finalsign = reqHandler.createSign(finalpackage);
payUrl+="?appid="+appid2+"&timeStamp="+timestamp+"&nonceStr="+nonceStr2+"&package="+packages+"&sign="+finalsign;
return payUrl;
}
}
- 微信支付服务器签名支付请求类:
'微信支付服务器签名支付请求请求类
'api说明:
'init(app_id, app_secret, partner_key, app_key);
'初始化函数,默认给一些参数赋值,如cmdno,date等。
'setKey(key_)'设置商户密钥
'getLasterrCode(),获取最后错误号
'GetToken();获取Token
'getTokenReal();Token过期后实时获取Token
'createMd5Sign(signParams);生成Md5签名
'genPackage(packageParams);获取package包
'createSHA1Sign(signParams);创建签名SHA1
'sendPrepay(packageParams);提交预支付
'getDebugInfo(),获取debug信息
*/
public class RequestHandler {
/** Token获取网关地址地址 */
private String tokenUrl;
/** 预支付网关url地址 */
private String gateUrl;
/** 查询支付通知网关URL */
private String notifyUrl;
/** 商户参数 */
private String appid;
private String appkey;
private String partnerkey;
private String appsecret;
private String key;
/** 请求的参数 */
private SortedMap parameters;
/** Token */
private String Token;
private String charset;
/** debug信息 */
private String debugInfo;
private String last_errcode;
private HttpServletRequest request;
private HttpServletResponse response;
/**
* 初始构造函数。
*
* @return
*/
public RequestHandler(HttpServletRequest request,
HttpServletResponse response) {
this.last_errcode = "0";
this.request = request;
this.response = response;
//this.charset = "GBK";
this.charset = "UTF-8";
this.parameters = new TreeMap();
// 验证notify支付订单网关
notifyUrl = "https://gw.tenpay.com/gateway/simpleverifynotifyid.xml";
}
/**
* 初始化函数。
*/
public void init(String app_id, String app_secret, String partner_key) {
this.last_errcode = "0";
this.Token = "token_";
this.debugInfo = "";
this.appid = app_id;
this.partnerkey = partner_key;
this.appsecret = app_secret;
this.key = partner_key;
}
public void init() {
}
/**
* 获取最后错误号
*/
public String getLasterrCode() {
return last_errcode;
}
/**
*获取入口地址,不包含参数值
*/
public String getGateUrl() {
return gateUrl;
}
/**
* 获取参数值
*
* @param parameter
* 参数名称
* @return String
*/
public String getParameter(String parameter) {
String s = (String) this.parameters.get(parameter);
return (null == s) ? "" : s;
}
//设置密钥
public void setKey(String key) {
this.partnerkey = key;
}
//设置微信密钥
public void setAppKey(String key){
this.appkey = key;
}
// 特殊字符处理
public String UrlEncode(String src) throws UnsupportedEncodingException {
return URLEncoder.encode(src, this.charset).replace("+", "%20");
}
// 获取package的签名包
public String genPackage(SortedMap<String, String> packageParams)
throws UnsupportedEncodingException {
String sign = createSign(packageParams);
StringBuffer sb = 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();
sb.append(k + "=" + UrlEncode(v) + "&");
}
// 去掉最后一个&
String packageValue = sb.append("sign=" + sign).toString();
// System.out.println("UrlEncode后 packageValue=" + packageValue);
return packageValue;
}
/**
* 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
*/
public String createSign(SortedMap<String, String> packageParams) {
StringBuffer sb = 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 (null != v && !"".equals(v) && !"sign".equals(k)
&& !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + this.getKey());
String sign = MD5Util.MD5Encode(sb.toString(), this.charset)
.toUpperCase();
return sign;
}
/**
* 创建package签名
*/
public boolean createMd5Sign(String signParams) {
StringBuffer sb = new StringBuffer();
Set es = this.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".equals(k) && null != v && !"".equals(v)) {
sb.append(k + "=" + v + "&");
}
}
// 算出摘要
String enc = TenpayUtil.getCharacterEncoding(this.request,
this.response);
String sign = MD5Util.MD5Encode(sb.toString(), enc).toLowerCase();
String tenpaySign = this.getParameter("sign").toLowerCase();
// debug信息
this.setDebugInfo(sb.toString() + " => sign:" + sign + " tenpaySign:"
+ tenpaySign);
return tenpaySign.equals(sign);
}
//输出XML
public String parseXML() {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = this.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(null != v && !"".equals(v) && !"appkey".equals(k)) {
sb.append("<" + k +">" + getParameter(k) + "</" + k + ">\n");
}
}
sb.append("</xml>");
return sb.toString();
}
/**
* 设置debug信息
*/
protected void setDebugInfo(String debugInfo) {
this.debugInfo = debugInfo;
}
public void setPartnerkey(String partnerkey) {
this.partnerkey = partnerkey;
}
public String getDebugInfo() {
return debugInfo;
}
public String getKey() {
return key;
}
}
三、jsp发起支付接口:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String appId = request.getParameter("appid");
String timeStamp = request.getParameter("timeStamp");
String nonceStr = request.getParameter("nonceStr");
String packageValue = request.getParameter("package");
String paySign = request.getParameter("sign");
%>
<!DOCTYPE HTML">
<html>
<head>
<title>微信支付</title>
<meta content="text/html;charset=utf-8" http-equiv="content-type"/>
<script type="text/javascript">
function onBridgeReady(){
WeixinJSBridge.invoke('getBrandWCPayRequest',{
"appId" : "<%=appId%>","timeStamp" : "<%=timeStamp%>", "nonceStr" : "<%=nonceStr%>", "package" : "<%=packageValue%>","signType" : "MD5", "paySign" : "<%=paySign%>"
},function(res){
//WeixinJSBridge.log(res.err_msg);
console.log(res.err_code + res.err_desc + res.err_msg);
if(res.err_msg == "get_brand_wcpay_request:ok"){
if("${param.wxPayType}"=="wxpt"){
window.location.href="http://www.ulin5.com/vip/my-groups_list.html";
}
}else if(res.err_msg == "get_brand_wcpay_request:cancel"){
if("${param.wxPayType}"=="wxds"){
window.history.back(-1)
//window.location.href="http://www.ulin5.com/vip/buser-mobile_myBalance.html";
}
}else{
alert("支付失败!");
}
})
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
</script>
</head>
<body>
</body>
</html>
微信支付基本完成了^_^