微信小程序支付(后台JAVA)

前台

//总结很多大神的文章,加上自己的一些理解,希望能帮到更多人,有问题可以评论,留言,看到后会回复

//钱是在后台管理里面配的,在前台页面通过字典取到

//调用微信的login方法,可以直接获取到code,发送code到后台获取openId, sessionKey, unionId

getCode: function () {
      var that = this;
      // 登录
      wx.login({
        success: function (res) {
          // 发送 res.code 到后台换取 openId, sessionKey, unionId
          if (res.code) {
            console.log('获取用户登录态success!' + res.code)
            that.paypay(that, res.code);
          } else {
            console.log('获取用户登录态失败!' + res.errMsg)
          }
        }
      })
    },

paypay(that, code) {
      var token = wx.getStorageSync('token');
      var memberId = wx.getStorageSync('memberId');
      var memberMoney = wx.getStorageSync('memberMoney')
      console.log(code);
      console.log("token" + token)
      console.log(memberMoney)
      wx.request({
        url: url,
        data: {
          code: code,
          memberId: memberId,
          price: memberMoney
        },
        header: { 
          'content-type': 'application/x-www-form-urlencoded',
          'Authorization': "Bearer " + token
        },
        success: function (res) {
          console.log(res);
          var result = JSON.parse(res.data.data)
          wx.requestPayment({
            'timeStamp': result.timeStamp,
            'nonceStr': result.nonceStr,
            'package': result.package,
            'signType': 'MD5',
            'paySign': result.sign,
            'success': function (res) {
              if (res.errMsg == "requestPayment:ok") {
                that.setData({ showWhich: 2 })
              }
            },
            'fail': function (res) {
            }
          })
        },
      })
    },

后台

Controller

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.wxpay.sdk.WXPayUtil;
import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;

/**
 * @date 2019/9/27
 * <p>
 * 微信小程序接口
 */
@RestController
@AllArgsConstructor
@RequestMapping("/wechatApplet")
@Api(value = "wechatApplet", description = "微信小程序接口")
public class WechatAppletController {

    /**
     * 通过code获取用户openId
     * @param code 前台wx.login获取到code
     */
    @GetMapping("/paypay")
    public R doUnifiedOrder(String code,String memberId,String price) {
        Map<String,String> map = WechatApplet.pay(code,price);
        String results = map.get("results");
        //更新订单,在数据库中先添加订单状态为未支付,在支付回调中订单状态改为已支付
        String orderNo = map.get("out_trade_no");
        BigDecimal money = new BigDecimal(price);
        //设置小数位数,第一个变量是小数位数,第二个变量是取舍方法(四舍五入)
        money = money.setScale(2, BigDecimal.ROUND_HALF_UP);
        BigDecimal fen = new BigDecimal(0.01);
        money = money.multiply(fen);
        MemberOrder order= new MemberOrder();
        order.setOrderNo(orderNo);
        order.setPrice(money);
        order.setPaymentMethod(Constant.PAY_WX);
        order.setStatus(Constant.ORDER_STATUS_TOPAY);
        order.setAmount(money);
        order.setOrderType("1");
        order.setIsCouponed("0");
        order.setCouponId("");
        order.setMemberId(memberId);
        memberOrderService.save(order);
        return new R<>(results);
    }

    /**
     * 支付回调
     * @param request
     * @param response
     * @throws InterruptedException
     */
    @PostMapping("/notify")
    public synchronized void notify(HttpServletRequest request, HttpServletResponse response) throws InterruptedException{
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream)request.getInputStream()));
            String line = null;
            StringBuilder sb = new StringBuilder();
            while((line = br.readLine()) != null){
                sb.append(line);
            }
            br.close();
            //sb为微信返回的xml
            String notityXml = sb.toString();
            String resXml = "";
            Map map = WXPayUtil.xmlToMap(notityXml);
            String returnCode = (String) map.get("return_code");
            String orderNO = map.get("out_trade_no").toString();
            if("SUCCESS".equals(returnCode)){
                //验证签名是否正确
                Map<String, String> validParams = WechatAppletUtil.paraFilter(map);  //回调验签时需要去除sign和空值参数
                String validStr = WechatAppletUtil.createLinkString(validParams);//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
                String sign = WechatAppletUtil.sign(validStr, WechatAppletConfig.API_KEY, "utf-8").toUpperCase();//拼装生成服务器端验证的签名
                //根据微信官网的介绍,此处不仅对回调的参数进行验签,还需要对返回的金额与系统订单的金额进行比对等
                if(sign.equals(map.get("sign"))){
                    //此处为业务逻辑
                    
                    resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                            + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                } else {
                    System.out.println("微信支付回调失败!签名不一致");
                }
            }else{
                memberOrder.setStatus(Constant.ORDER_STATUS_PAYFAIL);
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
            }
            System.out.println(resXml);
            System.out.println("微信支付回调数据结束");
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            out.write(resXml.getBytes());
            out.flush();
            out.close();
        } catch (Exception e) {
//            log.error(EPayError.PAY_NOTIFY_ERROR.getName(),orderId,e);
        }
    }

}

 

