微信小程序调用微信支付(java后台)

参考链接

准备事项

  1. 小程序appid
    进入
    在这里插入图片描述
    在这里插入图片描述

  2. 小程序AppSecret

  3. 商户id

  4. 商户平台api密钥

  5. 后台服务器(听说可以使用云开发就不用准备服务器,但是我这边使用的不是云开发)

开发工具

eclipse+tomcat+微信开发者工具

使用框架

spring,spring-mvc

小程序代码

  //获取用户openid
  getWXOpenId: function () {
    var that = this;
    wx.login({
      //获取code
      success: function (res) {
        var code = res.code; //返回code
        var appId = '填入appid';
        var secret = '填入sercret';
        wx.request({
          url: 'https://api.weixin.qq.com/sns/jscode2session?appid=' + appId + '&secret=' + secret + '&js_code=' + code + '&grant_type=authorization_code',
          data: {},
          header: {
            'content-type': 'json'
          },
          success: function (res) {
            var openid = res.data.openid //返回openid
            that.setData({
              openId: openid
            });
            that.buyGoods(openid);
          }
        })
      }
    });
  },

  //发送信息到后台
  buyGoods: function (openId) {
    var that = this;
    var courseName = 0;
    //后台服务地址
    var address = "后台接口路径";
    wx.request({
      url: address,
      data: {
        openId: openId,
        courseName: courseName
      },
      header: {
        'content-type': 'application/x-www-form-urlencoded' // 默认值
      },
      method: "POST",
      success: function (res) {
        console.log(res);
        that.doWxPay(res.data);
      },
      fail: function (err) {
        wx.showToast({
          icon: "none",
          title: '服务器异常,清稍候再试'
        })
      },
    });
  },

  doWxPay(param) {
    console.log(param);
    //小程序发起微信支付
    wx.requestPayment({
      timeStamp: param.data.timeStamp,//记住,这边的timeStamp一定要是字符串类型的,不然会报错
      nonceStr: param.data.nonceStr,
      package: param.data.package,
      signType: 'MD5',
      paySign: param.data.paySign,
      success: function (event) {
        // success
        console.log(event);
        wx.showToast({
          title: '支付成功',
          icon: 'success',
          duration: 2000
        });
      },
      fail: function (error) {
        // fail
        console.log("支付失败")
        console.log(error)
      },
      complete: function () {
        // complete
        console.log("pay complete")
      }
    });
  },

pom.xml

有很多是用不到的,可以自己去掉

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.jt</groupId>
  <artifactId>pay</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <dependencies>
  	<dependency>
  		<groupId>org.springframework</groupId>
  		<artifactId>spring-webmvc</artifactId>
  		<version>4.3.9.RELEASE</version>
  	</dependency>
  	<dependency>
  		<groupId>mysql</groupId>
  		<artifactId>mysql-connector-java</artifactId>
  		<version>5.1.40</version>
  	</dependency>
  	<dependency>
  		<groupId>com.alibaba</groupId>
  		<artifactId>druid</artifactId>
  		<version>1.0.29</version>
  	</dependency>
  	<dependency>
  		<groupId>org.mybatis</groupId>
  		<artifactId>mybatis</artifactId>
  		<version>3.2.8</version>
  	</dependency>
  	<dependency>
  		<groupId>org.mybatis</groupId>
  		<artifactId>mybatis-spring</artifactId>
  		<version>1.3.1</version>
  	</dependency>
  	<dependency>
  		<groupId>org.springframework</groupId>
  		<artifactId>spring-jdbc</artifactId>
  		<version>4.3.9.RELEASE</version>
  	</dependency>
  	<dependency>
  		<groupId>com.fasterxml.jackson.core</groupId>
  		<artifactId>jackson-databind</artifactId>
  		<version>2.8.5</version>
  	</dependency>
  	<dependency>
  		<groupId>junit</groupId>
  		<artifactId>junit</artifactId>
  		<version>4.12</version>
  	</dependency>
  	<dependency>
  		<groupId>jstl</groupId>
  		<artifactId>jstl</artifactId>
  		<version>1.2</version>
  	</dependency>
    <dependency>
		<groupId>org.apache.shiro</groupId>
		<artifactId>shiro-spring</artifactId>
		<version>1.3.2</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/commons-httpclient/commons-httpclient -->
	<dependency>
	    <groupId>commons-httpclient</groupId>
	    <artifactId>commons-httpclient</artifactId>
	    <version>3.1</version>
	</dependency>
	
	<dependency>
	    <groupId>org.apache.httpcomponents</groupId>
	    <artifactId>httpclient</artifactId>
	    <version>4.5.2</version>
	</dependency>
  </dependencies>
