怎么建立网站 个人
怎么建立网站 个人:微信公众号之调起h5支付(代码详解)
微信公众号支付
最近公司项目做了微信公众号支付 也在网上看了其他网友写的关于微信公众号支付的博客 首先很感谢他们把代码贡献出来 由于微信公众号支付API文档的变化 有些博客上的代码不能复用 近期贡献出来的代码 虽新但是代码不全 所以我把自己的代码整理一下 没有大神们封装的那么好 但是简单可用 供网友参考 如有疑惑或更好的方法请留言 后期会有微信其他的支付代码贡献给大家
- 第一部分 引入pom文件 我们用的是springboot框架 只需要引这一个依赖就行了
dependency groupId jdom /groupId artifactId jdom /artifactId version 1.1 /version /dependency第二部分 前端js页面
第一次用markdown编辑器 不熟悉 前端js页面代码不全 仅供参考
$(function(){ var url ******** //路径 var orderId; $.ajax({ type: post , url:url 这里添具体路径 , dataType: json , async:false, xhrFields: { withCredentials:true }, crossDomain: true, success:function(data){ orderId data.data.orderId; var content; content div class outer data.data.orderDesc /div div class inner data.data.orderMoney /div $( #content ).append(content) } }) var appId; var timeStamp; var nonceStr; var package; var signType; var paySign; $.ajax({ type: post , url:url 这里添具体路径 , dataType: json , async:false, data:{ orderId :orderId}, xhrFields: { withCredentials:true }, crossDomain: true, success:function(data){ console.log(data) appId data.data.appId timeStamp data.data.timeStamp nonceStr data.data.nonceStr package data.data.package signType data.data.signType paySign data.data.paySign } }) function onBridgeReady(){ WeixinJSBridge.invoke( getBrandWCPayRequest , { appId :appId, //公众号名称 由商户传入 timeStamp :timeStamp, //时间戳 自1970年以来的秒数 nonceStr :nonceStr, //随机串 package :package, signType :signType, //微信签名方式: paySign :paySign //微信签名 },function(res){ if(res.err_msg get_brand_wcpay_request:ok ){ window.location.replace( user_index.html ) }else if(res.err_msg get_brand_wcpay_request:cancel ){ alert( 取消支付! }else{ alert( 支付失败! } }); } function pay(){ 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(); } } $( #yajin ).click(function(){ pay() })})
第三部分后端代码
如果在用户登陆微信公众号时 就获取了openId,不用再用tookie获取了 拿来直接用就行了package yangxiaosong.yangweixin;
import com.alibaba.fastjson.JSONObject;
import yangxiaosong.controller.wxpayutils.WXConfig;
import yangxiaosong.dataobject.user.User;
import yangxiaosong.dataobject.wxpay.WXPayOrder;
import yangxiaosong.enums.PayStatusEnum;
import yangxiaosong.service.ReturnRecordService;
import yangxiaosong.service.UserService;
import yangxiaosong.service.WXPayOrderService;
import yangxiaosong.utils.CookieUtil;
import yangxiaosong.utils.RedisUtil;
import yangxiaosong.utils.ResultVOUtil;
import yangxiaosong.viewobject.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.jdom.JDOMException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.util.*;
RestController
RequestMapping(“/wxpay”)
Slf4j
CrossOrigin
public class WCPay {
Autowiredprivate WXPayOrderService wxPayOrderService; Autowiredprivate StringRedisTemplate redisTemplate; Autowiredprivate UserService userService; Autowiredprivate ReturnRecordService returnRecordService; RequestMapping(value toPay ,method RequestMethod.POST)public ResultVO dopay(HttpServletRequest request, String orderId) throws JDOMException, IOException { //获取user User user (User) getUserFromRedis(request).getData(); //判断orderId if (orderId null) { return ResultVOUtil.error(1, 订单号为空 } WXPayOrder wXPayOrder wxPayOrderService.findById(orderId); //判断订单是否为空 if (wXPayOrder null) { ResultVOUtil.error(1, 微信订单不存在 } log.info(wXPayOrder.toString()); /** * 商品描述 */ String body wXPayOrder.getOrderDesc(); /** * 商户订单号 */ String out_trade_no wXPayOrder.getOrderId(); /** * 标价金额 把元转化成分 千万不要转化成string类型 要转化成int类型 要不然会报错 一定要跟着文档走 要不然会报total_fee不正确的错误 */ int total_fee (wXPayOrder.getOrderMoney().multiply(new BigDecimal( 100 ))).intValue(); /** * 终端IP(订单生成的机器IP) */ String spbill_create_ip request.getRemoteAddr(); /** * openId */ String openid user.getUserOpenId(); SortedMap Object, Object packageParams new TreeMap Object, Object packageParams.put( appid , WXConfig.appid);//微信公众号id packageParams.put( mch_id , WXConfig.mch_id);//商户号 packageParams.put( nonce_str , WXConfig.nonce_str);//随机字符串 packageParams.put( body , body);//商品描述 packageParams.put( out_trade_no , out_trade_no);//商户订单号 packageParams.put( total_fee , total_fee);//订单金额 packageParams.put( spbill_create_ip ,spbill_create_ip);//用户的终端IP packageParams.put( notify_url , WXConfig.notify_url);//通知地址 packageParams.put( openid , openid);//用户的openid packageParams.put( trade_type , WXConfig.trade_type); //获取签名 String sign PayCommonUtil.createSign( UTF-8 , packageParams, WXConfig.paternerKey); log.info( sign sign); packageParams.put( sign ,sign);//签名 String requestXML PayCommonUtil.getRequestXml(packageParams); log.info(requestXML); String resXml HttpUtil.postData(WXConfig.url, requestXML); Map xmlMap XMLUtil.doXMLParse(resXml); log.info( xmlMap xmlMap); String returnCode (String) xmlMap.get( return_code String returnMsg (String) xmlMap.get( return_msg if( SUCCESS .equals(returnCode)){ String resultCode (String) xmlMap.get( result_code String errCodeDes (String) xmlMap.get( err_code_des if( SUCCESS .equals(resultCode)){ //获取预支付交易会话标识 String prepay_id (String) xmlMap.get( prepay_id log.info( prepay_id prepay_id); //把prepay_id保存到订单中,这一步 根据的自己的业务需求 是否需要保存到数据库中 wxPayOrderService.updateById(prepay_id, wXPayOrder.getOrderId()); //下面是公众号内调起的h5支付 看清楚和上边获取prepay_id的时候 注意单词的大小写 //字符串 String nonceStr packageParams.get( nonce_str ).toString(); // 订单详情扩展字符串 String packages prepay_id prepay_id; SortedMap Object, Object finalpackage new TreeMap Object, Object finalpackage.put( appId , WXConfig.appid); finalpackage.put( timeStamp , WXConfig.timeStamp); finalpackage.put( nonceStr ,nonceStr); finalpackage.put( package ,packages); finalpackage.put( signType , WXConfig.signType); sign PayCommonUtil.createSign( UTF-8 , finalpackage, WXConfig.paternerKey); finalpackage.put( paySign ,sign); finalpackage.put( orderNo ,out_trade_no); finalpackage.put( totalFee ,total_fee); log.info( finalpackage finalpackage); return ResultVOUtil.success(finalpackage); } } return null;}/** * 手机支付完成回调,这个方法主要是完成支付后 项目后台业务需要 * param request * param response *这个方法是用流传递的 *这个方法是用流传递的 *这个方法是用流传递的 */ RequestMapping(value /weixinNotify ,method RequestMethod.POST)public void WXPayBack(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException { // 读取参数 InputStream inputStream request.getInputStream(); StringBuffer sb new StringBuffer(); String s; BufferedReader in new BufferedReader(new InputStreamReader(inputStream, UTF-8 log.info( in.toString() in.toString()); while ((s in.readLine()) ! null) { sb.append(s); } in.close(); inputStream.close(); // 解析xml成map Map String, String m new HashMap String, String m XMLUtil.doXMLParse(sb.toString()); Iterator it m.keySet().iterator(); log.info( it it); // 过滤空 设置 TreeMap SortedMap Object, Object packageParams new TreeMap Object, Object while (it.hasNext()) { String parameter (String) it.next(); String parameterValue m.get(parameter); String v if (null ! parameterValue) { v parameterValue.trim(); } packageParams.put(parameter, v); } // 账号信息 String key WXConfig.paternerKey; // key // 判断签名是否正确 if (PayCommonUtil.isTenpaySign( UTF-8 , packageParams, key)) { log.info( 微信支付成功回调 // ------------------------------ // 处理业务开始 // ------------------------------ String resXml if ( SUCCESS .equals((String) packageParams.get( result_code ))) { // 这里是支付成功 String orderNo (String) packageParams.get( out_trade_no log.info( 微信订单号{}付款成功 ,orderNo); //这里 根据实际业务场景 做相应的操作 // 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了. resXml xml return_code ![CDATA[SUCCESS]] /return_code return_msg ![CDATA[OK]] /return_msg /xml //这一步开始就是写公司业务需要的代码了 不用参考我的 没有价值 //处理业务:修改状态码 保存微信支付订单号, Integer payStatus PayStatusEnum.SUCCESS_PAY.getCode(); //微信支付订单号 String transactionId (String) packageParams.get( transaction_id wxPayOrderService.updatePayStatus(payStatus,transactionId, orderNo); //用订单id把押金充值到user对象中 WXPayOrder payOrder wxPayOrderService.findById(orderNo); if(payOrder.getLendId() null payOrder.getOrderMoney().equals(new BigDecimal( 0.01 ))){ Integer integer2 userService.userCashUpdate(payOrder.getUserId()); log.info( 充值成功{} ,integer2); }else { Integer integer1 returnRecordService.updateById(payOrder.getLendId()); log.info( 逾期付款成功后改变归还的状态码{}付款成功 ,integer1); } } else { log.info( 支付失败,错误信息 {} ,packageParams.get( err_code resXml xml return_code ![CDATA[FAIL]] /return_code return_msg ![CDATA[报文为空]] /return_msg /xml } // ------------------------------ // 处理业务完毕 // ------------------------------ BufferedOutputStream out new BufferedOutputStream(response.getOutputStream()); out.write(resXml.getBytes()); out.flush(); out.close(); } else { log.info( 通知签名验证失败 }}//自己封装的获取user对象的方法 不一定适合你 //获取user对象方法private ResultVO getUserFromRedis(HttpServletRequest request){ //获取user Cookie token CookieUtil.get(request, openId if (token null) { log.error( token.getValue() token.getValue()); return ResultVOUtil.error(4, cookie是空! } log.error(token.getValue()); String tokenValue RedisUtil.get(token.getValue()) User user JSONObject.parseObject(tokenValue, User.class); log.info( user user.toString()); //对user进行判断 if(user null){ return ResultVOUtil.error(1, 用户还没登陆 请登陆 } return ResultVOUtil.success(user);}
}
第四部分 配置文件package yangxiaosong.controller.wxpayutils;
import java.util.Random;
public class WXConfig{
/** * 公众号id 公司提供 */public static final String appid /** * 公众号密钥 公司提供 */public static final String appSecret /** * 商户号 公司提供 */public static final String mch_id /** * 商户号密钥 公司提供 */public static final String paternerKey /** * 随机字符串 */public static final String nonce_str MD5.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());/** * 时间戳 */public static final String timeStamp Long.toString(System.currentTimeMillis()/1000);/** * 签名方式 */public static final String signType MD5 /** * 通知地址 */public static final String notify_url 在微信公众号里面设置的网址 外网可以访问的 如果不能访问 加8080端口号试试 /wxpay/weixinNotify /** * 交易类型 */public static final String trade_type JSAPI /** * 统一下单 https://api.mch.weixin.qq.com/pay/unifiedorder */public static final String url https://api.mch.weixin.qq.com/pay/unifiedorder
}
第五部分 就是工具类的使用了HttpUtilpackage yangxiaosong.yangweixin;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
public class HttpUtil {
private final static int CONNECT_TIMEOUT 5000; // in millisecondsprivate final static String DEFAULT_ENCODING UTF-8 public static String postData(String urlStr, String data) { return postData(urlStr, data, null);}public static String postData(String urlStr, String data, String contentType) { BufferedReader reader null; try { URL url new URL(urlStr); URLConnection conn url.openConnection(); conn.setDoOutput(true); conn.setConnectTimeout(CONNECT_TIMEOUT); conn.setReadTimeout(CONNECT_TIMEOUT); if (contentType ! null) conn.setRequestProperty( content-type , contentType); OutputStreamWriter writer new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING); if (data null) data writer.write(data); writer.flush(); writer.close(); reader new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING)); StringBuilder sb new StringBuilder(); String line null; while ((line reader.readLine()) ! null) { sb.append(line); sb.append( \r\n } return sb.toString(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (reader ! null) reader.close(); } catch (IOException e) { } } return null;}
}
PayCommonUtilpackage yangxiaosong.yangweixin;
import yangxiaosong.wxpayutils.MD5Util;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
public class PayCommonUtil {
/** * sign签名 * Author yangxiaosong * param characterEncoding * param packageParams * param API_KEY * return String * */ SuppressWarnings({ rawtypes })public static String createSign(String characterEncoding, SortedMap Object, Object packageParams, String API_KEY) { 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(); if( total_fee .equals(k)){ String v String.valueOf(entry.getValue()); if (null ! v ! .equals(v) ! sign .equals(k) ! key .equals(k)) { sb.append(k v } }else { String v (String) entry.getValue(); if (null ! v ! .equals(v) ! sign .equals(k) ! key .equals(k)) { sb.append(k v } } } sb.append( key API_KEY); String sign MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign;}/** * 将请求参数转换为xml格式的string * param parameters * return String * */ SuppressWarnings({ rawtypes })public static String getRequestXml(SortedMap Object, 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(); if( total_fee .equals(k)){ String v String.valueOf(entry.getValue()); sb.append( k v / k }else { String v (String) entry.getValue(); sb.append( k v / k } } sb.append( /xml return sb.toString();}/** * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 * *///方法一 map的键值对是object类型 SuppressWarnings({ rawtypes })public static boolean isTenpaySign(String characterEncoding, SortedMap Object, Object packageParams, String API_KEY) { 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(! sign .equals(k) null ! v ! .equals(v)) { sb.append(k v } } sb.append( key API_KEY); //算出摘要 String mysign MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase(); String tenpaySign ((String)packageParams.get( sign )).toLowerCase(); return tenpaySign.equals(mysign);}//方法一 map的键值对是object类型 SuppressWarnings({ rawtypes })public static boolean isTenpaySignB(String characterEncoding, Map String, String packageParams, String API_KEY) { 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(! sign .equals(k) null ! v ! .equals(v)) { sb.append(k v } } sb.append( key API_KEY); //算出摘要 String mysign MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase(); String tenpaySign ((String)packageParams.get( sign )).toLowerCase(); return tenpaySign.equals(mysign);}
}
XMLUtilpackage yangxiaosong.yangweixin;
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.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class XMLUtil {
/** * 解析xml,返回第一级元素键值对。如果第一级元素有子节点 则此节点的值是子节点的xml数据。 * * param strxml * return * throws JDOMException * throws IOException */ SuppressWarnings({ rawtypes , unchecked })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 XMLUtil.getChildrenText(children); } m.put(k, v); } // 关闭流 in.close(); return m;}/** * 获取子结点的xml * * param children * return String */ SuppressWarnings({ rawtypes })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(XMLUtil.getChildrenText(list)); } sb.append(value); sb.append( / name } } return sb.toString();}
}
点赞 2 评论 6(十一)Linux实操篇之定时任务调度crond、crontab -e、crontab -l、crontab-r 4620
centos7在VM15中使用vmtools来设置文件共享遇到的问题和解决办法 565