微信支付流程操作--内附代码

本文详细讲解了微信支付的实现过程,包括前端JS配置、后端下单请求、支付回调验证及数据库操作,帮助开发者快速掌握微信支付集成技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前做了微信支付,一直也没静下心来整理一下微信支付这块的内容,今天晚上有空,整理了一下,
微信支付流程我就不详细的介绍了,主要是代码部分,从请求开始到微信支付成功
1.首先,页面js部分,需要成功拉取支付请求那么就需要一些微信的内容
getWxConfig();

function getWxConfig(){
	var url =window.location.href; //返回当前的页面的url
	var data = {"url" : url};
	$.ajax({
  		url:'getwxjsconfig.do',
  		type:'get',
  		data:data,
  		dataType:'json',
  		async:false,
  		success:function(result){
  			var dataJson = eval(result);
  			appId = dataJson.appid;
  			timestamp = dataJson.timestamp;
  			nonceStr = dataJson.nonceStr;
  			signature = dataJson.signature;
  		},
  		error:function(XMLResponse){
//  			alert("error:"+XMLResponse.responseText);
  		}
    });
}

相对应的后台逻辑代码
Controller

@RequestMapping("getwxjsconfig")
	public void getConfig(String url,HttpServletResponse response,HttpServletRequest request) throws IOException{
		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter writer = response.getWriter();
		Map<String, String> ret = wxmanager.getJsConfig(url);
		System.out.println("dataUrl:" + url);
		JSONObject config = JSONObject.fromObject(ret);
//		System.out.println(config);
		writer.print(config);
		writer.flush();
		writer.close();
	}

采用最原始的流来会写数据
service层