</project>

后台java代码

package com.jt.sys.controller;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.httpclient.HttpException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.DigestUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;

import com.jt.common.vo.HttpRequest;
import com.jt.common.vo.HttpUtil;
import com.jt.common.vo.JsonResult;

@RequestMapping("/")
@Controller
public class PageController {
	@RequestMapping("test")
	public String test(){
		return "test";
	}
	
	@RequestMapping("buyStepOne")
	@ResponseBody
	public JsonResult buyStepOne(String openId,String courseName) throws UnsupportedEncodingException {
		String appId = "appid";
		String mch_id = "商户id";
		String notify_url = "支付成功后台回调地址";
		String trade_type = "JSAPI";
		//签名类型
		String SIGNTYPE = "MD5";
		//第一次发起支付请求的接口地址
		String pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
		//key为商户平台设置的密钥key
		String WXKeHukey = "商户平台密钥";
		//生成的随机字符串
		String nonce_str = getRandomString(32);
		//商品名称
		String body = new String(courseName.getBytes("ISO-8859-1"),"UTF-8");
		//本机的ip地址
		String spbill_create_ip = "ip地址";
		//商户订单号(先用时间代替)
		Date date = new Date();
		String orderNo = date.getTime()+"";
		String money = "1";//支付金额,单位:分,这边需要转成字符串类型,否则后面的签名会失败
		Map<String, String> packageParams = new HashMap<String, String>();
		//小程序ID,微信分配的小程序ID
		packageParams.put("appid", appId);
		//商户号,微信支付分配的商户号
		packageParams.put("mch_id", mch_id);
		//随机字符串,长度要求在32位以内。
		packageParams.put("nonce_str", nonce_str);
		//商品简单描述
		packageParams.put("body", body);
		//商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一
		packageParams.put("out_trade_no", orderNo);//商户订单号
		//订单总金额,单位为分
		packageParams.put("total_fee", money);
		//终端IP,支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP
		packageParams.put("spbill_create_ip", spbill_create_ip);
		//异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
		packageParams.put("notify_url", notify_url);
		//交易类型(JSAPI--JSAPI支付(或小程序支付)、NATIVE--Native支付、APP--app支付,MWEB--H5支付,不同trade_type决定了调起支付的方式,请根据支付产品正确上传)
		packageParams.put("trade_type", trade_type);
		//用户标识,trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。
		packageParams.put("openid", openId);
		
		  // 除去数组中的空值和签名参数
		packageParams = paraFilter(packageParams);
		String prestr = createLinkString(packageParams); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
		
		//MD5运算生成签名,这里是第一次签名,用于调用统一下单接口,key为商户平台设置的密钥key
		String mysign = sign(prestr, WXKeHukey, "utf-8").toUpperCase();
		System.out.println("=======================第一次签名:" + mysign + "=====================");

		//拼接统一下单接口使用的xml数据,要将上一步生成的签名一起拼接进去
		String xml = "<xml version='1.0' encoding='gbk'>" + "<appid>" + appId + "</appid>"
		 + "<body><![CDATA[" + body + "]]></body>"
		 + "<mch_id>" + mch_id + "</mch_id>"
		 + "<nonce_str>" + nonce_str + "</nonce_str>"
		 + "<notify_url>" + notify_url + "</notify_url>"
		 + "<openid>" + openId + "</openid>"
		 + "<out_trade_no>" + orderNo + "</out_trade_no>"
		 + "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>"
		 + "<total_fee>" + money + "</total_fee>"
		 + "<trade_type>" + trade_type + "</trade_type>"
		 + "<sign>" + mysign + "</sign>"
		 + "</xml>";
		 
		System.out.println("调试模式_统一下单接口 请求XML数据:" + xml);
		
		//调用统一下单接口,并接受返回的结果
		String result = httpRequest(pay_url, "POST", xml);
 
		System.out.println("调试模式_统一下单接口 返回XML数据:" + result);
 
		// 将解析结果存储在HashMap中
//		Map map = doXMLParse(result);
		
 
//		String return_code = (String) map.get("return_code");//返回状态码
		
		//解析结果,将结果存在map中
		Map map = doXMLToMap(result);
		String return_code = (String) map.get("return_code");
		
        //返回给移动端需要的参数
        Map<String, Object> response = new HashMap<String, Object>();
        if(return_code == "SUCCESS" || return_code.equals(return_code)){
            // 业务结果
            String prepay_id = (String) map.get("prepay_id");//返回的预付单信息
            response.put("nonceStr", nonce_str);
            response.put("package", "prepay_id=" + prepay_id);
            Long timeStamp = System.currentTimeMillis() / 1000;
            response.put("timeStamp", timeStamp + "");//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误


            String stringSignTemp = "appId=" + appId + "&nonceStr=" + nonce_str + "&package=prepay_id=" + prepay_id+ "&signType=" + SIGNTYPE + "&timeStamp=" + timeStamp;
            //再次签名,这个签名用于小程序端调用wx.requesetPayment方法
            String paySign = sign(stringSignTemp, WXKeHukey, "utf-8").toUpperCase();
            System.out.println("=======================第二次签名:" + paySign + "=====================");
            
            response.put("paySign", paySign);
            response.put("appid", appId);
        }
        	return new JsonResult(response);
        
		
	}
	
