700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > java实现微信扫码支付——模式二

java实现微信扫码支付——模式二

时间:2019-05-29 06:43:55

相关推荐

java实现微信扫码支付——模式二

大家可以先看下微信官方文档流程以及参数说明,本文介绍的是模式二支付,个人比较建议,只因为比较简单,如果对模式一有兴趣的同学也可以自己在微信官方文档上看一看。

官网地址:https://pay./wiki/doc/api/native.php?chapter=6_5

可能有些人第一次看官方文档看的不是很明白(比如我),所以我极简模式说一下(但是签名的规则还有一些校验我就不说了,官网很详细,这里就是流程):

1.用户请求系统支付功能接口,将必要的参数封装成,后台请求微信统一下单接口

https://api.mch./pay/unifiedorder

2.统一下单接口返回的是code_url用于生成二维码,也就是用户真正扫的二维码

3.支付完毕后,微信会把相关的支付结果及用户信息返回

特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。

不多说,上代码

以下代码中凡是出现*号的地方都要替换为自己的信息。

实体类(其中有些是小程序中用到的,可自行修改没什么好说的)。

public class PayParams implements Serializable {private String appId;//微信小程序appidprivate String secret;//微信小程序密钥private String grantType;//填写为 authorization_codeprivate String mchId;//商户号private String notifyUrl;//支付成功后的服务器回调urlprivate String tradeType;//支付类型private String payKey;//微信支付商户秘钥private String payUrl;//统一下单接口地址private String signType;//MD5private String openId;//openidprivate String productName;//商品名称private String orderId;//订单号private String money;//金额 必须为字符串类型,否则签名报错private String addressIp;//终端的IP//微信扫码支付private String wxAppId;//公众号账号ID private String wxNotify_url;//回调通知地址 private String wxTradeType;//交易类型 NATIVE 扫码支付private String wxNameUrl;//微信签名url https://api.mch./pay/unifiedorderpublic String getWxNameUrl() {return "https://api.mch./pay/unifiedorder";}public String getWxAppId() {return "*********";}public String getWxNotify_url() {return "http://"*********";";}public String getWxTradeType() {return "NATIVE";}public String getAppId() {return ""*********";";}public String getSecret() {return ""*********";";}public String getGrantType() {return "authorization_code";}public String getMchId() {return ""*********";";}public String getNotifyUrl() {return "http://"*********";";}public String getTradeType() {return "JSAPI";}public String getPayKey() {return ""*********";";}public String getPayUrl() {return "https://api.mch./pay/unifiedorder";}public String getSignType() {return "MD5";}public String getOpenId() {return openId;}public void setOpenId(String openId) {this.openId = openId;}public String getProductName() {return productName;}public void setProductName(String productName) {this.productName = productName;}public String getOrderId() {return orderId;}public void setOrderId(String orderId) {this.orderId = orderId;}public String getMoney() {return money;}public void setMoney(String money) {this.money = money;}public String getAddressIp() {return addressIp;}public void setAddressIp(String addressIp) {this.addressIp = addressIp;}public PayParams() {}public PayParams(String openId, String productName, String orderId, String money, String addressIp) {this.openId = openId;this.productName = productName;this.orderId = orderId;this.money = money;this.addressIp = addressIp;}}

定义接口:

public interface PayService {/*** 微信扫码支付*/ResultInfo createOrderByWx(PayParams payParams);}

响应类:

public class ResultInfo implements Serializable {private boolean success;private String msg;private Object data;public boolean isSuccess() {return success;}public void setSuccess(boolean success) {this.success = success;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}}

接口实现类:

public class PayServiceImpl implements PayService {private final static Logger logger = LoggerFactory.getLogger(PayServiceImpl.class);@Overridepublic ResultInfo createOrderByWx(PayParams payParams) {logger.info("进入微信扫码支付");ResultInfo rtf = new ResultInfo();try {//生成的随机字符串String randomStr = StrUtil.getRandomStringByLength(32);Map<String, String> packageParams = new HashMap<String, String>();packageParams.put("appid", payParams.getWxAppId());packageParams.put("mch_id", payParams.getMchId());packageParams.put("nonce_str", randomStr);packageParams.put("body", payParams.getProductName());packageParams.put("total_fee",payParams.getMoney());packageParams.put("out_trade_no", payParams.getOrderId());packageParams.put("spbill_create_ip", payParams.getAddressIp());packageParams.put("notify_url", payParams.getWxNotify_url());packageParams.put("trade_type", payParams.getWxTradeType());packageParams.put("openid",payParams.getOpenId());packageParams = PayUtil.paraFilter(packageParams);String prestr = PayUtil.createLinkString(packageParams);//MD5生成签名,调用统一下单接口String mysign = PayUtil.sign(prestr, payParams.getPayKey(), "utf-8").toUpperCase();//拼接统一下单接口使用的xml数据String xml = "<xml>" + "<appid>" + payParams.getWxAppId() + "</appid>"+ "<body><![CDATA[" + payParams.getProductName() + "]]></body>"+ "<mch_id>" + payParams.getMchId() + "</mch_id>"+ "<nonce_str>" + randomStr + "</nonce_str>"+ "<notify_url>" + payParams.getWxNotify_url() + "</notify_url>"+ "<openid>" + payParams.getOpenId() + "</openid>"+ "<out_trade_no>" + payParams.getOrderId() + "</out_trade_no>"+ "<spbill_create_ip>" + payParams.getAddressIp() + "</spbill_create_ip>"+ "<total_fee>" + payParams.getMoney() + "</total_fee>"+ "<trade_type>" + payParams.getWxTradeType() + "</trade_type>"+ "<sign>" + mysign + "</sign>"+ "</xml>";//调用统一下单接口,接收返回的结果String result = PayUtil.httpRequest(payParams.getWxNameUrl(), "POST", xml);// 将解析结果存储在HashMap中Map map = PayUtil.doXMLParse(result);String code_url = (String)map.get("code_url");if(StringUtils.isEmpty(code_url)){rtf.setSuccess(false);rtf.setMsg("发起失败");}else{rtf.setSuccess(true);rtf.setData(code_url);logger.info(rtf.toString());}}catch (Exception e){e.printStackTrace();rtf.setSuccess(false);rtf.setMsg("发起失败");}return rtf;}}

扫码支付的Controller,返回给前端的就是用户付款的二维码

/*** PC 微信扫码支付* @param request* @param response* @return*/@ResponseBody@RequestMapping(value = {"api/pay/wxPay"}, method = RequestMethod.GET)public ResultInfo wxPay(HttpServletRequest request, HttpServletResponse response, PayParams payParams){String ipAddr = IpUtil.getIpAddr(request);PayParams p = new PayParams();p.setProductName("微信扫码商品名称");p.setMoney("1");p.setAddressIp("192.168.58.1");p.setOpenId("*******");p.setOrderId(NonceStrUtil.getRandomStr());ResultInfo orderByWx = payService.createOrderByWx(p);String str = orderByWx.getData().toString();logger.info(str);if (!StringUtils.isEmpty(str)) {try {QRCodeUtil.writeToStream(str, response.getOutputStream());} catch (IOException e) {e.printStackTrace();} catch (WriterException e) {e.printStackTrace();}} else {orderByWx.setSuccess(false);orderByWx.setMsg("请求有误请重试");}return orderByWx;}

扫码支付完成后微信会发送通知,后端进行接收通知和校验签名

/*** PC 微信接口回调* @param request* @param response*/@ResponseBody@RequestMapping(value = {"account/wxCallBack"}, method = RequestMethod.POST)public void wxCallBack(HttpServletRequest request, HttpServletResponse response) {logger.info(" -------------微信回调-------------- ");//读取参数InputStream inputStream;StringBuffer sb = new StringBuffer();PayParams payParams = new PayParams();try {inputStream = request.getInputStream();String s;BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));while ((s = in.readLine()) != null) {sb.append(s);}logger.info("微信返回数据:"+sb.toString());in.close();inputStream.close();//解析xml成mapMap<String, String> m = new HashMap<String, String>();m = PayUtil.doXMLParse(sb.toString());//过滤空 设置 TreeMapSortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();Iterator it = m.keySet().iterator();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 = payParams.getPayKey(); // keyString out_trade_no = (String)packageParams.get("out_trade_no");//判断签名是否正确if(PayUtil.isTenpaySign("UTF-8", packageParams,key)) {logger.info("处理业务开始");//处理业务开始String resXml = "";if("SUCCESS".equals((String)packageParams.get("result_code"))){logger.info("判断签名正确 ");logger.info("SUCCESS".equals((String)packageParams.get("result_code"))+"");// 支付成功,执行实际的业务逻辑logger.info("执行生产的实际逻辑 ");} else {logger.info("支付失败,错误信息:" + packageParams.get("err_code")+ "----->订单号:"+out_trade_no+"----->支付失败时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));String err_code = (String)packageParams.get("err_code");resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";logger.info(resXml);}//处理完毕BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());out.write(resXml.getBytes());out.flush();out.close();} else{logger.info("通知签名验证失败---时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));}} catch (Exception e) {e.printStackTrace();}}

以上正文结束,下面是本次用的所有工具类:

public class IpUtil {/*** IpUtils工具类方法* 获取真实的ip地址* @param request* @return*/public static String getIpAddr(HttpServletRequest request) {String ip = request.getHeader("x-forwarded-for");if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;}}

public class NonceStrUtil {public static String getRandomStr(){String base = "0123456789";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < 19; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}long time = new Date().getTime();String randomStr = sb.append(time).toString();return randomStr;}}

public class PayUtil {/*** 签名字符串* text需要签名的字符串* key 密钥* input_charset编码格式* 签名结果*/public static String sign(String text, String key, String input_charset) {text = text + "&key=" + key;return DigestUtils.md5Hex(getContentBytes(text, input_charset));}/*** 签名字符串* text需要签名的字符串* sign 签名结果* key密钥* input_charset 编码格式* 签名结果*/public static boolean verify(String text, String sign, String key, String input_charset) {text = text + key;String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));if (mysign.equals(sign)) {return true;} else {return false;}}/*** @param content* @param charset* @return* @throws SignatureException* @throws UnsupportedEncodingException*/public static byte[] getContentBytes(String content, String charset) {if (charset == null || "".equals(charset)) {return content.getBytes();}try {return content.getBytes(charset);} catch (UnsupportedEncodingException e) {throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);}}/*** 生成6位或10位随机数 param codeLength(多少位)* @return*/public static String createCode(int codeLength) {String code = "";for (int i = 0; i < codeLength; i++) {code += (int) (Math.random() * 9);}return code;}private static boolean isValidChar(char ch) {if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))return true;if ((ch >= 0x4e00 && ch <= 0x7fff) || (ch >= 0x8000 && ch <= 0x952f))return true;// 简体中文汉字编码return false;}/*** 除去数组中的空值和签名参数* @param sArray 签名参数组* @return 去掉空值与签名参数后的新签名参数组*/public static Map<String, String> paraFilter(Map<String, String> sArray) {Map<String, String> result = new HashMap<String, String>();if (sArray == null || sArray.size() <= 0) {return result;}for (String key : sArray.keySet()) {String value = sArray.get(key);if (value == null || value.equals("") || key.equalsIgnoreCase("sign")|| key.equalsIgnoreCase("sign_type")) {continue;}result.put(key, value);}return result;}/*** 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串* @param params 需要排序并参与字符拼接的参数组* @return 拼接后字符串*/public static String createLinkString(Map<String, String> params) {List<String> keys = new ArrayList<String>(params.keySet());Collections.sort(keys);String prestr = "";for (int i = 0; i < keys.size(); i++) {String key = keys.get(i);String value = params.get(key);if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符prestr = prestr + key + "=" + value;} else {prestr = prestr + key + "=" + value + "&";}}return prestr;}/*** requestUrl请求地址* requestMethod请求方法* outputStr参数*/public static String httpRequest(String requestUrl,String requestMethod,String outputStr){// 创建SSLContextStringBuffer buffer = null;try{URL url = new URL(requestUrl);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod(requestMethod);conn.setDoOutput(true);conn.setDoInput(true);conn.connect();//往服务器端写内容if(null !=outputStr){OutputStream os=conn.getOutputStream();os.write(outputStr.getBytes("utf-8"));os.close();}// 读取服务器端返回的内容InputStream is = conn.getInputStream();InputStreamReader isr = new InputStreamReader(is, "utf-8");BufferedReader br = new BufferedReader(isr);buffer = new StringBuffer();String line = null;while ((line = br.readLine()) != null) {buffer.append(line);}br.close();}catch(Exception e){e.printStackTrace();}return buffer.toString();}public static String urlEncodeUTF8(String source){String result=source;try {result=.URLEncoder.encode(source, "UTF-8");} catch (UnsupportedEncodingException e) {// TODO Auto-generated catch blocke.printStackTrace();}return result;}/***解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。*strxml*JDOMException*IOException*/public static Map doXMLParse(String strxml) throws Exception {if(null == strxml || "".equals(strxml)) {return null;}Map m = new HashMap();InputStream in = String2Inputstream(strxml);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 = getChildrenText(children);}m.put(k, v);}//关闭流in.close();return m;}/*** 获取子结点的xml* @param children* @return String*/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(getChildrenText(list));}sb.append(value);sb.append("</" + name + ">");}}return sb.toString();}public static InputStream String2Inputstream(String str) {return new ByteArrayInputStream(str.getBytes());}/*** 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。* @return boolean*/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 = PayUtil.MD5Encode(sb.toString(), characterEncoding).toLowerCase();String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();//System.out.println(tenpaySign + " " + mysign);return tenpaySign.equals(mysign);}private static String MD5Encode(String origin, String charsetname) {String resultString = null;try {resultString = new String(origin);MessageDigest md = MessageDigest.getInstance("MD5");if (charsetname == null || "".equals(charsetname))resultString = byteArrayToHexString(md.digest(resultString.getBytes()));elseresultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));} catch (Exception exception) {}return resultString;}private static String byteArrayToHexString(byte b[]) {StringBuffer resultSb = new StringBuffer();for (int i = 0; i < b.length; i++)resultSb.append(byteToHexString(b[i]));return resultSb.toString();}private static String byteToHexString(byte b) {int n = b;if (n < 0)n += 256;int d1 = n / 16;int d2 = n % 16;return hexDigits[d1] + hexDigits[d2];}private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5","6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };}

public class QRCodeUtil {private static final int BLACK = 0xFF000000;private static final int WHITE = 0xFFFFFFFF;private QRCodeUtil() {}public static BufferedImage toBufferedImage(BitMatrix matrix) {int width = matrix.getWidth();int height = matrix.getHeight();BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);for (int x = 0; x < width; x++) {for (int y = 0; y < height; y++) {image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);}}return image;}public static void writeToFile(BitMatrix matrix, String format, File file)throws IOException {BufferedImage image = toBufferedImage(matrix);if (!ImageIO.write(image, format, file)) {throw new IOException("Could not write an image of format " + format + " to " + file);}}public static void writeToStream(String content,OutputStream stream)throws IOException, WriterException {int width = 300;int height = 300;String format = "gif";Hashtable hints = new Hashtable();//内容所使用编码hints.put(EncodeHintType.CHARACTER_SET, "utf-8");BitMatrix matrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);BufferedImage image = toBufferedImage(matrix);if (!ImageIO.write(image, format, stream)) {throw new IOException("Could not write an image of format " + format);}}}

public class StrUtil extends StringUtils{/*** StringUtils工具类方法* 获取一定长度的随机字符串,范围0-9,a-z* @param length:指定字符串长度* @return 一定长度的随机字符串*/public static String getRandomStringByLength(int length) {String base = "abcdefghijklmnopqrstuvwxyz0123456789";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < length; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}}

部分maven的坐标:

<!-- httpClient --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.2</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.0</version></dependency><!-- google 二维码 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>2.1</version></dependency><dependency><groupId>mons</groupId><artifactId>commons-lang3</artifactId><version>3.4</version></dependency><dependency><groupId>jdom</groupId><artifactId>jdom</artifactId><version>1.0</version></dependency>

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。