/**
	 * 获取微信jsapi配置
	 * @param url 调用接口的地址 
	 */
	public Map<String, String> getJsConfig(String url) {
		String appId = config.getAppID();
		String accessToken = "";
		String jspaiTicket = "";
		
		accessToken = getAccessToken(appId,config.getAppSecret());
		jspaiTicket = getJsApiTicket(accessToken);
//		System.out.println(accessToken);
		//获取签名
		Map<String, String> ret = Sign.sign(jspaiTicket,url);
		ret.put("appid", appId);
		return ret;
	}
	
	/**
	 * 获取access_token
	 * @param appID 公众号appid
	 * @param appSecret 开发者密码
	 */
	public String getAccessToken(String appID, String appSecret) {
		String accessToken = WxConfig.getAccessToken();
		String accessTime = WxConfig.getAccessTime();
		int tokenExpiresIn = WxConfig.getTokenExpiresIn();
		long accessTimeL = 0;
		long currentTimeL = 0;
		currentTimeL = new Date().getTime();
		accessTimeL = Util.getTimeFromString("yyyyMMddHHmmss", accessTime);
		
		String currentTime = "";
        int expiresIn = 0;

        if(accessToken.equals("") || accessTime.equals("") || (currentTimeL - accessTimeL) / 1000 >= tokenExpiresIn){
        	String url = WxConfig.getAccessUrl();
    		url = url.replace("APPID", appID);
    		url = url.replace("APPSECRET", appSecret);

    		currentTime= Util.getTime("yyyyMMddHHmmss");
    		CloseableHttpClient httpclient = HttpClients.createDefault();
    		HttpGet httpget = new HttpGet(url);
    		CloseableHttpResponse response = null;  
            String content = "";
        	try {  
                //执行get方法  
                response = httpclient.execute(httpget);  
                if(response.getStatusLine().getStatusCode()==200){  
                    content = EntityUtils.toString(response.getEntity(),"utf-8");  
                    JSONObject baseinfo=JSONObject.fromObject(content);
                    accessToken = baseinfo.getString("access_token");
                    expiresIn = baseinfo.getInt("expires_in") - 1800;
                    WxConfig.setAccessToken(accessToken);
                    WxConfig.setTokenExpiresIn(expiresIn);
                    WxConfig.setAccessTime(currentTime);
                }  
            } catch (ClientProtocolException e) {  
            	Util.logException("获取access_token失败" , e);  
            } catch (IOException e) {  
            	Util.logException("获取access_token失败" , e); 
            } catch (JSONException e){
            	Util.logException("获取access_token失败 " + " content:" + content , e); 
            } finally{
	        	try {
	        		if (response != null){
	        			response.close();
		        	}					
				} catch (IOException e) {
					Util.logException("response关闭异常", e);
				}
	        }
        } 
//        System.out.println("token:" + accessToken + " time:" + accessTime + " expires_in:" +tokenExpiresIn);
        return accessToken;
	}


	/**
	 * 获取jsapi_ticket
	 */
	public String getJsApiTicket(String token) {
		String jspaiTicket = WxConfig.getJsapiTicket();
		String tickeTime = WxConfig.getTicketTime();
		int ticketExpiresIn = WxConfig.getTicketExpiresIn();
		
		long tickeTimeL = 0;
		long currentTimeL = 0;
		currentTimeL = new Date().getTime();
		String currentTime = "";
        int expiresIn = 0;
		currentTime= Util.getTime("yyyyMMddHHmmss");
		tickeTimeL = Util.getTimeFromString("yyyyMMddHHmmss", tickeTime);
		
        if(jspaiTicket.equals("") || tickeTime.equals("") || (currentTimeL - tickeTimeL) / 1000 >= ticketExpiresIn){
        	String url = WxConfig.getTicketUrl();
//        	System.out.println(url);
    		url = url.replace("ACCESS_TOKEN", token);
        	CloseableHttpClient httpclient = HttpClients.createDefault();
    		HttpGet httpget = new HttpGet(url);
    		CloseableHttpResponse response = null;  
            String content = "";
        	try {  
                //执行get方法  
                response = httpclient.execute(httpget);  
                if(response.getStatusLine().getStatusCode()==200){  
                    content = EntityUtils.toString(response.getEntity(),"utf-8");  
                    JSONObject baseinfo=JSONObject.fromObject(content);
//                    System.out.println(baseinfo.getInt("errcode"));
                    if(baseinfo.getInt("errcode") == 40001){
                    	WxConfig.setTokenExpiresIn(0);
                    }else{
                    	jspaiTicket = baseinfo.getString("ticket");
                        expiresIn = baseinfo.getInt("expires_in") - 1800;
                        WxConfig.setJsapiTicket(jspaiTicket);
                        WxConfig.setTicketExpiresIn(expiresIn);
                        WxConfig.setTicketTime(currentTime);
                    }
                }  
            } catch (ClientProtocolException e) {  
                Util.logException("获取jsapiticket失败" , e); 
            } catch (IOException e) {  
            	Util.logException("获取jsapiticket失败" , e);  
            } finally{
	        	try {
	        		if (response != null){
	        			response.close();
		        	}					
				} catch (IOException e) {
					Util.logException("response关闭异常", e);
				}
	        }
        }          
//        System.out.println(jspaiTicket);
        return jspaiTicket;
	}

到这里 js方法必须验证通过,getWxConfig();
也就是说这个方法中返回值

success:function(result){
  			var dataJson = eval(result);
  			appId = dataJson.appid;
  			timestamp = dataJson.timestamp;
  			nonceStr = dataJson.nonceStr;
  			signature = dataJson.signature;
  		},

这些是必须有返回值的用来校验的,否则微信支付无法拉取,
然后页面上就应该添加校验的js了

wx.config({
		    debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
		    appId: appId, // 必填,公众号的唯一标识
		    timestamp: timestamp, // 必填,生成签名的时间戳
		    nonceStr: nonceStr, // 必填,生成签名的随机串
		    signature: signature ,// 必填,签名
		    jsApiList: [
		        'chooseWXPay',
		        'onMenuShareTimeline',
		        'onMenuShareAppMessage',
		        'hideMenuItems'
		      ] // 必填,需要使用的JS接口列表
		});
	}

如果说这个时候都通过了话,那么恭喜你,可以正式的去执行下单请求了

