欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

微信的扫码支付,web(h5)支付,小程序支付,代码干货

程序员文章站 2022-07-13 17:08:33
...

代码都没有经过精简,是为了看起来方便,有需要可以自行修改封装

扫码支付比较简单

	/**
	 *  统一下单
	 * @throws Exception## 标题
	 */
	@RequestMapping("/wxpay/unified/order")
	@ResponseBody
	public Map<String, String> doUnifiedOrder(
			Model model) throws Exception {
		Map<String, String> resp = null;
		//wxpayConfig是自行封装的一个类  下面会放出具体属性
		WxPayConfig wxPayConfig =new WxPayConfig (“可以把支付信息存库,需要用到都从库里拿”);
		//最好对数据库查询出的信息进行一下基本的判空
		if (config == null) {
			throw new Exception("config is null");
		}
		if (config.getAppId() == null || config.getAppId().trim().length() == 0) {
			throw new Exception("appid in config is empty");
		}
		if (config.getMchId() == null || config.getMchId().trim().length() == 0) {
			throw new Exception("appid in config is empty");
		}
		if (config.getSslCert() == null) {
			throw new Exception("cert stream in config is empty");
		}
		
		WxPay wxpay = new WxPay(wxPayConfig);
		//以下四个参数要根据具体需求使用 也可以都写死 只传orderId 和金额 这样是为了封装起来 使用方便
		String orderId=request.getParameter("order_id");
		String outTradeNo=request.getParameter("out_trade_no");
		String totalFee=request.getParameter("total_fee");
		String body_desc=request.getParameter("body_desc");
		logger.info("支付提交参数:outTradeNo"+outTradeNo+"orderId:"+orderId+"totalFee:"+totalFee+"body_desc:"+body_desc);
		Map<String, String> data = new HashMap<String, String>();
		data.put("body", body_desc);//支付描述
		data.put("out_trade_no", outTradeNo);//订单号
		data.put("device_info", "");//设备号
		data.put("fee_type", "CNY");//人民币类型
		data.put("total_fee", totalFee);//总金额
		
		String ip=“”;
		//获取ip
		 InetAddress ia=null;
	        try {
	            ia=ia.getLocalHost();
	            ip=ia.getHostAddress();
	        }catch (Exception e) {
	        	e.printStackTrace();
			}
		
		data.put("spbill_create_ip",ip);//IP地址
	
		data.put("notify_url", "https://xxxx.com/xxx/xx");//完整的微信回调通知地址 需要在微信公众号后台配置		
		data.put("product_id", orderId);
		data.put("trade_type", "NATIVE");  // 此处指定为扫码支付  0
		data.put("attach", orderId);
		try {
			logger.info("支付参数为:"+data);
			//发起请求支付了 resp为返回信息
			resp = wxpay.unifiedOrder(data);
			//WxPayLog是微信支付日志的记录 有需要可以加上 就不放出来了 
			WxPayLog wxPayLog=new WxPayLog();
			wxPayLog.setOrderId(Long.parseLong(orderId));
			wxPayLog.setCodeUrl(resp.get("code_url"));
			wxPayLog.setCreateTime(new Date());
			wxPayLog.setNonceStr(resp.get("nonce_str"));
			wxPayLog.setOutTradeNo(outTradeNo);
			wxPayLog.setResultCode(resp.get("result_code"));
			wxPayLog.setReturnMsg(resp.get("return_msg"));
			wxPayLog.setSign(resp.get("sign"));
			wxPayLog.setTradeType(0);
			wxPayLog.setTotalFee(Double.parseDouble(totalFee));
			wxPayLogRep.save(wxPayLog);
		} catch (Exception e) {
			e.printStackTrace();
		}
		logger.info("支付返回:"+resp);
		return resp;
	}

WxPayConfig 实体类

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
*
*封装支付需要的相关参数
**/
@Entity
public class WxPayConfig {
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Long Id;
	private String appId;//公众账号ID
	private String  appSecret;//公众号秘钥
	private String  mchId;//商户号
	private String  appKey;//app秘钥
	private String  isvName;//服务商名称
	private String  subMchId;//子商户号
	private Integer  payType;//支付类型  0.通过I,SV渠道接入 HMAC-SHA256   (默认)  1. *支付渠道 MD5,	
	//get set 方法补一下
}