WechatApplet

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * 微信小程序支付
 */
public class WechatApplet {

    /**
     * 小程序支付
     * @param code 前台wx.login获取到code
     * @return String
     */
    public static Map<String,String> pay(String code,String price) {
        //登录凭证校验
        String requestUrl = "https://api.weixin.qq.com/sns/jscode2session?appid="+ WechatAppletConfig.APPID +"&secret="+ WechatAppletConfig.APP_SECRET +"&js_code="+ code +"&grant_type=authorization_code";
        JSONObject jsonObject = PayCommonUtil.httpsRequest(requestUrl,"POST");
        String openid = jsonObject.get("openid").toString();
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//        String reqStr = getReqStr(openid);
        //组装预下单的请求数据
        Map<String,String> map = getReqStr(openid,price);
        String reqStr = map.get("req_body");
        String results = PayCommonUtil.httpsRequest(url,"POST",reqStr);//发送post数据到微信预下单
        Map<String,String> return_data = null;
        try {
            return_data = WXPayUtil.xmlToMap(results);//微信的一个工具类
        } catch (Exception e) {
            e.printStackTrace();
        }
        String return_code = return_data.get("return_code");
        if("SUCCESS".equals(return_code)){
            String prepay_id = return_data.get("prepay_id");
            //组装返回数据
            results = conPayParam(prepay_id);
        }else{
            results ="{\"return_code\":\"fail\"}";
        }
        map.put("results",results);
        return map;
    }

    //组装预下单的请求数据
    public static Map<String,String> getReqStr(String openid,String price){
        //获取本机的ip地址
        InetAddress addr = null;
        try {
            addr = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        String spbill_create_ip = addr.getHostAddress();
        Map<String,String> data = new HashMap<String,String>();
        String out_trade_no = setTradeNo();//
        data.put("appid", WechatAppletConfig.APPID);
        data.put("mch_id",WechatAppletConfig.MCH_ID);
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        data.put("sign_type", WechatAppletConfig.SIGN_TYPE);
        data.put("body", "");
        data.put("out_trade_no", out_trade_no);
        data.put("device_info", "");
        data.put("fee_type", "CNY");
        data.put("total_fee", price);
        data.put("spbill_create_ip", spbill_create_ip);
        data.put("notify_url", WechatAppletConfig.NOTIFY_URL);
        data.put("trade_type", "JSAPI");
        data.put("product_id", "12");
        data.put("openid", openid);
        try {
            String sign = WXPayUtil.generateSignature(data, WechatAppletConfig.API_KEY, WXPayConstants.SignType.MD5);
            data.put("sign", sign);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String reqBody = null;
        try {
            reqBody = WXPayUtil.mapToXml(data);
            data.put("req_body",reqBody);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return data;
    }

    //保证唯一
    public static String setTradeNo(){
        SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
        String newDate = sdf.format(new Date());
        String result="";
        Random random=new Random();
        for(int i=0;i<3;i++){
            result+=random.nextInt(10);
        }
        String orderid =  newDate + result;
        return orderid;
    }

    //组装返回客户端的请求数据       
    public static String conPayParam(String prepayid){
        Long timeStamp = System.currentTimeMillis() / 1000;
        Map<String,String> map = new HashMap<String,String>();
        map.put("appId", WechatAppletConfig.APPID);
        LocalDateTime time = LocalDateTime.now();
        map.put("timeStamp", timeStamp + "");
        map.put("nonceStr", WXPayUtil.generateNonceStr());
        map.put("package", "prepay_id=" + prepayid);
        map.put("signType", "MD5");
        String sign;
        try {
            sign = WXPayUtil.generateSignature(map, WechatAppletConfig.API_KEY, WXPayConstants.SignType.MD5);
            map.put("sign", sign);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return JSON.toJSONString(map);
    }
}

 

WechatAppletUtil

import org.apache.commons.codec.digest.DigestUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

public class WechatAppletUtil {
    /**
  * 签名字符串
  * @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.md5Hex(getContentBytes(text, input_charset));
    }
     /**
   * 签名字符串
   *  @param text 需要签名的字符串
   * @param sign 签名结果
   * @param key 密钥
   * @param input_charset 编码格式
   * @return 签名结果
   */
     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);
          }
     }
 
     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;
     }
     /**
   *
   * @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();
     }
     public static String urlEncodeUTF8(String source){
          String result=source;
          try {
               result=java.net.URLEncoder.encode(source, "UTF-8");
          } catch (UnsupportedEncodingException e) {
           // TODO Auto-generated catch block
               e.printStackTrace();
          }
          return result;
     }
     /**
   * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
   * @param strxml
   * @return
   * @throws JDOMException
   * @throws 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());
     }
}

 

WechatAppletConfig

/**
 * 微信小程序相关配置
 */
public class WechatAppletConfig {
    /**
     * APPID
     */
    public final static String APPID = "";
    /**
     * 商户号
     */
    public final static String MCH_ID = "";
    /**
     * AppSecret
     */
    public final static String APP_SECRET = "";
    /**
     * API密钥
     */
    public final static String API_KEY = "";
    /**
     * 签名加密方式
     */
    public final static String SIGN_TYPE = "MD5";
    /**
     * 价格(分)
     */
    public final static String MONEY = "1";
    /**

     * 内网穿透软件natapp
     * 支付回调地址(可先内网穿透,进行测试)
     */
    public final static String NOTIFY_URL = "http://vnrzu9.natappfree.cc/controller/wechatApplet/notify";
}

 

PayCommonUtil

import com.alibaba.fastjson.JSONObject;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.math.BigDecimal;
import java.net.ConnectException;
import java.net.URL;
import java.util.*;

public class PayCommonUtil {