//下单请求
		wx.ready(function(){
		    $("#submit").click(function(){
		    	tradeno = "";
				newpay();	
		    });
		});
function newpay(){
	payfee = 0.01;
	var data = "user=" + userid + "&body=" + "测试" + "&fee=" + payfee;
	$.ajax({
  		url:'newpay.do',
  		type:'post',
  		data:data,
  		dataType:'json',
  		success:function(result){
//  			alert(result.tradeno);
  			tradeno = result.tradeno;
  			alert(result.timeStamp+"--"+result.nonceStr+"--"+result.package+"--"+result.signType+"--"+result.paySign);
//  			alert(tradeno);
  			wx.chooseWXPay({
  				timestamp: result.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
				nonceStr: result.nonceStr, // 支付签名随机串,不长于 32 位
				package: result.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
				signType: result.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
				paySign: result.paySign, // 支付签名
				success: function (res) {
					
				}
  				
  			});
  		},
  		error:function(XMLResponse){
  			alert("error:"+XMLResponse.responseText);
  		}
    });
}

我这里为了省钱,把金额写死了,这个是页面动态获取的
下单Controller

//下单
	@RequestMapping("newpay")
	public void getConfig(HttpServletResponse response,HttpServletRequest request) throws Exception{
		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter writer = response.getWriter();
		
		String openid = request.getParameter("user");;
		String body = request.getParameter("body");
		int fee = (int) (Float.parseFloat(request.getParameter("fee")) * 100);
		String requestUrl = request.getRequestURL().toString(); 
		String requestUri = request.getRequestURI(); 
		String domain = requestUrl.replace(requestUri,"");
		String ip = Util.getIpAddr(request);
		
//		System.out.println(openid + "\n" + body + "\n" + fee + "\n" + domain + "\n" + ip);
		HashMap<String, String> paysign = paymanager.unifiedOrder(openid, body, fee, domain, ip);
		JSONObject res = JSONObject.fromObject(paysign);
		//TODO 在页面上判断一下res信息 然后插入数据 
		System.out.println(res);
		writer.print(res);
		writer.flush();
		writer.close();
	}

Service层

	@Autowired
	WxConfig config;
	
	//统一下单
	public HashMap<String, String> unifiedOrder(String openid,String body,int fee,String domain,String ip) {
		try {
			WXPayConfigImpl payconfig = WXPayConfigImpl.getInstance(config.getKey(), config.getAppID(),
					config.getMchID(), config.getCertLocalPath());
			WXPay wxpay = new WXPay(payconfig);
			String tradeno = NoTools.getInstance().getWXTradeNo();
			HashMap<String, String> data = new HashMap<String, String>();
	        data.put("body", body);
	        data.put("openid", openid);
	        data.put("out_trade_no", tradeno);
	        data.put("device_info", "");
	        data.put("fee_type", "CNY");
	        data.put("total_fee", fee+"");
	        data.put("spbill_create_ip", ip);
	        data.put("notify_url", domain + "/paynotify.do");
	        data.put("trade_type", "JSAPI");	        
	
            Map<String, String> r = wxpay.unifiedOrder(data);
//            System.out.println(r);
            if(r.get("return_code").equals("SUCCESS") && r.get("result_code").equals("SUCCESS")){
            	HashMap<String, String> paysign = new HashMap<String, String>();
            	paysign.put("appId", config.getAppID());
            	paysign.put("timeStamp", WXPayUtil.getCurrentTimestamp()+"");
            	paysign.put("nonceStr", WXPayUtil.generateUUID());
            	paysign.put("package", "prepay_id=" + r.get("prepay_id"));
            	paysign.put("signType", "MD5");
            	paysign.put("paySign", WXPayUtil.generateSignature(paysign,config.getKey(),SignType.MD5));
            	
            	WXPayOrders order = new WXPayOrders(tradeno, "", body, fee, Util.getTime("yyyy-MM-dd HH:mm:ss"),
            			null, openid, r.get("prepay_id"), r.get("trade_type"), 0, null, 0);
            	wxpayDao.newOrder(order);
            	paysign.put("tradeno", tradeno);
            	return paysign;
            }
	  
		} catch (Exception e) {
			Util.logException("微信支付下单失败", e);
		}
		return null;
	}