在回调函数 也就是传给微信的‘notify_url’路径所对应的方法中,微信会回调多次这个地址,在收到支付成功的消息,需要给微信返回已收到,不然会一直回调,具体次数不太记得了。时间相差也懒得去百度了

扫码支付的微信回调方法

/**
	 * 微信支付回调处理
	 * @return
	 * @throws Exception 
	 */
	@RequestMapping("/wxpay/notify")//具体url自行在发起支付的时候配置
	@ResponseBody
	public String wxNotify() throws Exception {
		logger.info("微信回调了");
		//获取微信传回的信息
		request.setCharacterEncoding("UTF-8");
		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		response.setHeader("Access-Control-Allow-Origin", "*");

		InputStream in = request.getInputStream();
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int len = 0;
		while ((len = in.read(buffer)) != -1) {
			out.write(buffer, 0, len);
		}
		out.close();
		in.close();
		String content = new String(out.toByteArray(), "utf-8");// 微信传回的信息是xml数据
		Map<String, String> map =xmlToMap(content);
		String resultCode = map.get("result_code");
		String outTradeNo=map.get("out_trade_no");
		String totalFee=map.get("total_fee");
		logger.info("微信返回数据:"+"resultCode:"+resultCode+"outTradeNo:"+outTradeNo+"totalFee:"+totalFee);
		WxPayLog payLog = new WxPayLog (‘找到支付之前存库的WxPayLog ,可以使用outTradeNo去库里查找’);
		// 可以根据当前payLog.getOrderId()去判断是哪一个支付的订单 或者具体是用哪一个参数去做的订单绑定都可以
			logger.info("订单Id:"+payLog.getOrderId());
			if (resultCode.equalsIgnoreCase("FAIL")) {//支付失败
			
			//将相对应订单状态修改一下,并返回给客户 ,暂时没遇到过支付失败,不过不代表没有
			
		  } else if (resultCode.equalsIgnoreCase("SUCCESS")) {// 支付成功
				logger.info("支付成功了");
				if(‘对比订单金额和微信返回金额是否一致‘) {
				}else {
				//支付金额不一致!
				}
			//  这里处理自己数据库中的订单表业务
		logger.info("返回数据为:"+JSON.toJSONString(map));
		//支付成功通知微信已接受到返回信息
		if(resultCode.equalsIgnoreCase("SUCCESS")) {
			String resSuccessXml = "<xml><return_code>SUCCESS</return_code></xml>";
			return resSuccessXml;
		}
		String resFailXml = "<xml><return_code>FAIL</return_code></xml>";
		return resFailXml;

	}

xml转换成map的方法

public static Map<String, String> xmlToMap(String strXML) throws Exception {
		try {
			Map<String, String> data = new HashMap<String, String>();
			DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
			InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
			org.w3c.dom.Document doc = documentBuilder.parse(stream);
			doc.getDocumentElement().normalize();
			NodeList nodeList = doc.getDocumentElement().getChildNodes();
			for (int idx = 0; idx < nodeList.getLength(); ++idx) {
				Node node = nodeList.item(idx);
				if (node.getNodeType() == Node.ELEMENT_NODE) {
					org.w3c.dom.Element element = (org.w3c.dom.Element) node;
					data.put(element.getNodeName(), element.getTextContent());
				}
			}
			try {
				stream.close();
			} catch (Exception ex) {
				// do nothing
			}
			return data;
		} catch (Exception ex) {
			throw ex;
		}
	}
扫码支付差不多就就这样,如果有坑可以留言

接下来web支付和小程序支付,两者几乎没差别,写在一起

/**
	 *web, 小程序支付
	 * @param request
	 * @return
	 */
	@RequestMapping(value="/wx/wxPayment")
	@ResponseBody
	public JSON payOut(HttpServletRequest request) {
		JSONObject map= new JSONObject();
		
		logger.info("进入微信支付请求");
	//可以继续使用上文的 WxPayConfig 
		WxPayConfig wxPayConfig= new WxPayConfig ();
		if (wxConfig == null) {
			map.put("code", 0);
			map.put("msg", "微信配置错误");
			return map;
		}
		String wxAppId = wxPayConfig.getWxAppId();
		String mchId = wxPayConfig.getMchId();
		String key = wxPayConfig.getPayKey();
		try {
			String appSecret=wxPayConfig.getWxAppSecret();
			Random random = new Random();
			String nonceStr = "";
			while (nonceStr.length() < 32) {
				int i = random.nextInt(35);
				nonceStr += randomStr[i];
			}
			SimpleDateFormat sdf2=new SimpleDateFormat("yyyyMMddHHmmss");
			String orderId =“”;//orderId可以用时间加上一个用户的id 这里随意 但是要保证微信回调的时候可以判断是哪一个用户发起的支付
			String openId=“”;//对应用户openId
	
			String body = "";// 此处填告知用户支付了什么业务 比如某某超市什么的,中文有可能报错,如果报错就使用英文试试排除这个错误,不过下文也有对参数进行编码,一般不会出错
			String totalFee =String.valueOf(product.getTotal());
			//金额单位转换为“分”
			float floatTotalFee = Float.parseFloat(totalFee); 
			int intTotalFee = (int) (floatTotalFee * 100);
			totalFee = "" + intTotalFee;
			String tradeType = "JSAPI";
		
			String spBillCreateIP = request.getRemoteAddr();
			
			//设置回调地址
			String notifyUrl = "";//和上文扫码支付一样的配置就好了,要注意商户后台的支付域名配置 最好是
			/*
			比如支付是:https://xxxx.com/wxpay/payment
			那回调的就要是:https://xxxx.com/wxpay/notify
			商户后台的支付域名配置:https://xxxx.com/wxpay 就要这样配置到最后"/"的上一级
			*/

			String sign =WxPayUtil.MD5("appid=" + wxAppId + "&body=" + body + "&mch_id=" + mchId + "&nonce_str=" + nonceStr
					+ "&notify_url=" + notifyUrl + "&openid=" + openId + "&out_trade_no=" +orderId
					+ "&spbill_create_ip=" + spBillCreateIP + "&total_fee=" + totalFee + "&trade_type=" + tradeType
					+ "&key=" + key);
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			// 创建Document
			Document document = builder.newDocument();
			// 创建Element
			Element xml = document.createElement("xml");
			xml.appendChild(makeElement("appid", wxAppId, document));
			xml.appendChild(makeElement("body", body, document));
			xml.appendChild(makeElement("mch_id", mchId, document));
			xml.appendChild(makeElement("nonce_str", nonceStr, document));
			xml.appendChild(makeElement("notify_url", notifyUrl, document));
			xml.appendChild(makeElement("openid", openId, document));
			xml.appendChild(makeElement("out_trade_no", orderId, document));
			xml.appendChild(makeElement("spbill_create_ip", spBillCreateIP, document));
			xml.appendChild(makeElement("total_fee", totalFee, document));
			xml.appendChild(makeElement("trade_type", tradeType, document));
			xml.appendChild(makeElement("sign", sign, document));
			document.appendChild(xml);
			TransformerFactory tf = TransformerFactory.newInstance();
			Transformer t = tf.newTransformer();
			t.setOutputProperty("encoding", "UTF-8");
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			t.transform(new DOMSource(document), new StreamResult(bos));
			String xmlStr = bos.toString("UTF-8");
			// 发送支付请求 HttpUtils 这个共具类很普遍 有需要请自行百度
			String result = HttpUtils.doPost("https://api.mch.weixin.qq.com/pay/unifiedorder", xmlStr);
			logger.info("微信支付返回:"+result);
			// 解析
			StringReader sr = new StringReader(result);
			InputSource is = new InputSource(sr);
			DocumentBuilderFactory factory2 = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder2 = factory2.newDocumentBuilder();
			Document doc = builder2.parse(is);
			String returnCode = doc.getElementsByTagName("return_code").item(0).getFirstChild().getNodeValue();
			System.out.println("returnCode:"+returnCode);
			//web,和小程序支付都会有一个预订单Id  prepay_id
			if (returnCode.equals("SUCCESS")) {
				String prepayId = doc.getElementsByTagName("prepay_id").item(0).getFirstChild().getNodeValue();//getChileElementValue(element, "prepay_id");
				map.put("appId", wxAppId);
				String timeStamp = Long.toString(System.currentTimeMillis());
				timeStamp = timeStamp.substring(0, 10);
				map.put("timeStamp", timeStamp);
				map.put("nonceStr", nonceStr);
				String packageStr = "prepay_id=" + prepayId;
				map.put("package", packageStr);
				map.put("signType", "MD5");
				//MD5加密 具体规则查询微信支付API
				String paySign = MD5("appId=" + wxAppId + "&nonceStr=" + nonceStr + "&package=" + packageStr
						+ "&signType=MD5&timeStamp=" + timeStamp + "&key=" + key);
				map.put("paySign", paySign);
				
				logger.info("调起支付:"+map);
				/* 小程序支付
				到这一步将map这个参数传给小程序端,小程序端根据以上参数就可以发起支付 
				返回签名错误的时候,就要检查paySign 参数是否有错,参数可以打印出来看看 方便找错
				*/
					/* web支付
				将map转换成json传到界面
				*/
				
				return map;
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("catch");
		}
		map.put("code", 0);
		map.put("msg", "系统错误");
		return map;
	}

web(h5)支付js

//先判断是否在微信环境下
if (typeof WeixinJSBridge == "undefined"){  
				            
				            alert("请在微信中打开网站进行支付");
				   
						    if( document.addEventListener ){
						    	//addEventListener() 方法用于向指定元素添加事件句柄。
						        document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
						    }else if (document.attachEvent){
						        document.attachEvent('WeixinJSBridgeReady', jsApiCall);
						        document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
						    }
					}else{
						   jsApiCall();
					}
//使用ajax访问上文的支付接口 返回的信息调用下文方法
	function jsapiPay (payment){
				 WeixinJSBridge.invoke(
					       'getBrandWCPayRequest', {
					           "appId":payment.appId,     //公众号名称,由商户传入     
					           "timeStamp":payment.timeStamp,         //时间戳,自1970年以来的秒数     
					           "nonceStr":payment.nonceStr, //随机串     
					           "package":payment.package1,     
					           "signType":"MD5",         //微信签名方式:     
					           "paySign":payment.paySign //微信签名 
					       },
					       function(res){     
					           if(res.err_msg == "get_brand_wcpay_request:ok" ) {
					           //表示支付成功
					        	   //更新相对应支付订单
					           }     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。 
					       }
					   );
			}

web支付,小程序支付回调函数

/**
	 * 微信支付回调
	 * @param request
	 * @return
	 */
	@RequestMapping(value="/wxPayNotify")
	public void wxNotify(HttpServletRequest request, HttpServletResponse response) {
		logger.info("微信支付回调");
		String postData = "";
		try {
			BufferedReader reader = null;
			StringBuilder buf = new StringBuilder();
			try {
				String line;
				reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
				while ((line = reader.readLine()) != null) {
					buf.append(line).append("\n");
				}
			} catch (Exception e) {
				throw e;
			}
			postData = buf.toString();
			logger.info("微信支付回调结果:"+postData);
		} catch (Exception e) {
			return ;//"success";
		}
		if (postData != null && !postData.equals("")) {
			StringReader sr = new StringReader(postData);
			InputSource is = new InputSource(sr);
			DocumentBuilderFactory factory2 = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder2 = null;
			try {
				builder2 = factory2.newDocumentBuilder();
			} catch (ParserConfigurationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			Document doc = null;
			try {
				doc = builder2.parse(is);
			} catch (SAXException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			String returnCode = doc.getElementsByTagName("return_code").item(0).getFirstChild().getNodeValue();
			System.out.println("支付成功返回:"+doc);
			if (returnCode.equalsIgnoreCase("SUCCESS")) {


				// ͨ解析
				String resultCode = doc.getElementsByTagName("result_code").item(0).getFirstChild().getNodeValue();

				String orderId = doc.getElementsByTagName("out_trade_no").item(0).getFirstChild().getNodeValue();
				//支付是否成功
				if (resultCode.equalsIgnoreCase("success")) {
					String merchantId =doc.getElementsByTagName("mch_id").item(0).getFirstChild().getNodeValue();
					String wxOrderId =doc.getElementsByTagName("transaction_id").item(0).getFirstChild().getNodeValue(); 
					String payTime =doc.getElementsByTagName("time_end").item(0).getFirstChild().getNodeValue();
					String tradeType =doc.getElementsByTagName("trade_type").item(0).getFirstChild().getNodeValue();
					String bankType =doc.getElementsByTagName("bank_type").item(0).getFirstChild().getNodeValue(); 
					String totalFee =doc.getElementsByTagName("total_fee").item(0).getFirstChild().getNodeValue(); 
					String feeType = doc.getElementsByTagName("fee_type").item(0).getFirstChild().getNodeValue();
					String cashFee = doc.getElementsByTagName("cash_fee").item(0).getFirstChild().getNodeValue();
					String cashFeeType = "CNY";//doc.getElementsByTagName("cash_fee_type").item(0).getFirstChild().getNodeValue();
					String sign = doc.getElementsByTagName("sign").item(0).getFirstChild().getNodeValue();
					/**
						进行相关订单操作和验证
					*/
					//获取签名要用到的key
				
					WxPayConfig wxPayConfig = new WxPayConfig ('支付的相关参数');
					if (wxPayConfig == null) {
						return ;
					}
					String mchId = wxPayConfig.getMch_Id();
					String key = wxPayConfig.getPay_Key();
					//签名认证
					Map<String , String> map = new HashMap<String,String>();
					NodeList nodeList = doc.getDocumentElement().getChildNodes();
					for (int idx = 0; idx < nodeList.getLength(); ++idx) {
						Node node = nodeList.item(idx);
						if (node.getNodeType() == Node.ELEMENT_NODE) {
							org.w3c.dom.Element element = (org.w3c.dom.Element) node;
							map.put(element.getNodeName(), element.getTextContent());
						}
					}
					String newSign = generateSignature(map, key);
					if(!sign.equals(newSign)) {
						return ;
					}
					payTime = payTime.substring(0, 4) + "-" + payTime.substring(4, 6) + "-" + payTime.substring(6, 8)
					+ " " + payTime.substring(8, 10) + ":" + payTime.substring(10, 12) + ":"
					+ payTime.substring(12, 14);
					java.sql.Timestamp timeStamp = java.sql.Timestamp.valueOf(payTime);
				//	根据具体支付流程去处理支付成功后的操作
					//支付日志存库
						WxPayNotify wxPayNotify = new WxPayNotify ();
						wxPayNotify.setMerchantId(merchantId);
						wxPayNotify.setWxOrderId(wxOrderId);
						wxPayNotify.setPayTime(timeStamp);
						wxPayNotify.setTradeType(tradeType);
						wxPayNotify.setBankType(bankType);
						wxPayNotify.setTotalFee(totalFee);
						wxPayNotify.setFeeType(feeType);
						wxPayNotify.setCashFee(cashFee);
						wxPayNotify.setFeeType(cashFeeType);
						wxPayNotify.setOrderId(orderId);
					//保存支付日志
					
					//修改订单状态
					
					response.setContentType("text/plain");
					try {
					//告知微信 收到支付成功的消息
						Writer out = response.getWriter();
						out.write("<xml>");
						out.write("<return_code><![CDATA[SUCCESS]]></return_code>");
						out.write("<return_msg><![CDATA[OK]]></return_msg>");
						out.write("</xml>");
						out.flush();
					} catch (IOException e) {
					}
				} else {
				}
			}
		}
		return ;
	}
/**
	 * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
	 * @param data 待签名数据
	 * @param key API**
	 * @return 签名
	 */
	public static String generateSignature(final Map<String, String> data, String key)  {
		Set<String> keySet = data.keySet();
		String[] keyArray = keySet.toArray(new String[keySet.size()]);
		Arrays.sort(keyArray);
		StringBuilder sb = new StringBuilder();
		for (String k : keyArray) {
			if (k.equals("sign")) {
				continue;
			}
			if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
				sb.append(k).append("=").append(data.get(k).trim()).append("&");
		}
		sb.append("key=").append(key);
		return MD5(sb.toString()).toUpperCase();
	}

有问题请留言,勿喷,只是为了记录一下微信支付注意事项