	/**
	 * 解析xml,取出其中的result_code,和prepay_id
	 */
	public HashMap<String ,String> doXMLToMap(String results) {
		HashMap<String ,String> result = new HashMap<String ,String>();
		String return_code = results.substring(results.indexOf("<return_code><![CDATA[")+22, results.indexOf("]]></return_code>"));
		result.put("return_code", return_code);
		if(return_code.equals("SUCCESS")) {
			String prepay_id = results.substring(results.indexOf("<prepay_id><![CDATA[")+20, results.indexOf("]]></prepay_id>"));
			result.put("prepay_id", prepay_id);
		}
		return result;
		
	}
	/**
	 *
	 * @param requestUrl请求地址
	 * @param requestMethod请求方法
	 * @param outputStr参数
	 */
	public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
		// 创建SSLContext
		StringBuffer 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();
	}

	/**
	 * 除去数组中的空值和签名参数
	 * 
	 * @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;
	}
	

/**
  * 签名字符串
  * @param text需要签名的字符串
  * @param key 密钥
  * @param input_charset编码格式
  * @return 签名结果
  */
	public static String sign(String text, String key, String input_charset) {
		text = text + "&key=" + key;
		return DigestUtils.md5DigestAsHex(getContentBytes(text, input_charset)); 
  }
	
	/**
	 * @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);
		}
	} 
	
	/**
	 * 支付成功后回调方法
	 * @return
	 */
	@RequestMapping("buySteptwo")
	@ResponseBody
	public JsonResult buySteptwo() {
		  
		Map<String, Object> params = getParamsMap();
        try {
	        //读取参数  
	        InputStream inputStream ;  
	        StringBuffer sb = new StringBuffer();  
	        inputStream = getRequest().getInputStream();  
	        String s ;  
	        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));  
	        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());  
	        //过滤空 设置 TreeMap  
	        SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();        
	        Iterator<String> it = m.keySet().iterator();  
	        while (it.hasNext()) {  
	            String parameter = it.next();  
	            String parameterValue = m.get(parameter);  
	            String v = "";  
	            if(null != parameterValue) {  
	                v = parameterValue.trim();  
	            }  
	            packageParams.put(parameter, v);  
	        }  
	        // 微信支付的API密钥  
	        String key = WXKeHukey; // key  
	        //判断签名是否正确  
	        if(PayForUtil.isTenpaySign("UTF-8", packageParams,key)) {  
	            //------------------------------  
	            //处理业务开始  
	            //------------------------------  
	            String resXml = "";  
	            if("SUCCESS".equals((String)packageParams.get("result_code"))){ 
	            	System.out.println("+++++++++++++++++++回调参数+++++++++++++++++++");
	            	System.out.println(packageParams);
	                // 这里是支付成功  
		            //执行自己的业务逻辑开始
		            String app_id = (String)packageParams.get("appid");
	                String mch_id = (String)packageParams.get("mch_id");  
	                String openid = (String)packageParams.get("openid"); 
	                String is_subscribe = (String)packageParams.get("is_subscribe");//是否关注公众号
	                //公用回传参数 
	                String attach = (String)packageParams.get("attach");
	                //商户订单号
	                String out_trade_no = ((String)packageParams.get("out_trade_no"));  
	                out_trade_no = out_trade_no.replace(" ", "");
	                //付款金额【以分为单位】
	                String total_fee = (String)packageParams.get("total_fee"); 
	                //微信生成的交易订单号
	                String transaction_id = (String)packageParams.get("transaction_id");//微信支付订单号
	                //支付完成时间
	                String time_end=(String)packageParams.get("time_end");
//	                //回调一次后进行判断,查询是否以及添加记录到数据库
//	                Record huidiao = Db.findById("c_order" , "order_number", transaction_id);
//	                if(huidiao != null) {
//	                	redirect("/");
//	                	return;
//	                }
	                //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.  
	                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"  
	                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";  
	                /**
	                 * 	支付成功后的处理
	                 */
	                Record order = 
	             			Db.findFirst("select * from c_order where order_number = '"+out_trade_no+"'");
	                System.out.println("+++++++++++++++++++++++++sql+++++++++++++++++++++++++");
	                System.out.println("select * from c_order where order_number = '"+out_trade_no+"'");
	                System.out.println(order);
	                //Record order = Db.findById("c_order" , "order_number", out_trade_no);
	                //付款金额
	                order.set("order_money" ,total_fee);
	                order.set("order_flag", 1 );
	                order.set("order_time",  new Date());
	                Db.update("c_order", "order_id",order);
	                //TODO 支付成功后去哪个页面 我这里先写首页
	                rendSuccessJson();
	                return;
	            } else {  
	                lg.info("支付失败,错误信息:" + packageParams.get("err_code"));  
	                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"  
	                        + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> "; 
	                //TODO 支付失败要做什么 我这里先不写
		            //------------------------------  
		            //处理业务完毕  
		            //------------------------------  
		            BufferedOutputStream out = new BufferedOutputStream(  
		                    getResponse().getOutputStream());  
		            out.write(resXml.getBytes());  
		            out.flush();  
		            out.close();
	            }  
	        } else{  
	            lg.info("通知签名验证失败");  
	        }  
        }catch(IOException e) {
        	e.printStackTrace();
        	rendFailedJson("IO处理失败,请联系管理员!");
        } catch (JDOMException e) {
			e.printStackTrace();
        	rendFailedJson("JDOM处理失败,请联系管理员!");
		}   
	
	}
	
	
	//length用户要求产生字符串的长度
	 public static String getRandomString(int length){
	     String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	     Random random=new Random();
	     StringBuffer sb=new StringBuffer();
	     for(int i=0;i<length;i++){
	       int number=random.nextInt(62);
	       sb.append(str.charAt(number));
	     }
	     return sb.toString();
	 }
}

用到的外部jar包和工具类

https://download.csdn.net/download/qq_40488121/11704169

补充说明

上线阶段出现了问题,因为微信不希望开发者将appid和密钥存储在前台,所以直接调用微信的接口获取appid是不行的,只能通过后台来获取,这样的话应该是前台一步到位发一次请求就好了,但是我比较懒,就直接把请求微信的接口直接改到后台,直接后台获取openid后放回,其他都不用变

	public void getOpenId() {
		Map<String, Object> params = getParamsMap();
		String code = params.get("code").toString();
		String secret = "ede5a99e35a0c99a1ea61df19a1aeb72";
		String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code";
		String openid = HttpUtil.doGet(url);
		renderText(openid);
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值