在这里插入图片描述

这是微信提供的SDK,代码里面设计到的类可以自行查找
注意我代码中的的paynotify这是支付结果通知,也就是支付成功之后需要的回调函数

//支付结果通知
	@RequestMapping("paynotify")
	public void payNotify(HttpServletResponse response,HttpServletRequest request) throws Exception{
		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter writer = response.getWriter();
		InputStream is = request.getInputStream();
      
        BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));       
       
        String buffer = null;
        StringBuffer sb = new StringBuffer();
       
        while ((buffer = br.readLine()) != null) {        
            sb.append(buffer);
        }
        Map<String, String> notify = WXPayUtil.xmlToMap(sb.toString());
        HashMap<String, String> res = paymanager.checkOrder(notify);
        String resStr = "";
        if(res.get("code").equals("1")){
        	resStr = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        }else{
        	resStr = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[" + res.get("msg") + "]]></return_msg></xml>";
        }
//        System.out.println(resStr);
        writer.print(resStr);
		writer.flush();
		writer.close();
	}

此时,支付已经能够成功拉取并且进行支付,在支付成功之后,生成的订单信息保存到本地的数据库中即可
明天我再更新微信退款这快的内容

之前我写的这一段忘记提醒读者进行微信支付的校验了 ,尤其是回调函数这一段,不论是微信还是支付宝都是需要对回调函数进行校验的,
看看微信支付的官方文档怎么说的。

支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。 
 
对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知
最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)
 
注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 
推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。
在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。 
 
特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。 
技术人员可登进微信商户后台扫描加入接口报警群。 

也就是说在paynotify这个回调请求里面,我用的是最简单的方式进行的回调函数的异步校验,这也是比较狗的,属于治标不治本的方法。

paynotify的Service层

//支付完成通知校验
	public HashMap<String, String> checkOrder(Map<String, String> notify) {
		HashMap<String, String> res = new HashMap<String, String>();
		try {
			if(WXPayUtil.isSignatureValid(notify, config.getKey())){
				WXPayOrders order = wxpayDao.getOrder(notify.get("out_trade_no"));
				if(order != null){
					if(order.getStatus() == 0){
						if(order.getTotalfee() == Integer.parseInt(notify.get("total_fee"))){
							SimpleDateFormat format1 = new SimpleDateFormat("yyyyMMddhhmmss");
							SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
							String datetime = notify.get("time_end");
							try {
								Date date = format1.parse(datetime);
								datetime = format2.format(date);
//								System.out.println(datetime);
							} catch (ParseException e) {
								Util.log(e.getMessage());
							}
							order.setTransactionid(notify.get("transaction_id"));
							order.setEndtiem(datetime);
							order.setStatus(1);
							wxpayDao.confirmPay(order);
						}
					}
					res.put("code", "1");
					res.put("msg", "OK");
				}else{
					res.put("code", "0");
					res.put("msg", "未找到订单");
				}
			}else{
				res.put("code", "0");
				res.put("msg", "签名错误");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return res;
	}

这个里面的getOrder就是使用订单号查询出入账的该订单数据,

WXPayUtil.isSignatureValid(notify, config.getKey()
这个方法则是微信提供的让我们来进行验签的
/**
 * 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
 *
 * @param data Map类型数据
 * @param key API密钥
 * @return 签名是否正确
 * @throws Exception
 */
public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
    return isSignatureValid(data, key, SignType.MD5);
}

然后我就在Controller里面做出了一个判断,也就是上边的那个

  if(res.get("code").equals("1")){
        	resStr = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        }else{
        	resStr = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[" + res.get("msg") + "]]></return_msg></xml>";
        }

而xml是微信支付所需要的格式,只有这样才能成功调用微信支付,以上就是微信支付全部的内容了,希望对大家有所帮助,如果还有不明白的,可以在下方添加评论,随时进行更新解答问题!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丶懿

你的鼓励是我最大的创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值