    /**
     * 发送https请求
     * @param requestUrl 请求地址
     * @param requestMethod 请求方式(GET、POST)
     * @param outputStr 提交的数据
     * @return 返回微信服务器响应的信息
     */
    public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
        try {
            // 创建SSLContext对象,并使用我们指定的信任管理器初始化
            TrustManager[] tm = { new MyX509TrustManager() };
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();
            URL url = new URL(requestUrl);
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            //conn.setSSLSocketFactory(ssf);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            // 设置请求方式(GET/POST)
            conn.setRequestMethod(requestMethod);
            conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
            // 当outputStr不为null时向输出流写数据
            if (null != outputStr) {
                OutputStream outputStream = conn.getOutputStream();
                // 注意编码格式
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }
            // 从输入流读取返回内容
            InputStream inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            // 释放资源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            conn.disconnect();
            return buffer.toString();
        } catch (ConnectException ce) {
//          log.error("连接超时:{}", ce);
        } catch (Exception e) {
//          log.error("https请求异常:{}", e);
        }
        return null;
    }

    /**
     * 发送https请求
     * 
     * @param requestUrl 请求地址
     * @param requestMethod 请求方式(GET、POST)
     * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
     */
     public static JSONObject httpsRequest(String requestUrl, String requestMethod) {
            JSONObject jsonObject = null;
            try {
                    // 创建SSLContext对象,并使用我们指定的信任管理器初始化
                    TrustManager[] tm = { new MyX509TrustManager() };
                    SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
                    sslContext.init(null, tm, new java.security.SecureRandom());
                    // 从上述SSLContext对象中得到SSLSocketFactory对象
                    SSLSocketFactory ssf = sslContext.getSocketFactory();
                    URL url = new URL(requestUrl);
                    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
                    //conn.setSSLSocketFactory(ssf);
                    conn.setDoOutput(true);
                    conn.setDoInput(true);
                    conn.setUseCaches(false);
                    conn.setConnectTimeout(3000);
                    // 设置请求方式(GET/POST)
                    conn.setRequestMethod(requestMethod);
                    //conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); 
                    // 当outputStr不为null时向输出流写数据
                    // 从输入流读取返回内容
                    InputStream inputStream = conn.getInputStream();
                    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
                    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
                    String str = null;
                    StringBuffer buffer = new StringBuffer();
                    while ((str = bufferedReader.readLine()) != null) {
                            buffer.append(str);
                    }
                    // 释放资源
                    bufferedReader.close();
                    inputStreamReader.close();
                    inputStream.close();
                    inputStream = null;
                    conn.disconnect();
                    jsonObject = JSONObject.parseObject(buffer.toString());
            } catch (ConnectException ce) {
//                    log.error("连接超时:{}", ce);
            } catch (Exception e) {
                    System.out.println(e);
//                    log.error("https请求异常:{}", e);
            }
            return jsonObject;
}

}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值